diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index fc10b62..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: 🐞 Bug Report -description: Template for reporting a bug -labels: ["bug"] -body: - - type: textarea - id: what_happens - attributes: - label: What happens? - description: Describe the current behavior clearly - placeholder: The app crashes when... - validations: - required: true - - - type: textarea - id: expected - attributes: - label: What did you expect to happen? - description: Describe what the correct behavior should be - placeholder: I expected that... - validations: - required: true - - - type: textarea - id: steps - attributes: - label: Steps to reproduce - description: Provide the steps needed to reproduce the issue - placeholder: | - 1. Open the app - 2. Click on "X" - 3. ... - validations: - required: false - - - type: textarea - id: environment - attributes: - label: Environment - description: Provide details about your system - placeholder: | - - OS: - - App version: - - Device: - validations: - required: false - - - type: textarea - id: logs - attributes: - label: Logs / screenshots (optional) - description: Attach logs, screenshots, or error messages if possible diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index dcbc2d2..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: ✨ Feature Request -description: Request a new feature or improvement -labels: ["enhancement"] -body: - - type: textarea - id: description - attributes: - label: Feature description - description: Explain what you would like to add - placeholder: I would like to... - validations: - required: true - - - type: textarea - id: reason - attributes: - label: Why is this useful? - description: Explain the problem this feature solves or the benefit it provides - placeholder: This feature would help because... - validations: - required: true - - - type: textarea - id: alternatives - attributes: - label: Alternatives considered - description: Mention if you tried other solutions - placeholder: I tried... - validations: - required: false - - - type: textarea - id: environment - attributes: - label: Environment (if relevant) - description: Provide system info if the feature is OS/version dependent - placeholder: | - - OS: - - App Version: - validations: - required: false - - - type: dropdown - id: priority - attributes: - label: Priority - description: How important is this feature for you? - options: - - Low - - Medium - - High - validations: - required: false - - - type: textarea - id: screenshots - attributes: - label: Screenshots or mockups (optional) - description: Add any images that help explain the feature - - - type: textarea - id: notes - attributes: - label: Additional notes (optional) - description: Anything else you'd like to add diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 61539f8..0000000 --- a/.gitignore +++ /dev/null @@ -1,40 +0,0 @@ -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -# https://github.com/github/gitignore/blob/master/Global/Xcode.gitignore - -# Mac OS X -*.DS_Store - -## Build generated -build/ -DerivedData/ -*.swp - -## Various settings -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xccheckout -*.xcscmblueprint - -# Files containing "*.local*" will not be committed -*.local* - -# Fastlane -fastlane/Preview.html -fastlane/screenshots/**/*.png -fastlane/test_output -fastlane/report.xml -fastlane/README.md - -notes.txt diff --git a/.periphery.yml b/.periphery.yml deleted file mode 100644 index 304ab71..0000000 --- a/.periphery.yml +++ /dev/null @@ -1,11 +0,0 @@ -project: ./VisualDiffer.xcworkspace -retain_objc_accessible: true -retain_public: true -schemes: -- VisualDiffer -exclude_tests: true -index_exclude: - - "**/NSEvent+VirtualKeys.swift" -report_exclude: - - "**/SharedKit/**" - - "**/SharedUI/**" \ No newline at end of file diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index e650c01..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -3.2.9 diff --git a/.swiftformat b/.swiftformat deleted file mode 100644 index d58edfb..0000000 --- a/.swiftformat +++ /dev/null @@ -1,18 +0,0 @@ -# version - ---swiftversion 6.2 ---disable sortImports ---disable wrapMultilineStatementBraces - -# format options - -# rules - ---enable redundantProperty ---enable wrapConditionalBodies ---enable wrapEnumCases - ---wrap-arguments before-first ---wrap-collections before-first - ---trailing-commas collections-only \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml deleted file mode 100644 index 4dc454e..0000000 --- a/.swiftlint.yml +++ /dev/null @@ -1,142 +0,0 @@ -excluded: - - Sources/*/FileSystemTestHelper.swift - - Tests/*/*.swift - -opt_in_rules: - - array_init - - attributes - - closure_end_indentation - - closure_spacing - - collection_alignment - - colon # promote to error - - conditional_returns_on_newline - - contains_over_filter_count - - contains_over_first_not_nil - - contains_over_range_nil_comparison - - convenience_type - - direct_return - - discouraged_assert - - discouraged_none_name - - discouraged_object_literal - - empty_collection_literal - - empty_count - - empty_string - - enum_case_associated_values_count - - fatal_error_message - - file_name - - file_name_no_space - - file_types_order - - final_test_case - - first_where - - force_unwrapping - - function_default_parameter_at_end - - implicitly_unwrapped_optional - - last_where - - legacy_random - - literal_expression_end_indentation - - multiline_arguments - - multiline_arguments_brackets - - multiline_function_chains - - multiline_literal_brackets - - multiline_parameters - - multiline_parameters_brackets - - nslocalizedstring_key - - operator_usage_whitespace - - overridden_super_call - - pattern_matching_keywords - - prefer_self_type_over_type_of_self - - redundant_nil_coalescing - - redundant_type_annotation - - return_value_from_void_function - - strict_fileprivate - - toggle_bool - - trailing_closure - - unneeded_parentheses_in_closure_argument - - vertical_whitespace_closing_braces - - vertical_whitespace_opening_braces - - yoda_condition - -disabled_rules: - - cyclomatic_complexity - - todo - - type_body_length - - trailing_comma - - discarded_notification_center_observer - - notification_center_detachment - - orphaned_doc_comment - -identifier_name: - allowed_symbols: [_] - excluded: - - i - - l - - r - - p - - x - - y - - ch - - fm - - li - - re - - vi - -# exclude names using in generics -type_name: - excluded: - - T - -analyzer_rules: - - unused_import - -attributes: - always_on_same_line: - - "@inline" - - "@MainActor" - - "@objc" - - "@nonobjc" - - "@Test" - -force_cast: warning -force_try: warning - -function_body_length: - warning: 100 - -legacy_hashing: error - -line_length: - ignores_urls: true - ignores_function_declarations: true - ignores_comments: true - ignores_interpolated_strings: true - excluded_lines_patterns: - # String format - - 'NSLocalizedString\(' - - 'String\.localizedStringWithFormat\(' - - 'String\(format' - - 'format: ".*",$' - - 'fatalError\(' - - 'NSLog\(' - - 'print\(' - - 'NSPredicate\(format' - - # Constraints - - 'constraint\(equalTo' - - # App Specific - - 'folder-\d+(-open)?' - - 'FileSizeFormatter\.default' - -multiline_arguments: - first_argument_location: next_line - only_enforce_after_first_closure_on_first_line: true - -private_over_fileprivate: - validate_extensions: true - -trailing_whitespace: - ignores_empty_lines: false - ignores_comments: true - -vertical_whitespace: - max_empty_lines: 2 diff --git a/AppConfig-Sparkle.xcconfig b/AppConfig-Sparkle.xcconfig deleted file mode 100644 index 1570879..0000000 --- a/AppConfig-Sparkle.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -// -// AppConfig-Sparkle.xcconfig -// VisualDiffer -// -// Created by davide ficano on 04/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -#include "AppConfig.xcconfig" - -SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Sparkle] = SPARKLE_ENABLED - -INFOPLIST_FILE[config=Sparkle] = ./VisualDiffer-Sparkle-Info.plist -CODE_SIGN_ENTITLEMENTS[config=Sparkle] = ./VisualDiffer-Sparkle.entitlements - diff --git a/AppConfig.xcconfig b/AppConfig.xcconfig deleted file mode 100644 index d20ce64..0000000 --- a/AppConfig.xcconfig +++ /dev/null @@ -1,29 +0,0 @@ -// -// AppConfig.xcconfig -// VisualDiffer -// -// Created by davide ficano on 01/02/22. -// Copyright (c) 2022 visualdiffer.com -// - -PRODUCT_BUNDLE_IDENTIFIER = com.visualdiffer - -ENABLE_HARDENED_RUNTIME = YES -// Ensure Sparkle works on Debug -ENABLE_HARDENED_RUNTIME[config=Debug] = NO - -ENABLE_USER_SELECTED_FILES = readwrite -ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES - -REGISTER_APP_GROUPS = YES - -ENABLE_APP_SANDBOX = YES -ENABLE_APP_SANDBOX[config=DebugNoSandbox] = NO - -LD_RUNPATH_SEARCH_PATHS=@executable_path/../Frameworks - -INFOPLIST_FILE = ./VisualDiffer-Info.plist - -#include "Signing.local.xcconfig" -#include "Versions.local.xcconfig" -#include "Warnings.xcconfig" diff --git a/AppDefaults/VDDefaults.plist b/AppDefaults/VDDefaults.plist deleted file mode 100644 index 048a5ac..0000000 --- a/AppDefaults/VDDefaults.plist +++ /dev/null @@ -1,58 +0,0 @@ - - - - - alwaysResolveSymlinks - - confirmCopy - - confirmMove - - confirmDelete - - includeFilteredItems - - stopLongOperation - - showInFinderNotVisibleFiles - - followSymLinks - - folderViewDateFormat - ddMMyyHHmmss - timestampToleranceSeconds - 0 - skipPackages - - hideEmptyFolders - - comparatorFlags - 129 - traverseFilteredFolders - - fileInfoFlags - 0 - expandAllFolders - - showWhitespaces - - showLineFilterType - 0 - showRecentDocumentsList - - foldersDifferenceNavigatorTraverseFolders - - foldersDifferenceNavigatorWrap - - foldersDifferenceNavigatorCenterInWindow - - comparatorBinaryBufferSize - 2097152 - filesStatusBarShowMessageTimeout - 3 - tabWidth - 4 - ignoreLineEndings - - - diff --git a/AppDefaults/VisualDiffer.sdef b/AppDefaults/VisualDiffer.sdef deleted file mode 100644 index 09117b3..0000000 --- a/AppDefaults/VisualDiffer.sdef +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/AppDefaults/colors.json b/AppDefaults/colors.json deleted file mode 100644 index 69d3a5b..0000000 --- a/AppDefaults/colors.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "menuColorsMap": { - "openWithDefaultAppContext": { - "textColor":"#0000ff" - } - }, - "consoleLogColorsMap": - { - "info": { - "textColor":"textForeground", - "backgroundColor":"textBackground" - }, - "warning": { - "textColor":"#967e35", - "backgroundColor":"textBackground" - }, - "error": { - "textColor":"#FF0000", - "backgroundColor":"textBackground" - } - }, - "folderColorsMap": - { - "old": { - "textColor":"#000", - "backgroundColor":"#edeeed" - }, - - "newer": { - "textColor":"#da0000", - "backgroundColor":"#ead8f6" - }, - - "orphan": { - "textColor": "#002a90", - "backgroundColor":"#dde7f7" - }, - - "changed": { - "textColor": "#da0000", - "backgroundColor":"#ffd3cc" - }, - - "same": { - "textColor": "#190000", - "backgroundColor":"#00000000" - }, - - "folder": { - "textColor": "#190000", - "backgroundColor":"#00000000" - }, - - "subFoldersSize": { - "textColor": "#9A9A9A", - "backgroundColor":"#00000000" - }, - - "filtered": { - "textColor": "#4a7c59", - "backgroundColor":"#e4f1d5" - }, - - "mismatchingTags": { - "textColor":"#AA0D91", - "backgroundColor":"#00000000" - }, - - "mismatchingLabels": { - "textColor":"#AA0D91", - "backgroundColor":"#00000000" - }, - - "selectedRow": { - "textColor": "#00000000", - "backgroundColor":"#CCFFCC" - } - }, - "fileColorsMap": - { - "changed": { - "textColor": "#4a7c59", - "backgroundColor":"#e4f1d5" - }, - - "added": { - "textColor": "#0000FF", - "backgroundColor":"#DCE5F5" - }, - - "deleted": { - "textColor": "#da0000", - "backgroundColor":"#ffd3cc" - }, - - "same": { - "textColor":"textForeground", - "backgroundColor":"textBackground" - }, - - "missing": { - "textColor": "#FFFFFFFF", - "backgroundColor":"#000000" - }, - - "merged": { - "textColor": "#000000", - "backgroundColor":"#FFCC00" - }, - - "thumbnail": { - "textColor": "#00000000", - "backgroundColor":"#FFFFFFFF" - }, - - "positionBox": { - "textColor": "#00000000", - "backgroundColor":"#FF9900" - }, - - "selectedRow": { - "textColor": "#00000000", - "backgroundColor":"#CCFFCC" - }, - - "sectionSeparatorLine": { - "textColor": "#000000", - "backgroundColor":"#00000000" - }, - - "lineNumber": { - "textColor": "#444444", - "backgroundColor":"#00000000" - }, - - "lineNumberSeparator": { - "textColor": "#aaaaaa", - "backgroundColor":"#00000000" - } - } -} diff --git a/AppDefaults/colorsDark.json b/AppDefaults/colorsDark.json deleted file mode 100644 index 6f95e05..0000000 --- a/AppDefaults/colorsDark.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "menuColorsMap": { - "openWithDefaultAppContext": { - "textColor":"#FF9300" - } - }, - "consoleLogColorsMap": - { - "info": { - "textColor":"textForeground", - "backgroundColor":"textBackground" - }, - "warning": { - "textColor":"#967e35", - "backgroundColor":"textBackground" - }, - "error": { - "textColor":"#ed3930", - "backgroundColor":"textBackground" - } - }, - "folderColorsMap": - { - "old": { - "textColor":"#A7A7A7", - "backgroundColor":"#00000000" - }, - - "newer": { - "textColor": "#f78c6c", - "backgroundColor":"#00000000" - }, - - "orphan": { - "textColor": "#82aaff", - "backgroundColor":"#00000000" - }, - - "changed": { - "textColor": "#ff4545", - "backgroundColor":"#00000000" - }, - - "same": { - "textColor":"textForeground", - "backgroundColor":"textBackground" - }, - - "folder": { - "textColor":"textForeground", - "backgroundColor":"textBackground" - }, - - "subFoldersSize": { - "textColor":"#9A9A9A", - "backgroundColor":"#00000000" - }, - - "filtered": { - "textColor":"#A8C023", - "backgroundColor":"#00000000" - }, - - "mismatchingTags": { - "textColor":"#f39c12", - "backgroundColor":"#00000000" - }, - - "mismatchingLabels": { - "textColor":"#f39c12", - "backgroundColor":"#00000000" - }, - - "selectedRow": { - "textColor": "#00000000", - "backgroundColor":"#525146" - } - }, - "fileColorsMap": - { - "changed": { - "textColor": "#C4EFB7", - "backgroundColor":"#5B8B6D" - }, - - "added": { - "textColor": "#70D7EA", - "backgroundColor":"#577271" - }, - - "deleted": { - "textColor": "#e92875", - "backgroundColor":"#FFD0D0" - }, - - "same": { - "textColor":"#FFFFFF", - "backgroundColor":"#00000000" - }, - - "missing": { - "textColor": "#555555", - "backgroundColor":"#FFFFFF" - }, - - "merged": { - "textColor": "#000000", - "backgroundColor":"#FFCC00" - }, - - "thumbnail": { - "textColor": "#00000000", - "backgroundColor":"#555555" - }, - - "positionBox": { - "textColor": "#00000000", - "backgroundColor":"#FF9900" - }, - - "selectedRow": { - "textColor": "#00000000", - "backgroundColor":"#525146" - }, - - "sectionSeparatorLine": { - "textColor": "#ffffff", - "backgroundColor":"#00000000" - }, - - "lineNumber": { - "textColor": "textForeground", - "backgroundColor":"#00000000" - }, - - "lineNumberSeparator": { - "textColor": "#ffffff", - "backgroundColor":"#00000000" - } - } -} diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib deleted file mode 100644 index f2f9b3b..0000000 --- a/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,468 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -UmVwb3J0IGEgYnVnGw - - - - - - - - - - - - - - - - - diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..4e3f91c --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +app.visualdiffer.com diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 21b9c31..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,35 +0,0 @@ -## 🛠️ Build - -Clone the repository and build the app: - -```bash -git clone https://github.com//visualdiffer.git -cd visualdiffer - -# copy files to local usage -cp ./Signing-Template.xcconfig ./Signing.local.xcconfig -cp ./Versions-Template.xcconfig ./Versions.local.xcconfig - -cp ./Signing-Template.xcconfig ./visdiff/Signing.local.xcconfig -cp ./Versions-Template.xcconfig ./visdiff/Versions.local.xcconfig - -cp ./Signing-Template.xcconfig ./Tests/Signing.local.xcconfig -cp ./Versions-Template.xcconfig ./Tests/Versions.local.xcconfig - -``` - -Open in Xcode - -## 👥Contributing - -Contributions, issues, and feature requests are welcome! -To contribute: - -1. Install [swiftformat](https://github.com/nicklockwood/SwiftFormat) and [swiftlint](https://github.com/realm/SwiftLint) -2. Fork the repository -2. Create a new branch (`feature/xyz` or `fix/abc`) -3. Run `./scripts/lint.sh` to apply `swiftformat` and `swiftlint` -3. Commit your changes with clear messages -4. Open a pull request describing your update - -⚠️ Please follow the existing code style and include tests or examples when possible. diff --git a/Gemfile b/Gemfile deleted file mode 100644 index e6e90e8..0000000 --- a/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source "https://rubygems.org" - -gem "fastlane" -gem "kramdown" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 3aec890..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,237 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.8) - abbrev (0.1.2) - addressable (2.8.8) - public_suffix (>= 2.0.2, < 8.0) - artifactory (3.0.17) - atomos (0.1.3) - aws-eventstream (1.4.0) - aws-partitions (1.1197.0) - aws-sdk-core (3.240.0) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.992.0) - aws-sigv4 (~> 1.9) - base64 - bigdecimal - jmespath (~> 1, >= 1.6.1) - logger - aws-sdk-kms (1.118.0) - aws-sdk-core (~> 3, >= 3.239.1) - aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.208.0) - aws-sdk-core (~> 3, >= 3.234.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.5) - aws-sigv4 (1.12.1) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - base64 (0.2.0) - bigdecimal (4.0.1) - claide (1.1.0) - colored (1.2) - colored2 (3.1.2) - commander (4.6.0) - highline (~> 2.0.0) - csv (3.3.5) - declarative (0.0.20) - digest-crc (0.7.0) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.6.20240107) - dotenv (2.8.1) - emoji_regex (3.2.3) - excon (0.112.0) - faraday (1.10.4) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.8) - faraday (>= 0.8.0) - http-cookie (>= 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.1) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.1.1) - multipart-post (~> 2.0) - faraday-net_http (1.0.2) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.1) - faraday (~> 1.0) - fastimage (2.4.0) - fastlane (2.230.0) - CFPropertyList (>= 2.3, < 4.0.0) - abbrev (~> 0.1.2) - addressable (>= 2.8, < 3.0.0) - artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - base64 (~> 0.2.0) - bundler (>= 1.12.0, < 3.0.0) - colored (~> 1.2) - commander (~> 4.6) - csv (~> 3.3) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - fastlane-sirp (>= 1.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.3) - google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-env (>= 1.6.0, < 2.0.0) - google-cloud-storage (~> 1.31) - highline (~> 2.0) - http-cookie (~> 1.0.5) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - logger (>= 1.6, < 2.0) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (>= 2.0.0, < 3.0.0) - mutex_m (~> 0.3.0) - naturally (~> 2.2) - nkf (~> 0.2.0) - optparse (>= 0.1.1, < 1.0.0) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.5) - simctl (~> 1.6.3) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (~> 3) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.4.1) - xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - fastlane-sirp (1.0.0) - sysrandom (~> 1.0) - gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.54.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.3) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.a) - rexml - google-apis-iamcredentials_v1 (0.17.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-playcustomapp_v1 (0.13.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.31.0) - google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.8.0) - google-cloud-env (>= 1.0, < 3.a) - google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.5.0) - google-cloud-storage (1.47.0) - addressable (~> 2.8) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.31.0) - google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) - mini_mime (~> 1.0) - googleauth (1.8.1) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (>= 0.16, < 2.a) - highline (2.0.3) - http-cookie (1.0.8) - domain_name (~> 0.5) - httpclient (2.9.0) - mutex_m - jmespath (1.6.2) - json (2.18.0) - jwt (2.10.2) - base64 - kramdown (2.5.1) - rexml (>= 3.3.9) - logger (1.7.0) - mini_magick (4.13.2) - mini_mime (1.1.5) - multi_json (1.18.0) - multipart-post (2.4.1) - mutex_m (0.3.0) - nanaimo (0.4.0) - naturally (2.3.0) - nkf (0.2.0) - optparse (0.8.1) - os (1.1.4) - plist (3.7.2) - public_suffix (7.0.0) - rake (13.3.1) - representable (3.2.0) - declarative (< 0.1.0) - trailblazer-option (>= 0.1.1, < 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.4.4) - rouge (3.28.0) - ruby2_keywords (0.0.5) - rubyzip (2.4.1) - security (0.1.5) - signet (0.21.0) - addressable (~> 2.8) - faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 4.0) - multi_json (~> 1.10) - simctl (1.6.10) - CFPropertyList - naturally - sysrandom (1.0.5) - terminal-notifier (2.0.0) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - trailblazer-option (0.1.2) - tty-cursor (0.7.1) - tty-screen (0.8.2) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - uber (0.1.0) - unicode-display_width (2.6.0) - word_wrap (1.0.0) - xcodeproj (1.27.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.4.0) - rexml (>= 3.3.6, < 4.0) - xcpretty (0.4.1) - rouge (~> 3.28.0) - xcpretty-travis-formatter (1.0.1) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - arm64-darwin-24 - ruby - -DEPENDENCIES - fastlane - kramdown - -BUNDLED WITH - 2.7.2 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f288702..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/Models/History.xcdatamodeld/History.xcdatamodel/contents b/Models/History.xcdatamodeld/History.xcdatamodel/contents deleted file mode 100644 index f9fabb6..0000000 --- a/Models/History.xcdatamodeld/History.xcdatamodel/contents +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Models/MyDocument.xcdatamodeld/.xccurrentversion b/Models/MyDocument.xcdatamodeld/.xccurrentversion deleted file mode 100644 index 45d9e53..0000000 --- a/Models/MyDocument.xcdatamodeld/.xccurrentversion +++ /dev/null @@ -1,8 +0,0 @@ - - - - - _XCCurrentVersionName - MyDocument1.4.xcdatamodel - - diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.0.xcdatamodel/elements b/Models/MyDocument.xcdatamodeld/MyDocument1.0.xcdatamodel/elements deleted file mode 100644 index af68fbb..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.0.xcdatamodel/elements and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.0.xcdatamodel/layout b/Models/MyDocument.xcdatamodeld/MyDocument1.0.xcdatamodel/layout deleted file mode 100644 index ca50ae6..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.0.xcdatamodel/layout and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.1.xcdatamodel/elements b/Models/MyDocument.xcdatamodeld/MyDocument1.1.xcdatamodel/elements deleted file mode 100644 index a7eb8a4..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.1.xcdatamodel/elements and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.1.xcdatamodel/layout b/Models/MyDocument.xcdatamodeld/MyDocument1.1.xcdatamodel/layout deleted file mode 100644 index e8fe24b..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.1.xcdatamodel/layout and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.2.xcdatamodel/elements b/Models/MyDocument.xcdatamodeld/MyDocument1.2.xcdatamodel/elements deleted file mode 100644 index 72678ee..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.2.xcdatamodel/elements and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.2.xcdatamodel/layout b/Models/MyDocument.xcdatamodeld/MyDocument1.2.xcdatamodel/layout deleted file mode 100644 index af7a0d9..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.2.xcdatamodel/layout and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.3.xcdatamodel/elements b/Models/MyDocument.xcdatamodeld/MyDocument1.3.xcdatamodel/elements deleted file mode 100644 index 7bcfcc8..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.3.xcdatamodel/elements and /dev/null differ diff --git a/Models/MyDocument.xcdatamodeld/MyDocument1.4.xcdatamodel/elements b/Models/MyDocument.xcdatamodeld/MyDocument1.4.xcdatamodel/elements deleted file mode 100644 index 4faa433..0000000 Binary files a/Models/MyDocument.xcdatamodeld/MyDocument1.4.xcdatamodel/elements and /dev/null differ diff --git a/README.md b/README.md deleted file mode 100644 index 90159d4..0000000 --- a/README.md +++ /dev/null @@ -1,129 +0,0 @@ -

- -

-

- - - - - -

- -# VisualDiffer - -## Overview -**VisualDiffer** is a macOS application designed to **visually compare folders and files** with clarity and speed. -It lets you instantly see what has changed between two directories — new, modified, or missing files — through a clean side-by-side interface. -The app helps developers, designers, and anyone managing multiple versions of projects to easily identify differences, filter unwanted files, and synchronize content more efficiently. - -### Key Features -- 🟩 **Side-by-side folder comparison** — instantly highlights differences between directories (added, removed, or modified files). -- 🧩 **File-level diff view** — inspect detailed content changes line-by-line (for supported file types). -- 🧹 **Powerful filters** — exclude version control, backup, or temporary files (e.g., `.git`, `.svn`, `.zip`, `.DS_Store`). -- 🖱️ **Drag & drop support** — compare folders by simply dragging them into the app window. -- 📦 **Export and automation** — integrate comparisons into scripts or workflows using CLI tools (if available). -- ⚡ **Fast comparison engine** — optimized to handle large folder structures efficiently. - -For more information, visit the [VisualDiffer Wiki](https://wiki.visualdiffer.com/). - -

- -

- ---- - -## ⚡ Caveats - -> [!WARNING] -> This is a port of the original project written in Objective-C. -> The Swift code was rewritten from scratch without using conversions made by AI models. -> Maximum care was taken in rewriting the code, but regressions or new bugs are possible. -> ---- - -## 📦 Installation - -> [!NOTE] -> The installed application: -> -> - is notarized -> - is sandboxed -> -> **Notarization** is Apple's automated security check for macOS apps. -> -> **Sandboxing** restricts what an app can access on your Mac, for example the application can only access files/folders the user explicitly grants access to. - -### From Homebrew - -You can install visualdiffer using homebrew with this command: - -```bash -brew install visualdiffer -``` - -### From GitHub Releases - -Download from [releases](https://github.com/visualdiffer/visualdiffer/releases/latest), unzip, and drag the app to Applications folder - -## 🛠️ Build - -Clone the repository and build the app: - -```bash -git clone https://github.com//visualdiffer.git -cd visualdiffer - -# copy files to local usage -cp ./Signing-Template.xcconfig ./Signing.local.xcconfig -cp ./Versions-Template.xcconfig ./Versions.local.xcconfig - -cp ./Signing-Template.xcconfig ./visdiff/Signing.local.xcconfig -cp ./Versions-Template.xcconfig ./visdiff/Versions.local.xcconfig - -cp ./Signing-Template.xcconfig ./Tests/Signing.local.xcconfig -cp ./Versions-Template.xcconfig ./Tests/Versions.local.xcconfig - -``` - -Open in Xcode - ---- - -## 👷‍♂️Contributors - -Thanks to everyone who has helped improve **VisualDiffer**! - -| Name | Role | -|------|:------| -| **[Davide Ficano](https://github.com/dafi)** | Creator & Maintainer | -| Pablo J. Malacara | Application Icon | -| [Aan/Petruknisme](https://github.com/aancw) | [Homebrew](https://brew.sh/) integration | - -## 👥Contributing - -Contributions, issues, and feature requests are welcome! -To contribute: - -1. Install [swiftformat](https://github.com/nicklockwood/SwiftFormat) and [swiftlint](https://github.com/realm/SwiftLint) -2. Fork the repository -2. Create a new branch (`feature/xyz` or `fix/abc`) -3. Run `./scripts/lint.sh` to apply `swiftformat` and `swiftlint` -3. Commit your changes with clear messages -4. Open a pull request describing your update - -⚠️ Please follow the existing code style and include tests or examples when possible. - ---- - -## License - -Released under the **GPL3 License**. -See [`LICENSE`](./LICENSE) for details. - ---- - -## Acknowledgments - -VisualDiffer was inspired by the need for a fast, reliable, and elegant folder comparison tool for macOS. -Thanks to all contributors, testers, and users who continue to improve the project. diff --git a/Signing-Template.xcconfig b/Signing-Template.xcconfig deleted file mode 100644 index 4cd6095..0000000 --- a/Signing-Template.xcconfig +++ /dev/null @@ -1,20 +0,0 @@ -// -// Signing.xcconfig -// VisualDiffer -// -// Created by davide ficano on 19/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -// Copy this file to Signing-local.xcconfig and edit the values -// The `*-local*` file will not be committed - -DEVELOPMENT_TEAM = - -PROVISIONING_PROFILE_SPECIFIER = - -CODE_SIGN_STYLE = Automatic - -CODE_SIGN_ENTITLEMENTS[config=Debug] = VisualDiffer.entitlements -CODE_SIGN_ENTITLEMENTS[config=Release] = VisualDiffer.entitlements -CODE_SIGN_ENTITLEMENTS[config=DebugNoSandbox] = ./VisualDifferTests/NoSandbox.entitlements diff --git a/Sources/App/AppDelegate.swift b/Sources/App/AppDelegate.swift deleted file mode 100644 index f5143f1..0000000 --- a/Sources/App/AppDelegate.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// AppDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 30/07/11. -// Copyright (c) 2011 visualdiffer.com -// - -@main -class AppDelegate: NSObject, NSApplicationDelegate { - #if SPARKLE_ENABLED - private var appUpdater = AppUpdater() - #endif - private var appearanceObservation: NSKeyValueObservation? - - func applicationWillFinishLaunching(_: Notification) { - initDefaults() - NSAppearance.change() - // ensure the colors are correctly updated after appearance change - CommonPrefs.shared.appearanceChanged(postNotification: true) - } - - func applicationDidFinishLaunching(_: Notification) { - appearanceObservation = NSApp.observe(\.effectiveAppearance, options: [.new]) { app, _ in - CommonPrefs.shared.appearanceChanged(postNotification: true, app) - ColoredFoldersManager.shared.refresh() - } - #if SPARKLE_ENABLED - appUpdater.configure() - #endif - } - - func applicationWillTerminate(_: Notification) { - appearanceObservation?.invalidate() - appearanceObservation = nil - } - - func applicationOpenUntitledFile(_: NSApplication) -> Bool { - NSDocumentController.shared.newDocument(self) - return true - } - - @objc func openWiki(_: AnyObject) { - if let url = URL(string: "https://wiki.visualdiffer.com") { - NSWorkspace.shared.open(url) - } - } - - @objc func bugReport(_: AnyObject) { - if let url = URL(string: "https://bugs.visualdiffer.com") { - NSWorkspace.shared.open(url) - } - } - - private func initDefaults() { - if let defaultsPath = Bundle.main.url(forResource: "VDDefaults", withExtension: "plist"), - let data = try? Data(contentsOf: defaultsPath), - let defaultsDict = try? PropertyListSerialization.propertyList( - from: data, - options: [], - format: nil - ) as? [String: Any] { - UserDefaults.standard.register(defaults: defaultsDict) - } - } -} diff --git a/Sources/App/AppUpdater.swift b/Sources/App/AppUpdater.swift deleted file mode 100644 index e0d4f9c..0000000 --- a/Sources/App/AppUpdater.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// AppUpdater.swift -// VisualDiffer -// -// Created by davide ficano on 03/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -#if SPARKLE_ENABLED - import Sparkle - - class AppUpdater { - private var updaterController = SPUStandardUpdaterController( - startingUpdater: true, - updaterDelegate: nil, - userDriverDelegate: nil - ) - - @MainActor func configure() { - guard let mainMenu = NSApp.mainMenu, - let applicationMenu = mainMenu.item(withTag: MainMenu.application.rawValue)?.submenu else { - return - } - let checkForUpdatesItem = NSMenuItem( - title: NSLocalizedString("Check for Updates…", comment: ""), - action: #selector(SPUStandardUpdaterController.checkForUpdates(_:)), - keyEquivalent: "" - ) - checkForUpdatesItem.target = updaterController - applicationMenu.insertItem(checkForUpdatesItem, at: 1) - } - } - -#endif diff --git a/Sources/App/Logger+App.swift b/Sources/App/Logger+App.swift deleted file mode 100644 index 4f4554d..0000000 --- a/Sources/App/Logger+App.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Logger+App.swift -// VisualDiffer -// -// Created by davide ficano on 17/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -extension Logger { - static let subsystem = Bundle.main.bundleIdentifier ?? "visualdiffer" - - static let general = Logger(subsystem: subsystem, category: "General") - static let ui = Logger(subsystem: subsystem, category: "UI") // swiftlint:disable:this identifier_name - static let fs = Logger(subsystem: subsystem, category: "FileSystem") // swiftlint:disable:this identifier_name - - #if DEBUG - static let debug = Logger(subsystem: subsystem, category: "Debug") - #endif -} diff --git a/Sources/Core/Common/AlignRule.swift b/Sources/Core/Common/AlignRule.swift deleted file mode 100644 index 1fbfb11..0000000 --- a/Sources/Core/Common/AlignRule.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// AlignRule.swift -// VisualDiffer -// -// Created by davide ficano on 08/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct AlignTemplateOptions: OptionSet { - let rawValue: UInt - - static let caseInsensitive = AlignTemplateOptions(rawValue: 1 << 0) -} - -typealias AlignRegExp = AlignRule.Pair -typealias AlignTemplate = AlignRule.Pair - -public struct AlignRule { - struct Pair { - let pattern: String - let options: T - } - - enum Key: String { - case regExp = "leftExpression" - case template = "rightExpression" - case regExpOptions = "leftOptions" - case templateOptions = "rightOptions" - } - - let regExp: AlignRegExp - let template: AlignTemplate - - init( - regExp: AlignRegExp, - template: AlignTemplate - ) { - self.regExp = regExp - self.template = template - } - - init?(_ dictionary: [String: Any]) { - guard let regExp = AlignRegExp(dictionary), - let template = AlignTemplate(dictionary) else { - return nil - } - self.init(regExp: regExp, template: template) - } -} - -extension AlignRule { - func toDictionary() -> [String: Any] { - [ - Key.regExp.rawValue: regExp.pattern, - Key.regExpOptions.rawValue: regExp.options.rawValue, - Key.template.rawValue: template.pattern, - Key.templateOptions.rawValue: template.options.rawValue, - ] - } - - /// Check if the `lhs` string matches the `rhs` using this rule - /// - lhs: the left string - /// - rhs: the right string - /// - Returns: `true` if the strings match,`false` otherwise - func matches( - name lhs: String, - with rhs: String - ) -> Bool { - guard let result = regExp.regularExpression()?.firstMatch( - in: lhs, - options: [], - range: NSRange(location: 0, length: lhs.count) - ) else { - return false - } - let replaced = lhs.replace( - template: template.pattern, - result: result - ) - if template.options.contains(.caseInsensitive) { - return rhs.hasPrefix(replaced, ignoreCase: true) - } - return rhs.hasPrefix(replaced) - } -} - -extension AlignRule.Pair where T == NSRegularExpression.Options { - private nonisolated(unsafe) static var cachedRegExpressions = [String: NSRegularExpression]() - - init() { - self.init(pattern: "", options: []) - } - - init?(_ dictionary: [String: Any]) { - guard let pattern = dictionary[AlignRule.Key.regExp.rawValue] as? String else { - return nil - } - let options = if let rawValue = dictionary[AlignRule.Key.regExpOptions.rawValue] as? UInt { - NSRegularExpression.Options(rawValue: rawValue) - } else { - dictionary[AlignRule.Key.regExpOptions.rawValue] as? NSRegularExpression.Options ?? [] - } - - self.init( - pattern: pattern, - options: options - ) - } - - func regularExpression() -> NSRegularExpression? { - let key = String(format: "%@%ld", pattern, options.rawValue) - var cachedRE = Self.cachedRegExpressions[key] - if cachedRE == nil { - cachedRE = try? NSRegularExpression( - pattern: pattern, - options: options - ) - if let cachedRE { - Self.cachedRegExpressions[key] = cachedRE - } - } - return cachedRE - } -} - -extension AlignRule.Pair where T == AlignTemplateOptions { - init() { - self.init(pattern: "", options: []) - } - - init?(_ dictionary: [String: Any]) { - guard let pattern = dictionary[AlignRule.Key.template.rawValue] as? String else { - return nil - } - let options = if let rawValue = dictionary[AlignRule.Key.templateOptions.rawValue] as? UInt { - AlignTemplateOptions(rawValue: rawValue) - } else { - dictionary[AlignRule.Key.templateOptions.rawValue] as? AlignTemplateOptions ?? [] - } - - self.init( - pattern: pattern, - options: options - ) - } -} diff --git a/Sources/Core/Common/ColorScheme.swift b/Sources/Core/Common/ColorScheme.swift deleted file mode 100644 index a56fcf4..0000000 --- a/Sources/Core/Common/ColorScheme.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// ColorScheme.swift -// VisualDiffer -// -// Created by davide ficano on 24/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -struct ColorSet { - enum Key: String { - case text = "textColor" - case background = "backgroundColor" - } - - let text: NSColor - let background: NSColor -} - -struct ColorScheme { - // periphery:ignore - let name: String - let colors: [String: ColorSet] - - init(name: String, definitions: [String: [String: String]]) { - self.name = name - colors = definitions.mapValues { - ColorSet(colors: $0) - } - } - - subscript(key: String) -> ColorSet? { - colors[key] - } -} - -extension ColorSet { - init(colors: [String: String]) { - var tempText = NSColor.black - var tempBackground = NSColor.white - - for (key, colorString) in colors { - let color = Self.resolveColor(from: colorString) - - switch ColorSet.Key(rawValue: key) { - case .text: - tempText = color - case .background: - tempBackground = color - default: - Logger.general.error("Found invalid color type: \(key)") - } - } - - text = tempText - background = tempBackground - } - - static func resolveColor(from colorString: String) -> NSColor { - switch colorString { - case "textForeground": - NSColor.textColor - case "textBackground": - NSColor.textBackgroundColor - default: - NSColor.colorFromHexRGBA(colorString) ?? NSColor.black - } - } -} diff --git a/Sources/Core/Common/DisplayPositionable.swift b/Sources/Core/Common/DisplayPositionable.swift deleted file mode 100644 index d2eab60..0000000 --- a/Sources/Core/Common/DisplayPositionable.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// DisplayPositionable.swift -// VisualDiffer -// -// Created by davide ficano on 12/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -public enum DisplaySide: Int, Sendable { - case left - case right -} - -protocol DisplayPositionable: AnyObject { - var side: DisplaySide { get set } -} diff --git a/Sources/Core/Common/ImageNames.swift b/Sources/Core/Common/ImageNames.swift deleted file mode 100644 index 7f20399..0000000 --- a/Sources/Core/Common/ImageNames.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// ImageNames.swift -// VisualDiffer -// -// Created by davide ficano on 06/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -let VDImageNameCopyLeft = NSImage.Name("copyl") -let VDImageNameCopyRight = NSImage.Name("copyr") -let VDImageNameSyncLeft = NSImage.Name("syncl") -let VDImageNameSyncRight = NSImage.Name("syncr") -let VDImageNameSyncBoth = NSImage.Name("syncBoth") -let VDImageNameMoveLeft = NSImage.Name("movel") -let VDImageNameMoveRight = NSImage.Name("mover") -let VDImageNameDelete = NSImage.Name("deleteRed") -let VDImageNameEmpty = NSImage.Name("empty") -let VDImageNameCopyLinesLeft = NSImage.Name("copyLinesl") -let VDImageNameCopyLinesRight = NSImage.Name("copyLinesr") -let VDImageNameDateTime = NSImage.Name("datetime") -let VDImageNameCollapse = NSImage.Name("collapse") -let VDImageNameExpand = NSImage.Name("expand") -let VDImageNameFilter = NSImage.Name("filter") -let VDImageNameNext = NSImage.Name("next") -let VDImageNamePrev = NSImage.Name("prev") -let VDImageNameRefresh = NSImage.Name("refresh") -let VDImageNamePreferences = NSImage.Name("sessionPreferences") -let VDImageNameFinder = NSImage.Name("finder") -let VDImageNameOpenWith = NSImage.Name("openWith") -let VDImageNameStop = NSImage.Name("stop") -let VDImageNameComparisonMethod = NSImage.Name("comparisonMethod") -let VDImageNameRewind = NSImage.Name("rewind") -let VDImageNameTop = NSImage.Name("top") -let VDImageNameBottom = NSImage.Name("bottom") -let VDImageNameBrowse = NSImage.Name("browse") -let VDImageNameSave = NSImage.Name("save") -let VDImageNameNextFile = NSImage.Name("file_next") -let VDImageNamePrevFile = NSImage.Name("file_prev") -let VDImageNameWordWrapOn = NSImage.Name("wordWrapOn") -let VDImageNameWordWrapOff = NSImage.Name("wordWrapOff") diff --git a/Sources/Core/Common/KeyEquivalent.swift b/Sources/Core/Common/KeyEquivalent.swift deleted file mode 100644 index 1b6c0df..0000000 --- a/Sources/Core/Common/KeyEquivalent.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// KeyEquivalent.swift -// VisualDiffer -// -// Created by davide ficano on 13/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -// swiftlint:disable identifier_name -@objc class KeyEquivalent: NSObject { - @objc static let leftArrow = "\u{2190}" - @objc static let rightArrow = "\u{2192}" - @objc static let upArrow = "\u{2191}" - @objc static let downArrow = "\u{2193}" - - @objc static let deleteBackspace = "\u{0008}" - @objc static let forwardDelete = "\u{007F}" - - @objc static let enter = "\r" - @objc static let escape = "\u{001B}" - @objc static let tab = "\t" - - @objc static let f1 = "\u{F704}" - @objc static let f2 = "\u{F705}" - @objc static let f3 = "\u{F706}" - @objc static let f4 = "\u{F707}" - @objc static let f5 = "\u{F708}" - @objc static let f6 = "\u{F709}" - @objc static let f7 = "\u{F70A}" - @objc static let f8 = "\u{F70B}" - @objc static let f9 = "\u{F70C}" - @objc static let f10 = "\u{F70D}" - @objc static let f11 = "\u{F70E}" - @objc static let f12 = "\u{F70F}" -} - -// swiftlint:enable identifier_name diff --git a/Sources/Core/Common/NSImage+SymbolCompat.swift b/Sources/Core/Common/NSImage+SymbolCompat.swift deleted file mode 100644 index 92431c8..0000000 --- a/Sources/Core/Common/NSImage+SymbolCompat.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// NSImage+SymbolCompat.swift -// VisualDiffer -// -// Created by davide ficano on 13/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Foundation - -@objc extension NSImage { - static func imageSymbolCompat(_ name: NSImage.Name) -> NSImage? { - if #available(macOS 11.0, *) { - if let symbolInfo = symbolMap[name] { - return NSImage(systemSymbolName: symbolInfo[0], accessibilityDescription: symbolInfo[1]) - } - } - return NSImage(named: name) - } - - private static let symbolMap: [NSImage.Name: [String]] = [ - NSImage.preferencesGeneralName: ["gearshape", "Preferences"], - NSImage.fontPanelName: ["textformat.size", "Fonts"], - NSImage.Name("prefs_text"): ["doc.plaintext", "Text Preferences"], - NSImage.Name("prefs_paths"): ["lock.open", "Trusted Paths"], - NSImage.Name("prefs_folder"): ["folder", "Folder"], - NSImage.Name("prefs_confirmations"): ["exclamationmark.triangle", "Confirmations"], - NSImage.Name("prefs_keyboard"): ["command", "Keyboard"], - ] -} diff --git a/Sources/Core/Common/NotificationCenter+Helper.swift b/Sources/Core/Common/NotificationCenter+Helper.swift deleted file mode 100644 index b1c4aaa..0000000 --- a/Sources/Core/Common/NotificationCenter+Helper.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// NotificationCenter+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 09/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -enum FileSavedKey: String, Hashable { - case leftPath - case rightPath -} - -enum PrefChangedKey: String, Hashable { - struct Target: RawRepresentable { - var rawValue: String - - static let folder = Target(rawValue: "folder") - static let file = Target(rawValue: "file") - } - - case target - case font - case encoding - case tabWidth -} - -extension Notification.Name { - static let prefsChanged = NSNotification.Name("com.visualdiffer.notification.prefsChanges") - static let appAppearanceDidChange = NSNotification.Name("com.visualdiffer.notification.appAppearanceDidChange") - static let fileSaved = NSNotification.Name("com.visualdiffer.notification.fileSaved") -} - -extension NotificationCenter { - func postPrefsChanged(userInfo: [AnyHashable: Any]? = nil) { - post( - name: .prefsChanged, - object: nil, - userInfo: userInfo - ) - } -} diff --git a/Sources/Core/Common/ViewLinkable.swift b/Sources/Core/Common/ViewLinkable.swift deleted file mode 100644 index 2fd33ec..0000000 --- a/Sources/Core/Common/ViewLinkable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// ViewLinkable.swift -// VisualDiffer -// -// Created by davide ficano on 12/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -protocol ViewLinkable { - associatedtype T - @MainActor var linkedView: T? { get set } -} diff --git a/Sources/Core/CommonPrefs/CommonPrefs+FileCompare.swift b/Sources/Core/CommonPrefs/CommonPrefs+FileCompare.swift deleted file mode 100644 index e4ff641..0000000 --- a/Sources/Core/CommonPrefs/CommonPrefs+FileCompare.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// CommonPrefs+FileCompare.swift -// VisualDiffer -// -// Created by davide ficano on 10/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -let defaultTabWidth = 4 - -enum FileColorAttribute: String { - case added - case changed - case deleted - case lineNumber - case lineNumberSeparator - case merged - case missing - case positionBox - case same - case sectionSeparatorLine - case selectedRow - case thumbnail -} - -extension CommonPrefs { - var tabWidth: Int { - get { - let value = integer(forKey: .tabWidth) - return value < 0 ? defaultTabWidth : value - } - - set { - set(newValue, forKey: .tabWidth) - NotificationCenter.default.postPrefsChanged(userInfo: [ - PrefChangedKey.target: PrefChangedKey.Target.file, - PrefChangedKey.tabWidth: newValue, - ]) - } - } - - var defaultEncoding: String.Encoding { - get { - if object(forKey: .defaultEncoding) == nil { - let value = integer(forKey: .defaultEncoding) - return value < 0 ? .utf8 : String.Encoding(rawValue: UInt(value)) - } - return .utf8 - } - - set { - set(newValue.rawValue, forKey: .defaultEncoding) - NotificationCenter.default.postPrefsChanged(userInfo: [ - PrefChangedKey.target: PrefChangedKey.Target.file, - PrefChangedKey.encoding: newValue.rawValue, - ]) - } - } - - var hideFileDiffDetails: Bool { - get { bool(forKey: .hideFileDiffDetails) } - set { set(newValue, forKey: .hideFileDiffDetails) } - } - - func fileColor(_ name: FileColorAttribute) -> ColorSet? { - guard let scheme = colorSchemeMap[CommonPrefs.Name.fileColorsMap.rawValue], - let colorSet = scheme[name.rawValue] else { - return nil - } - return colorSet - } -} diff --git a/Sources/Core/CommonPrefs/CommonPrefs+FolderCompare.swift b/Sources/Core/CommonPrefs/CommonPrefs+FolderCompare.swift deleted file mode 100644 index 90e919d..0000000 --- a/Sources/Core/CommonPrefs/CommonPrefs+FolderCompare.swift +++ /dev/null @@ -1,176 +0,0 @@ -// -// CommonPrefs+FolderCompare.swift -// VisualDiffer -// -// Created by davide ficano on 10/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -let defaultComparatorBinaryBufferSize = 2 * 1024 * 1024 - -enum FolderColorAttribute: String { - case unknown - case orphan - case old - case newer - case changed - case same - case folder - case subFoldersSize - case filtered - case mismatchingLabels - case mismatchingTags - case selectedRow -} - -extension CommonPrefs { - // MARK: - Comparator - - var comparatorOptions: ComparatorOptions { - get { - var flags = if let number = object(forKey: .comparatorFlags) as? NSNumber { - ComparatorOptions(number: number) - } else { - ComparatorOptions.timestamp - } - if flags.onlyAlignFlags.isEmpty { - flags.insert(.alignMatchCase) - } - return flags - } - - set { - set(newValue.rawValue, forKey: .comparatorFlags) - NotificationCenter.default.post(name: .prefsChanged, object: "comparatorFlags") - } - } - - var comparatorWithoutMethod: ComparatorOptions { - get { - comparatorOptions.onlyMethodFlags - } - - set { - comparatorOptions = comparatorOptions.changeWithoutMethod(newValue) - } - } - - var finderLabel: Bool { - get { - comparatorOptions.hasFinderLabel - } - - set { - comparatorOptions = comparatorOptions.changeFinderLabel(newValue) - } - } - - var finderTags: Bool { - get { - comparatorOptions.hasFinderTags - } - - set { - comparatorOptions = comparatorOptions.changeFinderTags(newValue) - } - } - - // MARK: - DisplayOptions - - var displayOptions: DisplayOptions { - get { - if let value = object(forKey: .displayFilters) as? NSNumber { - DisplayOptions(number: value) - } else { - .showAll - } - } - - set { - set(newValue.rawValue, forKey: .displayFilters) - NotificationCenter.default.post(name: .prefsChanged, object: "displayFilters") - } - } - - var displayFiltersWithoutMethod: DisplayOptions { - get { - displayOptions.onlyMethodFlags - } - - set { - displayOptions = displayOptions.changeWithoutMethod(newValue) - } - } - - // MARK: - FileExtraOptions - - var fileExtraOptions: FileExtraOptions { - get { - FileExtraOptions(rawValue: integer(forKey: .fileInfoFlags)) - } - - set { - set(newValue.rawValue, forKey: .fileInfoFlags) - } - } - - // MARK: - Filters - - var defaultFileFilters: String? { - get { - object(forKey: .defaultFileFilters) as? String - } - - set { - if let newValue { - set(newValue, forKey: .defaultFileFilters) - } else { - removeObject(forKey: .defaultFileFilters) - } - } - } - - var alwaysResolveSymlinks: Bool { - get { bool(forKey: .alwaysResolveSymlinks) } - set { set(newValue, forKey: .alwaysResolveSymlinks) } - } - - var followSymLinks: Bool { - get { bool(forKey: .followSymLinks) } - set { set(newValue, forKey: .followSymLinks) } - } - - var comparatorBinaryBufferSize: Int { - get { - let value = number(forKey: .comparatorBinaryBufferSize, defaultComparatorBinaryBufferSize).intValue - return value < 0 ? defaultComparatorBinaryBufferSize : value - } - - set { - set(newValue, forKey: .comparatorBinaryBufferSize) - } - } - - var showNotificationWhenWindowIsOnFront: Bool { - get { bool(forKey: .showNotificationWhenWindowIsOnFront) } - set { set(newValue, forKey: .showNotificationWhenWindowIsOnFront) } - } - - var folderViewDateFormat: String { - get { string(forKey: .folderViewDateFormat) ?? "ddMMyyHHmmss" } - set { set(newValue, forKey: .folderViewDateFormat) } - } - - var hideEmptyFolders: Bool { - get { bool(forKey: .hideEmptyFolders) } - set { set(newValue, forKey: .hideEmptyFolders) } - } - - func folderColor(_ name: FolderColorAttribute) -> ColorSet? { - guard let scheme = colorSchemeMap[CommonPrefs.Name.folderColorsMap.rawValue], - let colorSet = scheme[name.rawValue] else { - return nil - } - return colorSet - } -} diff --git a/Sources/Core/CommonPrefs/CommonPrefs+Font.swift b/Sources/Core/CommonPrefs/CommonPrefs+Font.swift deleted file mode 100644 index 71e9dd9..0000000 --- a/Sources/Core/CommonPrefs/CommonPrefs+Font.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// CommonPrefs+Font.swift -// VisualDiffer -// -// Created by davide ficano on 09/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension CommonPrefs { - func restoreDefaultFonts() { - removeObject(forKey: .folderListingFont) - removeObject(forKey: .fileTextFont) - - folderListingFont = defaultFolderListingFont() - fileTextFont = defaultFileTextFont() - } - - func defaultFolderListingFont() -> NSFont { - NSFontManager.shared.font( - withFamily: "Lucida Grande", - traits: [], - weight: 0, - size: 11.0 - ) - ?? NSFont.systemFont(ofSize: 11.0) - } - - func defaultFileTextFont() -> NSFont { - NSFontManager.shared.font( - withFamily: "monaco", - traits: [], - weight: 0, - size: 11.0 - ) - ?? NSFont.monospacedSystemFont(ofSize: 11.0, weight: .regular) - } - - var folderListingFont: NSFont { - get { - guard let font = font(forKey: .folderListingFont) else { - return defaultFolderListingFont() - } - return font - } - - set { - save(newValue, forKey: .folderListingFont) - NotificationCenter.default.postPrefsChanged(userInfo: [ - PrefChangedKey.target: PrefChangedKey.Target.folder, - PrefChangedKey.font: true, - ]) - } - } - - var fileTextFont: NSFont { - get { - guard let font = font(forKey: .fileTextFont) else { - return defaultFolderListingFont() - } - return font - } - - set { - save(newValue, forKey: .fileTextFont) - NotificationCenter.default.postPrefsChanged(userInfo: [ - PrefChangedKey.target: PrefChangedKey.Target.file, - PrefChangedKey.font: true, - ]) - } - } - - func font(forKey key: CommonPrefs.Name) -> NSFont? { - if let descriptorData = UserDefaults.standard.data(forKey: key.rawValue), - let fontDescriptor = try? NSKeyedUnarchiver.unarchivedObject( - ofClass: NSFontDescriptor.self, - from: descriptorData - ), - let font = NSFont(descriptor: fontDescriptor, size: 0) { - return font - } - return nil - } - - func save(_ font: NSFont, forKey key: CommonPrefs.Name) { - let descriptorData = try? NSKeyedArchiver.archivedData( - withRootObject: font.fontDescriptor, - requiringSecureCoding: false - ) - UserDefaults.standard.setValue(descriptorData, forKeyPath: key.rawValue) - } -} diff --git a/Sources/Core/CommonPrefs/CommonPrefs+Helper.swift b/Sources/Core/CommonPrefs/CommonPrefs+Helper.swift deleted file mode 100644 index b4dd50e..0000000 --- a/Sources/Core/CommonPrefs/CommonPrefs+Helper.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// CommonPrefs+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 09/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension CommonPrefs { - // periphery:ignore - func number(forKey: CommonPrefs.Name, _ defaultValue: Bool) -> NSNumber { - object(forKey: forKey) as? NSNumber ?? NSNumber(value: defaultValue) - } - - func number(forKey: CommonPrefs.Name, _ defaultValue: Int) -> NSNumber { - object(forKey: forKey) as? NSNumber ?? NSNumber(value: defaultValue) - } - - func bool(forKey key: CommonPrefs.Name) -> Bool { - UserDefaults.standard.bool(forKey: key.rawValue) - } - - func string(forKey key: CommonPrefs.Name) -> String? { - UserDefaults.standard.string(forKey: key.rawValue) - } - - func set(_ value: some Any, forKey key: CommonPrefs.Name) { - UserDefaults.standard.set(value, forKey: key.rawValue) - } - - func integer(forKey key: CommonPrefs.Name) -> Int { - UserDefaults.standard.integer(forKey: key.rawValue) - } - - func object(forKey key: CommonPrefs.Name) -> Any? { - UserDefaults.standard.object(forKey: key.rawValue) - } - - func removeObject(forKey key: CommonPrefs.Name) { - UserDefaults.standard.removeObject(forKey: key.rawValue) - } -} diff --git a/Sources/Core/CommonPrefs/CommonPrefs+Name.swift b/Sources/Core/CommonPrefs/CommonPrefs+Name.swift deleted file mode 100644 index e0e2851..0000000 --- a/Sources/Core/CommonPrefs/CommonPrefs+Name.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// CommonPrefs+Name.swift -// VisualDiffer -// -// Created by davide ficano on 09/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -public extension CommonPrefs { - struct Name: Hashable, Sendable { - let rawValue: String - - static let confirmShowInFinderNotVisibleFiles = Name(rawValue: "showInFinderNotVisibleFiles") - static let confirmReloadFiles = Name(rawValue: "reloadFiles") - static let confirmStopLongOperation = Name(rawValue: "stopLongOperation") - static let confirmShowInFinder = Name(rawValue: "showInFinder") - static let confirmCopy = Name(rawValue: "confirmCopy") - static let confirmDelete = Name(rawValue: "confirmDelete") - static let confirmMove = Name(rawValue: "confirmMove") - static let confirmIncludeFilteredItems = Name(rawValue: "includeFilteredItems") - static let confirmDontAskToSaveSession = Name(rawValue: "dontAskToSaveSession") - - static let colorsConfigPath = Name(rawValue: "colorsConfigPath") - static let escCloseWindow = Name(rawValue: "escCloseWindow") - - static let hideEmptyFolders = Name(rawValue: "hideEmptyFolders") - static let alwaysResolveSymlinks = Name(rawValue: "alwaysResolveSymlinks") - static let followSymLinks = Name(rawValue: "followSymLinks") - static let showNotificationWhenWindowIsOnFront = Name(rawValue: "showNotificationWhenWindowIsOnFront") - - static let folderListingFont = Name(rawValue: "folderListingFont") - static let comparatorFlags = Name(rawValue: "comparatorFlags") - static let comparatorBinaryBufferSize = Name(rawValue: "comparatorBinaryBufferSize") - static let displayFilters = Name(rawValue: "displayFilters") - static let defaultFileFilters = Name(rawValue: "defaultFileFilters") - static let folderViewDateFormat = Name(rawValue: "folderViewDateFormat") - static let fileInfoFlags = Name(rawValue: "fileInfoFlags") - static let traverseFilteredFolders = Name(rawValue: "traverseFilteredFolders") - static let expandAllFolders = Name(rawValue: "expandAllFolders") - - static let skipPackages = Name(rawValue: "skipPackages") - static let timestampToleranceSeconds = Name(rawValue: "timestampToleranceSeconds") - - static let folderColorsMap = Name(rawValue: "folderColorsMap") - - static let fileTextFont = Name(rawValue: "fileTextFont") - - static let tabWidth = Name(rawValue: "tabWidth") - static let defaultEncoding = Name(rawValue: "defaultEncoding") - static let fileColorsMap = Name(rawValue: "fileColorsMap") - static let hideFileDiffDetails = Name(rawValue: "hideFileDiffDetails") - - // These are not simple boolean flags - static let virtualResourceFork = Name(rawValue: "virtualResfork") - static let virtualFinderLabel = Name(rawValue: "virtualFinderLabel") - static let virtualFinderTags = Name(rawValue: "virtualFinderTags") - static let virtualComparatorWithoutMethod = Name(rawValue: "virtualComparatorWithoutMethod") - static let virtualDisplayFiltersWithoutMethod = Name(rawValue: "virtualDisplayFiltersWithoutMethod") - static let virtualAlignFlags = Name(rawValue: "VirtualAlignFlags") - static let virtualAlignRules = Name(rawValue: "VirtualAlignRules") - - init(rawValue: String) { - self.rawValue = rawValue - } - } -} diff --git a/Sources/Core/CommonPrefs/CommonPrefs.swift b/Sources/Core/CommonPrefs/CommonPrefs.swift deleted file mode 100644 index 43f52ca..0000000 --- a/Sources/Core/CommonPrefs/CommonPrefs.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// CommonPrefs.swift -// VisualDiffer -// -// Created by davide ficano on 30/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -private let darkColorSchemeFileName: String = "colorsDark" -private let lightColorSchemeFileName: String = "colors" - -public class CommonPrefs: @unchecked Sendable { - public static let shared = CommonPrefs() - - private(set) var colorSchemeMap = [String: ColorScheme]() - - private init() { - loadColors() - } - - func appearanceChanged( - postNotification: Bool, - _ object: Any? = nil - ) { - loadColors() - if postNotification { - NotificationCenter.default.post( - name: .appAppearanceDidChange, - object: object - ) - } - } - - // MARK: - Colors - - func loadColors() { - guard let colorsConfigPath = colorsConfigPath ?? defaultColorsConfigPath else { - fatalError("No color config file found") - } - guard let configColorMap = readColorConfig(colorsConfigPath) else { - fatalError("Can't read color config") - } - - colorSchemeMap.removeAll() - - for (schemeName, definitions) in configColorMap { - if let definitions = definitions as? [String: [String: String]] { - colorSchemeMap[schemeName] = ColorScheme(name: schemeName, definitions: definitions) - } - } - } - - var colorsConfigPath: String? { - string(forKey: .colorsConfigPath) - } - - var defaultColorsConfigPath: String? { - Bundle.main.path( - forResource: NSAppearance.isDarkMode ? darkColorSchemeFileName : lightColorSchemeFileName, - ofType: "json" - ) - } - - private func readColorConfig(_ configPath: String) -> [String: Any]? { - do { - return try readJSON(configPath) as? [String: Any] - } catch { - Logger.general.error("Error while loading colors from \(configPath), error \(error.localizedDescription). Try to load default colors config") - // fallback to defaults - if let path = defaultColorsConfigPath { - return try? readJSON(path) as? [String: Any] - } - } - return nil - } - - func readJSON(_ configPath: String) throws -> Any { - guard let stream = InputStream(fileAtPath: configPath) else { - throw FileError.openFile(path: configPath) - } - defer { - stream.close() - } - stream.open() - - return try JSONSerialization.jsonObject(with: stream) - } -} diff --git a/Sources/Core/Document/DiffOpenerDelegate.swift b/Sources/Core/Document/DiffOpenerDelegate.swift deleted file mode 100644 index b9578aa..0000000 --- a/Sources/Core/Document/DiffOpenerDelegate.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// DiffOpenerDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 27/09/15. -// Copyright (c) 2015 visualdiffer.com -// - -public typealias DiffOpenerDelegateBlock = (String?, String?) -> Bool - -/// -/// Who start comparison, generally a folder view controller opening a file comparison document -/// -@MainActor public protocol DiffOpenerDelegate: AnyObject { - func addChildDocument(_ document: VDDocument) - func removeChildDocument(_ document: VDDocument) - - /// - /// Open the successive file listed on opener window - /// Parameters: - /// - leftPath: the left path to search - /// - ritghtPath:the right path to search - /// - block: return true if the passed paths are used, false otherwise. - /// If paths are not found both `leftPath` and `rightPath` are nil - func nextDifferenceFiles(from leftPath: String?, rightPath: String?, block: DiffOpenerDelegateBlock) - - func prevDifferenceFiles(from leftPath: String?, rightPath: String?, block: DiffOpenerDelegateBlock) -} diff --git a/Sources/Core/Document/DocumentError.swift b/Sources/Core/Document/DocumentError.swift deleted file mode 100644 index 9c986e4..0000000 --- a/Sources/Core/Document/DocumentError.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// DocumentError.swift -// VisualDiffer -// -// Created by davide ficano on 26/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -enum DocumentError: Error { - case invalidSessionData - case unknownSessionType -} - -extension DocumentError: LocalizedError { - var errorDescription: String? { - switch self { - case .invalidSessionData: - NSLocalizedString("Invalid session data; maybe the file is corrupted", comment: "") - case .unknownSessionType: - NSLocalizedString("Session type is unknown", comment: "") - } - } -} diff --git a/Sources/Core/Document/SessionDiff.ItemType+Paths.swift b/Sources/Core/Document/SessionDiff.ItemType+Paths.swift deleted file mode 100644 index 3fcbee1..0000000 --- a/Sources/Core/Document/SessionDiff.ItemType+Paths.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// SessionDiff.ItemType+Paths.swift -// VisualDiffer -// -// Created by davide ficano on 03/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension SessionDiff.ItemType { - /** - * Throw error if paths aren't both of same type (both folders or both files) or don't exist - */ - func checkPaths(leftPath: String, rightPath: String) throws { - switch self { - case .folder: - try checkFolders(leftPath: leftPath, rightPath: rightPath) - case .file: - try checkFiles(leftPath: leftPath, rightPath: rightPath) - } - } - - func checkFolders(leftPath: String, rightPath: String) throws { - let fileManager = FileManager.default - var isDir = ObjCBool(false) - var invalidPath: String? - - // show error only for last invalid path - if !fileManager.fileExists(atPath: leftPath, isDirectory: &isDir) || !isDir.boolValue { - invalidPath = leftPath - } - - if !fileManager.fileExists(atPath: rightPath, isDirectory: &isDir) || !isDir.boolValue { - invalidPath = rightPath - } - - if let invalidPath { - throw SessionTypeError.invalidItem(path: invalidPath, isFolder: true) - } - } - - func checkFiles(leftPath: String, rightPath: String) throws { - let fileManager = FileManager.default - var invalidPath: String? - - if !leftPath.isEmpty, !fileManager.fileExists(atPath: leftPath) { - invalidPath = leftPath - } - if !rightPath.isEmpty, !fileManager.fileExists(atPath: rightPath) { - invalidPath = rightPath - } - if let invalidPath { - throw SessionTypeError.invalidItem(path: invalidPath, isFolder: false) - } else if leftPath.isEmpty, rightPath.isEmpty { - throw SessionTypeError.invalidAllItems(isFolder: false) - } - } -} diff --git a/Sources/Core/Document/SessionTypeError.swift b/Sources/Core/Document/SessionTypeError.swift deleted file mode 100644 index add620d..0000000 --- a/Sources/Core/Document/SessionTypeError.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// SessionTypeError.swift -// VisualDiffer -// -// Created by davide ficano on 08/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -enum SessionTypeError: Error { - case invalidItem(path: String, isFolder: Bool) - case invalidAllItems(isFolder: Bool) - case unknownError -} - -extension SessionTypeError: LocalizedError { - var errorDescription: String? { - switch self { - case let .invalidItem(path, isFolder): - if isFolder { - String.localizedStringWithFormat( - NSLocalizedString("The path '%@' no longer exists or isn't a valid folder", comment: ""), path - ) - } else { - String.localizedStringWithFormat( - NSLocalizedString("The path '%@' no longer exists or isn't a valid file", comment: ""), path - ) - } - case let .invalidAllItems(isFolder): - isFolder - // swiftlint:disable:next void_function_in_ternary - ? NSLocalizedString("The specified paths no longer exist or aren't both folders", comment: "") - : NSLocalizedString("The specified paths no longer exist or aren't both files", comment: "") - case .unknownError: - NSLocalizedString("An unknown error occurred", comment: "") - } - } -} diff --git a/Sources/Core/Document/VDDocument.swift b/Sources/Core/Document/VDDocument.swift deleted file mode 100644 index 51cadcc..0000000 --- a/Sources/Core/Document/VDDocument.swift +++ /dev/null @@ -1,327 +0,0 @@ -// -// VDDocument.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -import os.log - -public protocol DocumentWindowControllerDelegate: AnyObject { - func canClose(_ document: VDDocument) -> Bool -} - -// the attribute @objc is necessary to work correctly in Swift -@objc(VDDocument) public class VDDocument: NSPersistentDocument { - private var windowController: NSWindowController? - private var documentManagedObjectModel: NSManagedObjectModel? - - // Cocoa can call [self close] multiple times so we must check it manually - // see http://lists.apple.com/archives/cocoa-dev/2012/Sep/msg00428.html - private var isClosed = false - - let uuid = ProcessInfo.processInfo.globallyUniqueString - - var parentSession: DiffOpenerDelegate? - - // MARK: - sessionDiff field - - private var _sessionDiff: SessionDiff? - - var sessionDiff: SessionDiff? { - get { - if _sessionDiff != nil { - return _sessionDiff - } - - guard let moc = managedObjectContext else { - fatalError("managedObjectContext is nil") - } - let fetchRequest = SessionDiff.fetchRequest() - - do { - _sessionDiff = try moc.fetch(fetchRequest).first - } catch { - presentError(error) - } - - return _sessionDiff - } - - set { - _sessionDiff = newValue - } - } - - // MARK: - init - - // —initWithType:error:—that is called only when a new document is created, not when it is subsequently reopened. - @MainActor convenience init(type _: String) throws { - self.init() - - guard let moc = managedObjectContext else { - return - } - moc.rollback() - - moc.updateWithoutRecordingModifications { - _sessionDiff = SessionDiff.newObject(moc) - if let sessionDiff { - VDDocumentController.shared.fillSessionDiff(sessionDiff) - } - } - } - - override open nonisolated func read(from absoluteURL: URL, ofType typeName: String) throws { - try super.read(from: absoluteURL, ofType: typeName) - - try MainActor.assumeIsolated { - try isReadSessionDiffValid() - } - } - - override open func save(_ sender: Any?) { - super.save(sender) - - // If user drags path ensure it is saved as secure bookmark - guard let sessionDiff, - let leftPath = sessionDiff.leftPath, - let rightPath = sessionDiff.rightPath else { - return - } - - let leftUrl = URL(filePath: leftPath) - let rightUrl = URL(filePath: rightPath) - - SecureBookmark.shared.add(leftUrl) - SecureBookmark.shared.add(rightUrl) - } - - func isReadSessionDiffValid() throws { - guard let sessionDiff, - let leftPath = sessionDiff.leftPath, - let rightPath = sessionDiff.rightPath else { - throw DocumentError.invalidSessionData - } - - let leftUrl = URL(filePath: leftPath) - let rightUrl = URL(filePath: rightPath) - - let leftSecureUrl = SecureBookmark.shared.secure(fromBookmark: leftUrl, startSecured: true) - defer { - SecureBookmark.shared.stopAccessing(url: leftSecureUrl) - } - - let rightSecureURL = SecureBookmark.shared.secure(fromBookmark: rightUrl, startSecured: true) - defer { - SecureBookmark.shared.stopAccessing(url: rightSecureURL) - } - - guard let itemType = sessionDiff.itemType else { - throw DocumentError.unknownSessionType - } - try itemType.checkPaths(leftPath: leftPath, rightPath: rightPath) - } - - override public func prepareSavePanel(_ savePanel: NSSavePanel) -> Bool { - // Prepare a suggested file name only if the document is new (ie never saved before) - if fileURL != nil { - return super.prepareSavePanel(savePanel) - } - guard let sessionDiff, - var leftName = fileName(sessionDiff.leftPath), - var rightName = fileName(sessionDiff.rightPath), - let defaultType = NSDocumentController.shared.defaultType, - let ext = fileNameExtension(forType: defaultType, saveOperation: .saveOperation) else { - return super.prepareSavePanel(savePanel) - } - - if leftName.isEmpty { - leftName = rightName - } - if rightName.isEmpty { - rightName = leftName - } - savePanel.nameFieldStringValue = if leftName == rightName { - String(format: "%@.%@", leftName, ext) - } else { - String(format: "%@ vs %@.%@", leftName, rightName, ext) - } - - return true - } - - func fileName(_ absolutePath: String?) -> String? { - guard let absolutePath else { - return nil - } - let dotSet = CharacterSet(charactersIn: ".") - - var url = URL(filePath: absolutePath) - url.deletePathExtension() - - return url.lastPathComponent.trimmingCharacters(in: dotSet) - } - - override public func makeWindowControllers() { - guard let sessionDiff, - sessionDiff.leftPath != nil, - sessionDiff.rightPath != nil else { - super.makeWindowControllers() - return - } - switch sessionDiff.itemType { - case .folder: - let fwc = FoldersWindowController() - windowController = fwc - addWindowController(fwc) - fwc.startComparison() - case .file: - let fwc = FilesWindowController() - windowController = fwc - addWindowController(fwc) - fwc.startComparison() - default: - Logger.general.error("Invalid session type") - } - // disable restoration for new documents - // otherwise when the application restarts it restores an invalid document - if fileURL == nil { - windowController?.window?.isRestorable = false - } - } - - override public func canClose(withDelegate delegate: Any, shouldClose shouldCloseSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) { - if let delegate = windowController as? DocumentWindowControllerDelegate, - delegate.canClose(self) { - close() - } - - super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo) - } - - override public func close() { - if isClosed { - super.close() - return - } - isClosed = true - - // TODO: update only if document is edited but do not call self.isDocumentEdited because is overridden - if super.isDocumentEdited { - HistorySessionManager.shared.update(document: self, closeDocument: true) - } - DistributedNotificationCenter.default.post( - name: .documentClosed, - object: uuid - ) - - super.close() - } - - override public nonisolated func write( - to absoluteURL: URL, - ofType typeName: String, - for saveOperation: NSDocument.SaveOperationType, - originalContentsURL absoluteOriginalContentsURL: URL? - ) throws { - try super.write( - to: absoluteURL, - ofType: typeName, - for: saveOperation, - originalContentsURL: absoluteOriginalContentsURL - ) - - // documents never saved (eg new documents) has restoration disabled but now - // they can safety restored as application restart time - MainActor.assumeIsolated { - windowController?.window?.isRestorable = true - } - } - - override public var isDocumentEdited: Bool { - // If pref is set to true then consider the document without modifications so the save dialog will never shown - if CommonPrefs.shared.bool(forKey: .dontAskSave) { - return false - } - return super.isDocumentEdited - } - - override public var managedObjectModel: NSManagedObjectModel? { - // By default the Core Data framework creates a merged model from all models in the application bundle - // so we force it to use only the document model - if documentManagedObjectModel == nil { - if let modelURL = Bundle.main.url(forResource: "MyDocument", withExtension: "momd") { - documentManagedObjectModel = NSManagedObjectModel(contentsOf: modelURL) - } - } - return documentManagedObjectModel - } - - // displayName is declared as null_resettable so the unwrap is by design - // swiftlint:disable:next implicitly_unwrapped_optional - override public var displayName: String! { - get { - // Show title using the sessionDiff path instead of the resolved one - guard let sessionDiff, - var leftPath = sessionDiff.leftPath, - let rightPath = sessionDiff.rightPath else { - return super.displayName - } - if leftPath.isEmpty, rightPath.isEmpty { - return super.displayName - } - let leftName = URL(filePath: leftPath).lastPathComponent - let rightName = URL(filePath: rightPath).lastPathComponent - - if leftName != rightName { - return String( - format: "%@ - %@ <=> %@", - super.displayName, - leftName, - rightName - ) - } - if leftPath.isEmpty { - leftPath = rightPath - } - return String( - format: "%@ - %@", - super.displayName, - leftName - ) - } - - set { - super.displayName = newValue - } - } - - override public func configurePersistentStoreCoordinator( - for url: URL, - ofType fileType: String, - modelConfiguration configuration: String?, - storeOptions: [String: Any]? = nil - ) throws { - // use lightweight migration - var options = storeOptions ?? [:] - options[NSMigratePersistentStoresAutomaticallyOption] = true - options[NSInferMappingModelAutomaticallyOption] = true - - try super.configurePersistentStoreCoordinator( - for: url, - ofType: fileType, - modelConfiguration: configuration, - storeOptions: options - ) - } -} - -public extension Notification.Name { - static let documentClosed = Notification.Name("VDDocumentClosedNotification") -} - -public extension CommonPrefs.Name { - static let dontAskSave = CommonPrefs.Name(rawValue: "dontAskToSaveSession") -} diff --git a/Sources/Core/Document/VDDocumentController.swift b/Sources/Core/Document/VDDocumentController.swift deleted file mode 100644 index 4b24544..0000000 --- a/Sources/Core/Document/VDDocumentController.swift +++ /dev/null @@ -1,161 +0,0 @@ -// -// VDDocumentController.swift -// VisualDiffer -// -// Created by davide ficano on 24/08/11. -// Copyright (c) 2011 visualdiffer.com -// - -import os.log - -enum MainMenu: Int { - case application - case file - case edit - case navigate - case actions - case view - case window - case help -} - -class VDDocumentController: NSDocumentController { - private let documentWindow: DocumentWindow - - override class var shared: VDDocumentController { - // swiftlint:disable:next force_cast - NSDocumentController.shared as! VDDocumentController - } - - override init() { - documentWindow = DocumentWindow() - - super.init() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func removeDocument(_ document: NSDocument) { - super.removeDocument(document) - - if documents.isEmpty { - newDocument(nil) - } - } - - // MARK: - Actions - - override func newDocument(_ sender: Any?) { - // documentWindow can call NSDocumentController.newDocument so if it's the caller we redirect to `super` - if let sender = sender as? DocumentWindow, sender === documentWindow { - super.newDocument(sender) - } else { - documentWindow.newDocument(self) - } - } - - override func openDocument(_ sender: Any?) { - documentWindow.orderOut(self) - super.openDocument(sender) - } - - override func addDocument(_ document: NSDocument) { - documentWindow.orderOut(self) - super.addDocument(document) - } - - override func openUntitledDocumentAndDisplay(_ displayDocument: Bool) throws -> NSDocument { - // This method is called from newDocument originally called from showDiff - let doc = try super.openUntitledDocumentAndDisplay(displayDocument) - - if let doc = doc as? VDDocument { - HistorySessionManager.shared.add(document: doc) - } - return doc - } - - // MARK: - SessionDiff related methods - - @discardableResult - func fillSessionDiff(_ sessionDiff: SessionDiff) -> Bool { - documentWindow.fillSessionDiff(sessionDiff) - } - - @discardableResult - func openDifferDocument(leftUrl: URL?, rightUrl: URL?) throws -> VDDocument? { - // at least one path must be set - if leftUrl == nil && rightUrl == nil { - return nil - } - - let hasMissingPath = leftUrl == nil || rightUrl == nil - var isFolder = false - var leftPathExists = false - var rightPathExists = false - - let canOpenDocument = if hasMissingPath { - true - } else if let leftUrl, let rightUrl, leftUrl.matchesFileType( - of: rightUrl, - isDir: &isFolder, - leftExists: &leftPathExists, - rightExists: &rightPathExists - ) { - true - } else { - false - } - - guard canOpenDocument else { - if !leftPathExists, let leftUrl { - throw SessionTypeError.invalidItem(path: leftUrl.osPath, isFolder: isFolder) - } - if !rightPathExists, let rightUrl { - throw SessionTypeError.invalidItem(path: rightUrl.osPath, isFolder: isFolder) - } - throw SessionTypeError.unknownError - } - // when comparing folders both paths must be set - if isFolder, hasMissingPath { - throw SessionTypeError.invalidAllItems(isFolder: true) - } - return try openDocumentWithBlock { document in - if let sessionDiff = document.sessionDiff { - sessionDiff.itemType = isFolder ? .folder : .file - sessionDiff.leftPath = leftUrl?.standardizingPath ?? "" - sessionDiff.leftReadOnly = false - sessionDiff.rightPath = rightUrl?.standardizingPath ?? "" - sessionDiff.rightReadOnly = false - sessionDiff.expandAllFolders = CommonPrefs.shared.bool(forKey: .expandAllFolders) - } - } - } - - /** - * Open document and allows to update the sessionDiff without mark it as edited - */ - @discardableResult - func openDocumentWithBlock(_ block: (VDDocument) -> Void) throws -> VDDocument? { - let docController = NSDocumentController.shared - guard let defaultType = docController.defaultType, - let doc = try? docController.makeUntitledDocument(ofType: defaultType) as? VDDocument, - let moc = doc.managedObjectContext else { - return nil - } - - moc.rollback() - moc.updateWithoutRecordingModifications { - doc.sessionDiff = SessionDiff.newObject(moc) - block(doc) - } - - docController.addDocument(doc) - doc.makeWindowControllers() - doc.showWindows() - - return doc - } -} diff --git a/Sources/Core/Document/Window/DocumentWindow.swift b/Sources/Core/Document/Window/DocumentWindow.swift deleted file mode 100644 index dd3bf80..0000000 --- a/Sources/Core/Document/Window/DocumentWindow.swift +++ /dev/null @@ -1,344 +0,0 @@ -// -// DocumentWindow.swift -// VisualDiffer -// -// Created by davide ficano on 20/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DocumentWindow: NSWindow, FileDropImageViewDelegate, HistoryControllerDelegate { - private var isFoldersDiff = false - // if true the user changed values from sessionPreferencesSheet - // so we open the document ignoring the HistoryManager session (if any), - // otherwise the history session 'overwrite' the chosen user settings - private var userChosenPreferences = false - - private lazy var sessionPreferencesSheet: SessionPreferencesWindow = { - let sheet = SessionPreferencesWindow() - sheet.fillWithUserDefaults() - - return sheet - }() - - private lazy var historyController: HistoryController = { - let view = HistoryController() - view.delegate = self - - return view - }() - - private lazy var searchHistory: HistorySearchField = { - let view = HistorySearchField(frame: .zero) - view.historyController = historyController - - return view - }() - - private let separator: NSBox = .separator() - - private lazy var compareButton: NSButton = { - let view = NSButton( - title: NSLocalizedString("Compare", comment: ""), - target: self, - action: #selector(showDiffs) - ) - - view.toolTip = NSLocalizedString("Start Comparison ⌘↩︎", comment: "") - view.keyEquivalent = KeyEquivalent.enter - view.keyEquivalentModifierMask = .command - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var recentPopup = RecentDocumentPopupMenu( - title: NSLocalizedString("Open Recent", comment: ""), - target: self, - action: #selector(openRecent) - ) - - private lazy var sessionPreferencesButton: NSButton = { - let view = NSButton( - title: NSLocalizedString("Session Settings", comment: ""), - target: self, - action: #selector(selectSessionPreferences) - ) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var leftPathChooser = PathChooser( - userDefault: "leftPaths", - dropTitle: NSLocalizedString("Left", comment: ""), - dropDelegate: self, - chooseTitle: NSLocalizedString("Left...", comment: "") - ) - - private lazy var rightPathChooser = PathChooser( - userDefault: "rightPaths", - dropTitle: NSLocalizedString("Right", comment: ""), - dropDelegate: self, - chooseTitle: NSLocalizedString("Right...", comment: "") - ) - - init() { - let styleMask: NSWindow.StyleMask = [.titled, .closable, .resizable] - - super.init( - contentRect: NSRect(x: 0, y: 0, width: 600, height: 330), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - - title = NSLocalizedString("Compare Folders or Files", comment: "") - isReleasedWhenClosed = false - hasShadow = true - isRestorable = true - titlebarSeparatorStyle = .automatic - setFrameAutosaveName("compareDialog") - minSize = NSSize(width: 600, height: 330) - - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(leftPathChooser.dropView) - contentView.addSubview(rightPathChooser.dropView) - contentView.addSubview(leftPathChooser.chooserView) - contentView.addSubview(rightPathChooser.chooserView) - - contentView.addSubview(sessionPreferencesButton) - contentView.addSubview(recentPopup) - contentView.addSubview(compareButton) - contentView.addSubview(separator) - contentView.addSubview(searchHistory) - contentView.addSubview(historyController.scrollView) - } - - setupViewsConstraints() - } - - private func setupViewsConstraints() { - guard let contentView else { - return - } - let table = historyController.scrollView - let leftDropView = leftPathChooser.dropView - let rightDropView = rightPathChooser.dropView - - NSLayoutConstraint.activate([ - leftDropView.leadingAnchor.constraint(equalTo: separator.leadingAnchor), - leftDropView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 40), - leftDropView.widthAnchor.constraint(equalToConstant: 50), - leftDropView.heightAnchor.constraint(equalToConstant: 50), - - rightDropView.leadingAnchor.constraint(equalTo: leftDropView.trailingAnchor, constant: 8), - rightDropView.topAnchor.constraint(equalTo: leftDropView.topAnchor), - rightDropView.widthAnchor.constraint(equalToConstant: 50), - rightDropView.heightAnchor.constraint(equalToConstant: 50), - - leftPathChooser.chooserView.leadingAnchor.constraint(equalTo: rightDropView.trailingAnchor, constant: 8), - leftPathChooser.chooserView.trailingAnchor.constraint(equalTo: separator.trailingAnchor), - leftPathChooser.chooserView.topAnchor.constraint(equalTo: leftDropView.topAnchor), - - rightPathChooser.chooserView.leadingAnchor.constraint(equalTo: leftPathChooser.chooserView.leadingAnchor), - rightPathChooser.chooserView.trailingAnchor.constraint(equalTo: leftPathChooser.chooserView.trailingAnchor), - rightPathChooser.chooserView.topAnchor.constraint(equalTo: leftPathChooser.chooserView.bottomAnchor, constant: 6), - - sessionPreferencesButton.leadingAnchor.constraint(equalTo: separator.leadingAnchor), - sessionPreferencesButton.topAnchor.constraint(equalTo: leftDropView.bottomAnchor, constant: 12), - - recentPopup.trailingAnchor.constraint(equalTo: compareButton.leadingAnchor, constant: -5), - recentPopup.topAnchor.constraint(equalTo: sessionPreferencesButton.topAnchor), - recentPopup.widthAnchor.constraint(greaterThanOrEqualToConstant: 130), - - compareButton.trailingAnchor.constraint(equalTo: separator.trailingAnchor), - compareButton.topAnchor.constraint(equalTo: sessionPreferencesButton.topAnchor), - compareButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 130), - - separator.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - separator.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - separator.topAnchor.constraint(equalTo: compareButton.bottomAnchor, constant: 8), - - searchHistory.leadingAnchor.constraint(equalTo: separator.leadingAnchor), - searchHistory.trailingAnchor.constraint(equalTo: separator.trailingAnchor), - searchHistory.topAnchor.constraint(equalTo: separator.topAnchor, constant: 10), - - table.leadingAnchor.constraint(equalTo: separator.leadingAnchor), - table.trailingAnchor.constraint(equalTo: separator.trailingAnchor), - table.topAnchor.constraint(equalTo: searchHistory.bottomAnchor, constant: 5), - table.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20), - ]) - } - - @objc func showDiffs(_: AnyObject?) { - let leftUrl = URL(filePath: leftPathChooser.currentPath) - let rightUrl = URL(filePath: rightPathChooser.currentPath) - var leftExists = false - var rightExists = false - let isOk = leftUrl.matchesFileType( - of: rightUrl, - isDir: &isFoldersDiff, - leftExists: &leftExists, - rightExists: &rightExists - ) - - if isOk { - let leftPath = leftPathChooser.currentPath - let rightPath = rightPathChooser.currentPath - leftPathChooser.addPath(leftPath) - rightPathChooser.addPath(rightPath) - - if !userChosenPreferences, - HistorySessionManager.shared.containsHistory(leftPath: leftPath, rightPath: rightPath) { - try? HistorySessionManager.shared.openDocument(leftPath: leftPath, rightPath: rightPath) - } else { - // Create a new document - // the history session is updated inside openUntitledDocumentAndDisplay:error - // Pass `self` to newDocument otherwise the comparison doesn't start - NSDocumentController.shared.newDocument(self) - } - } else { - var errDesc = NSLocalizedString("Left and right paths must both be folders or files", comment: "") - if !leftExists { - errDesc = NSLocalizedString("Left file no longer exists", comment: "") - } else if !rightExists { - errDesc = NSLocalizedString("Right file no longer exists", comment: "") - } - let alert = NSAlert() - alert.messageText = errDesc - alert.beginSheetModal(for: self) - } - } - - @objc func openRecent(_ sender: AnyObject) { - guard let sender = sender as? NSPopUpButton else { - return - } - let index = sender.indexOfSelectedItem - 1 - if index < 0 { - return - } - guard let url = sender.selectedItem?.representedObject as? URL else { - let alert = NSAlert() - - alert.messageText = NSLocalizedString("The document could not be opened.", comment: "") - alert.alertStyle = .critical - alert.runModal() - return - } - - let docController = NSDocumentController.shared - docController.openDocument(withContentsOf: url, display: true) { _, _, error in - if let error { - self.presentError(error) - } else { - self.orderOut(self) - } - } - } - - // MARK: - FileDropImageViewDelegate delegate methods - - func fileDropImageViewUpdatePath(_ view: FileDropView, paths: [URL]) -> Bool { - if paths.count < 2 { - if view === leftPathChooser.dropView { - leftPathChooser.currentPath = paths[0].path - SecureBookmark.shared.add(paths[0]) - } else if view === rightPathChooser.dropView { - rightPathChooser.currentPath = paths[0].path - SecureBookmark.shared.add(paths[0]) - } - } else { - leftPathChooser.currentPath = paths[0].path - rightPathChooser.currentPath = paths[1].path - - SecureBookmark.shared.add(paths[0]) - SecureBookmark.shared.add(paths[1]) - } - return true - } - - // MARK: - History Controller - - func history(controller _: HistoryController, doubleClickedEntity _: HistoryEntity?) { - showDiffs(nil) - } - - func history(controller _: HistoryController, selectedEntities entities: [HistoryEntity]) { - if entities.count == 1, - let leftPath = entities[0].leftPath, - let rightPath = entities[0].rightPath { - leftPathChooser.currentPath = leftPath - rightPathChooser.currentPath = rightPath - } - } - - func history(controller _: HistoryController, droppedPaths paths: [URL]) -> Bool { - if paths.count == 1 { - let rightPath = rightPathChooser.currentPath - let trimmedPath = rightPath.trimmingCharacters(in: CharacterSet.whitespaces) - if trimmedPath.isEmpty { - rightPathChooser.currentPath = paths[0].path - } else { - leftPathChooser.currentPath = paths[0].path - rightPathChooser.currentPath = "" - } - SecureBookmark.shared.add(paths[0]) - } else if paths.count > 1 { - leftPathChooser.currentPath = paths[0].path - rightPathChooser.currentPath = paths[1].path - - SecureBookmark.shared.add(paths[0]) - SecureBookmark.shared.add(paths[1]) - } - return true - } - - @objc func find(_: AnyObject) { - makeFirstResponder(searchHistory) - } - - @objc func selectSessionPreferences(_: AnyObject) { - sessionPreferencesSheet.beginSheet( - self, - sessionDiff: nil, - selectedTab: .comparison - ) { - self.userChosenPreferences = $0 == .OK - } - } - - @objc func fillSessionDiff(_ sessionDiff: SessionDiff) -> Bool { - // Update all properties handled by preference sheet - sessionPreferencesSheet.updateSessionDiff(sessionDiff) - - // Update all properties handled by Document Controller - sessionDiff.leftPath = leftPathChooser.currentPath - sessionDiff.leftReadOnly = leftPathChooser.isReadOnly - - sessionDiff.rightPath = rightPathChooser.currentPath - sessionDiff.rightReadOnly = rightPathChooser.isReadOnly - - sessionDiff.itemType = isFoldersDiff ? .folder : .file - - return sessionDiff.leftPath != nil - } - - func newDocument(_: Any?) { - userChosenPreferences = false - - leftPathChooser.selectLastUsedPath() - rightPathChooser.selectLastUsedPath() - leftPathChooser.isReadOnly = false - rightPathChooser.isReadOnly = false - - recentPopup.refresh() - - center() - makeKeyAndOrderFront(self) - } -} diff --git a/Sources/Core/Document/Window/NSArrayController+Paths.swift b/Sources/Core/Document/Window/NSArrayController+Paths.swift deleted file mode 100644 index 582f29a..0000000 --- a/Sources/Core/Document/Window/NSArrayController+Paths.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// NSArrayController+Paths.swift -// VisualDiffer -// -// Created by davide ficano on 21/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSArrayController { - convenience init(forUserDefault userDefault: String) { - self.init() - - avoidsEmptySelection = true - preservesSelection = true - selectsInsertedObjects = true - clearsFilterPredicateOnInsertion = true - isEditable = true - objectClass = NSMutableArray.self - - bind( - NSBindingName.contentArray, - to: NSUserDefaultsController.shared, - withKeyPath: String(format: "values.%@", userDefault), - options: [NSBindingOption.continuouslyUpdatesValue: true] - ) - } - - @MainActor func addPath(_ newPath: String) { - guard let arrangedObjects = arrangedObjects as? [String] else { - return - } - // move last used on top - if let index = arrangedObjects.firstIndex(of: newPath) { - remove(atArrangedObjectIndex: index) - } - insert(newPath, atArrangedObjectIndex: 0) - // Set maximum saved items - let docController = NSDocumentController.shared - let maxCount = docController.maximumRecentDocumentCount - let len = arrangedObjects.count - maxCount - if len > 0 { - let indexSet = IndexSet(maxCount ..< arrangedObjects.count) - remove(atArrangedObjectIndexes: indexSet) - } - } -} diff --git a/Sources/Core/Document/Window/PathChooser.swift b/Sources/Core/Document/Window/PathChooser.swift deleted file mode 100644 index 047342b..0000000 --- a/Sources/Core/Document/Window/PathChooser.swift +++ /dev/null @@ -1,179 +0,0 @@ -// -// PathChooser.swift -// VisualDiffer -// -// Created by davide ficano on 22/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation -import Cocoa - -@MainActor class PathChooser: NSObject, NSComboBoxDelegate { - var currentPath: String { - get { - comboBox.stringValue - } - - set { - comboBox.stringValue = URL(filePath: newValue).standardizingPath - dropView.filePath = comboBox.stringValue - } - } - - var isReadOnly: Bool { - get { - readOnlyButton.state == .on - } - - set { - readOnlyButton.state = newValue ? .on : .off - } - } - - var dropView: FileDropView - lazy var chooserView: NSStackView = { - let view = NSStackView() - - view.orientation = .horizontal - view.alignment = .centerY - view.spacing = 4 - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var readOnlyButton: NSButton = { - let view = NSButton(frame: .zero) - - view.title = "" - view.toolTip = NSLocalizedString("Make read-only", comment: "") - view.setButtonType(.switch) - view.bezelStyle = .flexiblePush - view.image = NSImage(named: NSImage.lockUnlockedTemplateName) - view.alternateImage = NSImage(named: NSImage.lockLockedTemplateName) - view.imagePosition = .imageLeft - view.alignment = .left - view.refusesFirstResponder = true - view.state = .on - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var comboBox: NSComboBox = { - let view = NSComboBox(frame: .zero) - - view.completes = true - view.isButtonBordered = true - view.drawsBackground = true - view.translatesAutoresizingMaskIntoConstraints = false - - view.delegate = self - view.target = self - view.action = #selector(comboBoxSelectText) - - return view - }() - - private var chooseButton: NSButton - private var comboBoxPaths: NSArrayController - - init( - userDefault: String, - dropTitle: String, - dropDelegate: FileDropImageViewDelegate, - chooseTitle: String - ) { - dropView = FileDropView.create(title: dropTitle, delegate: dropDelegate) - chooseButton = NSButton(title: chooseTitle, target: nil, action: nil) - comboBoxPaths = NSArrayController(forUserDefault: userDefault) - - super.init() - - setupViews(dropTitle, dropDelegate: dropDelegate, chooseTitle: chooseTitle) - bindControls() - } - - private func setupViews( - _: String, - dropDelegate _: FileDropImageViewDelegate, - chooseTitle _: String - ) { - chooseButton.target = self - chooseButton.action = #selector(choosePath) - - [ - readOnlyButton, - comboBox, - chooseButton, - ].forEach { chooserView.addArrangedSubview($0) } - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - readOnlyButton.leadingAnchor.constraint(equalTo: chooserView.leadingAnchor), - readOnlyButton.topAnchor.constraint(equalTo: chooserView.topAnchor), - readOnlyButton.bottomAnchor.constraint(equalTo: chooserView.bottomAnchor), - - comboBox.leadingAnchor.constraint(equalTo: readOnlyButton.trailingAnchor, constant: 4), - comboBox.trailingAnchor.constraint(equalTo: chooseButton.leadingAnchor, constant: -8), - comboBox.topAnchor.constraint(equalTo: chooserView.topAnchor), - - chooseButton.trailingAnchor.constraint(equalTo: chooserView.trailingAnchor), - chooseButton.topAnchor.constraint(equalTo: chooserView.topAnchor), - chooseButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 80), - ]) - } - - func selectLastUsedPath() { - guard let arr = comboBoxPaths.arrangedObjects as? [String], - !arr.isEmpty else { - return - } - - comboBox.selectItem(at: 0) - currentPath = arr[0] - } - - @objc func choosePath(_: AnyObject) { - let url = URL(filePath: currentPath) - let selectedUrl = url.selectPath( - panelTitle: NSLocalizedString("Select File or Folder", comment: ""), - chooseFiles: true, - chooseDirectories: true - ) - - if let selectedUrl { - currentPath = selectedUrl.osPath - } - } - - func bindControls() { - comboBox.bind( - NSBindingName.content, - to: comboBoxPaths, - withKeyPath: "arrangedObjects", - options: nil - ) - } - - func comboBoxSelectionDidChange(_ notification: Notification) { - if let comboBox = notification.object as? NSComboBox, - let filePath = comboBox.objectValueOfSelectedItem as? String { - dropView.filePath = filePath - } - } - - @objc func comboBoxSelectText(_ sender: AnyObject) { - if let comboBox = sender as? NSComboBox { - dropView.filePath = comboBox.stringValue - } - } - - func addPath(_ newPath: String) { - comboBoxPaths.addPath(newPath) - } -} diff --git a/Sources/Core/Document/Window/RecentDocumentPopupMenu.swift b/Sources/Core/Document/Window/RecentDocumentPopupMenu.swift deleted file mode 100644 index 4062ddc..0000000 --- a/Sources/Core/Document/Window/RecentDocumentPopupMenu.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// RecentDocumentPopupMenu.swift -// VisualDiffer -// -// Created by davide ficano on 08/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -private let showRecentDocumentsListPrefName = "showRecentDocumentsList" - -class RecentDocumentPopupMenu: PopUpButtonUrl, NSMenuDelegate { - init(title _: String, target: AnyObject?, action: Selector?) { - super.init( - title: NSLocalizedString("Open Recent", comment: ""), - target: target, - action: action, - delegate: nil - ) - - menu?.delegate = self - } - - func refresh() { - let showRecentDocumentsList = UserDefaults.standard.bool(forKey: showRecentDocumentsListPrefName) - let hasRecentDocuments = !NSDocumentController.shared.recentDocumentURLs.isEmpty - isHidden = !(showRecentDocumentsList && hasRecentDocuments) - } - - // MARK: - Main menu methods - - func menuNeedsUpdate(_: NSMenu) { - let documentURLs = NSDocumentController.shared.recentDocumentURLs.sorted { - $0.lastPathComponent.caseInsensitiveCompare($1.lastPathComponent) == .orderedAscending - } - - clear() - fill(documentURLs) - } -} diff --git a/Sources/Core/History/HistoryEntity.swift b/Sources/Core/History/HistoryEntity.swift deleted file mode 100644 index ae42f71..0000000 --- a/Sources/Core/History/HistoryEntity.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// HistoryEntity.swift -// VisualDiffer -// -// Created by davide ficano on 10/01/16. -// Copyright (c) 2016 visualdiffer.com -// - -// keep the same entity name that Core Data expects from the model file -// the attribute @objc is necessary to work correctly in Swift -@objc(HistoryEntity) class HistoryEntity: SessionDiff { - static let name = "HistoryEntity" - - @NSManaged var starred: NSNumber - @NSManaged var updateTime: Date - - static func requestUpdateTime() -> NSFetchRequest { - let request = NSFetchRequest(entityName: Self.name) - request.sortDescriptors = [ - NSSortDescriptor(key: "updateTime", ascending: false), - ] - - return request - } - - static func searchPathRequest( - leftPath: String, - rightPath: String - ) -> NSFetchRequest { - let request = NSFetchRequest(entityName: HistoryEntity.name) - request.predicate = NSPredicate( - format: "leftPath == %@ and rightPath = %@", - URL(filePath: leftPath).standardizingPath, - URL(filePath: rightPath).standardizingPath - ) - - return request - } - - func fill(with sessionDiff: SessionDiff) { - updateTime = Date() - starred = false - - Self.copySessionDiff(source: sessionDiff, destination: self) - } - - func fill(sessionDiff: SessionDiff) { - Self.copySessionDiff(source: self, destination: sessionDiff) - } - - private static func copySessionDiff(source: SessionDiff, destination: SessionDiff) { - destination.leftPath = source.leftPath - destination.leftReadOnly = source.leftReadOnly - - destination.rightPath = source.rightPath - destination.rightReadOnly = source.rightReadOnly - - destination.expandAllFolders = source.expandAllFolders - - destination.itemType = source.itemType - - destination.comparatorOptions = source.comparatorOptions - destination.displayOptions = source.displayOptions - destination.followSymLinks = source.followSymLinks - destination.timestampToleranceSeconds = source.timestampToleranceSeconds - destination.exclusionFileFilters = source.exclusionFileFilters - destination.skipPackages = source.skipPackages - destination.fileExtraOptions = source.fileExtraOptions - destination.traverseFilteredFolders = source.traverseFilteredFolders - destination.fileNameAlignments = source.fileNameAlignments - - destination.currentSortColumn = source.currentSortColumn - destination.isCurrentSortAscending = source.isCurrentSortAscending - destination.currentSortSide = source.currentSortSide - - destination.extraData = source.extraData - } -} diff --git a/Sources/Core/History/HistorySessionManager.swift b/Sources/Core/History/HistorySessionManager.swift deleted file mode 100644 index bc87d08..0000000 --- a/Sources/Core/History/HistorySessionManager.swift +++ /dev/null @@ -1,217 +0,0 @@ -// -// HistorySessionManager.swift -// VisualDiffer -// -// Created by davide ficano on 08/04/15. -// Copyright (c) 2015 visualdiffer.com -// - -import os.log - -class HistorySessionManager: @unchecked Sendable { - static let defaultMaxItemsCount = 50 - - static let historyFileName = "historySessions.sqlite" - - static let shared = HistorySessionManager() - - private(set) var historyMOC: NSManagedObjectContext - - private var documents = Set() - private var maximumHistorySessionCount = 0 - - private init() { - historyMOC = Self.initializeCoreData() - - maximumHistorySessionCount = maxItemCountPref() - } - - func maxItemCountPref() -> Int { - let maxItemCount = CommonPrefs.shared.number( - forKey: .History.maximumHistoryCount, - Self.defaultMaxItemsCount - ).intValue - - if maxItemCount >= 0 { - return maxItemCount - } - return Self.defaultMaxItemsCount - } - - func add(document: VDDocument) { - update(session: document, closeDocument: false) - } - - // Update the document and close it (if closeDocument is true) - func update( - document: VDDocument, - closeDocument: Bool - ) { - // documents loaded from disk aren't handled by history - // so they aren't present on self.documents dictionary - if documents.contains(document.uuid) { - update(session: document, closeDocument: closeDocument) - } - } - - func update( - session document: VDDocument, - closeDocument: Bool - ) { - historyMOC.performAndWait { - setSession(document: document) - - if closeDocument { - documents.remove(document.uuid) - } - } - } - - func setSession(document: VDDocument) { - MainActor.assumeIsolated { - guard let sessionDiff = document.sessionDiff, - let leftPath = sessionDiff.leftPath, - let rightPath = sessionDiff.rightPath else { - return - } - // if we are adding a new document history will be nil - // if we are updating and left or right path has been changed then history will be nil, too. - // don't overwrite the session if the paths are changed but create a new one - guard let history = findBy(leftPath: leftPath, rightPath: rightPath) ?? createNewHistory() else { - return - } - - history.fill(with: sessionDiff) - do { - try historyMOC.save() - } catch { - Logger.general.error("Unable to save history \(error)") - } - documents.insert(document.uuid) - } - } - - func createNewHistory() -> HistoryEntity? { - guard let history = NSEntityDescription.insertNewObject( - forEntityName: HistoryEntity.name, - into: historyMOC - ) as? HistoryEntity else { - return nil - } - return history - } - - func containsHistory( - leftPath: String, - rightPath: String - ) -> Bool { - do { - let count = try historyMOC.count( - for: HistoryEntity.searchPathRequest(leftPath: leftPath, rightPath: rightPath) - ) - return (count == NSNotFound || count == 0) ? false : true - } catch {} - return false - } - - @discardableResult - func fill( - sessionDiff: SessionDiff?, - leftPath: String, - rightPath: String - ) -> Bool { - guard let sessionDiff, - let history = findBy(leftPath: leftPath, rightPath: rightPath) else { - return false - } - - history.fill(sessionDiff: sessionDiff) - - return true - } - - func openDocument( - leftPath: String, - rightPath: String - ) throws { - _ = try MainActor.assumeIsolated { - try VDDocumentController.shared.openDocumentWithBlock { (doc: VDDocument) in - self.fill( - sessionDiff: doc.sessionDiff, - leftPath: leftPath, - rightPath: rightPath - ) - self.add(document: doc) - } - } - } - - func findBy(leftPath: String, rightPath: String) -> HistoryEntity? { - do { - return try historyMOC.fetch(HistoryEntity.searchPathRequest(leftPath: leftPath, rightPath: rightPath)).first - } catch { - Logger.general.error("Error fetching objects: \(error.localizedDescription)") - } - return nil - } - - static func initializeCoreData() -> NSManagedObjectContext { - guard let modelURL = Bundle.main.url(forResource: "History", withExtension: "momd"), - let mom = NSManagedObjectModel(contentsOf: modelURL) else { - fatalError("Error initializing Managed Object Model") - } - - let psc1 = NSPersistentStoreCoordinator(managedObjectModel: mom) - let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) - moc.persistentStoreCoordinator = psc1 - - guard let storeURL = Self.historyConfigPath(), - let psc = moc.persistentStoreCoordinator else { - fatalError("Error getting store URL") - } - - // start a lightweight migration for added/deleted attributes - let options = [ - NSMigratePersistentStoresAutomaticallyOption: true, - NSInferMappingModelAutomaticallyOption: true, - ] - - do { - _ = try psc.addPersistentStore( - type: .sqlite, - configuration: nil, - at: storeURL, - options: options - ) - } catch let error as NSError { - fatalError("Error initializing PSC: \(error.localizedDescription)\n\(error.userInfo)") - } - return moc - } - - static func historyConfigPath() -> URL? { - let historyPath = FileManager.default.urls( - for: .applicationSupportDirectory, - in: .userDomainMask - )[0] - - if !FileManager.default.fileExists(atPath: historyPath.osPath) { - do { - try FileManager.default.createDirectory( - at: historyPath, - withIntermediateDirectories: true - ) - } catch { - Logger.general.error("Unable to create history directory \(historyPath), reason \(error)") - return nil - } - } - return historyPath.appendingPathComponent(Self.historyFileName) - } -} - -extension CommonPrefs.Name { - enum History { - static let maximumHistoryCount = CommonPrefs.Name(rawValue: "maximumHistorySessionCount") - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff+AlignRule.swift b/Sources/Core/SessionDiff/SessionDiff+AlignRule.swift deleted file mode 100644 index 92da9dc..0000000 --- a/Sources/Core/SessionDiff/SessionDiff+AlignRule.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// SessionDiff+AlignRule.swift -// VisualDiffer -// -// Created by davide ficano on 28/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -extension SessionDiff { - var fileNameAlignments: [AlignRule]? { - get { - guard let fileNameAlignmentsData else { - return nil - } - let allowedClasses: [AnyClass] = [ - NSArray.self, - NSMutableDictionary.self, - NSString.self, - NSNumber.self, - ] - do { - let data = try NSKeyedUnarchiver.unarchivedObject( - ofClasses: allowedClasses, - from: fileNameAlignmentsData - ) - return (data as? [[String: Any]])?.compactMap { AlignRule($0) } - } catch { - Logger.general.error("Unable to unarchive file name alignments array \(error)") - - return nil - } - } - - set { - if let newValue, !newValue.isEmpty { - let list = newValue.map { $0.toDictionary() } - do { - fileNameAlignmentsData = try NSKeyedArchiver.archivedData( - withRootObject: list, - requiringSecureCoding: false - ) - } catch { - Logger.general.error("Unable to save file name alignments array \(error)") - fileNameAlignmentsData = nil - } - } else { - fileNameAlignmentsData = nil - } - } - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff+ExtraData.swift b/Sources/Core/SessionDiff/SessionDiff+ExtraData.swift deleted file mode 100644 index 45fa80a..0000000 --- a/Sources/Core/SessionDiff/SessionDiff+ExtraData.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// SessionDiff+ExtraData.swift -// VisualDiffer -// -// Created by davide ficano on 16/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct ExtraData { - private enum Keys { - static let diffResultOptions = "diffResultOptions" - } - - var diffResultOptions: DiffResult.Options = [] - - init() {} - - init(dictionary: [String: Any]) { - if let options = dictionary[Keys.diffResultOptions] as? Int { - diffResultOptions = DiffResult.Options(rawValue: options) - } - } - - func toDictionary() -> [String: Any] { - [ - Keys.diffResultOptions: diffResultOptions.rawValue, - ] - } - - static func fromUserDefaults() -> ExtraData { - let options: [(CommonPrefs.Name, DiffResult.Options)] = [ - (.ignoreLineEndings, .ignoreLineEndings), - (.ignoreLeadingWhitespaces, .ignoreLeadingWhitespaces), - (.ignoreTrailingWhitespaces, .ignoreTrailingWhitespaces), - (.ignoreInternalWhitespaces, .ignoreInternalWhitespaces), - (.ignoreCharacterCase, .ignoreCharacterCase), - ] - - var data = ExtraData() - - for (prefName, option) in options { - data.diffResultOptions.setValue(CommonPrefs.shared.bool(forKey: prefName), element: option) - } - - return data - } -} - -extension SessionDiff { - @NSManaged public var extraDataJSON: Data? - - var extraData: ExtraData { - get { - guard let data = extraDataJSON, - let dictionary = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { - return ExtraData() - } - return ExtraData(dictionary: dictionary) - } - set { - extraDataJSON = try? JSONSerialization.data(withJSONObject: newValue.toDictionary()) - } - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff+ItemComparator.swift b/Sources/Core/SessionDiff/SessionDiff+ItemComparator.swift deleted file mode 100644 index e93fb40..0000000 --- a/Sources/Core/SessionDiff/SessionDiff+ItemComparator.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// SessionDiff+ItemComparator.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension SessionDiff { - func comparator( - withDelegate delegate: ItemComparatorDelegate, - bufferSize: Int - ) -> ItemComparator { - guard let leftPath, - let rightPath else { - fatalError("Both leftPath and rightPath must be set") - } - let options = comparatorOptions - let (isLeftCaseSensitive, isRightCaseSensitive) = options.fileNameCase( - leftPath: URL(filePath: leftPath), - rightPath: URL(filePath: rightPath) - ) - return ItemComparator( - options: options, - delegate: delegate, - bufferSize: bufferSize, - timestampToleranceSeconds: Int(timestampToleranceSeconds), - isLeftCaseSensitive: isLeftCaseSensitive, - isRightCaseSensitive: isRightCaseSensitive, - fileNameAlignments: fileNameAlignments - ) - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff+NSSortDescriptor.swift b/Sources/Core/SessionDiff/SessionDiff+NSSortDescriptor.swift deleted file mode 100644 index 3528894..0000000 --- a/Sources/Core/SessionDiff/SessionDiff+NSSortDescriptor.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// SessionDiff+NSSortDescriptor.swift -// VisualDiffer -// -// Created by davide ficano on 30/06/20. -// Copyright (c) 2020 visualdiffer.com -// - -extension NSSortDescriptor { - enum SessionDiff: String { - case name = "fileName" - case size = "fileSize" - case modificationDate = "fileModificationDate" - } -} - -extension SessionDiff.Column { - func toSortDescriptorKey() -> NSSortDescriptor.SessionDiff { - switch self { - case .name: - .name - case .size: - .size - case .modificationDate: - .modificationDate - } - } - - static func from(sortDescription: NSSortDescriptor.SessionDiff) -> SessionDiff.Column { - switch sortDescription { - case .name: - .name - case .size: - .size - case .modificationDate: - .modificationDate - } - } -} - -extension SessionDiff { - func updateSortColumn( - from sortDescriptor: NSSortDescriptor, - side: Side - ) { - let key: NSSortDescriptor.SessionDiff = if let key = sortDescriptor.key { - NSSortDescriptor.SessionDiff(rawValue: key) ?? .name - } else { - .name - } - currentSortColumn = SessionDiff.Column.from(sortDescription: key) - isCurrentSortAscending = sortDescriptor.ascending - currentSortSide = side - } - - func columnSortDescriptor() -> NSSortDescriptor { - let key = currentSortColumn.toSortDescriptorKey() - let ascending = isCurrentSortAscending - return NSSortDescriptor(key: key.rawValue, ascending: ascending) - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff+ResolvePath.swift b/Sources/Core/SessionDiff/SessionDiff+ResolvePath.swift deleted file mode 100644 index cfe1494..0000000 --- a/Sources/Core/SessionDiff/SessionDiff+ResolvePath.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// SessionDiff+ResolvePath.swift -// VisualDiffer -// -// Created by davide ficano on 08/12/20. -// Copyright (c) 2020 visualdiffer.com -// - -extension SessionDiff { - /** - * Return the left or right path, if necessary resolve the symlink or choose path if not sandboxed - * If the selected path is different from the current one update self.leftPath or self.rightPath - * @param side the path side - * @param fileType the file type to select using open panel - * @param alwaysResolveSymlinks determine if symlinks must be resolved - * @return the selected path - */ - @MainActor func resolvePath( - for side: Side, - chooseFileType fileType: ItemType, - alwaysResolveSymlinks: Bool - ) -> URL? { - let resolveLeft = side == .left - let resolvePath = resolveLeft ? leftPath : rightPath - - // if path is empty we are comparing a single file - // so it isn't necessary to verify the path - guard let resolvePath, - !resolvePath.isEmpty else { - return nil - } - - let resolvedInfo = URL(filePath: resolvePath) - .resolveSymLinksAndAliases( - chooseFiles: fileType == .file, - chooseDirectories: fileType == .folder, - panelTitle: side.panelTitle(chooseFileType: fileType), - alwaysResolveSymlinks: alwaysResolveSymlinks - ) - // assign to sessionDiff only if path differs otherwise the document is considered dirty - guard let (resolvedUrl, selectedAnotherPath) = resolvedInfo else { - return nil - } - - if selectedAnotherPath { - if resolveLeft { - leftPath = resolvedUrl.osPath - } else { - rightPath = resolvedUrl.osPath - } - } - - return resolvedUrl - } -} - -extension SessionDiff.Side { - func panelTitle(chooseFileType fileType: SessionDiff.ItemType) -> String { - let sideText = (self == .left) - ? NSLocalizedString("Left", comment: "Left") - : NSLocalizedString("Right", comment: "Right") - - let typeText = (fileType == .folder) - ? NSLocalizedString("Folder", comment: "Folder type") - : NSLocalizedString("File", comment: "File type") - - let format = NSLocalizedString( - "%@ %@ Not Accessible (Maybe required by sandbox)", - comment: "Message when a folder or file is not accessible" - ) - - return String(format: format, sideText, typeText) - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff+Types.swift b/Sources/Core/SessionDiff/SessionDiff+Types.swift deleted file mode 100644 index a1cef48..0000000 --- a/Sources/Core/SessionDiff/SessionDiff+Types.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// SessionDiff+Types.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension SessionDiff { - enum ItemType: Int16 { - case folder = 1 - case file - } - - enum Column: Int { - case name = 0 - case size - case modificationDate - } - - enum Side: Int { - case left = 0 - case right - } -} diff --git a/Sources/Core/SessionDiff/SessionDiff.swift b/Sources/Core/SessionDiff/SessionDiff.swift deleted file mode 100644 index fc82352..0000000 --- a/Sources/Core/SessionDiff/SessionDiff.swift +++ /dev/null @@ -1,212 +0,0 @@ -// -// SessionDiff.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -@objc(SessionDiff) // swiftlint:disable:next attributes -public class SessionDiff: NSManagedObject { - // leftPath and rightPath are computed properties because they use custom validation - @NSManaged var leftReadOnly: Bool - @NSManaged var rightReadOnly: Bool - - // legacy fields exposed with computed properties and custom types - @NSManaged private var comparatorFlags: Int32 - @NSManaged private var displayFilters: Int32 - @NSManaged private var differType: Int16 - @NSManaged private var fileInfoFlags: Int32 - - @NSManaged var expandAllFolders: Bool - @NSManaged var exclusionFileFilters: String? - - @NSManaged var followSymLinks: Bool - @NSManaged var traverseFilteredFolders: Bool - @NSManaged var timestampToleranceSeconds: Double - - @NSManaged var skipPackages: Bool - @NSManaged var allowedPackages: String? - - @NSManaged var fileNameAlignmentsData: Data? - - // Not yet used - // periphery:ignore - @NSManaged private var ignoreDST: NSNumber? - // periphery:ignore - @NSManaged private var ignoreTimeZone: NSNumber? - - // Used only with ItemType == .folder - // exposed with computed properties and custom types - // periphery:ignore - @NSManaged private var visibleColumns: NSNumber? - @NSManaged private var sortColumn: Int32 - @NSManaged private var sortAscending: Bool - @NSManaged private var sortSide: Int32 - - // Only expand subfolders with differences - // periphery:ignore - @NSManaged private var expandOnlyWithDiffs: Bool -} - -extension SessionDiff { - static let entityName = "Session" - - @nonobjc public class func fetchRequest() -> NSFetchRequest { - NSFetchRequest(entityName: entityName) - } - - static func newObject(_ moc: NSManagedObjectContext) -> SessionDiff? { - NSEntityDescription.insertNewObject( - forEntityName: entityName, - into: moc - ) as? SessionDiff - } - - override open func awakeFromInsert() { - super.awakeFromInsert() - - let prefs = CommonPrefs.shared - - displayOptions = prefs.displayOptions - comparatorOptions = prefs.comparatorOptions - if let defaultFileFilters = prefs.defaultFileFilters { - exclusionFileFilters = defaultFileFilters - } - followSymLinks = prefs.followSymLinks - - traverseFilteredFolders = prefs.bool(forKey: .traverseFilteredFolders) - timestampToleranceSeconds = prefs.number(forKey: .timestampToleranceSeconds, 0).doubleValue - skipPackages = prefs.bool(forKey: .skipPackages) - fileExtraOptions = FileExtraOptions(rawValue: prefs.integer(forKey: .fileInfoFlags)) - expandAllFolders = prefs.bool(forKey: .expandAllFolders) - - extraData = ExtraData.fromUserDefaults() - - leftReadOnly = false - rightReadOnly = false - - itemType = .folder - } - - override open func awakeFromFetch() { - super.awakeFromFetch() - - if let path = leftPath { - leftPath = URL(filePath: path).standardizingPath - } - if let path = rightPath { - rightPath = URL(filePath: path).standardizingPath - } - - if timestampToleranceSeconds < 0 { - timestampToleranceSeconds = 0 - } - - assignDefaultAlignFlags() - } - - var exclusionFileFiltersPredicate: NSPredicate? { - guard let exclusionFileFilters else { - return nil - } - return NSPredicate(format: exclusionFileFilters) - } - - var leftPath: String? { - get { pathValue("leftPath") } - set { setPathValue(forKey: "leftPath", path: newValue) } - } - - var rightPath: String? { - get { pathValue("rightPath") } - set { setPathValue(forKey: "rightPath", path: newValue) } - } - - private func pathValue(_ keyName: String) -> String? { - willAccessValue(forKey: keyName) - let value = primitiveValue(forKey: keyName) as? String - didAccessValue(forKey: keyName) - - return value - } - - private func setPathValue(forKey keyName: String, path: String?) { - let value = path?.standardizingPath - - willChangeValue(forKey: keyName) - setPrimitiveValue(value, forKey: keyName) - didChangeValue(forKey: keyName) - } - - // existing sessions may not have a valid default value for Align flag - private func assignDefaultAlignFlags() { - let options = comparatorOptions - if options.onlyAlignFlags.isEmpty { - let alignFlag = CommonPrefs.shared.comparatorOptions.onlyAlignFlags - comparatorOptions = [options, alignFlag] - } - } - - var itemType: ItemType? { - get { ItemType(rawValue: differType) } - set { - if let newValue { - differType = newValue.rawValue - } else { - differType = 0 - } - } - } - - var comparatorOptions: ComparatorOptions { - get { .init(rawValue: Int(comparatorFlags)) } - set { comparatorFlags = Int32(newValue.rawValue) } - } - - var displayOptions: DisplayOptions { - get { .init(rawValue: Int(displayFilters)) } - set { displayFilters = Int32(newValue.rawValue) } - } - - var fileExtraOptions: FileExtraOptions { - get { .init(rawValue: Int(fileInfoFlags)) } - set { fileInfoFlags = Int32(newValue.rawValue) } - } - - var isCurrentSortAscending: Bool { - get { sortAscending } - set { sortAscending = newValue } - } - - var currentSortColumn: Column { - get { Column(rawValue: Int(sortColumn)) ?? .name } - set { sortColumn = Int32(newValue.rawValue) } - } - - var currentSortSide: Side { - get { Side(rawValue: Int(sortSide)) ?? .left } - set { sortSide = Int32(newValue.rawValue) } - } - - /// Returns the default file filters in CommonPrefs, if CommonPrefs doesn't contain any value - /// then inspect the Session model default value - static func defaultFileFilters() -> String? { - if let defaultFilters = CommonPrefs.shared.defaultFileFilters { - return defaultFilters - } - - let model = NSManagedObjectModel.mergedModel(from: nil) - let entity = model?.entitiesByName[entityName] - let attribute = entity?.attributesByName["exclusionFileFilters"] - return attribute?.defaultValue as? String - } - - func observeComparatorOptions(_ closure: @escaping @Sendable (ComparatorOptions) -> Void) -> NSKeyValueObservation { - observe(\.comparatorFlags, options: [.new]) { _, change in - if let flags = change.newValue { - closure(ComparatorOptions(rawValue: Int(flags))) - } - } - } -} diff --git a/Sources/Features/FilesCompare/Components/FileInfoBar.swift b/Sources/Features/FilesCompare/Components/FileInfoBar.swift deleted file mode 100644 index b9e3b24..0000000 --- a/Sources/Features/FilesCompare/Components/FileInfoBar.swift +++ /dev/null @@ -1,210 +0,0 @@ -// -// FileInfoBar.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -private let separatorWidth: CGFloat = 1.0 -private let itemSpacing: CGFloat = 6.0 -private let separatorColor = NSColor(calibratedWhite: 0.52, alpha: 1.0) - -protocol FileInfoBarDelegate: AnyObject { - func fileInfoBar(_ fileInfoBar: FileInfoBar, changedEncoding encoding: String.Encoding) -} - -class FileInfoBar: NSView { - private lazy var labelText: NSTextField = createLabelText() - private lazy var encodingPopup: NSPopUpButton = createEncodingPopup() - private lazy var eolText: NSTextField = createEolText() - - var delegate: FileInfoBarDelegate? - - var encoding: String.Encoding? { - get { - if let enc = (encodingPopup.selectedItem?.representedObject as? NSNumber)?.uintValue { - String.Encoding(rawValue: enc) - } else { - nil - } - } - - set { - if let newValue, - let index = encodingPopup.menu?.indexOfItem(withRepresentedObject: newValue.rawValue) { - if index != -1 { - encodingPopup.selectItem(at: index) - } - } - } - } - - var eol: EndOfLine = .missing { - didSet { - eolText.stringValue = eol.description - } - } - - var fileAttrs: [FileAttributeKey: Any]? { - didSet { - setLabel(fileAttrs) - } - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - let stackView = NSStackView(views: [ - labelText, - createSeparator(), - encodingPopup, - createSeparator(), - eolText, - ]) - - stackView.orientation = .horizontal - stackView.spacing = itemSpacing - stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.alignment = .centerY - - addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: topAnchor), - stackView.trailingAnchor.constraint(equalTo: trailingAnchor), - stackView.bottomAnchor.constraint(equalTo: bottomAnchor), - ]) - } - - private func createLabelText() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.isEditable = false - view.isBordered = false - view.drawsBackground = false - view.alignment = .right - view.translatesAutoresizingMaskIntoConstraints = false - - let cell = TextFieldVerticalCentered() - cell.lineBreakMode = .byClipping - - view.cell = cell - - // set the font after the cell otherwise it is lost - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - return view - } - - private func createEncodingPopup() -> NSPopUpButton { - let view = NSPopUpButton(frame: .zero) - - view.cell = EncodingPopUpButtonCell(textCell: "") - view.isBordered = false - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(encodingAction) - - return view - } - - private func createEolText() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.isEditable = false - view.isBordered = false - view.drawsBackground = false - view.translatesAutoresizingMaskIntoConstraints = false - - let cell = TextFieldVerticalCentered() - cell.lineBreakMode = .byClipping - - view.cell = cell - - // set the font after the cell otherwise it is lost - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - return view - } - - private func createSeparator() -> NSView { - let view = NSView() - view.wantsLayer = true - view.layer?.backgroundColor = separatorColor.cgColor - view.translatesAutoresizingMaskIntoConstraints = false - - view.widthAnchor.constraint(equalToConstant: separatorWidth).isActive = true - - return view - } - - // MARK: - Actions methods - - @objc func encodingAction(_: AnyObject) { - if let delegate, - let encoding { - delegate.fileInfoBar(self, changedEncoding: encoding) - } - } - - // MARK: UI methods - - func setLabel(_ text: String) { - labelText.stringValue = text - } - - func setLabel(_ fileAttrs: [FileAttributeKey: Any]?) { - guard let fileAttrs, - let modificationDate = fileAttrs[.modificationDate] as? Date else { - setLabel("") - return - } - let dateFormatter = DateFormatter() - - dateFormatter.dateFormat = DateFormatter.dateFormat( - fromTemplate: "ddMMyyyyHHmmss", - options: 0, - locale: Locale.current - ) - let dateText = dateFormatter.string(from: modificationDate) - let fileSize = fileAttrs[.size] as? NSNumber ?? NSNumber(value: -1) - - setLabel(String.localizedStringWithFormat(NSLocalizedString("%@ - %@ bytes", comment: ""), dateText, fileSize)) - } - - /** - * update attributes reloading from the path - * return true if the modification date has been changed - */ - func updateFileAttrsFromPath(_ path: String) -> Bool { - var changed = false - - // don't do anything if file no longer exists (for example when launched as external diff) - guard FileManager.default.fileExists(atPath: path) else { - return changed - } - let attrs = try? FileManager.default.attributesOfItem(atPath: path) - if let attrs, - let updatedDate = attrs[.modificationDate] as? Date, - let currentDate = fileAttrs?[.modificationDate] as? Date, - currentDate != updatedDate { - changed = true - } - - fileAttrs = attrs - - return changed - } -} diff --git a/Sources/Features/FilesCompare/Components/FilePanelView+File.swift b/Sources/Features/FilesCompare/Components/FilePanelView+File.swift deleted file mode 100644 index 82a4582..0000000 --- a/Sources/Features/FilesCompare/Components/FilePanelView+File.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// FilePanelView+File.swift -// VisualDiffer -// -// Created by davide ficano on 06/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilePanelView { - func readFile(_ path: URL) throws -> String { - treeView.isDirty = false - fileInfoBar.encoding = CommonPrefs.shared.defaultEncoding - fileInfoBar.fileAttrs = nil - - let secureURL = SecureBookmark.shared.secure(fromBookmark: path, startSecured: true) - defer { - if let secureURL { - SecureBookmark.shared.stopAccessing(url: secureURL) - } - } - - fileInfoBar.fileAttrs = try FileManager.default.attributesOfItem(atPath: path.osPath) - - return try readContent(path) - } - - private func readContent(_ path: URL) throws -> String { - // pessimistic lock - treeView.isEditAllowed = false - - let result = try path.readStructuredContent(encoding: fileInfoBar.encoding ?? String.Encoding.utf8) - let content = result.plainText - let docType = result.contentType - let encoding = result.encoding - - if content != nil, - let docType { - treeView.isEditAllowed = docType == .plainText - } - - if let encoding { - fileInfoBar.encoding = encoding - } - return content ?? "" - } -} diff --git a/Sources/Features/FilesCompare/Components/FilePanelView.swift b/Sources/Features/FilesCompare/Components/FilePanelView.swift deleted file mode 100644 index ee5d8e1..0000000 --- a/Sources/Features/FilesCompare/Components/FilePanelView.swift +++ /dev/null @@ -1,124 +0,0 @@ -// -// FilePanelView.swift -// VisualDiffer -// -// Created by davide ficano on 12/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FilePanelView: TablePanelView { - lazy var columnSlider: NSSlider = createSlider() - var fileInfoBar: FileInfoBar - - var isEditAllowed: Bool { - get { - treeView.isEditAllowed - } - set { - treeView.isEditAllowed = newValue - } - } - - init(side: DisplaySide) { - fileInfoBar = Self.createFileInfoBar() - super.init(treeView: FilesTableView(frame: .zero), bottomBar: fileInfoBar) - self.side = side - } - - private static func createFileInfoBar() -> FileInfoBar { - let view = FileInfoBar(frame: .zero) - - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createSlider() -> NSSlider { - let view = NSSlider(frame: .zero) - - view.toolTip = NSLocalizedString("Horizontal synchronized scroll", comment: "") - view.sliderType = .linear - view.tickMarkPosition = .above - view.minValue = 0 - view.maxValue = 100 - view.controlSize = .small - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - override func addSubviews() { - super.addSubviews() - - // fileInfoBar is added as bottomBar inside super.addSubviews - addSubview(columnSlider) - } - - override func setupBottomBarConstraints() { - let constraints = columnSlider.leadingAnchor.constraint(equalTo: leadingAnchor) - - NSLayoutConstraint.activate([ - scrollView.bottomAnchor.constraint(equalTo: columnSlider.topAnchor), - - constraints, - columnSlider.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4), - columnSlider.bottomAnchor.constraint(equalTo: bottomBar.topAnchor), - - bottomBar.leadingAnchor.constraint(equalTo: leadingAnchor), - bottomBar.trailingAnchor.constraint(equalTo: trailingAnchor), - bottomBar.bottomAnchor.constraint(equalTo: bottomAnchor), - ]) - } - - func setSliderMaxValue(_ left: [DiffLine], right: [DiffLine]) { - var maxColumn = 0 - - for (index, line) in left.enumerated() { - let tempMax = max(line.text.count, right[index].text.count) - if tempMax > maxColumn { - maxColumn = tempMax - } - } - columnSlider.maxValue = Double(maxColumn) - } - - func setSliderChangeAction(_ target: AnyObject?, action: Selector?) { - columnSlider.target = target - columnSlider.action = action - } - - func setDelegate(_ delegate: PathControlDelegate - & FilesTableViewDelegate - & FileInfoBarDelegate - & NSTableViewDataSource) { - pathViewDelegate = delegate - - treeView.delegate = delegate - treeView.dataSource = delegate - - fileInfoBar.delegate = delegate - } - - /** - * If word wrap is enabled calling reloadData() can cause the rows to be not visible - * until table is resized or mouse is dragged, calling this method the rows are updated correctly - */ - func reloadTreeData() { - guard let scrollView = treeView.enclosingScrollView else { - treeView.reloadData() - return - } - - let savedOrigin = scrollView.documentVisibleRect.origin - - let all = IndexSet(integersIn: 0 ..< treeView.numberOfRows) - treeView.noteHeightOfRows(withIndexesChanged: all) - - treeView.reloadData(restoreSelection: true) - treeView.layoutSubtreeIfNeeded() - - treeView.scroll(savedOrigin) - - treeView.setNeedsDisplay(treeView.bounds) - } -} diff --git a/Sources/Features/FilesCompare/Components/FileThumbnailView.swift b/Sources/Features/FilesCompare/Components/FileThumbnailView.swift deleted file mode 100644 index 59261ca..0000000 --- a/Sources/Features/FilesCompare/Components/FileThumbnailView.swift +++ /dev/null @@ -1,128 +0,0 @@ -// -// FileThumbnailView.swift -// VisualDiffer -// -// Created by davide ficano on 25/07/11. -// Copyright (c) 2011 visualdiffer.com -// - -class FileThumbnailView: NSView { - var diffResult: DiffResult? - var view: FilesTableView? - var linesCount = 0 - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - } - - override var isFlipped: Bool { - true - } - - private func getPositionFromLine(_ lineNumber: Int, linesCount: Int, bounds: NSRect) -> CGFloat { - CGFloat(lineNumber) * bounds.size.height / CGFloat(linesCount) - } - - private func getLineFromPosition(_ point: NSPoint, linesCount: Int, bounds: NSRect) -> Int { - Int(point.y * CGFloat(linesCount) / bounds.size.height) - } - - private func createRect(_ section: DiffSection, bounds: NSRect) -> NSRect { - guard let diffResult else { - return .zero - } - let resultLinesCount = diffResult.leftSide.lines.count - let top = getPositionFromLine(section.start, linesCount: resultLinesCount, bounds: bounds) - let height = getPositionFromLine(section.end - section.start + 1, linesCount: resultLinesCount, bounds: bounds) - - return NSRect(x: 0, y: top, width: 0, height: height) - } - - private func drawPositionBox(_ bounds: NSRect) { - guard let view else { - return - } - let firstRow = view.firstVisibleRow - let lastRow = view.lastVisibleRow - - let posRect = NSRect( - x: 0, - y: getPositionFromLine(firstRow, linesCount: linesCount, bounds: bounds), - width: bounds.size.width, - height: getPositionFromLine(lastRow - firstRow, linesCount: linesCount, bounds: bounds) - ) - - if let backgroundColor = CommonPrefs.shared.fileColor(.positionBox)?.background { - backgroundColor.setFill() - posRect.frame(withWidth: 2.0) - } - } - - override func draw(_: NSRect) { - guard let diffResult, - let backgroundColor = CommonPrefs.shared.fileColor(.thumbnail)?.background else { - drawPositionBox(bounds) - return - } - // draw background - backgroundColor.setFill() - bounds.fill() - - for section in diffResult.sections { - var rect = createRect(section, bounds: bounds) - let index = section.start - - let leftLine = diffResult.leftSide.lines[index] - rect.size.width = bounds.size.width / 2 - if let oldColor = leftLine.colors?.text { - oldColor.setFill() - rect.fill() - } - - let newLine = diffResult.rightSide.lines[index] - rect.origin.x = rect.size.width - if let newColor = newLine.colors?.text { - newColor.setFill() - rect.fill() - } - } - drawPositionBox(bounds) - } - - // http://borkware.com/quickies/one?topic=NSView - override func resetCursorRects() { - super.resetCursorRects() - addCursorRect(bounds, cursor: .pointingHand) - } - - // MARK: - Mouse events - - override func mouseDown(with event: NSEvent) { - guard let diffResult, - let view else { - return - } - let localPoint = convert(event.locationInWindow, from: nil) - let leftLinesCount = diffResult.leftSide.lines.count - var line = getLineFromPosition(localPoint, linesCount: leftLinesCount, bounds: bounds) - - if leftLinesCount != linesCount { - line = diffResult.leftSide.lines[line].filteredIndex - } - view.scrollTo(row: line, center: true) - - let indexes = IndexSet(integer: line) - view.selectRowIndexes(indexes, byExtendingSelection: false) - view.linkedView?.selectRowIndexes(indexes, byExtendingSelection: false) - } - - override func scrollWheel(with event: NSEvent) { - // redirect scroll to view - view?.scrollWheel(with: event) - } -} diff --git a/Sources/Features/FilesCompare/Components/FilesScopeBar.swift b/Sources/Features/FilesCompare/Components/FilesScopeBar.swift deleted file mode 100644 index 8d19453..0000000 --- a/Sources/Features/FilesCompare/Components/FilesScopeBar.swift +++ /dev/null @@ -1,247 +0,0 @@ -// -// FilesScopeBar.swift -// VisualDiffer -// -// Created by davide ficano on 26/10/11. -// Copyright (c) 2011 visualdiffer.com -// - -// Items for scopebarFileGroupDisplayOptions -private let showWhitespacesId = "WhiteSpacesId" - -// Items for scopebarFileGroupFilterOptions -private let allId = "AllId" -private let differencesId = "JustDiffsId" -private let justMatchesId = "JustMatchesId" - -@objc protocol FilesScopeBarDelegate: AnyObject { - @objc func filesScopeBar(_ filesScopeBar: FilesScopeBar, action: FilesScopeBarAction) -} - -@objc enum FilesScopeBarAction: Int { - case showWhitespaces - case showAllLines - case showJustMatchingLines - case showJustDifferentLines -} - -enum FileScopeGroupOptions: Int { - case display - case filter -} - -@MainActor class FilesScopeBar: MGScopeBar, @preconcurrency MGScopeBarDelegate { - private var groupItems = [[ScopeBarGroupKey: Any]]() - private var labels = [String: String]() - - var showLinesFilter: DiffLine.Visibility = .all { - didSet { - showLinesFilter.saveToUserDefaults() - } - } - - var showWhitespaces = false { - didSet { - CommonPrefs.shared.set(showWhitespaces, forKey: .FileScope.showWhitespaces) - } - } - - var actionDelegate: FilesScopeBarDelegate? - @objc var findView: FindText - - override init(frame frameRect: NSRect) { - findView = FindText(frame: NSRect(x: 0, y: 0, width: 400, height: 25)) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - fontSize = 11.0 - } - - @objc func initScopeBar(_ actionDelegate: FilesScopeBarDelegate) { - showLinesFilter = DiffLine.Visibility.loadFromUserDefaults() - showWhitespaces = CommonPrefs.shared.bool(forKey: .FileScope.showWhitespaces) - self.actionDelegate = actionDelegate - delegate = self - - groupItems.removeAll() - - groupItems.append([ - .selectionMode: MGScopeBarGroupSelectionMode.multiple, - .items: [ - mkItem(showWhitespacesId, NSLocalizedString("Show Whitespace", comment: "")), - ], - ]) - - groupItems.append([ - .separator: true, - .selectionMode: MGScopeBarGroupSelectionMode.radio, - .items: [ - mkItem(allId, NSLocalizedString("All", comment: "")), - mkItem(differencesId, NSLocalizedString("Just Differences", comment: "")), - mkItem(justMatchesId, NSLocalizedString("Just Matches", comment: "")), - ], - ]) - - // Dictionary doesn't preserve order so we can't use it to fill the array - // So first fill the array then labels - labels.removeAll() - for group in groupItems { - if let groupItems = group[.items] as? [[ScopeBarItem: String]] { - for dict in groupItems { - if let identifier = dict[.identifier], - let name = dict[.name] { - labels[identifier] = name - } - } - } - } - - notifyDefaultSelections = false - smartResizeEnabled = true - - reloadData() - } - - private func mkItem(_ identifier: String, _ name: String) -> [ScopeBarItem: String] { - [ - .identifier: identifier, - .name: name, - ] - } - - // MARK: - MGScopeBarDelegate methods - - func numberOfGroups(in _: MGScopeBar) -> Int { - groupItems.count - } - - func scopeBar(_: MGScopeBar, itemIdentifiersForGroup groupNumber: Int) -> [Any] { - guard let items = groupItems[groupNumber][.items], - let itemIdentifiers = items as? [[ScopeBarItem: String]] else { - fatalError("Unexpected data format in groupItems") - } - return itemIdentifiers.compactMap { $0[.identifier] } - } - - func scopeBar(_: MGScopeBar, labelForGroup groupNumber: Int) -> String? { - groupItems[groupNumber][.label] as? String // might be nil, which is fine (nil means no label). - } - - func scopeBar(_: MGScopeBar, titleOfItem identifier: String, inGroup groupNumber: Int) -> String? { - if groupItems[groupNumber][.items] != nil { - return labels[identifier] - } - return nil - } - - func scopeBar(_: MGScopeBar, selectionModeForGroup groupNumber: Int) -> MGScopeBarGroupSelectionMode { - (groupItems[groupNumber][.selectionMode] as? MGScopeBarGroupSelectionMode) ?? .radio - } - - func scopeBar(_: MGScopeBar, showSeparatorBeforeGroup groupNumber: Int) -> Bool { - // Optional method. If not implemented, all groups except the first will have a separator before them. - groupItems[groupNumber][.separator] as? Bool ?? false - } - - func scopeBar(_: MGScopeBar, imageForItem _: String, inGroup _: Int) -> NSImage? { - nil - } - - func accessoryView(for _: MGScopeBar) -> NSView? { - findView - } - - func scopeBar(_: MGScopeBar, selectedStateChanged _: Bool, forItem identifier: String, inGroup groupNumber: Int) { - guard let actionDelegate, - let group = FileScopeGroupOptions(rawValue: groupNumber) else { - return - } - switch group { - case .display: - if identifier == showWhitespacesId { - showWhitespaces.toggle() - actionDelegate.filesScopeBar(self, action: .showWhitespaces) - } - case .filter: - if identifier == allId { - showLinesFilter = .all - actionDelegate.filesScopeBar(self, action: .showAllLines) - } else if identifier == justMatchesId { - showLinesFilter = .matches - actionDelegate.filesScopeBar(self, action: .showJustMatchingLines) - } else if identifier == differencesId { - showLinesFilter = .differences - actionDelegate.filesScopeBar(self, action: .showJustDifferentLines) - } - } - } - - func showLineFilter( - _ type: DiffLine.Visibility, - informDelegate: Bool - ) { - showLinesFilter = type - - setSelected( - true, - forItem: showLinesFilter.identifier, - inGroup: FileScopeGroupOptions.filter.rawValue, - informDelegate: informDelegate - ) - } - - func showWhitespaces( - _ show: Bool, - informDelegate: Bool - ) { - showWhitespaces = show - setSelected( - show, - forItem: showWhitespacesId, - inGroup: FileScopeGroupOptions.display.rawValue, - informDelegate: informDelegate - ) - } - - @discardableResult - override func becomeFirstResponder() -> Bool { - findView.becomeFirstResponder() - } -} - -extension CommonPrefs.Name { - enum FileScope { - static let showWhitespaces = CommonPrefs.Name(rawValue: "showWhitespaces") - static let showLinesFilterType = CommonPrefs.Name(rawValue: "showLinesFilterType") - } -} - -extension DiffLine.Visibility { - static func loadFromUserDefaults() -> Self { - DiffLine.Visibility(rawValue: CommonPrefs.shared.integer(forKey: .FileScope.showLinesFilterType)) ?? .all - } - - func saveToUserDefaults() { - CommonPrefs.shared.set(rawValue, forKey: .FileScope.showLinesFilterType) - } - - var identifier: String { - switch self { - case .all: - allId - case .matches: - justMatchesId - case .differences: - differencesId - } - } -} diff --git a/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableView+Menu.swift b/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableView+Menu.swift deleted file mode 100644 index 161fd79..0000000 --- a/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableView+Menu.swift +++ /dev/null @@ -1,154 +0,0 @@ -// -// FilesTableView+Menu.swift -// VisualDiffer -// -// Created by davide ficano on 20/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -@objc protocol FilesTableViewContextMenu { - func copyFileNames(_ sender: AnyObject?) - func copyFullPaths(_ sender: AnyObject?) - func copyLines(_ sender: AnyObject?) - func deleteLines(_ sender: AnyObject?) - func popupOpenWithApp(_ sender: AnyObject?) - func saveFile(_ sender: AnyObject?) - func selectSection(_ sender: AnyObject?) - func showInFinder(_ sender: AnyObject?) - func showWhitespaces(_ sender: AnyObject?) -} - -extension FilesTableView { - override class var defaultMenu: NSMenu? { - let theMenu = NSMenu(title: NSLocalizedString("Contextual Menu", comment: "")) - theMenu.autoenablesItems = false - - theMenu.addItem( - withTitle: NSLocalizedString("Show Whitespace", comment: ""), - action: #selector(FilesTableViewContextMenu.showWhitespaces), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Select Section", comment: ""), - action: #selector(FilesTableViewContextMenu.selectSection), - keyEquivalent: "" - ) - theMenu.addItem(NSMenuItem.separator()) - - theMenu.addItem( - withTitle: NSLocalizedString("Copy Lines", comment: ""), - action: #selector(FilesTableViewContextMenu.copyLines), - keyEquivalent: "" - ) - theMenu.addItem(NSMenuItem.separator()) - theMenu.addItem( - withTitle: NSLocalizedString("Delete Lines", comment: ""), - action: #selector(FilesTableViewContextMenu.deleteLines), - keyEquivalent: "" - ) - theMenu.addItem(NSMenuItem.separator()) - - var item: NSMenuItem - theMenu.addItem( - withTitle: NSLocalizedString("Copy Path", comment: ""), - action: #selector(FilesTableViewContextMenu.copyFullPaths), - keyEquivalent: "" - ) - item = theMenu.addItem( - withTitle: NSLocalizedString("Copy File Name", comment: ""), - action: #selector(FilesTableViewContextMenu.copyFileNames), - keyEquivalent: "" - ) - item.keyEquivalentModifierMask = .option - item.isAlternate = true - - theMenu.addItem( - withTitle: NSLocalizedString("Show in Finder", comment: ""), - action: #selector(FilesTableViewContextMenu.showInFinder), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Open With", comment: ""), - action: #selector(FilesTableViewContextMenu.popupOpenWithApp), - keyEquivalent: "" - ) - - theMenu.addItem(NSMenuItem.separator()) - theMenu.addItem( - withTitle: NSLocalizedString("Save", comment: ""), - action: #selector(FilesTableViewContextMenu.saveFile), - keyEquivalent: "" - ) - - return theMenu - } - - // taken from http://www.cocoadev.com/index.pl?RightClickSelectInTableView - // taken from cyberduck-src-3.6.1/3.6.1/source/ch/cyberduck/ui/cocoa/view/CDListView.m - override func menu(for event: NSEvent) -> NSMenu? { - let wherePoint = convert(event.locationInWindow, from: nil) - let row = row(at: wherePoint) - - // if mouse isn't outside any row simply doesn't show menu - if row < 0 { - return nil - } - - // highlight the view containing the menu - superview?.window?.makeFirstResponder(superview) - - let indexes = selectedRowIndexes - if !indexes.contains(row) { - selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false) - } - let theMenu = Self.defaultMenu - - if let theMenu, - let delegate = delegate as? TableViewContextMenuDelegate { - var lastVisibleItem: NSMenuItem? - var hasVisibleItemBeforeSeparator = false - var hasVisibleItems = false - - for item in theMenu.items { - var hide = false - - if item.isSeparatorItem { - item.isHidden = !hasVisibleItemBeforeSeparator - hasVisibleItemBeforeSeparator = false - lastVisibleItem = item - } else { - let isValid = delegate.tableView(self, menuItem: item, hideMenuItem: &hide) - if isValid { - item.isHidden = false - item.isEnabled = true - } else { - item.isHidden = hide - item.isEnabled = false - } - if !item.isHidden { - lastVisibleItem = item - hasVisibleItemBeforeSeparator = true - hasVisibleItems = true - } - } - } - - // hide last item if it's a separator - if let lastVisibleItem, - lastVisibleItem.isSeparatorItem { - lastVisibleItem.isHidden = true - } - - if !hasVisibleItems { - theMenu.addItem( - withTitle: NSLocalizedString("No actions for current selection", comment: ""), - action: nil, - keyEquivalent: "" - ) - .isEnabled = false - } - } - - return theMenu - } -} diff --git a/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableView.swift b/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableView.swift deleted file mode 100644 index 2e7ff45..0000000 --- a/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableView.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// FilesTableView.swift -// VisualDiffer -// -// Created by davide ficano on 20/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -@MainActor protocol FilesTableViewDelegate: NSTableViewDelegate { - func setLastUsedViewResponder(_ view: FilesTableView) - func filesTableView(_ view: FilesTableView, scrollHorizontally leftScroll: Bool) - func filesTableView(_ view: FilesTableView, doubleClick lickedRow: Int) - func deleteKeyPressed(_ view: FilesTableView) -} - -class FilesTableView: NSTableView, @preconcurrency DisplayPositionable, ViewLinkable { - var linkedView: FilesTableView? - var side: DisplaySide = .left - var diffSide: DiffSide? - var isEditAllowed = false - @objc dynamic var isDirty = false { - didSet { - guard isDirty != oldValue, - let document = window?.windowController?.document else { - return - } - - // don't use NSDocumentController.currentDocument() because can be nil - // when application is not active (eg when reloading file outside application) - if isDirty { - document.updateChangeCount(.changeDone) - } else { - document.updateChangeCount(.changeUndone) - } - } - } - - var filesTableDelegate: FilesTableViewDelegate? { - delegate as? FilesTableViewDelegate - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - style = .plain - rowSizeStyle = .custom - usesAutomaticRowHeights = false - - allowsEmptySelection = true - allowsColumnReordering = false - allowsColumnResizing = true - allowsMultipleSelection = true - allowsColumnSelection = true - allowsTypeSelect = true - - focusRingType = .none - allowsExpansionToolTips = true - autosaveTableColumns = false - intercellSpacing = .zero - - // Needed by drop - registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL]) - setDraggingSourceOperationMask(.every, forLocal: true) - setDraggingSourceOperationMask(.every, forLocal: false) - - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("cellLineText")) - column.resizingMask = .autoresizingMask - - addTableColumn(column) - headerView = nil - - doubleAction = #selector(handleDoubleClick) - } - - override func becomeFirstResponder() -> Bool { - filesTableDelegate?.setLastUsedViewResponder(self) - return super.becomeFirstResponder() - } - - // MARK: Keys and mouse handlers - - @objc func handleDoubleClick(_: AnyObject) { - if clickedRow != -1 { // make sure double click was not in table header - filesTableDelegate?.filesTableView(self, doubleClick: clickedRow) - } - } - - override func keyDown(with event: NSEvent) { - let keyCode = KeyCode(rawValue: event.keyCode) - - // When an arrow key is pressed the NSNumericPadKeyMask and NSFunctionKeyMask - // are always set so we don't check them - let isArrowKeyDownWithModifiers = event.modifierFlags.contains([.shift, .control, .option, .command]) - - if event.isDeleteShortcutKey(true) { - filesTableDelegate?.deleteKeyPressed(self) - } else if keyCode == .leftArrow, !isArrowKeyDownWithModifiers { - filesTableDelegate?.filesTableView(self, scrollHorizontally: true) - } else if keyCode == .rightArrow, !isArrowKeyDownWithModifiers { - filesTableDelegate?.filesTableView(self, scrollHorizontally: false) - } else { - super.keyDown(with: event) - } - } - - override func scrollWheel(with event: NSEvent) { - super.scrollWheel(with: event) - - if event.deltaX != 0 { - filesTableDelegate?.filesTableView(self, scrollHorizontally: event.deltaX > 0) - } - } - - func reloadData(restoreSelection: Bool) { - if restoreSelection { - let currentSelection = selectedRowIndexes - reloadData() - selectRowIndexes(currentSelection, byExtendingSelection: false) - } else { - reloadData() - } - } -} diff --git a/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableViewFindTextDelegate.swift b/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableViewFindTextDelegate.swift deleted file mode 100644 index 076bd94..0000000 --- a/Sources/Features/FilesCompare/Components/FilesTableView/FilesTableViewFindTextDelegate.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// FilesTableViewFindTextDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 23/11/20. -// Copyright (c) 2020 visualdiffer.com -// - -@MainActor @objc class FilesTableViewFindTextDelegate: NSObject, @preconcurrency FindTextDelegate { - let view: FilesTableView - - private var lines = [Int]() - - @objc init(view: FilesTableView) { - self.view = view - } - - func find(findText _: FindText, searchPattern pattern: String) -> Bool { - let globPattern = pattern.convertGlobMetaCharsToRegexpMetaChars() - guard let re = try? NSRegularExpression( - pattern: globPattern, - options: .caseInsensitive - ), - let left = view.diffSide?.lines, - let right = view.linkedView?.diffSide?.lines else { - return false - } - - for i in 0 ..< left.count { - var line = left[i].text - var range = re.rangeOfFirstMatch( - in: line, - options: [], - range: NSRange(location: 0, length: line.count) - ) - if range.location == NSNotFound { - line = right[i].text - range = re.rangeOfFirstMatch( - in: line, - options: [], - range: NSRange(location: 0, length: line.count) - ) - } - - if range.location != NSNotFound { - lines.append(i) - } - } - return true - } - - func find(findText _: FindText, moveToMatchIndex index: Int) -> Bool { - let row = lines[index] - - guard let dataSource = view.dataSource, - let count = dataSource.numberOfRows?(in: view) else { - return false - } - - if row < count { - view.scrollRowToVisible(row) - let indexes = IndexSet(integer: row) - view.selectRowIndexes(indexes, byExtendingSelection: false) - view.linkedView?.selectRowIndexes(indexes, byExtendingSelection: false) - - return true - } - lines.remove(at: index) - - return false - } - - func numberOfMatches(in _: FindText) -> Int { - lines.count - } - - func clearMatches(in _: FindText) { - lines.removeAll() - } -} diff --git a/Sources/Features/FilesCompare/Components/LineNumberTableCellView.swift b/Sources/Features/FilesCompare/Components/LineNumberTableCellView.swift deleted file mode 100644 index 8b200b3..0000000 --- a/Sources/Features/FilesCompare/Components/LineNumberTableCellView.swift +++ /dev/null @@ -1,295 +0,0 @@ -// -// LineNumberTableCellView.swift -// VisualDiffer -// -// Created by davide ficano on 29/07/11. -// Copyright (c) 2011 visualdiffer.com -// - -struct LineNumberCellData { - let separatorWidth: CGFloat = 1.0 - let leftWidth: CGFloat = 10.0 - let rightWidth: CGFloat = 10.0 - let sectionSeparatorHeight: CGFloat = 2.0 - let leadingTextPadding: CGFloat = 2.0 -} - -class LineNumberTableCellView: NSTableCellView { - var lineNumberWidth: CGFloat = 0.0 { - didSet { - lineNumberWidthConstraint?.constant = lineNumberWidth - } - } - - var cellData = LineNumberCellData() - - private let lineNumberTextField: NSTextField = { - let view = NSTextField() - view.isBezeled = false - view.drawsBackground = false - view.isEditable = false - view.isSelectable = false - view.alignment = .right - view.lineBreakMode = .byClipping - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private let contentTextField: NSTextField = { - let view = NSTextField(wrappingLabelWithString: "") - view.isBezeled = false - view.drawsBackground = false - view.isEditable = false - view.isSelectable = false - view.alignment = .left - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private let separatorView: NSView = { - let view = NSView() - - view.translatesAutoresizingMaskIntoConstraints = false - return view - }() - - private let missingLineImageView: TiledImageView = { - let view = TiledImageView() - view.translatesAutoresizingMaskIntoConstraints = false - view.isHidden = true - - return view - }() - - private let sectionSeparatorView: NSView = { - let view = NSView() - view.translatesAutoresizingMaskIntoConstraints = false - view.isHidden = true - - return view - }() - - private var lineNumberWidthConstraint: NSLayoutConstraint? - private var lineNumberCenterYConstraint: NSLayoutConstraint? - private var lineNumberTopConstraint: NSLayoutConstraint? - - var diffLine: DiffLine? { - didSet { - updateContent() - } - } - - var formattedText: String? { - didSet { - updateContent() - } - } - - var font: NSFont? { - didSet { - lineNumberTextField.font = font - contentTextField.font = font - updateContent() - } - } - - var isWordWrapEnabled: Bool = false { - didSet { - updateWordWrapSettings() - updateContent() - } - } - - var isHighlighted = false { - didSet { - updateColors() - } - } - - dynamic var isSelected: Bool = false { - didSet { - if oldValue != isSelected { - updateColors() - } - } - } - - init() { - super.init(frame: .zero) - - setupViews() - setupConstraints() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Setup - - private func setupViews() { - addSubview(lineNumberTextField) - addSubview(separatorView) - addSubview(contentTextField) - addSubview(missingLineImageView) - addSubview(sectionSeparatorView) - } - - private func setupConstraints() { - // align tyoe: center (no wrap) or top (wrap) - lineNumberCenterYConstraint = lineNumberTextField.centerYAnchor.constraint(equalTo: centerYAnchor) - lineNumberTopConstraint = lineNumberTextField.topAnchor.constraint(equalTo: topAnchor) - lineNumberCenterYConstraint?.isActive = true - - lineNumberWidthConstraint = lineNumberTextField.widthAnchor.constraint(equalToConstant: lineNumberWidth) - lineNumberWidthConstraint?.isActive = true - - NSLayoutConstraint.activate([ - lineNumberTextField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: cellData.leftWidth), - lineNumberTextField.heightAnchor.constraint(equalTo: heightAnchor), - - separatorView.leadingAnchor.constraint(equalTo: lineNumberTextField.trailingAnchor, constant: cellData.rightWidth), - separatorView.topAnchor.constraint(equalTo: topAnchor), - separatorView.bottomAnchor.constraint(equalTo: bottomAnchor), - separatorView.widthAnchor.constraint(equalToConstant: cellData.separatorWidth), - - contentTextField.leadingAnchor.constraint(equalTo: separatorView.trailingAnchor, constant: cellData.leadingTextPadding), - contentTextField.trailingAnchor.constraint(equalTo: trailingAnchor), - contentTextField.topAnchor.constraint(equalTo: topAnchor), - contentTextField.bottomAnchor.constraint(equalTo: bottomAnchor), - - missingLineImageView.leadingAnchor.constraint(equalTo: separatorView.trailingAnchor), - missingLineImageView.trailingAnchor.constraint(equalTo: trailingAnchor), - missingLineImageView.topAnchor.constraint(equalTo: topAnchor), - missingLineImageView.bottomAnchor.constraint(equalTo: bottomAnchor), - - sectionSeparatorView.leadingAnchor.constraint(equalTo: leadingAnchor), - sectionSeparatorView.trailingAnchor.constraint(equalTo: trailingAnchor), - sectionSeparatorView.bottomAnchor.constraint(equalTo: bottomAnchor), - sectionSeparatorView.heightAnchor.constraint(equalToConstant: cellData.sectionSeparatorHeight), - ]) - } - - private func updateWordWrapSettings() { - if isWordWrapEnabled { - contentTextField.cell?.wraps = true - contentTextField.cell?.isScrollable = false - contentTextField.maximumNumberOfLines = 0 - contentTextField.lineBreakMode = .byWordWrapping - - lineNumberCenterYConstraint?.isActive = false - lineNumberTopConstraint?.isActive = true - } else { - contentTextField.cell?.wraps = false - contentTextField.cell?.isScrollable = true - contentTextField.maximumNumberOfLines = 1 - contentTextField.lineBreakMode = .byClipping - - lineNumberTopConstraint?.isActive = false - lineNumberCenterYConstraint?.isActive = true - } - } - - // MARK: - Update Content - - private func updateContent() { - guard let diffLine else { - hideAllContent() - return - } - - if diffLine.number > 0 { - showLineNumberAndText() - lineNumberTextField.stringValue = String(format: "%ld", diffLine.number) - contentTextField.stringValue = formattedText ?? diffLine.text - updateColors() - updateSectionSeparator() - } else { - showMissingLineImage() - } - } - - private func showLineNumberAndText() { - lineNumberTextField.isHidden = false - contentTextField.isHidden = false - missingLineImageView.isHidden = true - } - - private func showMissingLineImage() { - lineNumberTextField.isHidden = true - contentTextField.isHidden = true - missingLineImageView.isHidden = false - - if let emptyImage = NSImage(named: VDImageNameEmpty) { - let color = CommonPrefs.shared.fileColor(.missing)?.background ?? NSColor.labelColor - missingLineImageView.image = emptyImage.tinted(with: color) - } - } - - private func hideAllContent() { - lineNumberTextField.isHidden = true - contentTextField.isHidden = true - missingLineImageView.isHidden = true - sectionSeparatorView.isHidden = true - } - - private func updateColors() { - guard let diffLine else { - return - } - - if isSelected { - lineNumberTextField.textColor = diffLine.color(for: .text, isSelected: isHighlighted) - } else { - lineNumberTextField.textColor = CommonPrefs.shared.fileColor(.lineNumber)?.text - } - - let textColor = diffLine.color(for: .text, isSelected: isSelected) - let backgroundColor = diffLine.color(for: .background, isSelected: isSelected) - - contentTextField.wantsLayer = true - contentTextField.textColor = textColor - contentTextField.layer?.backgroundColor = backgroundColor.cgColor - - if let separatorColor = CommonPrefs.shared.fileColor(.lineNumberSeparator)?.text { - separatorView.wantsLayer = true - separatorView.layer?.backgroundColor = separatorColor.cgColor - } - } - - private func updateSectionSeparator() { - guard let diffLine else { - sectionSeparatorView.isHidden = true - return - } - - if diffLine.isSectionSeparator, - let sectionColor = CommonPrefs.shared.fileColor(.sectionSeparatorLine)?.text { - sectionSeparatorView.isHidden = false - sectionSeparatorView.wantsLayer = true - sectionSeparatorView.layer?.backgroundColor = sectionColor.cgColor - } else { - sectionSeparatorView.isHidden = true - } - } - - /** - * This is called by the parent as discussed on - * https://developer.apple.com/documentation/appkit/nstablecellview/1483206-backgroundstyle?language=objc - * "The default implementation automatically forwards calls to all subviews that implement setBackgroundStyle" - */ - override var backgroundStyle: NSView.BackgroundStyle { - didSet { - isHighlighted = backgroundStyle == .emphasized - needsDisplay = true - } - } - - // Ensure we're opaque for better performance - override var isOpaque: Bool { - true - } -} diff --git a/Sources/Features/FilesCompare/Components/LineNumberTableRowView.swift b/Sources/Features/FilesCompare/Components/LineNumberTableRowView.swift deleted file mode 100644 index 45e5cab..0000000 --- a/Sources/Features/FilesCompare/Components/LineNumberTableRowView.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// LineNumberTableRowView.swift -// VisualDiffer -// -// Created by davide ficano on 22/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -class LineNumberTableRowView: NSTableRowView { - override func drawSelection(in dirtyRect: NSRect) { - guard let selectionColor = CommonPrefs.shared.fileColor(.selectedRow)?.background else { - return - } - - // if the enclosing tableview has the focus use the color without modification - if isEmphasized { - selectionColor.set() - } else { - (selectionColor.shadow(withLevel: 0.25) ?? selectionColor).set() - } - dirtyRect.fill() - } -} diff --git a/Sources/Features/FilesCompare/Components/NSAlert+DirtyFiles.swift b/Sources/Features/FilesCompare/Components/NSAlert+DirtyFiles.swift deleted file mode 100644 index 723e126..0000000 --- a/Sources/Features/FilesCompare/Components/NSAlert+DirtyFiles.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// NSAlert+DirtyFiles.swift -// VisualDiffer -// -// Created by davide ficano on 23/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSApplication.ModalResponse { - static let saveOnlyLeft = NSApplication.ModalResponse(rawValue: 2000) - static let saveOnlyRight = NSApplication.ModalResponse(rawValue: 2001) - static let saveBoth = NSApplication.ModalResponse(rawValue: 2002) - static let saveDontSave = NSApplication.ModalResponse(rawValue: 2003) -} - -extension NSAlert { - func saveFiles(isLeftDirty: Bool, isRightDirty: Bool) -> NSApplication.ModalResponse { - if isLeftDirty, isRightDirty { - addButton(withTitle: NSLocalizedString("Save Checked", comment: "")).tag = NSApplication.ModalResponse.OK.rawValue - addButton(withTitle: NSLocalizedString("Cancel", comment: "")).tag = NSApplication.ModalResponse.cancel.rawValue - addButton(withTitle: NSLocalizedString("Don't Save", comment: "")).tag = NSApplication.ModalResponse.saveDontSave.rawValue - - messageText = NSLocalizedString("Save Modified Files?", comment: "") - accessoryView = SaveFileAccessoryView(withLeftChecked: true, rightChecked: true) - } else if isLeftDirty { - addButton(withTitle: NSLocalizedString("Yes", comment: "")).tag = NSApplication.ModalResponse.saveOnlyLeft.rawValue - - let button = addButton(withTitle: NSLocalizedString("No", comment: "")) - button.keyEquivalentModifierMask = .command - button.keyEquivalent = NSLocalizedString("d", comment: "The key equivalent for No") - button.tag = NSApplication.ModalResponse.saveDontSave.rawValue - - addButton(withTitle: NSLocalizedString("Cancel", comment: "")).tag = NSApplication.ModalResponse.cancel.rawValue - - messageText = NSLocalizedString("Save Left File?", comment: "") - } else if isRightDirty { - addButton(withTitle: NSLocalizedString("Yes", comment: "")).tag = NSApplication.ModalResponse.saveOnlyRight.rawValue - - let button = addButton(withTitle: NSLocalizedString("No", comment: "")) - button.keyEquivalentModifierMask = .command - button.keyEquivalent = NSLocalizedString("d", comment: "The key equivalent for No") - button.tag = NSApplication.ModalResponse.saveDontSave.rawValue - - addButton(withTitle: NSLocalizedString("Cancel", comment: "")).tag = NSApplication.ModalResponse.cancel.rawValue - - messageText = NSLocalizedString("Save Right File?", comment: "") - } else { - return .saveDontSave - } - - alertStyle = .warning - let returnCode = runModal() - - if returnCode == .OK { - if let accessoryView = accessoryView as? SaveFileAccessoryView { - if accessoryView.saveLeft, accessoryView.saveRight { - return .saveBoth - } - if accessoryView.saveLeft { - return .saveOnlyLeft - } - if accessoryView.saveRight { - return .saveOnlyRight - } - } - // both sides are not selected - return .saveDontSave - } - - return returnCode - } -} diff --git a/Sources/Features/FilesCompare/Components/RowHeightCalculator.swift b/Sources/Features/FilesCompare/Components/RowHeightCalculator.swift deleted file mode 100644 index 6aae8d0..0000000 --- a/Sources/Features/FilesCompare/Components/RowHeightCalculator.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// RowHeightCalculator.swift -// VisualDiffer -// -// Created by davide ficano on 26/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -@MainActor protocol RowHeightDataSource: AnyObject { - var lineNumberWidth: CGFloat { get } - var tableFont: NSFont { get } - - func columnWidth(at side: DisplaySide) -> CGFloat - func line(at row: Int, side: DisplaySide) -> DiffLine? - - func reloadTableData() -} - -private let minWidthBeforeWrap: CGFloat = 20.0 -private let minHeight: CGFloat = 20.0 -private let fontSizeExtraPoint: CGFloat = 4.0 - -private let horizontalPadding: CGFloat = 12.0 -private let verticalPadding: CGFloat = 4.0 - -@MainActor class RowHeightCalculator { - private var heightCache: [Int: CGFloat] = [:] - - weak var dataSource: RowHeightDataSource? - - var isWordWrapEnabled: Bool = false { - didSet { - clearCache() - } - } - - func clearCache() { - heightCache.removeAll(keepingCapacity: true) - } - - func height(for row: Int) -> CGFloat { - if let height = heightCache[row] { - return height - } - let height = calculateRowHeight(row) - heightCache[row] = height - - return height - } - - private func minimumHeight(of font: NSFont) -> CGFloat { - max(minHeight, font.pointSize + fontSizeExtraPoint) - } - - private func calculateCellHeight(forRow row: Int, side: DisplaySide) -> CGFloat { - guard let dataSource, - let text = dataSource.line(at: row, side: side)?.text else { - return 0 - } - let font = dataSource.tableFont - - if !isWordWrapEnabled { - return minimumHeight(of: font) - } - - let lineNumberWidth = dataSource.lineNumberWidth - let columnWidth = dataSource.columnWidth(at: side) - - let textWidth = columnWidth - lineNumberWidth - horizontalPadding - - guard textWidth > minWidthBeforeWrap else { - return minimumHeight(of: font) - } - - let attributes: [NSAttributedString.Key: Any] = [.font: font] - let attributedString = NSAttributedString(string: text, attributes: attributes) - - let rect = attributedString.boundingRect( - with: NSSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude), - options: [.usesLineFragmentOrigin, .usesFontLeading] - ) - - let textHeight = ceil(rect.height) - - // do not add extra height to non-wrapped text - if textHeight < minHeight { - return minimumHeight(of: font) - } - - return textHeight + verticalPadding + font.pointSize - } - - private func calculateRowHeight(_ row: Int) -> CGFloat { - let leftHeight = calculateCellHeight(forRow: row, side: .left) - let rightHeight = calculateCellHeight(forRow: row, side: .right) - - return max(leftHeight, rightHeight) - } - - func reloadData() { - clearCache() - dataSource?.reloadTableData() - } -} diff --git a/Sources/Features/FilesCompare/Components/SaveFileAccessoryView.swift b/Sources/Features/FilesCompare/Components/SaveFileAccessoryView.swift deleted file mode 100644 index e3cb093..0000000 --- a/Sources/Features/FilesCompare/Components/SaveFileAccessoryView.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// SaveFileAccessoryView.swift -// VisualDiffer -// -// Created by davide ficano on 07/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class SaveFileAccessoryView: NSView { - private let leftButton: NSButton - private let rightButton: NSButton - - @objc var saveLeft: Bool { - get { - leftButton.state == .on - } - - set { - leftButton.state = newValue ? .on : .off - } - } - - @objc var saveRight: Bool { - get { - rightButton.state == .on - } - - set { - rightButton.state = newValue ? .on : .off - } - } - - @objc convenience init(withLeftChecked leftChecked: Bool, rightChecked: Bool) { - self.init(frame: NSRect(x: 0, y: 0, width: 100, height: 60)) - saveLeft = leftChecked - saveRight = rightChecked - } - - override init(frame frameRect: NSRect) { - leftButton = NSButton( - checkboxWithTitle: NSLocalizedString("Left", comment: ""), - target: nil, - action: nil - ) - leftButton.translatesAutoresizingMaskIntoConstraints = false - rightButton = NSButton( - checkboxWithTitle: NSLocalizedString("Right", comment: ""), - target: nil, - action: nil - ) - rightButton.translatesAutoresizingMaskIntoConstraints = false - - super.init(frame: frameRect) - - saveLeft = true - saveRight = true - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(leftButton) - addSubview(rightButton) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - leftButton.leadingAnchor.constraint(equalTo: leadingAnchor), - leftButton.trailingAnchor.constraint(equalTo: trailingAnchor), - leftButton.topAnchor.constraint(equalTo: topAnchor), - leftButton.heightAnchor.constraint(equalToConstant: 40), - - rightButton.leadingAnchor.constraint(equalTo: leadingAnchor), - rightButton.trailingAnchor.constraint(equalTo: trailingAnchor), - rightButton.bottomAnchor.constraint(equalTo: leftButton.bottomAnchor, constant: 5), - ]) - } -} diff --git a/Sources/Features/FilesCompare/Components/TextFieldVerticalCentered.swift b/Sources/Features/FilesCompare/Components/TextFieldVerticalCentered.swift deleted file mode 100644 index 7306507..0000000 --- a/Sources/Features/FilesCompare/Components/TextFieldVerticalCentered.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// TextFieldVerticalCentered.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -class TextFieldVerticalCentered: NSTextFieldCell { - override func titleRect(forBounds rect: NSRect) -> NSRect { - let titleSize = attributedStringValue.size() - var titleFrame = super.titleRect(forBounds: rect) - titleFrame.origin.y = rect.origin.y + (rect.size.height - titleSize.height) / 2.0 - - return titleFrame - } - - override func drawInterior(withFrame cellFrame: NSRect, in _: NSView) { - let titleRect = titleRect(forBounds: cellFrame) - attributedStringValue.draw(in: titleRect) - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Clipboard.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Clipboard.swift deleted file mode 100644 index 3ec0a7a..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Clipboard.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// FilesWindowController+Clipboard.swift -// VisualDiffer -// -// Created by davide ficano on 25/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -@MainActor extension FilesWindowController { - @objc func copyUrlsToClipboard(_: AnyObject) { - if let path = lastUsedView.side == .left ? sessionDiff.leftPath : sessionDiff.rightPath { - let url = URL(filePath: path, directoryHint: .notDirectory) - - NSPasteboard.general.copy(urls: [url]) - } - } - - @objc func copy(_ sender: AnyObject) { - copyLinesToClipboard(sender) - } - - @objc func copyLinesToClipboard(_: AnyObject) { - guard let diffSide = lastUsedView.diffSide else { - return - } - let selectedRows = lastUsedView.selectedRowIndexes - let arr = diffSide.lines - var lines = selectedRows.map { arr[$0].text } - - // add an empty line - lines.append("") - NSPasteboard.general.copy(lines: lines) - } - - @objc func paste(_ sender: AnyObject) { - pasteLinesToClipboard(sender) - } - - @objc func pasteLinesToClipboard(_: AnyObject) { - guard let diffResult else { - return - } - let row = lastUsedView.selectedRow - - if row < 0 { - return - } - - let pasteboard = NSPasteboard.general - let supportedTypes = [NSPasteboard.PasteboardType.string] - guard pasteboard.availableType(from: supportedTypes) != nil, - let text = pasteboard.string(forType: .string) else { - return - } - - diffResult.insert( - text: text, - at: row, - side: lastUsedView.side - ) - lastUsedView.isDirty = true - refreshAfterTextEdit() - } - - @objc func cut(_ sender: AnyObject) { - cutToClipboard(sender) - } - - @objc func cutToClipboard(_ sender: AnyObject) { - copyLinesToClipboard(sender) - deleteLines(sender) - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Common.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Common.swift deleted file mode 100644 index a4e3f13..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Common.swift +++ /dev/null @@ -1,153 +0,0 @@ -// -// FilesWindowController+Common.swift -// VisualDiffer -// -// Created by davide ficano on 01/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -func swap(_ lhs: inout T, _ rhs: inout T) { - let tmp = lhs - - lhs = rhs - rhs = tmp -} - -extension FilesWindowController { - // MARK: - Refresh after edit - - @objc func refreshAfterTextEdit() { - guard let diffResult else { - return - } - // refresh sections otherwise moving between differences can position to wrong line - diffResult.refreshSections() - differenceCounters.update(counters: DiffCountersItem.diffCounter(withResult: diffResult)) - cachedLineTextMap.removeAllObjects() - - leftView.reloadData() - rightView.reloadData() - - updateDetailLines(lastUsedView.selectedRow) - - fileThumbnail.needsDisplay = true - } - - func askReload() -> Bool { - let informativeLines = [ - NSLocalizedString("Another application has made changes to the file for this document.", comment: ""), - NSLocalizedString("You can choose to compare using the modified version on the file system, or keep the existing one", comment: ""), - NSLocalizedString("(Reverting will lose any unsaved changes.)", comment: ""), - ] - - return NSAlert.showModalConfirm( - messageText: NSLocalizedString("The file(s) have been changed on the file system. Do you want to reload all modified files?", comment: ""), - informativeText: informativeLines.joined(separator: "\n"), - suppressPropertyName: CommonPrefs.Name.confirmReloadFiles.rawValue, - yesText: NSLocalizedString("Reload", comment: ""), - noText: NSLocalizedString("Keep", comment: "") - ) - } - - // MARK: - Details lines - - @objc func updateDetailLines(_ row: Int) { - if row < 0 { - setColor(for: nil, view: leftDetailsTextView) - setColor(for: nil, view: rightDetailsTextView) - } else { - if let leftSide = leftView.diffSide, - let rightSide = rightView.diffSide { - let oldLine = leftSide.lines[row] - let newLine = rightSide.lines[row] - - setColor(for: oldLine, view: leftDetailsTextView) - setColor(for: newLine, view: rightDetailsTextView) - } - } - } - - func setColor(for diffLine: DiffLine?, view lineView: NSTextView) { - // set the text to be sure textStorage has a valid length - guard let diffLine else { - lineView.string = "" - return - } - lineView.string = getLine(diffLine) + (scopeBar.showWhitespaces ? "" : diffLine.component.eol.visibleSymbol) - - if let colors = diffLine.colors { - lineView.setTextColor(colors.text, backgroundColor: colors.background) - } - } - - // MARK: - Cache lines - - func getLine(_ diffLine: DiffLine) -> String { - if let line = cachedLineTextMap.object(forKey: diffLine) as? String { - return line as String - } - let line = visibleWhitespaces.getString( - diffLine.component, - isWhitespacesVisible: scopeBar.showWhitespaces - ) - cachedLineTextMap.setObject(line as NSString, forKey: diffLine) - - return line - } - - // MARK: - Actions - - @objc func refresh(_: AnyObject?) { - guard alertSaveDirtyFiles() else { - return - } - let index = leftView.selectedRowIndexes - let row = leftView.firstVisibleRow - - reloadAllMove(toFirstDifference: false) - - if row <= leftView.numberOfRows { - leftView.scrollTo(row: row, center: false) - leftView.selectRowIndexes(index, byExtendingSelection: false) - } else { - moveToDifference(true, showAnim: true) - } - } - - @objc func swapSides(_: AnyObject) { - swap(&sessionDiff.leftPath, &sessionDiff.rightPath) - swap(&leftView.diffSide, &rightView.diffSide) - swap(&leftPanelView.fileInfoBar.fileAttrs, &rightPanelView.fileInfoBar.fileAttrs) - swap(&leftPanelView.fileInfoBar.encoding, &rightPanelView.fileInfoBar.encoding) - swap(&leftPanelView.fileInfoBar.eol, &rightPanelView.fileInfoBar.eol) - - reloadRowHeights() - fileThumbnail.needsDisplay = true - } - - @objc func toggleWordWrap(_: AnyObject) { - setWordWrap(enabled: !rowHeightCalculator.isWordWrapEnabled) - } - - // MARK: Find Methods - - @objc func find(_: AnyObject) { - window?.makeFirstResponder(scopeBar) - } - - @objc func findPrevious(_: AnyObject) { - scopeBar.findView.moveToMatch(false) - } - - @objc func findNext(_: AnyObject) { - scopeBar.findView.moveToMatch(true) - } - - @objc func setLeftReadOnly(_: AnyObject) { - sessionDiff.leftReadOnly.toggle() - } - - @objc func setRightReadOnly(_: AnyObject) { - sessionDiff.rightReadOnly.toggle() - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Document.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Document.swift deleted file mode 100644 index 1307675..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Document.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// FilesWindowController+Document.swift -// VisualDiffer -// -// Created by davide ficano on 04/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: @preconcurrency DocumentWindowControllerDelegate { - override open var document: AnyObject? { - didSet { - // create a shortcut to sessionDiff held by document - if let sessionDiff = (document as? VDDocument)?.sessionDiff { - self.sessionDiff = sessionDiff - preferences.from(sessionDiff: sessionDiff) - setupUIState() - } - } - } - - // MARK: - DocumentWindowControllerDelegate methods - - // this message is sent by NSDocument::canCloseDocumentWithDelegate - public func canClose(_ document: VDDocument) -> Bool { - var closeDoc = false - - if leftView.isDirty || rightView.isDirty { - closeDoc = alertSaveDirtyFiles() - - if closeDoc { - let tmpLeftDirty = leftView.isDirty - let tmpRightDirty = rightView.isDirty - - leftView.isDirty = false - rightView.isDirty = false - if document.isDocumentEdited { - leftView.isDirty = tmpLeftDirty - rightView.isDirty = tmpRightDirty - closeDoc = false - } - } - } - return closeDoc - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+FileInfoBarDelegate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+FileInfoBarDelegate.swift deleted file mode 100644 index 18f5d61..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+FileInfoBarDelegate.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// FilesWindowController+FileInfoBarDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 01/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: @preconcurrency FileInfoBarDelegate { - func fileInfoBar(_: FileInfoBar, changedEncoding _: String.Encoding) {} -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+FilesScopeBar.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+FilesScopeBar.swift deleted file mode 100644 index f29f226..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+FilesScopeBar.swift +++ /dev/null @@ -1,109 +0,0 @@ -// -// FilesWindowController+FilesScopeBar.swift -// VisualDiffer -// -// Created by davide ficano on 23/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: @preconcurrency FilesScopeBarDelegate { - // MARK: - FilesScopeBar Delegate - - func filesScopeBar( - _ filesScopeBar: FilesScopeBar, - action: FilesScopeBarAction - ) { - switch action { - case .showWhitespaces: - showWhitespaces(filesScopeBar) - case .showAllLines: - showAllLines(filesScopeBar) - case .showJustMatchingLines: - showJustMatchingLines(filesScopeBar) - case .showJustDifferentLines: - showJustDifferentLines(filesScopeBar) - } - } - - @objc func showAllLines(_: AnyObject) { - refreshLinesStatus() - } - - @objc func showJustMatchingLines(_: AnyObject) { - refreshLinesStatus() - } - - @objc func showJustDifferentLines(_: AnyObject) { - refreshLinesStatus() - } - - @objc func refreshLinesStatus() { - guard let diffResult else { - return - } - let diffResultInUse: DiffResult? - - // always use the current selected filter - switch scopeBar.showLinesFilter { - case .all: - currentDiffResult?.resetSectionSeparators() - diffResultInUse = diffResult - case .matches: - filteredDiffResult = DiffResult.justMatchingLines(diffResult) - diffResultInUse = filteredDiffResult - case .differences: - filteredDiffResult = DiffResult.justDifferentLines(diffResult) - diffResultInUse = filteredDiffResult - } - - currentDiffResult = diffResultInUse - let visibleRow = calcVisibleRow(for: diffResultInUse) - - leftView.diffSide = diffResultInUse?.leftSide - rightView.diffSide = diffResultInUse?.rightSide - - fileThumbnail.diffResult = diffResult - fileThumbnail.linesCount = currentDiffResult?.leftSide.lines.count ?? 0 - fileThumbnail.view = leftView - fileThumbnail.needsDisplay = true - - reloadRowHeights() - - leftView.scrollTo(row: visibleRow, center: true) - } - - func calcVisibleRow(for destDiffResult: DiffResult?) -> Int { - guard let destDiffResult, - let currentDiffResult else { - return 0 - } - // determine the current line number - let currentVisibleRow = leftView.firstVisibleRow - - if currentDiffResult === destDiffResult { - return currentVisibleRow - } - let count = currentDiffResult.leftSide.lines.count - var lineNumber = -1 - var diffLines: [DiffLine]? - - // Find the first valid line number (eg not missing lines) on left or right - for row in currentVisibleRow ..< count { - var line = currentDiffResult.leftSide.lines[row] - if line.number > 0 { - lineNumber = line.number - diffLines = destDiffResult.leftSide.lines - break - } - line = currentDiffResult.rightSide.lines[row] - if line.number > 0 { - lineNumber = line.number - diffLines = destDiffResult.rightSide.lines - break - } - } - - // Now find the row from selected diffLines - return diffLines?.firstIndex { $0.number >= lineNumber } ?? 0 - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+FilesTableViewContextMenu.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+FilesTableViewContextMenu.swift deleted file mode 100644 index 59c5af6..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+FilesTableViewContextMenu.swift +++ /dev/null @@ -1,186 +0,0 @@ -// -// FilesWindowController+FilesTableViewContextMenu.swift -// VisualDiffer -// -// Created by davide ficano on 30/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -@MainActor extension FilesWindowController: @preconcurrency FilesTableViewContextMenu { - @objc func copyFileNames(_: AnyObject?) { - if let path = lastUsedView.side == .left ? sessionDiff.leftPath : sessionDiff.rightPath { - let url = URL(filePath: path, directoryHint: .notDirectory) - - NSPasteboard.general.copy(lines: [url.lastPathComponent]) - } - } - - @objc func copyFullPaths(_: AnyObject?) { - if let path = lastUsedView.side == .left ? sessionDiff.leftPath : sessionDiff.rightPath { - NSPasteboard.general.copy(lines: [path]) - } - } - - @objc func showWhitespaces(_ sender: AnyObject?) { - cachedLineTextMap.removeAllObjects() - - // the scopeBar button toggles automatically but when this method is called - // from the menu we set it manually - if sender !== scopeBar { - scopeBar.showWhitespaces(!scopeBar.showWhitespaces, informDelegate: false) - } - updateDetailLines(leftView.selectedRow) - - leftView.reloadData(restoreSelection: true) - rightView.reloadData(restoreSelection: true) - } - - @objc func showInFinder(_: AnyObject?) { - guard let path = lastUsedView.side == .left ? sessionDiff.leftPath : sessionDiff.rightPath else { - return - } - NSWorkspace.shared.show(inFinder: [path]) - } - - @objc func openWithApp(_ sender: AnyObject?) { - guard let app = sender?.representedObject as? String, - let editorData = lastUsedView.editorData(sessionDiff) else { - return - } - openWith(app: URL(filePath: app), attributes: [editorData]) - } - - @objc func popupOpenWithApp(_: AnyObject?) { - // Make happy Cocoa otherwise without action the menuitem is always grayed - } - - @objc func openWithOther(_: AnyObject) { - guard let editorData = lastUsedView.editorData(sessionDiff) else { - return - } - openWithOtherApp(editorData) - } - - @objc func copyLines(_: AnyObject?) { - let selectedRows = lastUsedView.selectedRowIndexes - - guard !selectedRows.isEmpty, - let diffResult, - let currentDiffResult, - let linkedView = lastUsedView.linkedView else { - return - } - - DiffResult.copyLines( - all: diffResult, - current: currentDiffResult, - rows: selectedRows, - source: lastUsedView.side, - visibility: scopeBar.showLinesFilter == .differences ? .differences : .all - ) - - linkedView.isDirty = true - refreshAfterTextEdit() - } - - @objc func deleteLines(_: AnyObject?) { - let selectedRows = lastUsedView.selectedRowIndexes - - guard !selectedRows.isEmpty, - let diffResult, - let currentDiffResult else { - return - } - - DiffResult.deleteLines( - all: diffResult, - current: currentDiffResult, - rows: selectedRows, - side: lastUsedView.side, - visibility: scopeBar.showLinesFilter - ) - - lastUsedView.isDirty = true - refreshAfterTextEdit() - } - - @objc func selectSection(_: AnyObject?) { - guard let indexes = currentDiffResult?.findSectionIndexSet(with: lastUsedView.selectedRow) else { - return - } - lastUsedView.selectRowIndexes(indexes, byExtendingSelection: false) - } - - @objc func selectAdjacentSections(_: AnyObject?) { - guard let indexes = currentDiffResult?.findAdjacentSections(from: lastUsedView.selectedRow) else { - return - } - - lastUsedView.selectRowIndexes(indexes, byExtendingSelection: false) - } - - @objc func saveFile(_ sender: AnyObject?) { - let view = if sender === leftPanelView.pathView.saveButton { - leftView - } else if sender === rightPanelView.pathView.saveButton { - rightView - } else { - lastUsedView - } - if view.isDirty { - do { - try saveView(view) - // remove mode - if let diffSide = view.diffSide { - for line in diffSide.lines { - line.mode = .normal - } - } - view.reloadData() - updateDetailLines(lastUsedView.selectedRow) - } catch { - NSAlert(error: error).runModal() - } - } - } - - // MARK: - Open with external applications - - func openWith( - app: URL, - attributes: [OpenEditorAttribute] - ) { - let editor = OpenEditor(attributes: attributes) - do { - try editor.open(withApplication: app) - } catch let error as NSError { - if let window { - NSAlert(error: error).beginSheetModal(for: window) - } - } - } - - func openWithOtherApp(_ attributes: OpenEditorAttribute) { - let editor = OpenEditor(attributes: attributes) - - do { - try editor.browseApplicationAndLaunch() - } catch let error as NSError { - if let window { - NSAlert(error: error).beginSheetModal(for: window) - } - } - } - - @objc func copyLinesToLeft(_ sender: AnyObject?) { - if lastUsedView.side == .right { - copyLines(sender) - } - } - - @objc func copyLinesToRight(_ sender: AnyObject?) { - if lastUsedView.side == .left { - copyLines(sender) - } - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+JumpLine.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+JumpLine.swift deleted file mode 100644 index 25318f1..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+JumpLine.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// FilesWindowController+JumpLine.swift -// VisualDiffer -// -// Created by davide ficano on 06/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - @objc func jumpToLine(_: AnyObject) { - guard let window, - let currentDiffResult else { - return - } - let jumpToLineWindow = JumpToLineWindow.createSheet() - - jumpToLineWindow.lineNumber = findStartJumpLine() - jumpToLineWindow.side = lastUsedView.side - jumpToLineWindow.leftMaxLineNumber = currentDiffResult.leftSide.linesCount - jumpToLineWindow.rightMaxLineNumber = currentDiffResult.rightSide.linesCount - - jumpToLineWindow.beginSheet(window) { - self.jumpToLineHandler($0, jumpToLineWindow: jumpToLineWindow) - } - } - - private func findStartJumpLine() -> Int { - let row = lastUsedView.selectedRow - - if row < 0 { - return 1 - } - - guard let arr = lastUsedView.diffSide?.lines else { - return 1 - } - let lineNumber = arr[row].number - - if lineNumber < 0 { - return 1 - } - return lineNumber - } - - func jumpToLineHandler( - _ returnCode: NSApplication.ModalResponse, - jumpToLineWindow: JumpToLineWindow - ) { - guard let currentDiffResult, - returnCode == .OK else { - return - } - var row = -1 - var view: FilesTableView - switch jumpToLineWindow.side { - case .left: - row = currentDiffResult.leftSide.findLineIndex(by: jumpToLineWindow.lineNumber) ?? 0 - view = leftView - case .right: - row = currentDiffResult.rightSide.findLineIndex(by: jumpToLineWindow.lineNumber) ?? 0 - view = rightView - } - if row >= 0 { - view.scrollTo(row: row, center: true) - view.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false) - window?.makeFirstResponder(view) - } - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+LinesDetail.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+LinesDetail.swift deleted file mode 100644 index c5e2871..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+LinesDetail.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// FilesWindowController+LinesDetail.swift -// VisualDiffer -// -// Created by davide ficano on 06/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: NSTextViewDelegate { - @objc func toggleDetails(_: AnyObject) { - let isHidden = !linesDetailView.isHidden - linesDetailView.isHidden = isHidden - CommonPrefs.shared.hideFileDiffDetails = isHidden - } - - public func textView(_ textView: NSTextView, menu: NSMenu, for _: NSEvent, at _: Int) -> NSMenu? { - if textView === leftDetailsTextView || textView === rightDetailsTextView { - // Add items to NSTextField context menu - menu.insertItem( - NSMenuItem.separator(), - at: 0 - ) - menu.insertItem( - withTitle: NSLocalizedString("Hide Details", comment: ""), - action: #selector(toggleDetails), - keyEquivalent: "", - at: 0 - ) - } - return menu - } - - func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - if textView === leftDetailsTextView || textView === rightDetailsTextView { - // let the window determine what to do, close window when ESC is pressed or ignore the command - if commandSelector == #selector(cancelOperation) { - window?.cancelOperation(textView) - return true - } - } - return false - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Menu.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Menu.swift deleted file mode 100644 index 23a61a7..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Menu.swift +++ /dev/null @@ -1,232 +0,0 @@ -// -// FilesWindowController+Menu.swift -// VisualDiffer -// -// Created by davide ficano on 29/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - private static func editMenu() -> NSMenu { - let editMenu = NSMenu(title: NSLocalizedString("Edit", comment: "")) - - editMenu.addItem( - withTitle: NSLocalizedString("Cut", comment: ""), - action: #selector(cut(_:)), - keyEquivalent: "x" - ) - // tells Swift to use "copy:" method that takes a parameter - // otherwise silently crashes without working for copy lines or copy text selected in NSTextView - editMenu.addItem( - withTitle: NSLocalizedString("Copy", comment: ""), - action: #selector(copy(_:)), - keyEquivalent: "c" - ) - // TODO: copy urls no longer work for some sandbox problem so we disable it entirely -// editMenu.addItem(withTitle: NSLocalizedString("Copy URLs", comment: ""), action: #selector(copyUrlsToClipboard),keyEquivalent: "u") - editMenu.addItem( - withTitle: NSLocalizedString("Paste", comment: ""), - action: #selector(paste(_:)), - keyEquivalent: "v" - ) - - let deleteItem = NSMenuItem( - title: NSLocalizedString("Delete", comment: ""), - action: #selector(deleteLines), - keyEquivalent: "" - ) - editMenu.addItem(deleteItem) - - editMenu.addItem(NSMenuItem.separator()) - - editMenu.addItem( - withTitle: NSLocalizedString("Select All", comment: ""), - action: #selector(selectAll), - keyEquivalent: "a" - ) - editMenu.addItem( - withTitle: NSLocalizedString("Select Section", comment: ""), - action: #selector(selectSection), - keyEquivalent: "d" - ) - - let selectAdjacent = NSMenuItem( - title: NSLocalizedString("Select Adjacent Sections", comment: ""), - action: #selector(selectAdjacentSections), - keyEquivalent: "D" - ) - selectAdjacent.isAlternate = true - editMenu.addItem(selectAdjacent) - - editMenu.addItem(NSMenuItem.separator()) - - let findItem = NSMenuItem( - title: NSLocalizedString("Find", comment: ""), - action: nil, - keyEquivalent: "" - ) - let findSubmenu = NSMenu(title: NSLocalizedString("Find", comment: "")) - findSubmenu.addItem( - withTitle: NSLocalizedString("Find...", comment: ""), - action: #selector(find), - keyEquivalent: "f" - ) - findSubmenu.addItem( - withTitle: NSLocalizedString("Find Next", comment: ""), - action: #selector(findNext), - keyEquivalent: "g" - ) - findSubmenu.addItem( - withTitle: NSLocalizedString("Find Previous", comment: ""), - action: #selector(findPrevious), - keyEquivalent: "G" - ) - editMenu.setSubmenu(findSubmenu, for: findItem) - editMenu.addItem(findItem) - - return editMenu - } - - private static func actionsMenu() -> NSMenu { - let actionsMenu = NSMenu(title: NSLocalizedString("Actions", comment: "")) - - let left = NSMenuItem( - title: NSLocalizedString("Copy Lines to Left", comment: ""), - action: #selector(copyLinesToLeft), - keyEquivalent: KeyEquivalent.leftArrow - ) - left.keyEquivalentModifierMask = [.control, .command] - left.tag = 1 - actionsMenu.addItem(left) - - let right = NSMenuItem( - title: NSLocalizedString("Copy Lines to Right", comment: ""), - action: #selector(copyLinesToRight), - keyEquivalent: KeyEquivalent.rightArrow - ) - right.keyEquivalentModifierMask = [.control, .command] - right.tag = 2 - actionsMenu.addItem(right) - - let deleteLines = NSMenuItem( - title: NSLocalizedString("Delete Lines", comment: ""), - action: #selector(deleteLines), - keyEquivalent: KeyEquivalent.deleteBackspace - ) - actionsMenu.addItem(deleteLines) - - actionsMenu.addItem(NSMenuItem.separator()) - - actionsMenu.addItem( - withTitle: NSLocalizedString("Copy Filenames", comment: ""), - action: #selector(copyFileNames), - keyEquivalent: "" - ) - actionsMenu.addItem( - withTitle: NSLocalizedString("Show in Finder", comment: ""), - action: #selector(showInFinder), - keyEquivalent: "" - ) - actionsMenu.addItem( - withTitle: NSLocalizedString("Open With", comment: ""), - action: #selector(popupOpenWithApp), - keyEquivalent: "" - ) - - return actionsMenu - } - - private static func viewMenu() -> NSMenu { - let viewMenu = NSMenu(title: NSLocalizedString("View", comment: "")) - - viewMenu.addItem( - withTitle: NSLocalizedString("Set Left Read-Only", comment: ""), - action: #selector(setLeftReadOnly), - keyEquivalent: "" - ) - viewMenu.addItem( - withTitle: NSLocalizedString("Set Right Read-Only", comment: ""), - action: #selector(setRightReadOnly), - keyEquivalent: "" - ) - viewMenu.addItem(NSMenuItem.separator()) - - viewMenu.addItem( - withTitle: NSLocalizedString("Swap Sides", comment: ""), - action: #selector(swapSides), - keyEquivalent: "" - ) - viewMenu.addItem( - withTitle: NSLocalizedString("Word Wrap", comment: ""), - action: #selector(toggleWordWrap), - keyEquivalent: "W" - ) - viewMenu.addItem( - withTitle: NSLocalizedString("Refresh", comment: ""), - action: #selector(refresh), - keyEquivalent: "r" - ) - viewMenu.addItem(NSMenuItem.separator()) - - let fontItem = NSMenuItem( - title: NSLocalizedString("Font", comment: ""), - action: nil, - keyEquivalent: "" - ) - let fontMenu = NSMenu(title: NSLocalizedString("Font", comment: "")) - fontMenu.addItem( - withTitle: NSLocalizedString("Larger", comment: ""), - action: #selector(zoomLargerFont), - keyEquivalent: "+" - ) - fontMenu.addItem( - withTitle: NSLocalizedString("Smaller", comment: ""), - action: #selector(zoomSmallerFont), - keyEquivalent: "-" - ) - fontMenu.addItem(NSMenuItem.separator()) - fontMenu.addItem( - withTitle: NSLocalizedString("Reset", comment: ""), - action: #selector(zoomResetFont), - keyEquivalent: "0" - ) - viewMenu.setSubmenu(fontMenu, for: fontItem) - viewMenu.addItem(fontItem) - - viewMenu.addItem( - withTitle: NSLocalizedString("Show Details", comment: ""), - action: #selector(toggleDetails), - keyEquivalent: "" - ) - - let toolbarItem = NSMenuItem( - title: NSLocalizedString("Show Toolbar", comment: ""), - action: #selector(NSWindow.toggleToolbarShown), - keyEquivalent: "t" - ) - toolbarItem.keyEquivalentModifierMask = [.option, .command] - viewMenu.addItem(toolbarItem) - - viewMenu.addItem( - withTitle: NSLocalizedString("Customize Toolbar…", comment: ""), - action: #selector(NSWindow.runToolbarCustomizationPalette), - keyEquivalent: "" - ) - - return viewMenu - } - - static func switchMenu() { - @MainActor enum StaticMenus { - static let edit = FilesWindowController.editMenu() - static let actions = FilesWindowController.actionsMenu() - static let view = FilesWindowController.viewMenu() - } - guard let mainMenu = NSApp.mainMenu else { - return - } - mainMenu.item(withTag: MainMenu.edit.rawValue)?.submenu = StaticMenus.edit - mainMenu.item(withTag: MainMenu.actions.rawValue)?.submenu = StaticMenus.actions - mainMenu.item(withTag: MainMenu.view.rawValue)?.submenu = StaticMenus.view - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+MenuDelegate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+MenuDelegate.swift deleted file mode 100644 index 5e54d5c..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+MenuDelegate.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// FilesWindowController+MenuDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 24/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: NSMenuDelegate, NSMenuItemValidation { - public func menuNeedsUpdate(_ menu: NSMenu) { - if menu.identifier == .Files.openWithToolbarMenu { - // preserve root item - let root = menu.item(at: 0) - menu.removeAllItems() - - if let root { - menu.addItem(root) - } - - if let path = lastUsedView.side == .left ? resolvedLeftPath : resolvedRightPath { - menu.addMenuItemsForFile( - path, - openAppAction: #selector(openWithApp), - openOtherAppAction: #selector(openWithOther) - ) - } - } - } - - // called for menu bar items - public func validateMenuItem(_ item: NSMenuItem) -> Bool { - let action = item.action - let isLeftView = lastUsedView.side == .left - - if action == #selector(popupOpenWithApp) { - let url: URL? = if let path = isLeftView ? sessionDiff.leftPath : sessionDiff.rightPath { - URL(filePath: path) - } else { - nil - } - item.menu?.setSubmenu( - NSMenu.appsMenuForFile( - url, - openAppAction: #selector(openWithApp), - openOtherAppAction: #selector(openWithOther) - ), - for: item - ) - return true - } else if action == #selector(saveFile) { - item.title = isLeftView ? NSLocalizedString("Save Left File", comment: "") : NSLocalizedString("Save Right File", comment: "") - return lastUsedView.isDirty - } else if action == #selector(previousDifference) { - if let sections = currentDiffResult?.sections { - return !sections.isEmpty - } - return false - } else if action == #selector(nextDifference) { - if let sections = currentDiffResult?.sections { - return !sections.isEmpty - } - return false - } else if action == #selector(copyLinesToLeft) { - if lastUsedView.side == .right { - item.isHidden = false - return !sessionDiff.leftReadOnly && leftView.isEditAllowed - } else { - item.isHidden = true - return false - } - } else if action == #selector(copyLinesToRight) { - if isLeftView { - item.isHidden = false - return !sessionDiff.rightReadOnly && rightView.isEditAllowed - } else { - item.isHidden = true - return false - } - } else if action == #selector(deleteLines) { - if isLeftView { - item.title = NSLocalizedString("Delete Lines from Left", comment: "") - return !sessionDiff.leftReadOnly && leftView.isEditAllowed - } else { - item.title = NSLocalizedString("Delete Lines from Right", comment: "") - return !sessionDiff.rightReadOnly && rightView.isEditAllowed - } - } else if action == #selector(pasteLinesToClipboard) || action == #selector(cutToClipboard) { - if scopeBar.showLinesFilter == .all { - if isLeftView { - return !sessionDiff.leftReadOnly && leftView.isEditAllowed - } else { - return !sessionDiff.rightReadOnly && rightView.isEditAllowed - } - } - return false - } else if action == #selector(setLeftReadOnly) { - item.state = sessionDiff.leftReadOnly ? .on : .off - return true - } else if action == #selector(setRightReadOnly) { - item.state = sessionDiff.rightReadOnly ? .on : .off - return true - } else if action == #selector(toggleDetails) { - item.title = linesDetailView.isHidden - ? NSLocalizedString("Show Details", comment: "") - : NSLocalizedString("Hide Details", comment: "") - return true - } else if action == #selector(previousDifferenceFiles) - || action == #selector(nextDifferenceFiles) { - return (document as? VDDocument)?.parentSession != nil - } else if action == #selector(toggleWordWrap) { - item.state = rowHeightCalculator.isWordWrapEnabled ? .on : .off - return true - } - return true - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+NSToolbarDelegate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+NSToolbarDelegate.swift deleted file mode 100644 index 6580589..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+NSToolbarDelegate.swift +++ /dev/null @@ -1,236 +0,0 @@ -// -// FilesWindowController+NSToolbarDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 07/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSToolbarItem.Identifier { - enum Files { - static let copyLines = NSToolbarItem.Identifier("CopyLines") - static let prevDifference = NSToolbarItem.Identifier("PrevDifference") - static let nextDifference = NSToolbarItem.Identifier("NextDifference") - static let prevDifferenceFiles = NSToolbarItem.Identifier("PrevDifferenceFiles") - static let nextDifferenceFiles = NSToolbarItem.Identifier("NextDifferenceFiles") - static let openWith = NSToolbarItem.Identifier("OpenWith") - static let showInFinder = NSToolbarItem.Identifier("ShowInFinder") - static let sessionPreferences = NSToolbarItem.Identifier("SessionPreferences") - static let wordWrap = NSToolbarItem.Identifier("WordWrap") - } -} - -extension NSUserInterfaceItemIdentifier { - enum Files { - static let openWithToolbarMenu = NSUserInterfaceItemIdentifier("FileOpenWithToolbarMenuIdentifier") - } -} - -extension FilesWindowController: NSToolbarDelegate, NSToolbarItemValidation { - @objc public func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - [ - .Files.nextDifference, - .Files.prevDifference, - .space, - .Files.copyLines, - .space, - .Files.nextDifferenceFiles, - .Files.prevDifferenceFiles, - .Files.sessionPreferences, - ] - } - - @objc public func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - [ - .space, - .flexibleSpace, - .Files.nextDifference, - .Files.prevDifference, - .Files.copyLines, - .Files.nextDifferenceFiles, - .Files.prevDifferenceFiles, - .Files.openWith, - .Files.showInFinder, - .Files.wordWrap, - .Files.sessionPreferences, - ] - } - - @objc public func toolbarWillAddItem(_ notification: Notification) { - guard let item = notification.userInfo?["item"] as? NSToolbarItem else { - return - } - updateToolbarButton(item) - - if item.itemIdentifier == .Files.openWith { - item.view?.menu?.delegate = self - } - } - - @objc public func toolbar(_: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar _: Bool) -> NSToolbarItem? { - if itemIdentifier == .Files.copyLines { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Copy Lines", comment: ""), - tooltip: NSLocalizedString("Copy Lines", comment: ""), - image: NSImage(named: VDImageNameCopyLinesLeft), - target: self, - action: #selector(copyLines) - ) - } else if itemIdentifier == .Files.prevDifference { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Prev Difference", comment: ""), - tooltip: NSLocalizedString("Go to previous difference", comment: ""), - image: NSImage(named: VDImageNamePrev), - target: self, - action: #selector(previousDifference) - ) - } else if itemIdentifier == .Files.nextDifference { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Next Difference", comment: ""), - tooltip: NSLocalizedString("Go to next difference", comment: ""), - image: NSImage(named: VDImageNameNext), - target: self, - action: #selector(nextDifference) - ) - } else if itemIdentifier == .Files.prevDifferenceFiles { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Previous File", comment: ""), - tooltip: NSLocalizedString("Go to previous file with differences", comment: ""), - image: NSImage(named: VDImageNamePrevFile), - target: self, - action: #selector(previousDifferenceFiles) - ) - } else if itemIdentifier == .Files.nextDifferenceFiles { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Next File", comment: ""), - tooltip: NSLocalizedString("Go to next file with differences", comment: ""), - image: NSImage(named: VDImageNameNextFile), - target: self, - action: #selector(nextDifferenceFiles) - ) - } else if itemIdentifier == .Files.openWith { - let popupButton = NSPopUpButton( - identifier: .Files.openWithToolbarMenu, - menuTitle: NSLocalizedString("ToolbarOpenWith", comment: ""), - menuImage: NSImage(named: VDImageNameOpenWith) - ) - popupButton.target = self - popupButton.action = #selector(popupOpenWithApp) - popupButton.menu?.delegate = self - - let item = CustomValidationToolbarItem(itemIdentifier: itemIdentifier) - .with( - label: NSLocalizedString("Open With", comment: ""), - tooltip: NSLocalizedString("Open using the selected application", comment: ""), - image: NSImage(named: VDImageNameFinder), - target: nil, - action: nil - ) - item.view = popupButton - - return item - } else if itemIdentifier == .Files.showInFinder { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Show in Finder", comment: ""), - tooltip: NSLocalizedString("Show in Finder", comment: ""), - image: NSImage(named: VDImageNameFinder), - target: self, - action: #selector(showInFinder) - ) - } else if itemIdentifier == .Files.sessionPreferences { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Session Settings", comment: ""), - tooltip: NSLocalizedString("Edit Session Settings", comment: ""), - image: NSImage(named: VDImageNamePreferences), - target: self, - action: #selector(openSessionSettingsSheet) - ) - } else if itemIdentifier == .Files.wordWrap { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Word Wrap", comment: ""), - tooltip: NSLocalizedString("Word Wrap", comment: ""), - image: NSImage(named: VDImageNameWordWrapOff), - target: self, - action: #selector(toggleWordWrap) - ) - } - - return nil - } - - open func validateToolbarItem(_ item: NSToolbarItem) -> Bool { - var enabled = true - let side = lastUsedView.side - - if item.itemIdentifier == .Files.prevDifference { - guard let sections = currentDiffResult?.sections else { - return false - } - return !sections.isEmpty - } else if item.itemIdentifier == .Files.nextDifference { - guard let sections = currentDiffResult?.sections else { - return false - } - return !sections.isEmpty - } else if item.itemIdentifier == .Files.prevDifferenceFiles - || item.itemIdentifier == .Files.nextDifferenceFiles { - return (document as? VDDocument)?.parentSession != nil - } - - if side == .left { - if item.itemIdentifier == .Files.copyLines { - return !sessionDiff.rightReadOnly && rightView.isEditAllowed - } - } else if side == .right { - if item.itemIdentifier == .Files.copyLines { - return !sessionDiff.leftReadOnly && leftView.isEditAllowed - } - } - - let isLeftView = side == .left - let path = isLeftView ? resolvedLeftPath : resolvedRightPath - if item.itemIdentifier == .Files.showInFinder { - enabled = path != nil - } else if item.itemIdentifier == .Files.openWith { - enabled = path != nil - } - - return enabled - } - - @MainActor func updateToolbarButton(_ item: NSToolbarItem) { - if item.itemIdentifier == .Files.wordWrap { - item.image = NSImage( - named: rowHeightCalculator.isWordWrapEnabled ? VDImageNameWordWrapOn : VDImageNameWordWrapOff - ) - return - } - switch lastUsedView.side { - case .left: - if item.itemIdentifier == .Files.copyLines { - item.image = NSImage(named: VDImageNameCopyLinesRight) - } - case .right: - if item.itemIdentifier == .Files.copyLines { - item.image = NSImage(named: VDImageNameCopyLinesLeft) - } - } - } - - @MainActor func updateToolbar() { - guard let items = window?.toolbar?.visibleItems else { - return - } - for item in items { - updateToolbarButton(item) - } - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+NSWindowDelegate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+NSWindowDelegate.swift deleted file mode 100644 index 9055d95..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+NSWindowDelegate.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// FilesWindowController+NSWindowDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 01/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -// The amount of seconds the message text stay visible on status bar before restoring the counters -let statusBarShowMessageTimeoutPrefName = "filesStatusBarShowMessageTimeout" - -extension FilesWindowController: NSWindowDelegate { - public func windowWillClose(_: Notification) { - removeObservers() - if let document = document as? VDDocument { - document.parentSession?.removeChildDocument(document) - } - } - - public func windowDidBecomeMain(_: Notification) { - Self.switchMenu() - - reloadFilesIfNeeded() - } - - public func window(_: NSWindow, willUseFullScreenPresentationOptions proposedOptions: NSApplication.PresentationOptions = []) -> NSApplication.PresentationOptions { - [proposedOptions, .autoHideToolbar] - } - - @objc func resetStatusBarMessage(_: AnyObject?) { - guard let diffResult else { - return - } - differenceCounters.update(counters: DiffCountersItem.diffCounter(withResult: diffResult)) - } - - func reloadFilesIfNeeded() { - guard let resolvedLeftPath, - let resolvedRightPath else { - return - } - - let leftChanged = leftPanelView.fileInfoBar.updateFileAttrsFromPath(resolvedLeftPath.osPath) - let rightChanged = rightPanelView.fileInfoBar.updateFileAttrsFromPath(resolvedRightPath.osPath) - - if leftChanged || rightChanged { - if askReload() { - leftView.isDirty = false - rightView.isDirty = false - refresh(nil) - if leftChanged, rightChanged { - differenceCounters.stringValue = NSLocalizedString("Reloaded left and right files", comment: "") - } else if leftChanged { - differenceCounters.stringValue = NSLocalizedString("Reloaded left file", comment: "") - } else if rightChanged { - differenceCounters.stringValue = NSLocalizedString("Reloaded right file", comment: "") - } - perform( - #selector(resetStatusBarMessage), - with: nil, - afterDelay: UserDefaults.standard.double(forKey: statusBarShowMessageTimeoutPrefName) - ) - } - } - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Navigate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Navigate.swift deleted file mode 100644 index 629c2a1..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Navigate.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// FilesWindowController+Navigate.swift -// VisualDiffer -// -// Created by davide ficano on 27/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - @objc func previousDifference(_: AnyObject) { - moveToDifference(false, showAnim: true) - } - - @objc func nextDifference(_: AnyObject) { - moveToDifference(true, showAnim: true) - } - - @objc func previousDifferenceFiles(_: AnyObject) { - navigateToFile(false) - } - - @objc func nextDifferenceFiles(_: AnyObject) { - navigateToFile(true) - } - - @objc func moveToDifference(_ gotoNext: Bool, showAnim: Bool) { - let currentPos = lastUsedView.selectedRow - var didWrap = false - - let section = if gotoNext { - currentDiffResult?.findNextSection(by: currentPos, wrapAround: true, didWrap: &didWrap) - } else { - currentDiffResult?.findPrevSection(by: currentPos, wrapAround: true, didWrap: &didWrap) - } - if let section { - let indexes = IndexSet(integer: section.start) - - // ensure all sections are visible moving to end but set selection to start - leftView.scrollTo(row: section.start, center: true) - leftView.selectRowIndexes(indexes, byExtendingSelection: false) - rightView.selectRowIndexes(indexes, byExtendingSelection: false) - if didWrap, showAnim { - scopeBar.findView.showWrapWindow() - } - } - } - - func navigateToFile(_ navigateToNext: Bool) { - guard let document = document as? VDDocument, - let parentSession = document.parentSession else { - return - } - - let block: DiffOpenerDelegateBlock = { leftPath, rightPath in - if leftPath == nil, rightPath == nil { - self.showOSDTop(!navigateToNext) - return false - } - if !self.alertSaveDirtyFiles() { - return false - } - // the document is marked as dirty when we set the sessionDiff properties - // so we update leftPath and rightPath without recording modifications - document.managedObjectContext?.updateWithoutRecordingModifications { - self.sessionDiff.leftPath = leftPath - self.sessionDiff.rightPath = rightPath - self.reloadAllMove(toFirstDifference: true) - } - - return true - } - - if navigateToNext { - parentSession.nextDifferenceFiles( - from: sessionDiff.leftPath, - rightPath: sessionDiff.rightPath, - block: block - ) - } else { - parentSession.prevDifferenceFiles( - from: sessionDiff.leftPath, - rightPath: sessionDiff.rightPath, - block: block - ) - } - } - - func showOSDTop(_ noPrevFile: Bool) { - guard let window else { - return - } - if noPrevFile { - topBottomView.setImage(NSImage(named: VDImageNameTop)) - topBottomView.setText(NSLocalizedString("No Previous File", comment: "")) - } else { - topBottomView.setImage(NSImage(named: VDImageNameBottom)) - topBottomView.setText(NSLocalizedString("No Next File", comment: "")) - } - topBottomView.animateInside(window.frame) - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+PathControlDelegate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+PathControlDelegate.swift deleted file mode 100644 index 928ddb1..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+PathControlDelegate.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// FilesWindowController+PathControlDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 25/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: PathControlDelegate { - func pathControl(_ pathControl: PathControl, willContextMenu menu: NSMenu) { - guard let url = pathControl.url else { - return - } - menu.setSubmenu( - NSMenu.appsMenuForFile( - url, - openAppAction: #selector(openWithApp), - openOtherAppAction: #selector(openWithOther) - ), - for: menu.addItem( - withTitle: NSLocalizedString("Open With", comment: ""), - action: nil, - keyEquivalent: "" - ) - ) - } - - func pathControl(_: PathControl, chosenUrl _: URL) { - // no need to check which path is changed (left or right) because - // the binding value has already set sessionDiff.Path - reloadAllMove(toFirstDifference: false) - } - - public func pathControl(_: NSPathControl, willDisplay openPanel: NSOpenPanel) { - openPanel.canChooseDirectories = false - openPanel.canChooseFiles = true - } - - public func pathControl(_ pathControl: PathControl, openWithApp app: URL) { - guard let editorData = editorDataFrom(pathControl) else { - return - } - openWith(app: app, attributes: [editorData]) - } - - public func pathControlOpenWithOtherApp(_ pathControl: PathControl) { - guard let editorData = editorDataFrom(pathControl) else { - return - } - openWithOtherApp(editorData) - } - - func editorDataFrom(_ pathControl: PathControl) -> OpenEditorAttribute? { - if pathControl === leftPanelView.pathView.pathControl { - return leftView.editorData(sessionDiff) - } else if pathControl === rightPanelView.pathView.pathControl { - return rightView.editorData(sessionDiff) - } - fatalError("Unable to determine which editor data to return") - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Read.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Read.swift deleted file mode 100644 index b732684..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Read.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// FilesWindowController+Read.swift -// VisualDiffer -// -// Created by davide ficano on 27/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -@MainActor extension FilesWindowController { - @objc func startComparison() { - reloadAllMove(toFirstDifference: true) - } - - func reloadAllMove(toFirstDifference moveToFirstDifference: Bool) { - resolvedLeftPath = sessionDiff.resolvePath( - for: .left, - chooseFileType: .file, - alwaysResolveSymlinks: CommonPrefs.shared.alwaysResolveSymlinks - ) - resolvedRightPath = sessionDiff.resolvePath( - for: .right, - chooseFileType: .file, - alwaysResolveSymlinks: CommonPrefs.shared.alwaysResolveSymlinks - ) - - let leftContent: String - let rightContent: String - - do { - leftContent = if let resolvedLeftPath { - try leftPanelView.readFile(resolvedLeftPath) - } else { - "" - } - rightContent = if let resolvedRightPath { - try rightPanelView.readFile(resolvedRightPath) - } else { - "" - } - } catch let error as NSError { - showError(error) - return - } - - if resolvedLeftPath != nil, resolvedLeftPath == resolvedRightPath { - leftPanelView.isEditAllowed = false - rightPanelView.isEditAllowed = false - } else { - leftPanelView.isEditAllowed = true - rightPanelView.isEditAllowed = true - } - - let newDiffResult = DiffResult() - - diffResult = newDiffResult - currentDiffResult = nil - - newDiffResult.diff(leftText: leftContent, rightText: rightContent, options: preferences.diffResultOptions) - setSliderMaxValue() - differenceCounters.update(counters: DiffCountersItem.diffCounter(withResult: newDiffResult)) - refreshLinesStatus() - - // update eol for files - leftPanelView.fileInfoBar.eol = newDiffResult.leftSide.eol - rightPanelView.fileInfoBar.eol = newDiffResult.rightSide.eol - - // force toolbar to enable items - window?.toolbar?.validateVisibleItems() - - synchronizeWindowTitleWithDocumentName() - - leftView.deselectAll(nil) - rightView.deselectAll(nil) - - if moveToFirstDifference { - moveToDifference(true, showAnim: false) - } - - reloadRowHeights() - } - - func showError(_ error: NSError) { - guard let window else { - return - } - let alert = NSAlert(error: error) - - alert.beginSheetModal(for: window) - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+RowHeightDataSource.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+RowHeightDataSource.swift deleted file mode 100644 index 23435b3..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+RowHeightDataSource.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// FilesWindowController+RowHeightDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 28/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: RowHeightDataSource { - var tableFont: NSFont { - currentFont - } - - func columnWidth(at side: DisplaySide) -> CGFloat { - let panel = side == .left ? leftPanelView : rightPanelView - - return if let column = panel.treeView.tableColumns.first { - column.width - } else { - max(panel.scrollView.bounds.width, 100) - } - } - - func line(at row: Int, side: DisplaySide) -> DiffLine? { - guard let currentDiffResult else { - return nil - } - let diffSide = side == .left ? currentDiffResult.leftSide : currentDiffResult.rightSide - - return diffSide.lines[row] - } - - func reloadTableData() { - leftView.reloadData(restoreSelection: true) - rightView.reloadData(restoreSelection: true) - } - - func reloadRowHeights() { - updateRowHeights() - - rowHeightCalculator.reloadData() - } - - private func calculateLineNumberWidth() -> CGFloat { - // count is indentical on left and right so we can use only the left lines count - // no matters if the missing lines increase the count - let maxLineCount = currentDiffResult?.leftSide.lines.count ?? 0 - - var attributes = [NSAttributedString.Key: Any]() - attributes[.font] = currentFont - - let str = String(format: "%lld", max(maxLineCount, 100)) as NSString - - return str.size(withAttributes: attributes).width - } - - func updateRowHeights(clearCache: Bool = true) { - lineNumberWidth = calculateLineNumberWidth() - if clearCache { - rowHeightCalculator.clearCache() - } - } - - // MARK: - Helper methods - - func setupHeightSynchronizer() { - rowHeightCalculator.dataSource = self - - updateRowHeights() - } - - func setWordWrap(enabled: Bool) { - // reset the slider position before reloading data to prevent display issues - leftPanelView.columnSlider.isHidden = enabled - leftPanelView.columnSlider.doubleValue = 0 - - rightPanelView.columnSlider.isHidden = enabled - rightPanelView.columnSlider.doubleValue = 0 - - rowHeightCalculator.isWordWrapEnabled = enabled - - let row = lastUsedView.firstVisibleRow - - leftPanelView.reloadTreeData() - rightPanelView.reloadTreeData() - - lastUsedView.scrollTo(row: row, center: false) - - updateToolbar() - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Save.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Save.swift deleted file mode 100644 index 71dccb5..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Save.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// FilesWindowController+Save.swift -// VisualDiffer -// -// Created by davide ficano on 24/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - @objc func alertSaveDirtyFiles() -> Bool { - if !leftView.isDirty, !rightView.isDirty { - return true - } - - let returnCode = NSAlert() - .saveFiles(isLeftDirty: leftView.isDirty, isRightDirty: rightView.isDirty) - - do { - switch returnCode { - case .saveDontSave: - return true - case .cancel: - return false - case .saveBoth: - try saveView(leftView) - try saveView(rightView) - return true - case .saveOnlyLeft: - try saveView(leftView) - return true - case .saveOnlyRight: - try saveView(rightView) - return true - default: - return true - } - } catch { - NSAlert(error: error).runModal() - return false - } - } - - func saveView(_ view: FilesTableView) throws { - guard let diffResult else { - return - } - var path: URL? - var diffSide: DiffSide - - let isLeftView = view.side == .left - - if isLeftView { - path = resolvedLeftPath - // view.lineData could be different from diffResult so we use it directly - diffSide = diffResult.leftSide - } else { - path = resolvedRightPath - diffSide = diffResult.rightSide - } - var secureURL: URL? - defer { - if let secureURL { - SecureBookmark.shared.stopAccessing(url: secureURL) - } - } - - if let path { - secureURL = SecureBookmark.shared.secure(fromBookmark: path, startSecured: true) - try checkFile(path, side: view.side) - } else { - if let url = choosePath() { - if isLeftView { - resolvedLeftPath = url - sessionDiff.leftPath = url.osPath - } else { - resolvedRightPath = url - sessionDiff.rightPath = url.osPath - } - } else { - return - } - } - - guard let path else { - return - } - - let fileInfoBar = isLeftView ? leftPanelView.fileInfoBar : rightPanelView.fileInfoBar - try diffSide.write(path: path, encoding: fileInfoBar.encoding ?? String.Encoding.utf8) - - fileInfoBar.fileAttrs = try FileManager.default.attributesOfItem(atPath: path.osPath) - view.isDirty = false - - setSliderMaxValue() - fileThumbnail.needsDisplay = true - window?.toolbar?.validateVisibleItems() - - // userInfo contains the modified file paths, here only one path is added - let position: FileSavedKey = isLeftView ? .leftPath : .rightPath - let userInfo = [ - position: path.osPath, - ] - NotificationCenter.default.post( - name: .fileSaved, - object: nil, - userInfo: userInfo - ) - } - - private func choosePath() -> URL? { - let savePanel = NSSavePanel() - savePanel.title = NSLocalizedString("Save File As", comment: "") - // since 10.11 the title is no longer shown so we use the message property - savePanel.message = NSLocalizedString("Save File As", comment: "") - - if savePanel.runModal() == .OK { - return savePanel.url - } - - return nil - } - - private func checkFile(_ path: URL, side: DisplaySide) throws { - // Check if file exists otherwise shows a comprehensive message because Cocoa doesn't - if FileManager.default.fileExists(atPath: path.osPath) { - return - } - throw FileError.fileNotExists(path: path, side: side) - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+SessionPreferences.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+SessionPreferences.swift deleted file mode 100644 index 6ab2fd4..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+SessionPreferences.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// FilesWindowController+SessionPreferences.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - @objc func openSessionSettingsSheet(_: AnyObject) { - guard let window else { - return - } - sessionPreferencesSheet.beginSheet( - window, - preferences: preferences, - selectedTab: .comparison - ) { - self.updateSessionPreferences($0) - } - } - - private func updateSessionPreferences(_ returnCode: NSApplication.ModalResponse) { - if returnCode == .OK { - preferences = sessionPreferencesSheet.preferences - sessionDiff.extraData.diffResultOptions = preferences.diffResultOptions - startComparison() - } - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+Slider.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+Slider.swift deleted file mode 100644 index 557b694..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+Slider.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// FilesWindowController+Slider.swift -// VisualDiffer -// -// Created by davide ficano on 01/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - @objc func sliderMoved(_ sender: AnyObject) { - let other = sender === leftPanelView.columnSlider ? rightPanelView.columnSlider : leftPanelView.columnSlider - other.doubleValue = sender.doubleValue - - leftView.reloadData(restoreSelection: true) - rightView.reloadData(restoreSelection: true) - } - - @objc func setSliderMaxValue() { - guard let diffResult else { - return - } - leftPanelView.setSliderMaxValue( - diffResult.leftSide.lines, - right: diffResult.rightSide.lines - ) - rightPanelView.setSliderMaxValue( - diffResult.leftSide.lines, - right: diffResult.rightSide.lines - ) - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+SplitViewDelegate.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+SplitViewDelegate.swift deleted file mode 100644 index 25fa65d..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+SplitViewDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// FilesWindowController+SplitViewDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 27/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController: NSSplitViewDelegate { - func splitViewDidResizeSubviews(_: Notification) { - rowHeightCalculator.reloadData() - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+TableView.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+TableView.swift deleted file mode 100644 index be3de94..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+TableView.swift +++ /dev/null @@ -1,224 +0,0 @@ -// -// FilesWindowController+TableView.swift -// VisualDiffer -// -// Created by davide ficano on 24/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -@MainActor extension FilesWindowController: NSTableViewDataSource, - NSTableViewDelegate, - FilesTableViewDelegate, - TableViewContextMenuDelegate { - public func numberOfRows(in tableView: NSTableView) -> Int { - (tableView as? FilesTableView)?.diffSide?.lines.count ?? 0 - } - - func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat { - rowHeightCalculator.height(for: row) - } - - public func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - guard let filesTableView = tableView as? FilesTableView, - let identifier = tableColumn?.identifier, - let diffSide = filesTableView.diffSide else { - return nil - } - let arr = diffSide.lines - let diffLine = arr[row] - - let view = tableView.makeView( - withIdentifier: identifier, - owner: nil - ) as? LineNumberTableCellView ?? LineNumberTableCellView() - - view.diffLine = diffLine - view.font = treeViewFont() - view.isSelected = tableView.isRowSelected(row) - view.formattedText = formattedText(diffLine) - view.isWordWrapEnabled = rowHeightCalculator.isWordWrapEnabled - view.lineNumberWidth = lineNumberWidth - - return view - } - - public func tableView(_: NSTableView, rowViewForRow _: Int) -> NSTableRowView? { - LineNumberTableRowView() - } - - public func tableViewSelectionDidChange(_ notification: Notification) { - guard let tableView = notification.object as? FilesTableView else { - return - } - updateDetailLines(tableView.selectedRow) - - let visibleRows = tableView.rows(in: tableView.visibleRect) - - for row in visibleRows.location ..< NSMaxRange(visibleRows) { - if let cellView = tableView.view( - atColumn: 0, - row: row, - makeIfNecessary: false - ) as? LineNumberTableCellView { - cellView.isSelected = tableView.isRowSelected(row) - } - } - } - - // MARK: - Drag&Drop - - public func tableView(_ tableView: NSTableView, validateDrop info: any NSDraggingInfo, proposedRow _: Int, proposedDropOperation _: NSTableView.DropOperation) -> NSDragOperation { - let pasteboard = info.draggingPasteboard - - var result: NSDragOperation = [] - - tableView.setDropRow(-1, dropOperation: .on) - if pasteboard.availableType(from: [.fileURL]) == nil { - return result - } - guard let arr = pasteboard.readObjects(forClasses: [NSURL.self]) as? [URL] else { - return result - } - - let path1 = arr[0].osPath - var isDir = ObjCBool(false) - let isValidPath1 = FileManager.default.fileExists( - atPath: path1, - isDirectory: &isDir - ) && isDir.boolValue == false - - if arr.count < 2 { - if isValidPath1 { - result = .copy - } - } else { - let path2 = arr[1].osPath - let isValidPath2 = FileManager.default.fileExists( - atPath: path2, - isDirectory: &isDir - ) && isDir.boolValue == false - if isValidPath1, isValidPath2 { - result = .copy - } - } - - return result - } - - public func tableView(_ tableView: NSTableView, acceptDrop info: any NSDraggingInfo, row _: Int, dropOperation _: NSTableView.DropOperation) -> Bool { - let pasteboard = info.draggingPasteboard - if pasteboard.availableType(from: [.fileURL]) == nil { - return false - } - guard let arr = pasteboard.readObjects(forClasses: [NSURL.self]) as? [URL] else { - return false - } - - if arr.count < 2 { - guard let path = arr.last?.osPath else { - return false - } - if tableView === leftView { - sessionDiff.leftPath = path - } else { - sessionDiff.rightPath = path - } - } else { - sessionDiff.leftPath = arr[0].osPath - sessionDiff.rightPath = arr[1].osPath - } - - reloadAllMove(toFirstDifference: false) - - return true - } - - func tableView(_: NSTableView, menuItem: NSMenuItem, hideMenuItem hide: inout Bool) -> Bool { - let action = menuItem.action - - hide = true - var isValid = false - if action == #selector(showInFinder) { - isValid = true - } else if action == #selector(copyFullPaths) - || action == #selector(copyFileNames) { - isValid = true - } else if action == #selector(showWhitespaces) { - if scopeBar.showWhitespaces { - menuItem.title = NSLocalizedString("Hide Whitespace", comment: "") - } else { - menuItem.title = NSLocalizedString("Show Whitespace", comment: "") - } - - isValid = true - } else if action == #selector(popupOpenWithApp) { - // validateMenuItem fills the menu - return validateMenuItem(menuItem) - } else if action == #selector(selectSection) { - isValid = true - } else if action == #selector(selectAdjacentSections) { - isValid = true - } else if action == #selector(copyLines) { - if lastUsedView.side == .left { - isValid = !sessionDiff.rightReadOnly - } else { - isValid = !sessionDiff.leftReadOnly - } - hide = !isValid - } else if action == #selector(deleteLines) { - isValid = validateMenuItem(menuItem) - } else if action == #selector(saveFile) { - isValid = lastUsedView.isDirty - } else if action == #selector(toggleDetails) { - isValid = true - } - - return isValid - } - - // MARK: - FilesTableViewDelegate methods - - func setLastUsedViewResponder(_ view: FilesTableView) { - lastUsedView = view - - updateToolbar() - } - - func filesTableView(_: FilesTableView, doubleClick _: Int) { - if let event = NSApp.currentEvent, - event.modifierFlags.contains(.shift) { - selectAdjacentSections(nil) - } else { - selectSection(nil) - } - } - - func filesTableView(_: FilesTableView, scrollHorizontally leftScroll: Bool) { - if rowHeightCalculator.isWordWrapEnabled { - return - } - let columnSlider = leftPanelView.columnSlider - - columnSlider.doubleValue += leftScroll ? -1 : 1 - sliderMoved(columnSlider) - } - - func deleteKeyPressed(_: FilesTableView) { - deleteLines(nil) - } - - func formattedText(_ diffLine: DiffLine) -> String { - let startingColumn = leftPanelView.columnSlider.integerValue - let line = getLine(diffLine) - - if line.count < startingColumn { - return "\u{27a5}" - } - if startingColumn > 0 { - let index = line.index(line.startIndex, offsetBy: startingColumn) - return String(line[index ..< line.endIndex]) - } - - return line - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+UICreation.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+UICreation.swift deleted file mode 100644 index 636d6ad..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+UICreation.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// FilesWindowController+UICreation.swift -// VisualDiffer -// -// Created by davide ficano on 08/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - func createDifferenceCounters() -> DifferenceCounters { - let view = DifferenceCounters(frame: .zero) - - view.isHidden = false - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func createStatusbarText() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.drawsBackground = false - view.isBezeled = false - view.isBordered = false - view.isEditable = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.controlSize = .small - view.alignment = .right - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func createLinesDetailViewWith() -> NSView { - let leftScrollView = createLineDetailScrollViewWithDocumentView(leftDetailsTextView) - let rightScrollView = createLineDetailScrollViewWithDocumentView(rightDetailsTextView) - - let linesDetailView = NSView(frame: .zero) - linesDetailView.translatesAutoresizingMaskIntoConstraints = false - - linesDetailView.addSubview(leftScrollView) - linesDetailView.addSubview(rightScrollView) - - NSLayoutConstraint.activate([ - leftScrollView.leadingAnchor.constraint(equalTo: linesDetailView.leadingAnchor), - leftScrollView.trailingAnchor.constraint(equalTo: linesDetailView.trailingAnchor), - leftScrollView.topAnchor.constraint(equalTo: linesDetailView.topAnchor), - leftScrollView.heightAnchor.constraint(greaterThanOrEqualToConstant: 22), - - rightScrollView.leadingAnchor.constraint(equalTo: linesDetailView.leadingAnchor), - rightScrollView.trailingAnchor.constraint(equalTo: linesDetailView.trailingAnchor), - rightScrollView.topAnchor.constraint(equalTo: leftScrollView.bottomAnchor, constant: 1), - rightScrollView.bottomAnchor.constraint(equalTo: linesDetailView.bottomAnchor), - rightScrollView.heightAnchor.constraint(greaterThanOrEqualToConstant: 22), - ]) - - return linesDetailView - } - - func createLineDetailScrollViewWithDocumentView(_ documentView: NSView) -> NSScrollView { - let view = NSScrollView(frame: .zero) - - view.hasVerticalScroller = false - view.hasHorizontalScroller = false - view.documentView = documentView - view.borderType = .bezelBorder - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func createLineDetailTextView() -> NSTextView { - let view = NSTextView(frame: .zero) - - view.isSelectable = true - view.isEditable = false - view.isRichText = true - view.disableWordWrap() - - view.delegate = self - - return view - } - - func createFilesScopeBar() -> FilesScopeBar { - let view = FilesScopeBar(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - view.initScopeBar(self) - view.reloadData() - - return view - } - - func createThumbnailView() -> FileThumbnailView { - let view = FileThumbnailView(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func createLineDetailsStackWithViews(_ views: [NSView]) -> NSStackView { - let view = NSStackView(views: views) - - view.orientation = .vertical - view.alignment = .width - view.distribution = .fill - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - static func createFilePanelsWithFrame(_ frame: NSRect) -> NSSplitView { - let view = NSSplitView(frame: frame) - - view.dividerStyle = .thin - view.isVertical = true - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - static func createFilePanelsSplitView( - leftPanelView: FilePanelView, - rightPanelView: FilePanelView - ) -> NSSplitView { - let filePanels = createFilePanelsWithFrame(NSRect(x: 0, y: 0, width: 1, height: 0)) - - filePanels.addArrangedSubview(leftPanelView) - filePanels.addArrangedSubview(rightPanelView) - - leftPanelView.setLinkPanels(rightPanelView) - - return filePanels - } - - /** - * The view on top of screen, it contains the thumbnail on the left and file difference panels on the right - */ - func createTopView(_ leftView: NSView, rightView: NSView) -> NSView { - let topView = NSView(frame: .zero) - - topView.addSubview(leftView) - topView.addSubview(rightView) - - NSLayoutConstraint.activate([ - leftView.leadingAnchor.constraint(equalTo: topView.leadingAnchor), - leftView.topAnchor.constraint(equalTo: topView.topAnchor, constant: 18), - leftView.bottomAnchor.constraint(equalTo: topView.bottomAnchor), - leftView.widthAnchor.constraint(equalToConstant: 15), - - rightView.leadingAnchor.constraint(equalTo: leftView.trailingAnchor), - rightView.trailingAnchor.constraint(equalTo: topView.trailingAnchor), - rightView.topAnchor.constraint(equalTo: topView.topAnchor), - rightView.bottomAnchor.constraint(equalTo: topView.bottomAnchor), - ]) - - return topView - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController+UISetup.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController+UISetup.swift deleted file mode 100644 index c8c346b..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController+UISetup.swift +++ /dev/null @@ -1,244 +0,0 @@ -// -// FilesWindowController+UISetup.swift -// VisualDiffer -// -// Created by davide ficano on 02/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesWindowController { - @objc func initAllViews() { - setupWindowLayout() - - // must be called after setting up all views - setupWindow() - leftPanelView.treeView.nextKeyView = rightView - } - - func setupWindowLayout() { - let detailsStackView = createLineDetailsStackWithViews([ - createTopView(fileThumbnail, rightView: filePanels), - linesDetailView, - ]) - - if let contentView = window?.contentView { - contentView.addSubview(differenceCounters) - contentView.addSubview(statusbarText) - contentView.addSubview(scopeBar) - contentView.addSubview(detailsStackView) - } - - setupDetailsStackConstraints(detailsStackView) - setupConstraints() - - updateUI() - - linesDetailView.isHidden = CommonPrefs.shared.hideFileDiffDetails - } - - func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - var leadingMargin: CGFloat = 7 - var trailingMargin: CGFloat = 5 - if #available(macOS 26, *) { - leadingMargin = 16 - trailingMargin = 16 - } - - NSLayoutConstraint.activate([ - scopeBar.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - scopeBar.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - scopeBar.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 1), - scopeBar.heightAnchor.constraint(equalToConstant: 25), - - differenceCounters.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: leadingMargin), - differenceCounters.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4), - // fills at least 3/4 of width space - differenceCounters.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.75), - - statusbarText.leadingAnchor.constraint(equalTo: differenceCounters.trailingAnchor, constant: 5), - statusbarText.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -trailingMargin), - statusbarText.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5), - statusbarText.widthAnchor.constraint(greaterThanOrEqualToConstant: 120), - ]) - } - - func setupDetailsStackConstraints(_ stackView: NSStackView) { - guard let contentView = window?.contentView else { - return - } - - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: scopeBar.bottomAnchor), - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.bottomAnchor.constraint(equalTo: differenceCounters.topAnchor, constant: -2), - - fileThumbnail.leadingAnchor.constraint(equalTo: stackView.leadingAnchor), - fileThumbnail.widthAnchor.constraint(equalToConstant: 15), - - // linesDetailView has a fixed height - linesDetailView.heightAnchor.constraint(equalToConstant: 46), - ]) - } - - func setupWindow() { - guard let window else { - return - } - window.delegate = self - window.toolbar = NSToolbar(identifier: "FilesToolbar", delegate: self) - window.makeFirstResponder(leftPanelView.treeView) - - window.collectionBehavior = [window.collectionBehavior, .fullScreenPrimary] - } - - @objc func updateUI() { - updateTreeViewFont() - - // invalidate the line text cache - cachedLineTextMap.removeAllObjects() - - reloadRowHeights() - - updateTabWidth() - - updateDetailLines(leftView.selectedRow) - - updateStatusbarText() - } - - func updateStatusbarText() { - var arr = [String]() - - arr.append(String(format: NSLocalizedString("Tab Width: %ld", comment: ""), CommonPrefs.shared.tabWidth)) - - if fontZoomFactor > 0 { - arr.append("\(100 + Int(fontZoomFactor) * 10)%") - } - statusbarText.stringValue = arr.joined(separator: ", ") - } - - func updateTabWidth() { - let tabWidth = CommonPrefs.shared.tabWidth - leftDetailsTextView.setTabStop(tabWidth) - rightDetailsTextView.setTabStop(tabWidth) - - visibleWhitespaces.tabWidth = tabWidth - } - - func updateTreeViewFont() { - let font = treeViewFont() - - leftView.updateFont(font, reloadData: false) - rightView.updateFont(font, reloadData: false) - leftDetailsTextView.font = font - rightDetailsTextView.font = font - } - - func treeViewFont() -> NSFont { - currentFont = CommonPrefs.shared.fileTextFont - - if fontZoomFactor > 0 { - currentFont = NSFontManager.shared.convert( - currentFont, - toSize: currentFont.pointSize + fontZoomFactor - ) - } - - return currentFont - } - - /** - * Setup elements requiring the sessionDiff is correctly defined, this method must be called after setDocument - */ - func setupUIState() { - setupObservers() - - window?.setFrameAutosaveName(String(format: "%lx%lx", sessionDiff.leftPath?.hash ?? 0, sessionDiff.rightPath?.hash ?? 0)) - - scopeBar.findView.delegate = FilesTableViewFindTextDelegate(view: leftView) - updateScopeBar() - - leftPanelView.bindControls() - rightPanelView.bindControls() - } - - func updateScopeBar() { - scopeBar.showWhitespaces(scopeBar.showWhitespaces, informDelegate: false) - scopeBar.showLineFilter(scopeBar.showLinesFilter, informDelegate: false) - } - - func setupObservers() { - NotificationCenter.default.addObserver( - self, - selector: #selector(fontDidChange), - name: .prefsChanged, - object: nil - ) - - // a register for those notifications on the synchronized content view. - NotificationCenter.default.addObserver( - self, - selector: #selector(synchronizedViewContentBoundsDidChange), - name: NSView.boundsDidChangeNotification, - object: leftPanelView.scrollView.contentView - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(appAppearanceDidChange), - name: .appAppearanceDidChange, - object: nil - ) - } - - func removeObservers() { - NotificationCenter.default.removeObserver( - self, - name: .prefsChanged, - object: nil - ) - - NotificationCenter.default.removeObserver( - self, - name: NSView.boundsDidChangeNotification, - object: leftPanelView.scrollView.contentView() - ) - NotificationCenter.default.removeObserver( - self, - name: .appAppearanceDidChange, - object: nil - ) - } - - @objc private func fontDidChange(notification: Notification) { - guard let userInfo = notification.userInfo, - let target = userInfo[PrefChangedKey.target] as? PrefChangedKey.Target else { - return - } - - if target == .file { - fontZoomFactor = 0 - } - } - - @objc func appAppearanceDidChange(_: Notification) { - leftView.reloadData() - rightView.reloadData() - - if let diffResult { - differenceCounters.update(counters: DiffCountersItem.diffCounter(withResult: diffResult)) - } else { - fatalError("diffResult is nil, why???") - } - } - - // MARK: - File and view scroll - - @objc func synchronizedViewContentBoundsDidChange(_: Notification) { - fileThumbnail.needsDisplay = true - } -} diff --git a/Sources/Features/FilesCompare/Controller/FilesWindowController.swift b/Sources/Features/FilesCompare/Controller/FilesWindowController.swift deleted file mode 100644 index 83f16c9..0000000 --- a/Sources/Features/FilesCompare/Controller/FilesWindowController.swift +++ /dev/null @@ -1,148 +0,0 @@ -// -// FilesWindowController.swift -// VisualDiffer -// -// Created by davide ficano on 20/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -class FilesWindowController: NSWindowController { - var lineNumberWidth: CGFloat = 0 - - var rowHeightCalculator = RowHeightCalculator() - - // swiftlint:disable:next implicitly_unwrapped_optional - @objc var sessionDiff: SessionDiff! - - let cachedLineTextMap: NSMapTable - - var currentFont: NSFont - var fontZoomFactor: CGFloat = 0 { - didSet { - if fontZoomFactor < 0 || fontZoomFactor > 10 { - fontZoomFactor = oldValue - } - updateUI() - } - } - - lazy var differenceCounters: DifferenceCounters = createDifferenceCounters() - lazy var statusbarText: NSTextField = createStatusbarText() - - lazy var fileThumbnail: FileThumbnailView = createThumbnailView() - - let visibleWhitespaces: VisibleWhitespaces - - lazy var scopeBar: FilesScopeBar = createFilesScopeBar() - var lastUsedView: FilesTableView - - var diffResult: DiffResult? - var filteredDiffResult: DiffResult? - var currentDiffResult: DiffResult? - - var resolvedLeftPath: URL? - var resolvedRightPath: URL? - - lazy var linesDetailView: NSView = createLinesDetailViewWith() - lazy var leftDetailsTextView: NSTextView = createLineDetailTextView() - lazy var rightDetailsTextView: NSTextView = createLineDetailTextView() - - lazy var topBottomView: WindowOSD = .init( - // swiftlint:disable:next force_unwrapping - image: NSImage(named: VDImageNameBottom)!, - parent: window - ) - - let filePanels: NSSplitView - let leftPanelView: FilePanelView - let rightPanelView: FilePanelView - - @objc var leftView: FilesTableView { - leftPanelView.treeView - } - - @objc var rightView: FilesTableView { - rightPanelView.treeView - } - - var preferences = FilePreferences() - - lazy var sessionPreferencesSheet: FileSessionPreferencesWindow = .init() - - init() { - let window = WindowCancelOperation.createWindow() - - currentFont = CommonPrefs.shared.fileTextFont - - visibleWhitespaces = VisibleWhitespaces() - visibleWhitespaces.tabWidth = CommonPrefs.shared.tabWidth - - cachedLineTextMap = NSMapTable( - keyOptions: .objectPointerPersonality, - valueOptions: .strongMemory - ) - - // panels - leftPanelView = FilePanelView(side: .left) - rightPanelView = FilePanelView(side: .right) - filePanels = Self.createFilePanelsSplitView( - leftPanelView: leftPanelView, - rightPanelView: rightPanelView - ) - - lastUsedView = leftPanelView.treeView - - super.init(window: window) - - setup( - filePanel: leftPanelView, - delegate: self, - sliderTarget: self, - sliderAction: #selector(sliderMoved) - ) - setup( - filePanel: rightPanelView, - delegate: self, - sliderTarget: self, - sliderAction: #selector(sliderMoved) - ) - - shouldCascadeWindows = false - - initAllViews() - - filePanels.delegate = self - - setupHeightSynchronizer() - setWordWrap(enabled: false) - } - - func setup( - filePanel: FilePanelView, - delegate: PathControlDelegate & FilesTableViewDelegate & FileInfoBarDelegate & NSTableViewDataSource, - sliderTarget target: AnyObject?, - sliderAction action: Selector? - ) { - filePanel.setDelegate(delegate) - filePanel.setSliderChangeAction(target, action: action) - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Zoom Font - - @objc func zoomLargerFont(_: AnyObject) { - fontZoomFactor += 1 - } - - @objc func zoomSmallerFont(_: AnyObject) { - fontZoomFactor -= 1 - } - - @objc func zoomResetFont(_: AnyObject) { - fontZoomFactor = 0 - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffChangeType.swift b/Sources/Features/FilesCompare/DiffResult/DiffChangeType.swift deleted file mode 100644 index 9dbf540..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffChangeType.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// DiffChangeType.swift -// VisualDiffer -// -// Created by davide ficano on 27/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -enum DiffChangeType { - case matching - case added - case deleted - case changed - case missing -} - -extension DiffChangeType: CustomStringConvertible { - var description: String { - switch self { - case .added: - "added" - case .deleted: - "deleted" - case .matching: - "same" - case .changed: - "changed" - case .missing: - "missing" - } - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffLine.swift b/Sources/Features/FilesCompare/DiffResult/DiffLine.swift deleted file mode 100644 index f40bf14..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffLine.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// DiffLine.swift -// VisualDiffer -// -// Created by davide ficano on 21/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -class DiffLine { - enum Visibility: Int { - case all - case matches - case differences - } - - enum DisplayMode { - case normal - case merged - } - - static let invalidLineNumber = -1 - - var type: DiffChangeType - var number: Int - // mode is used by UI to determine how to draw line - var mode: DisplayMode = .normal - var component: DiffLineComponent - var isSectionSeparator = false - var filteredIndex = 0 - - var text: String { - component.text - } - - init( - with type: DiffChangeType, - number: Int, - component: DiffLineComponent - ) { - self.type = type - self.number = number - self.component = component - } - - static func missingLine() -> DiffLine { - DiffLine( - with: .missing, - number: invalidLineNumber, - component: DiffLineComponent(text: "", eol: .missing) - ) - } - - func makeMissing() { - type = .missing - component = DiffLineComponent(text: "", eol: .missing) - number = Self.invalidLineNumber - } -} - -extension DiffLine: CustomStringConvertible { - var description: String { - String(format: "%ld %@ : %@", number, type.description, text) - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffLineComponent.swift b/Sources/Features/FilesCompare/DiffResult/DiffLineComponent.swift deleted file mode 100644 index 3d431c3..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffLineComponent.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// DiffLineComponent.swift -// VisualDiffer -// -// Created by davide ficano on 19/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct DiffLineComponent { - let text: String - let eol: EndOfLine - - static func splitLines(_ text: String) -> [DiffLineComponent] { - var lines: [DiffLineComponent] = [] - - Self.enumerateLines(text: text) { - lines.append($0) - } - - return lines - } - - static func enumerateLines(text: String, _ callback: @escaping (_ component: DiffLineComponent) -> Void) { - text.enumerateSubstrings( - in: text.startIndex ..< text.endIndex, - options: [.byLines] - ) { substring, substringRange, _, _ in - if let substring { - let eol = eol(from: text, substringRange: substringRange) - callback(DiffLineComponent(text: substring, eol: eol)) - } - } - } - - private static func eol(from text: String, substringRange: Range) -> EndOfLine { - let wholeRange = text.startIndex ..< text.endIndex - - guard substringRange.upperBound < wholeRange.upperBound else { - return .missing - } - return EndOfLine.from(character: text[substringRange.upperBound]) - } -} - -extension DiffLineComponent { - var withEol: String { - text + eol.stringValue - } -} - -extension [DiffLineComponent] { - func detectEOL() -> EndOfLine { - guard let firstEol = first?.eol else { - return .missing - } - - return dropFirst().contains { $0.eol != .missing && $0.eol != firstEol } - ? .mixed - : firstEol - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffResult+Dump.swift b/Sources/Features/FilesCompare/DiffResult/DiffResult+Dump.swift deleted file mode 100644 index ebd6d73..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffResult+Dump.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// DiffResult+Dump.swift -// VisualDiffer -// -// Created by davide ficano on 12/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -// periphery:ignore:all - -#if DEBUG - - import os.log - - extension DiffResult { - func fullDump() { - let home = URL(filePath: NSHomeDirectory()) - let fullPath = home.appendingPathComponent("diffResult.txt").osPath - Logger.debug.info("Dump path: '\(fullPath)'") - - try? FileManager.default.removeItem(atPath: fullPath) - var fileHandle = FileHandle(forWritingAtPath: fullPath) - - if fileHandle == nil { - FileManager.default.createFile(atPath: fullPath, contents: nil, attributes: nil) - fileHandle = FileHandle(forWritingAtPath: fullPath) - } - guard let fileHandle else { - Logger.debug.error("Unable to open file ' \(fullPath)'") - return - } - defer { - fileHandle.closeFile() - } - - dump(fileHandle) - Logger.debug.info("Dump completed") - } - - static func writeLine(_ fileHandle: FileHandle, line: String) { - if let data = String(format: "%@\n", line).data(using: .ascii) { - fileHandle.write(data) - } - } - - func dump(_ fileHandle: FileHandle) { - summary.dump(fileHandle) - - DiffResult.writeLine(fileHandle, line: "leftSide") - leftSide.dump(fileHandle) - - DiffResult.writeLine(fileHandle, line: "rightSide") - rightSide.dump(fileHandle) - - DiffResult.writeLine(fileHandle, line: "sections") - for section in sections { - section.dump(fileHandle) - } - } - } - - extension DiffSummary { - func dump(_ fileHandle: FileHandle) { - DiffResult.writeLine(fileHandle, line: "matching = \(matching)") - DiffResult.writeLine(fileHandle, line: "changed = \(changed)") - DiffResult.writeLine(fileHandle, line: "deleted = \(deleted)") - DiffResult.writeLine(fileHandle, line: "added = \(added)") - } - } - - extension DiffSide { - func dump(_ fileHandle: FileHandle) { - DiffResult.writeLine(fileHandle, line: "eof = \(eol)") - - for line in lines { - line.dump(fileHandle) - } - } - } - - extension DiffLine { - func dump(_ fileHandle: FileHandle) { - DiffResult.writeLine(fileHandle, line: "type = \(type) number = \(number) mode = \(mode)") -// DiffResult.writeLine(fileHandle, line: "text = \(text)") - DiffResult.writeLine(fileHandle, line: "isSectionSeparator = \(isSectionSeparator) filteredIndex = \(filteredIndex)") - } - } - - extension DiffSection { - func dump(_ fileHandle: FileHandle) { - DiffResult.writeLine(fileHandle, line: "start = \(start) end = \(end)") - } - } -#endif diff --git a/Sources/Features/FilesCompare/DiffResult/DiffResult+Lines.swift b/Sources/Features/FilesCompare/DiffResult/DiffResult+Lines.swift deleted file mode 100644 index ac3db2e..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffResult+Lines.swift +++ /dev/null @@ -1,210 +0,0 @@ -// -// DiffResult+Lines.swift -// VisualDiffer -// -// Created by davide ficano on 10/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension DiffResult { - /// - /// Copy lines from source to destination. - /// - Parameters: - /// - all: the DiffResult with all lines visible - /// - current: the DiffResult currently used, contains the filtered lines, could be the same of allResult if no filter is applied - /// - rows: the rows to copy - /// - source: the source side used to copy line - /// - visibility: used to update the filtered DiffResult - /// - static func copyLines( - all: DiffResult, - current: DiffResult, - rows: IndexSet, - source side: DisplaySide, - visibility: DiffLine.Visibility - ) { - let fromLeft = side == .left - let srcLines = fromLeft ? current.leftSide.lines : current.rightSide.lines - let destLines = fromLeft ? current.rightSide.lines : current.leftSide.lines - - for row in rows.reversed() { - let srcLine = srcLines[row] - let destLine = destLines[row] - - if srcLine.type == .missing { - if visibility == .differences { - if fromLeft { - all.remove(line: srcLine) - } else { - all.remove(line: destLine) - } - } - current.removeLine(at: row) - } else { - srcLine.type = .matching - - destLine.mode = .merged - destLine.type = .matching - destLine.component = srcLine.component - - // now lines match so remove them from view - if visibility == .differences { - current.removeLine(at: row) - } - } - } - - let diffSide = fromLeft ? all.rightSide : all.leftSide - diffSide.renumberLines() - } - - /// - /// Delete lines from source. - /// - Parameters: - /// - all: the DiffResult with all lines visible - /// - current: the DiffResult currently used, contains the filtered lines, could be the same of allDiffResult if no filter is applied - /// - rows: the rows to delete - /// - side: the side used to delete lines - /// - visibility: used to update the filtered DiffResult - /// - static func deleteLines( - all: DiffResult, - current: DiffResult, - rows: IndexSet, - side: DisplaySide, - visibility: DiffLine.Visibility - ) { - let fromLeft = side == .left - let srcLines = fromLeft ? current.leftSide.lines : current.rightSide.lines - let destLines = fromLeft ? current.rightSide.lines : current.leftSide.lines - - for row in rows.reversed() { - let srcLine = srcLines[row] - let destLine = destLines[row] - - // can't delete an already missing line - if srcLine.type == .missing { - continue - } - - if destLine.type == .missing { - if visibility == .differences { - if fromLeft { - all.remove(line: srcLine) - } else { - all.remove(line: destLine) - } - } - current.removeLine(at: row) - } else { - srcLine.makeMissing() - - if fromLeft { - destLine.type = .added - } else { - destLine.type = .deleted - } - - // now lines differ so remove them from view - if visibility == .matches { - current.removeLine(at: row) - } - } - } - let destAllLinesStatus = fromLeft ? all.leftSide : all.rightSide - - destAllLinesStatus.renumberLines() - } - - static func justDifferentLines(_ result: DiffResult) -> DiffResult { - let onlyMismatches = DiffResult(sections: DiffSection.compact(sections: result.sections)) - - let leftSide = onlyMismatches.leftSide - let rightSide = onlyMismatches.rightSide - - var lastInSection: DiffLine? - // previous is used to group by difference type (eg. .added or .changed) - var previous: DiffLine? - - for line in result.leftSide.lines { - line.isSectionSeparator = false - if line.type != .matching { - if let previous, - line.type != previous.type { - previous.isSectionSeparator = true - } - leftSide.add(line: line) - lastInSection = line - previous = line - } else { - if let lastInSection { - lastInSection.isSectionSeparator = true - } - lastInSection = nil - } - line.filteredIndex = leftSide.lines.count - 1 - } - - for line in result.rightSide.lines { - line.isSectionSeparator = false - if line.type != .matching { - if let previous, - line.type != previous.type { - previous.isSectionSeparator = true - } - rightSide.add(line: line) - lastInSection = line - previous = line - } else { - if let lastInSection { - lastInSection.isSectionSeparator = true - } - lastInSection = nil - } - line.filteredIndex = rightSide.lines.count - 1 - } - - return onlyMismatches - } - - static func justMatchingLines(_ result: DiffResult) -> DiffResult { - // Difference sections are not visible - let onlyMatches = DiffResult() - - let leftSide = onlyMatches.leftSide - let rightSide = onlyMatches.rightSide - - var lastInSection: DiffLine? - - for line in result.leftSide.lines { - line.isSectionSeparator = false - if line.type == .matching { - leftSide.add(line: line) - lastInSection = line - } else { - if let lastInSection { - lastInSection.isSectionSeparator = true - } - lastInSection = nil - } - line.filteredIndex = leftSide.lines.count - 1 - } - - lastInSection = nil - for line in result.rightSide.lines { - line.isSectionSeparator = false - if line.type == .matching { - rightSide.add(line: line) - lastInSection = line - } else { - if let lastInSection { - lastInSection.isSectionSeparator = true - } - lastInSection = nil - } - line.filteredIndex = rightSide.lines.count - 1 - } - - return onlyMatches - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffResult+Section.swift b/Sources/Features/FilesCompare/DiffResult/DiffResult+Section.swift deleted file mode 100644 index e991463..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffResult+Section.swift +++ /dev/null @@ -1,142 +0,0 @@ -// -// DiffResult+Section.swift -// VisualDiffer -// -// Created by davide ficano on 08/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension DiffResult { - func createSections() -> [DiffSection] { - let lines = leftSide.lines - var sections = [DiffSection]() - var i = 0 - - while i < lines.count { - let line = lines[i] - - if line.type != .matching { - // find end - let start = i - let currStatus = line.type - i += 1 - i = lines[i...].firstIndex { $0.type != currStatus } ?? lines.count - i -= 1 - sections.append(DiffSection(start: start, end: i)) - } - - i += 1 - } - - return sections - } - - func findNextSection( - by position: Int, - wrapAround: Bool, - didWrap: inout Bool - ) -> DiffSection? { - var sectionFound: DiffSection? - var sectionIndex = 0 - - while sectionIndex < sections.count { - let section = sections[sectionIndex] - sectionFound = section - - if section.start > position { - break - } - sectionIndex += 1 - } - - if sectionIndex < sections.count { - didWrap = false - return sectionFound - } - if wrapAround { - sectionFound = sections.isEmpty ? nil : sections[0] - didWrap = sectionFound != nil - } - return sectionFound - } - - func findPrevSection( - by position: Int, - wrapAround: Bool, - didWrap: inout Bool - ) -> DiffSection? { - var sectionFound: DiffSection? - var sectionIndex = sections.count - 1 - - while sectionIndex >= 0 { - let section = sections[sectionIndex] - sectionFound = section - - if section.end < position { - break - } - sectionIndex -= 1 - } - - if sectionIndex >= 0 { - didWrap = false - return sectionFound - } - if wrapAround { - sectionFound = sections.last - didWrap = sectionFound != nil - } - return sectionFound - } - - func findSection(with line: Int) -> DiffSection? { - sections.first { $0.start <= line && line <= $0.end } - } - - func findSectionIndexSet(with index: Int) -> IndexSet? { - guard index >= 0, - let range = findSection(with: index)?.range else { - return nil - } - - return IndexSet(integersIn: range) - } - - func resetSectionSeparators() { - for (index, leftLine) in leftSide.lines.enumerated() { - leftLine.isSectionSeparator = false - let rightLine = rightSide.lines[index] - rightLine.isSectionSeparator = false - } - } - - func findAdjacentSections(from index: Int) -> IndexSet? { - guard index >= 0 else { - return nil - } - var section = findSection(with: index) - - if section == nil { - return nil - } - var indexes = IndexSet() - - // Store this value because section is overwritten to find previous sections - // swiftlint:disable:next force_unwrapping - var endRow = section!.end - - repeat { - if let tmpSection = section { - indexes.insert(integersIn: tmpSection.range) - section = findSection(with: tmpSection.start - 1) - } - } while section != nil - - while let section = findSection(with: endRow + 1) { - indexes.insert(integersIn: section.range) - endRow = section.end - } - - return indexes - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffResult.Options.swift b/Sources/Features/FilesCompare/DiffResult/DiffResult.Options.swift deleted file mode 100644 index 75cbd51..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffResult.Options.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// DiffResult.Options.swift -// VisualDiffer -// -// Created by davide ficano on 01/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension DiffResult.Options { - static let allIgnoreWhitespaces: Self = [ - .ignoreLeadingWhitespaces, - .ignoreTrailingWhitespaces, - .ignoreInternalWhitespaces, - ] - - func stripWhitespaces(text: String) -> String { - if intersection(.allIgnoreWhitespaces).isEmpty { - return text - } - var isLeading = true - var whitespaceStart = -1 - var whitespaceCount = 0 - - var stripped = String() - stripped.reserveCapacity(text.count) - - for (offset, ch) in text.enumerated() { - if ch.isWhitespace { - if isLeading { - if !contains(.ignoreLeadingWhitespaces) { - stripped.append(ch) - } - } else { - if whitespaceStart == -1 { - whitespaceStart = offset - } - whitespaceCount += 1 - } - } else { - isLeading = false - - if whitespaceCount > 0, !contains(.ignoreInternalWhitespaces) { - let start = text.index(text.startIndex, offsetBy: whitespaceStart) - let end = text.index(start, offsetBy: whitespaceCount) - stripped.append(contentsOf: text[start ..< end]) - } - - whitespaceStart = -1 - whitespaceCount = 0 - stripped.append(ch) - } - } - - if whitespaceCount > 0, !contains(.ignoreTrailingWhitespaces) { - let start = text.index(text.startIndex, offsetBy: whitespaceStart) - let end = text.index(start, offsetBy: whitespaceCount) - stripped.append(contentsOf: text[start ..< end]) - } - - return stripped - } - - func applyTransformations(component: DiffLineComponent) -> String { - var stripped = stripWhitespaces(text: component.text) - - if contains(.ignoreCharacterCase) { - stripped = stripped.lowercased() - } - - if contains(.ignoreLineEndings) { - return stripped - } - return stripped + component.eol.stringValue - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffResult.swift b/Sources/Features/FilesCompare/DiffResult/DiffResult.swift deleted file mode 100644 index 88fca31..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffResult.swift +++ /dev/null @@ -1,315 +0,0 @@ -// -// DiffResult.swift -// VisualDiffer -// -// Created by davide ficano on 21/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -class DiffResult { - struct Options: OptionSet { - let rawValue: Int - - static let ignoreLineEndings = Options(rawValue: 1 << 0) - static let ignoreLeadingWhitespaces = Options(rawValue: 1 << 1) - static let ignoreTrailingWhitespaces = Options(rawValue: 1 << 2) - static let ignoreInternalWhitespaces = Options(rawValue: 1 << 3) - static let ignoreCharacterCase = Options(rawValue: 1 << 4) - } - - private(set) var leftSide = DiffSide() - private(set) var rightSide = DiffSide() - private(set) var sections = [DiffSection]() - private(set) var summary = DiffSummary() - - convenience init(sections: [DiffSection]) { - self.init() - - self.sections = sections - } - - func diff( - leftText: String, - rightText: String, - options: Options = [] - ) { - sections = [] - summary.reset() - - let leftLines = DiffLineComponent.splitLines(leftText) - let rightLines = DiffLineComponent.splitLines(rightText) - - leftSide.eol = leftLines.detectEOL() - rightSide.eol = rightLines.detectEOL() - - // swiftlint:disable force_cast - let stringifier: (Any) -> String = { - options.applyTransformations(component: $0 as! DiffLineComponent) - } - // swiftlint:enable force_cast - - let udiff = UnifiedDiff(originalLines: leftLines, revisedLines: rightLines, stringifier: stringifier) - var script = udiff.diff_2(false) - - var leftIndex = 0 - var rightIndex = 0 - - while let localScript = script { - updateMatchingLines( - leftCount: Int(localScript.line0), - rightCount: Int(localScript.line1), - leftLines: leftLines, - rightLines: rightLines, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - - let commonLines = Int(min(localScript.deleted, localScript.inserted)) - let diffLines = Int(localScript.deleted - localScript.inserted) - - var start = leftSide.lines.count - - // Create a new section to distinguish adjacent - // .deleted from .added lines - if commonLines > 0, localScript.deleted != localScript.inserted { - sections.append(DiffSection( - start: start, - end: leftSide.lines.count + commonLines - 1 - )) - start = leftSide.lines.count + commonLines - } - - updateChangedLines( - lineCount: commonLines, - leftLines: leftLines, - rightLines: rightLines, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - - if diffLines > 0 { - updateDeletedLines( - lineCount: diffLines, - leftLines: leftLines, - rightLines: rightLines, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - } else { - updateAddedLines( - lineCount: -diffLines, - leftLines: leftLines, - rightLines: rightLines, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - } - - sections.append(DiffSection( - start: start, - end: leftSide.lines.count - 1 - )) - - script = localScript.link - } - - updateMatchingLines( - leftCount: leftLines.count, - rightCount: rightLines.count, - leftLines: leftLines, - rightLines: rightLines, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - } - - // swiftlint:disable:next function_parameter_count - private func updateMatchingLines( - leftCount: Int, - rightCount: Int, - leftLines: [DiffLineComponent], - rightLines: [DiffLineComponent], - leftIndex: inout Int, - rightIndex: inout Int - ) { - summary.matching += leftCount - leftIndex - - while leftIndex < leftCount { - let line = DiffLine( - with: .matching, - number: leftIndex + 1, - component: leftLines[leftIndex] - ) - leftSide.add(line: line) - leftIndex += 1 - } - - while rightIndex < rightCount { - let line = DiffLine( - with: .matching, - number: rightIndex + 1, - component: rightLines[rightIndex] - ) - rightSide.add(line: line) - rightIndex += 1 - } - } - - private func updateChangedLines( - lineCount: Int, - leftLines: [DiffLineComponent], - rightLines: [DiffLineComponent], - leftIndex: inout Int, - rightIndex: inout Int - ) { - // update changed lines - summary.changed += lineCount - - for _ in 0 ..< lineCount { - let leftLine = DiffLine( - with: .changed, - number: leftIndex + 1, - component: leftLines[leftIndex] - ) - leftSide.add(line: leftLine) - - let rightLine = DiffLine( - with: .changed, - number: rightIndex + 1, - component: rightLines[rightIndex] - ) - rightSide.add(line: rightLine) - leftIndex += 1 - rightIndex += 1 - } - } - - private func updateDeletedLines( - lineCount: Int, - leftLines: [DiffLineComponent], - rightLines _: [DiffLineComponent], - leftIndex: inout Int, - rightIndex _: inout Int - ) { - summary.deleted += lineCount - - for _ in 0 ..< lineCount { - let line = DiffLine( - with: .deleted, - number: leftIndex + 1, - component: leftLines[leftIndex] - ) - leftSide.add(line: line) - leftIndex += 1 - } - for _ in 0 ..< lineCount { - let line = DiffLine.missingLine() - rightSide.add(line: line) - } - } - - private func updateAddedLines( - lineCount: Int, - leftLines _: [DiffLineComponent], - rightLines: [DiffLineComponent], - leftIndex _: inout Int, - rightIndex: inout Int - ) { - for _ in 0 ..< lineCount { - let line = DiffLine.missingLine() - leftSide.add(line: line) - } - - summary.added += lineCount - - for _ in 0 ..< lineCount { - let line = DiffLine( - with: .added, - number: rightIndex + 1, - component: rightLines[rightIndex] - ) - rightSide.add(line: line) - rightIndex += 1 - } - } - - func insert( - text: String, - at startIndex: Int, - side: DisplaySide - ) { - switch side { - case .left: - insertLines( - text: text, - destination: leftSide, - otherSide: rightSide, - at: startIndex, - type: .deleted - ) - case .right: - insertLines( - text: text, - destination: rightSide, - otherSide: leftSide, - at: startIndex, - type: .added - ) - } - } - - private func insertLines( - text: String, - destination: DiffSide, - otherSide: DiffSide, - at startIndex: Int, - type: DiffChangeType - ) { - var index = startIndex - - DiffLineComponent.enumerateLines(text: text) { component in - let destLine = destination.lines[index] - if destLine.type == .missing { - destLine.component = component - let otherLine = otherSide.lines[index] - // simple comparison - if otherLine.text == component.text { - otherLine.type = .matching - destLine.type = .matching - } else { - otherLine.type = .changed - destLine.type = .changed - } - } else { - // line numbers will be set correctly below - destination.insert(DiffLine(with: type, number: 0, component: component), at: index) - otherSide.insert(DiffLine.missingLine(), at: index) - } - index += 1 - } - destination.renumberLines() - } - - func removeLine(at index: Int) { - leftSide.removeLine(at: index) - rightSide.removeLine(at: index) - } - - @discardableResult - func remove(line: DiffLine) -> Bool { - if let index = leftSide.index(of: line) { - removeLine(at: index) - return true - } - return false - } - - func refreshSections() { - sections = createSections() - refreshSummary() - } - - func refreshSummary() { - summary.refresh(leftSide.lines) - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffSection.swift b/Sources/Features/FilesCompare/DiffResult/DiffSection.swift deleted file mode 100644 index 29dcef5..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffSection.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// DiffSection.swift -// VisualDiffer -// -// Created by davide ficano on 07/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DiffSection { - private(set) var start: Int - private(set) var end: Int - - var range: ClosedRange { - start ... end - } - - init(start: Int = 0, end: Int = 0) { - self.start = start - self.end = end - } - - static func compact(sections: [DiffSection]) -> [DiffSection] { - var start = 0 - var compacted = [DiffSection]() - compacted.reserveCapacity(sections.count) - - for section in sections { - let len = section.end - section.start + 1 - compacted.append(DiffSection(start: start, end: start + section.end - section.start)) - start += len - } - return compacted - } -} - -extension DiffSection: CustomStringConvertible { - var description: String { - String(format: "(%ld, %ld)", start, end) - } -} - -extension DiffSection: Equatable { - static func == (lhs: DiffSection, rhs: DiffSection) -> Bool { - lhs.start == rhs.start && lhs.end == rhs.end - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffSide.swift b/Sources/Features/FilesCompare/DiffResult/DiffSide.swift deleted file mode 100644 index 5cb227e..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffSide.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// DiffSide.swift -// VisualDiffer -// -// Created by davide ficano on 21/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -class DiffSide { - private(set) var lines: [DiffLine] = [] - var eol: EndOfLine = .unix - - var linesCount: Int { - lines.reversed().first { $0.number > 0 }?.number ?? 0 - } - - func findLineIndex(by lineNumber: Int) -> Int? { - lines.firstIndex { $0.number == lineNumber } - } - - func add(line: DiffLine) { - lines.append(line) - } - - func removeLine(at index: Int) { - lines.remove(at: index) - } - - func index(of line: DiffLine) -> Int? { - lines.firstIndex { $0 === line } - } - - func insert(_ line: DiffLine, at index: Int) { - lines.insert(line, at: index) - } - - func write( - path: URL, - encoding: String.Encoding - ) throws { - let fm = FileManager.default - let osPath = path.osPath - - if !fm.fileExists(atPath: osPath) { - // NSFileHandle needs an existing file - fm.createFile(atPath: osPath, contents: nil) - } - let fileHandle = try FileHandle(forWritingTo: path) - defer { fileHandle.closeFile() } - fileHandle.truncateFile(atOffset: 0) - - for line in lines where line.type != .missing { - if let data = line.component.withEol.data(using: encoding) { - fileHandle.write(data) - } - } - } - - func renumberLines() { - var lineNumber = 1 - - for line in lines where line.type != .missing { - line.number = lineNumber - lineNumber += 1 - } - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/DiffSummary.swift b/Sources/Features/FilesCompare/DiffResult/DiffSummary.swift deleted file mode 100644 index 8a3744d..0000000 --- a/Sources/Features/FilesCompare/DiffResult/DiffSummary.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// DiffSummary.swift -// VisualDiffer -// -// Created by davide ficano on 21/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -struct DiffSummary { - var matching = 0 - var added = 0 - var deleted = 0 - var changed = 0 - - mutating func reset() { - matching = 0 - added = 0 - deleted = 0 - changed = 0 - } - - mutating func refresh(_ lines: [DiffLine]) { - reset() - - for line in lines { - switch line.type { - case .matching: - matching += 1 - case .changed: - changed += 1 - case .deleted: - deleted += 1 - case .missing: - added += 1 - case .added: - // does nothing - break - } - } - } -} - -extension DiffSummary: Equatable { - static func == (lhs: DiffSummary, rhs: DiffSummary) -> Bool { - lhs.matching == rhs.matching && - lhs.added == rhs.added && - lhs.deleted == rhs.deleted && - lhs.changed == rhs.changed - } -} diff --git a/Sources/Features/FilesCompare/DiffResult/EndOfLine.swift b/Sources/Features/FilesCompare/DiffResult/EndOfLine.swift deleted file mode 100644 index 0068dd3..0000000 --- a/Sources/Features/FilesCompare/DiffResult/EndOfLine.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// EndOfLine.swift -// VisualDiffer -// -// Created by davide ficano on 21/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -enum EndOfLine: Int { - case unix - case pcCR - case pcCRLF - case missing - case mixed - - static func from(character ch: Character) -> EndOfLine { - switch ch { - case "\r": - .pcCR - case "\r\n": - .pcCRLF - case "\n": - .unix - default: - .missing - } - } -} - -extension EndOfLine: CustomStringConvertible { - var stringValue: String { - switch self { - case .unix: - "\n" - case .pcCR: - "\r" - case .pcCRLF: - "\r\n" - case .missing: - "" - case .mixed: - "" - } - } - - var description: String { - switch self { - case .unix: - "Unix (LF)" - case .pcCR: - "DOS (CR)" - case .pcCRLF: - "DOS (CR+LF)" - case .missing: - "None" - case .mixed: - "MIXED" - } - } - - var visibleSymbol: String { - switch self { - case .unix: - "\u{00B6}" - case .pcCR, .pcCRLF: - "\u{00A4}" - default: - "" - } - } -} diff --git a/Sources/Features/FilesCompare/Extensions/DiffCountersItem+DiffResult.swift b/Sources/Features/FilesCompare/Extensions/DiffCountersItem+DiffResult.swift deleted file mode 100644 index 41e44fb..0000000 --- a/Sources/Features/FilesCompare/Extensions/DiffCountersItem+DiffResult.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// DiffCountersItem+DiffResult.swift -// VisualDiffer -// -// Created by davide ficano on 10/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -extension DiffCountersItem { - static func diffCounter(withResult result: DiffResult) -> [DiffCountersItem] { - var items = [DiffCountersItem]() - let summary = result.summary - - if summary.deleted > 0 { - let count = String.localizedStringWithFormat(NSLocalizedString("%ld deleted", comment: ""), summary.deleted) - if let color = DiffChangeType.deleted.colors?.text { - items.append(diffCounterItem(withText: count, color: color)) - } - } - if summary.added > 0 { - let count = String.localizedStringWithFormat(NSLocalizedString("%ld added", comment: ""), summary.added) - if let color = DiffChangeType.added.colors?.text { - items.append(diffCounterItem(withText: count, color: color)) - } - } - if summary.changed > 0 { - let count = String.localizedStringWithFormat(NSLocalizedString("%ld changed", comment: ""), summary.changed) - if let color = DiffChangeType.changed.colors?.text { - items.append(diffCounterItem(withText: count, color: color)) - } - } - if summary.matching > 0 { - let count = String.localizedStringWithFormat(NSLocalizedString("%ld identical", comment: ""), summary.matching) - if let color = DiffChangeType.matching.colors?.text { - items.append(diffCounterItem(withText: count, color: color)) - } - } - - return items - } -} diff --git a/Sources/Features/FilesCompare/Extensions/DiffResult/DiffChangeType+Color.swift b/Sources/Features/FilesCompare/Extensions/DiffResult/DiffChangeType+Color.swift deleted file mode 100644 index cfcd96b..0000000 --- a/Sources/Features/FilesCompare/Extensions/DiffResult/DiffChangeType+Color.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// DiffChangeType+Color.swift -// VisualDiffer -// -// Created by davide ficano on 27/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -extension DiffChangeType { - var colors: ColorSet? { - CommonPrefs.shared.fileColor(color) - } - - var color: FileColorAttribute { - switch self { - case .matching: - .same - case .added: - .added - case .deleted: - .deleted - case .changed: - .changed - case .missing: - .missing - } - } -} diff --git a/Sources/Features/FilesCompare/Extensions/DiffResult/DiffLine+Color.swift b/Sources/Features/FilesCompare/Extensions/DiffResult/DiffLine+Color.swift deleted file mode 100644 index 0a16419..0000000 --- a/Sources/Features/FilesCompare/Extensions/DiffResult/DiffLine+Color.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// DiffLine+Color.swift -// VisualDiffer -// -// Created by davide ficano on 27/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -extension DiffLine { - var colors: ColorSet? { - let color: FileColorAttribute = mode == .merged ? .merged : type.color - return CommonPrefs.shared.fileColor(color) - } - - func color(for colorKey: ColorSet.Key, isSelected: Bool) -> NSColor { - var color: NSColor? - - if isSelected { - if colorKey == .text { - color = colors?.text - } else if colorKey == .background { - color = CommonPrefs.shared.fileColor(.selectedRow)?.background - } - } else { - if colorKey == .text { - color = colors?.text - } else if colorKey == .background { - color = colors?.background - } - } - - return color ?? NSColor.black - } -} diff --git a/Sources/Features/FilesCompare/Extensions/FilesTableView+EditorData.swift b/Sources/Features/FilesCompare/Extensions/FilesTableView+EditorData.swift deleted file mode 100644 index 1fa23b7..0000000 --- a/Sources/Features/FilesCompare/Extensions/FilesTableView+EditorData.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// FilesTableView+EditorData.swift -// VisualDiffer -// -// Created by davide ficano on 01/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FilesTableView { - func editorData(_ sessionDiff: SessionDiff) -> OpenEditorAttribute? { - let path = switch side { - case .left: - sessionDiff.leftPath - case .right: - sessionDiff.rightPath - } - guard let path else { - return nil - } - var editorData = OpenEditorAttribute(path: path) - - if let diffSide, selectedRow >= 0 { - let subset = diffSide.lines[0 ... selectedRow] - for line in subset.reversed() where line.type != .missing { - editorData.lineNumber = line.number - break - } - } - return editorData - } -} diff --git a/Sources/Features/FilesCompare/Windows/JumpToLineWindow.swift b/Sources/Features/FilesCompare/Windows/JumpToLineWindow.swift deleted file mode 100644 index 4b63fd8..0000000 --- a/Sources/Features/FilesCompare/Windows/JumpToLineWindow.swift +++ /dev/null @@ -1,181 +0,0 @@ -// -// JumpToLineWindow.swift -// VisualDiffer -// -// Created by davide ficano on 18/10/11. -// Copyright (c) 2011 visualdiffer.com -// - -class JumpToLineWindow: NSWindow, NSWindowDelegate, NSSearchFieldDelegate { - var side: DisplaySide = .left - var lineNumber = -1 - var leftMaxLineNumber = -1 - var rightMaxLineNumber = -1 - - private lazy var searchField: NSSearchField = { - let view = NSSearchField(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - view.delegate = self - - return view - }() - - private lazy var maxLineNumber: NSTextField = { - let view = NSTextField(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - view.isBezeled = false - view.isBordered = false - view.drawsBackground = false - view.controlSize = .small - view.alignment = .right - view.focusRingType = .none - view.isEditable = false - view.isSelectable = false - view.textColor = .controlTextColor - - return view - }() - - private lazy var jumpSide: NSSegmentedControl = { - let labels = [ - NSLocalizedString("Left", comment: ""), - NSLocalizedString("Right", comment: ""), - ] - let view = NSSegmentedControl( - labels: labels, - trackingMode: .selectOne, - target: self, - action: #selector(sideChanged) - ) - - view.translatesAutoresizingMaskIntoConstraints = false - view.segmentStyle = .rounded - view.segmentDistribution = .fit - view.alignment = .center - - return view - }() - - private lazy var standardButtons = StandardButtons( - primaryTitle: NSLocalizedString("Jump", comment: ""), - secondaryTitle: NSLocalizedString("Cancel", comment: ""), - target: self, - action: #selector(closeSheet) - ) - - static func createSheet() -> JumpToLineWindow { - let styleMask: NSWindow.StyleMask = [ - .titled, - .closable, - .miniaturizable, - .resizable, - ] - - return JumpToLineWindow( - contentRect: NSRect(x: 0, y: 0, width: 410, height: 100), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - } - - override init( - contentRect: NSRect, - styleMask style: NSWindow.StyleMask, - backing backingStoreType: NSWindow.BackingStoreType, - defer flag: Bool - ) { - super.init( - contentRect: contentRect, - styleMask: style, - backing: backingStoreType, - defer: flag - ) - - minSize = NSSize(width: 400, height: 100) - delegate = self - - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(searchField) - contentView.addSubview(maxLineNumber) - contentView.addSubview(jumpSide) - contentView.addSubview(standardButtons) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - searchField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - searchField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), - searchField.trailingAnchor.constraint(equalTo: maxLineNumber.leadingAnchor, constant: -5), - - maxLineNumber.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - maxLineNumber.trailingAnchor.constraint(equalTo: jumpSide.leadingAnchor, constant: -20), - - jumpSide.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - jumpSide.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - override func beginSheet(_ sheetWindow: NSWindow, completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil) { - searchField.integerValue = lineNumber - jumpSide.selectedSegment = side.rawValue - - enableJumpButton() - - sheetWindow.beginSheet(self, completionHandler: handler) - } - - private func enableJumpButton() { - guard let side = DisplaySide(rawValue: jumpSide.selectedSegment) else { - fatalError("invalid jump side \(jumpSide.selectedSegment)") - } - let value = searchField.intValue - var isEnabled = false - - switch side { - case .left: - maxLineNumber.stringValue = String(format: "/%ld", leftMaxLineNumber) - isEnabled = value >= 1 && value <= leftMaxLineNumber - case .right: - maxLineNumber.stringValue = String(format: "/%ld", rightMaxLineNumber) - isEnabled = value >= 1 && value <= rightMaxLineNumber - } - standardButtons.primaryButton.isEnabled = isEnabled - } - - @objc func closeSheet(_ sender: AnyObject) { - guard let side = DisplaySide(rawValue: jumpSide.selectedSegment) else { - fatalError("invalid jump side \(jumpSide.selectedSegment)") - } - self.side = side - lineNumber = searchField.integerValue - sheetParent?.endSheet(self, returnCode: NSApplication.ModalResponse(rawValue: sender.tag)) - } - - @objc func sideChanged(_: AnyObject) { - enableJumpButton() - } - - func controlTextDidChange(_: Notification) { - enableJumpButton() - } - - func windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize { - // Only allow horizontal sizing - NSSize(width: frameSize.width, height: sender.contentView?.frame.size.height ?? frameSize.height) - } -} diff --git a/Sources/Features/FilesCompare/Windows/SessionPreferences/FilePreferences.swift b/Sources/Features/FilesCompare/Windows/SessionPreferences/FilePreferences.swift deleted file mode 100644 index 9455018..0000000 --- a/Sources/Features/FilesCompare/Windows/SessionPreferences/FilePreferences.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// FilePreferences.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct FilePreferences { - var diffResultOptions: DiffResult.Options = [] -} - -extension FilePreferences { - mutating func from(sessionDiff: SessionDiff) { - diffResultOptions = sessionDiff.extraData.diffResultOptions - } -} - -extension CommonPrefs.Name { - static let ignoreLineEndings = CommonPrefs.Name(rawValue: "ignoreLineEndings") - static let ignoreLeadingWhitespaces = CommonPrefs.Name(rawValue: "ignoreLeadingWhitespaces") - static let ignoreTrailingWhitespaces = CommonPrefs.Name(rawValue: "ignoreTrailingWhitespaces") - static let ignoreInternalWhitespaces = CommonPrefs.Name(rawValue: "ignoreInternalWhitespaces") - static let ignoreCharacterCase = CommonPrefs.Name(rawValue: "ignoreCharacterCase") -} diff --git a/Sources/Features/FilesCompare/Windows/SessionPreferences/FileSessionPreferencesWindow+PreferencesBoxDataSource.swift b/Sources/Features/FilesCompare/Windows/SessionPreferences/FileSessionPreferencesWindow+PreferencesBoxDataSource.swift deleted file mode 100644 index 191f9a1..0000000 --- a/Sources/Features/FilesCompare/Windows/SessionPreferences/FileSessionPreferencesWindow+PreferencesBoxDataSource.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// FileSessionPreferencesWindow+PreferencesBoxDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FileSessionPreferencesWindow: @preconcurrency PreferencesBoxDataSource { - func preferenceBox(_: PreferencesBox, boolForKey key: CommonPrefs.Name) -> Bool { - switch key { - case .ignoreLineEndings: - preferences.diffResultOptions.contains(.ignoreLineEndings) - case .ignoreLeadingWhitespaces: - preferences.diffResultOptions.contains(.ignoreLeadingWhitespaces) - case .ignoreTrailingWhitespaces: - preferences.diffResultOptions.contains(.ignoreTrailingWhitespaces) - case .ignoreInternalWhitespaces: - preferences.diffResultOptions.contains(.ignoreInternalWhitespaces) - case .ignoreCharacterCase: - preferences.diffResultOptions.contains(.ignoreCharacterCase) - default: - false - } - } - - func preferenceBox(_: PreferencesBox, setBool value: Bool, forKey key: CommonPrefs.Name) { - switch key { - case .ignoreLineEndings: - preferences.diffResultOptions.setValue(value, element: .ignoreLineEndings) - case .ignoreLeadingWhitespaces: - preferences.diffResultOptions.setValue(value, element: .ignoreLeadingWhitespaces) - case .ignoreTrailingWhitespaces: - preferences.diffResultOptions.setValue(value, element: .ignoreTrailingWhitespaces) - case .ignoreInternalWhitespaces: - preferences.diffResultOptions.setValue(value, element: .ignoreInternalWhitespaces) - case .ignoreCharacterCase: - preferences.diffResultOptions.setValue(value, element: .ignoreCharacterCase) - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, isEnabled _: CommonPrefs.Name) -> Bool { - true - } -} diff --git a/Sources/Features/FilesCompare/Windows/SessionPreferences/FileSessionPreferencesWindow.swift b/Sources/Features/FilesCompare/Windows/SessionPreferences/FileSessionPreferencesWindow.swift deleted file mode 100644 index 67ddcbe..0000000 --- a/Sources/Features/FilesCompare/Windows/SessionPreferences/FileSessionPreferencesWindow.swift +++ /dev/null @@ -1,220 +0,0 @@ -// -// FileSessionPreferencesWindow.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FileSessionPreferencesWindow: NSWindowController, NSTabViewDelegate { - private lazy var tabView: NSTabView = createTabView() - private lazy var standardButtons: StandardButtons = .init( - primaryTitle: NSLocalizedString("OK", comment: ""), - secondaryTitle: NSLocalizedString("Cancel", comment: ""), - target: self, - action: #selector(closeSessionSettingsSheet) - ) - - private var loadedTabs = Set() - var preferences = FilePreferences() - - // TabView Identifiers - private static let comparisonPanelIdentifier = NSUserInterfaceItemIdentifier("Comparison") - - required init() { - super.init(window: Self.createPanel()) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - if let contentView = window?.contentView { - contentView.addSubview(tabView) - contentView.addSubview(standardButtons) - } - - setupConstraints() - } - - private static func createPanel() -> NSPanel { - let styleMask: NSWindow.StyleMask = [ - .titled, - .closable, - .miniaturizable, - .resizable, - ] - - let view = NSPanel( - contentRect: NSRect(x: 0, y: 0, width: 500, height: 400), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - view.hasShadow = true - view.isRestorable = true - view.setFrameAutosaveName("fileSessionPreferencesFrame") - view.minSize = NSSize(width: 500, height: 400) - - return view - } - - private func createTabView() -> NSTabView { - let view = NSTabView(frame: .zero) - - view.tabViewType = .topTabsBezelBorder - view.allowsTruncatedLabels = false - view.drawsBackground = true - view.translatesAutoresizingMaskIntoConstraints = false - - view.delegate = self - - let tabInfo = [ - (Self.comparisonPanelIdentifier, NSLocalizedString("Comparison", comment: "")), - ] - - for (identifier, label) in tabInfo { - let item = NSTabViewItem(identifier: identifier) - item.label = label - view.addTabViewItem(item) - } - - return view - } - - private func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - NSLayoutConstraint.activate([ - tabView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - tabView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), - tabView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), - - // very bad approach because I'm unable to have the tabView fills the height and place the button below it. - // Problem: If I place the button below the tabView when I change tab the button resizes vertically instead of stay fixed - // Workaround: I move the tabView to parent bottom and reduce its size (i.e. -40) - tabView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -40), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - func beginSheet( - _ sheetWindow: NSWindow, - preferences: FilePreferences, - selectedTab: FileSessionPreferencesWindow.Tab, - completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil - ) { - guard let window else { - return - } - fill(with: preferences) - - tabView.selectTabViewItem(at: selectedTab.rawValue) - - sheetWindow.beginSheet(window, completionHandler: handler) - } - - @objc func closeSessionSettingsSheet(_ sender: AnyObject) { - guard let window else { - return - } - let response = NSApplication.ModalResponse(sender.tag) - if response == .OK { - window.endEditing() - } - window.sheetParent?.endSheet(window, returnCode: response) - } - - func fill(with preferences: FilePreferences) { - self.preferences = preferences - reload(tabItem: tabView.selectedTabViewItem) - } - - // MARK: - TabView delegate - - func tabView(_: NSTabView, willSelect tabViewItem: NSTabViewItem?) { - guard let tabViewItem, - let identifier = tabViewItem.identifier as? NSUserInterfaceItemIdentifier else { - return - } - - if loadedTabs.contains(identifier) { - return - } - loadedTabs.insert(identifier) - - if identifier == Self.comparisonPanelIdentifier { - let view = FilePreferencesComparisonPanel(frame: .zero) - view.delegate = self - tabViewItem.view = view - } - reload(tabItem: tabViewItem) - } - - func tabView(_: NSTabView, didSelect _: NSTabViewItem?) { - resize() - } - - func reload(tabItem: NSTabViewItem?) { - if let view = tabItem?.view as? PreferencesPanelDataSource { - view.reloadData() - } - } - - // MARK: - Position and size - - func contentRect() -> NSRect { - var contentRect = NSRect.zero - - if let selectedTabView = tabView.selectedTabViewItem?.view { - for view in selectedTabView.subviews { - // the result of Swift's GCRect.union is different from NSUnionRect - // so we stay on it - // swiftlint:disable:next legacy_nsgeometry_functions - contentRect = NSUnionRect(contentRect, view.frame) - } - } - - return contentRect - } - - func toolbarHeight(_ window: NSWindow?) -> CGFloat { - guard let window else { - return 0 - } - let windowFrame = NSWindow.contentRect(forFrameRect: window.frame, styleMask: window.styleMask) - return windowFrame.size.height - (window.contentView?.frame.size.height ?? 0) - } - - func minWindowHeight() -> CGFloat { - let contentRect = contentRect() - // Border top + toolbar - return contentRect.size.height + 40 + toolbarHeight(window) - } - - func resize() { - guard let window, - window.isVisible else { - return - } - let newSize = NSSize( - width: window.contentView?.frame.size.width ?? 0, - height: minWindowHeight() - ) - - window.setContentSize(newSize) - } -} - -extension FileSessionPreferencesWindow { - enum Tab: Int { - case comparison - } -} diff --git a/Sources/Features/FilesCompare/Windows/SessionPreferences/Panels/Comparison/FilePreferencesComparisonPanel.swift b/Sources/Features/FilesCompare/Windows/SessionPreferences/Panels/Comparison/FilePreferencesComparisonPanel.swift deleted file mode 100644 index 0815346..0000000 --- a/Sources/Features/FilesCompare/Windows/SessionPreferences/Panels/Comparison/FilePreferencesComparisonPanel.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// FilePreferencesComparisonPanel.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FilePreferencesComparisonPanel: NSView, PreferencesPanelDataSource { - var delegate: PreferencesBoxDataSource? { - didSet { - for view in stackView.views { - if let delegate, - let box = view as? PreferencesBox { - box.delegate = delegate - } - } - } - } - - let stackView: NSStackView - - override init(frame frameRect: NSRect) { - stackView = NSStackView.preferences(with: [ - FileComparisonBox(title: NSLocalizedString("Comparison", comment: "")), - ]) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - translatesAutoresizingMaskIntoConstraints = false - addSubview(stackView) - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - stackView.topAnchor.constraint(equalTo: topAnchor), - heightAnchor.constraint(equalToConstant: 360), - ]) - - for view in stackView.views { - view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true - view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true - } - } - - func reloadData() { - for view in stackView.views { - if let box = view as? PreferencesBox { - box.reloadData() - } - } - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions+Description.swift b/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions+Description.swift deleted file mode 100644 index 5c112a2..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions+Description.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// ComparatorOptions+Description.swift -// VisualDiffer -// -// Created by davide ficano on 21/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension ComparatorOptions: CustomStringConvertible, CustomDebugStringConvertible { - public var description: String { - switch self { - case .filename: - NSLocalizedString("Compare file names only", comment: "") - case .asText: - NSLocalizedString("Compare file content ignoring line ending differences", comment: "") - case .content: - NSLocalizedString("Compare file content only", comment: "") - case .size: - NSLocalizedString("Compare file sizes", comment: "") - case .timestamp: - NSLocalizedString("Compare file timestamps", comment: "") - case [.timestamp, .size]: - NSLocalizedString("Compare file timestamps and sizes", comment: "") - case [.timestamp, .content, .size]: - NSLocalizedString("Compare file timestamp, size and content", comment: "") - default: - "\(rawValue)" - } - } - - public var debugDescription: String { - var arr = [String]() - - if contains(.timestamp) { - arr.append("TIMESTAMP") - } - if contains(.size) { - arr.append("SIZE") - } - if contains(.content) { - arr.append("CONTENT") - } - if contains(.asText) { - arr.append("AS_TEXT") - } - if contains(.filename) { - arr.append("FILENAME") - } - - if contains(.finderLabel) { - arr.append("FINDER_LABEL") - } - if contains(.finderTags) { - arr.append("FINDER_TAGS") - } - - if contains(.alignFileSystemCase) { - arr.append("ALIGN_FILESYSTEM_CASE") - } - if contains(.alignMatchCase) { - arr.append("ALIGN_MATCH_CASE") - } - if contains(.alignIgnoreCase) { - arr.append("ALIGN_IGNORE_CASE") - } - - return arr.joined(separator: ",") - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions+Helper.swift b/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions+Helper.swift deleted file mode 100644 index a83cfd0..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions+Helper.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// ComparatorOptions+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 02/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -public extension ComparatorOptions { - init(number: NSNumber?) { - self.init(rawValue: number?.intValue ?? 0) - } - - func toNumber() -> NSNumber { - NSNumber(value: rawValue) - } -} - -public extension ComparatorOptions { - var onlyMethodFlags: Self { - intersection(.typeMask) - } - - var withoutMethodFlags: Self { - subtracting(.typeMask) - } - - var onlyAlignFlags: Self { - intersection(.alignMask) - } - - var withoutAlignFlags: Self { - subtracting(.alignMask) - } - - func changeWithoutMethod(_ flags: Self) -> Self { - withoutMethodFlags.union(flags) - } - - func changeWithoutMethod(_ flags: Int) -> Self { - withoutMethodFlags.union(.init(rawValue: flags)) - } - - func changeAlign(_ newValue: Self) -> Self { - guard newValue.isSubset(of: .alignMask) else { - fatalError("Invalid options: \(newValue)") - } - - var changed = withoutAlignFlags - changed.insert(newValue) - return changed - } - - func changeAlign(_ newValue: Int) -> Self { - changeAlign(.init(rawValue: newValue)) - } - - var hasFinderLabel: Bool { - contains(.finderLabel) - } - - // remove tags flag because is mutually exclusive with label flag - func changeFinderLabel(_ isOn: Bool) -> Self { - if isOn { - subtracting(.finderTags).union(.finderLabel) - } else { - subtracting(.finderTags).subtracting(.finderLabel) - } - } - - var hasFinderTags: Bool { - contains(.finderTags) - } - - // remove label flag because is mutually exclusive with tag flag - func changeFinderTags(_ isOn: Bool) -> Self { - if isOn { - subtracting(.finderLabel).union(.finderTags) - } else { - subtracting(.finderLabel).subtracting(.finderTags) - } - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions.swift b/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions.swift deleted file mode 100644 index 44847c4..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/ComparatorOptions/ComparatorOptions.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// ComparatorOptions.swift -// VisualDiffer -// -// Created by davide ficano on 29/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -public struct ComparatorOptions: OptionSet, Sendable { - public let rawValue: Int - - public static let timestamp = ComparatorOptions(rawValue: 1 << 0) - public static let size = ComparatorOptions(rawValue: 1 << 1) - public static let content = ComparatorOptions(rawValue: 1 << 2) - public static let contentTimestamp: ComparatorOptions = [.content, .timestamp] - public static let asText = ComparatorOptions(rawValue: 1 << 3) - - public static let finderLabel = ComparatorOptions(rawValue: 1 << 4) - public static let finderTags = ComparatorOptions(rawValue: 1 << 5) - - public static let alignFileSystemCase = ComparatorOptions(rawValue: 1 << 6) - public static let alignMatchCase = ComparatorOptions(rawValue: 1 << 7) - public static let alignIgnoreCase = ComparatorOptions(rawValue: 1 << 8) - - public static let filename = ComparatorOptions(rawValue: 1 << 9) - - // Masks - public static let typeMask: ComparatorOptions = [.filename, .timestamp, .size, .content, .asText] - public static let alignMask: ComparatorOptions = [.alignFileSystemCase, .alignMatchCase, .alignIgnoreCase] - public static let supportFolderCompare: ComparatorOptions = [.finderLabel, .finderTags] - - public init(rawValue: Int) { - self.rawValue = rawValue - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareChangeType.swift b/Sources/Features/FoldersCompare/CompareItem/CompareChangeType.swift deleted file mode 100644 index faf4cc5..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareChangeType.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// CompareChangeType.swift -// VisualDiffer -// -// Created by davide ficano on 25/06/20. -// Copyright (c) 2020 visualdiffer.com -// - -enum CompareChangeType { - case unknown - case orphan - case old - case newer - case changed - case same - case folder - case subFoldersSize - case filtered - case mismatchingTags - case mismatchingLabels -} - -extension CompareChangeType: CustomStringConvertible { - var description: String { - switch self { - case .unknown: "unknown" - case .orphan: "orphan" - case .old: "old" - case .newer: "newer" - case .changed: "changed" - case .same: "same" - case .folder: "folder" - case .subFoldersSize: "subFoldersSize" - case .filtered: "filtered" - case .mismatchingTags: "mismatchingTags" - case .mismatchingLabels: "mismatchingLabels" - } - } -} - -extension CompareChangeType { - var color: FolderColorAttribute { - switch self { - case .unknown: .unknown - case .orphan: .orphan - case .old: .old - case .newer: .newer - case .changed: .changed - case .same: .same - case .folder: .folder - case .subFoldersSize: .subFoldersSize - case .filtered: .filtered - case .mismatchingTags: .mismatchingTags - case .mismatchingLabels: .mismatchingLabels - } - } -} - -extension CommonPrefs { - func changeTypeColor(_ type: CompareChangeType) -> ColorSet? { - folderColor(type.color) - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Accessors.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+Accessors.swift deleted file mode 100644 index 9642f28..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Accessors.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// CompareItem+Accessors.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension CompareItem { - // MARK: - FileOptions Accessors - - var isValidFile: Bool { - fileOptions[.isValidFile] - } - - var isSymbolicLink: Bool { - fileOptions[.isSymbolicLink] - } - - var isFile: Bool { - fileOptions[.isFile] - } - - var isFolder: Bool { - fileOptions[.isFolder] - } - - var isPackage: Bool { - fileOptions[.isPackage] - } - - var isOrphanFile: Bool { - isValidFile && isFile && orphanFiles > 0 - } - - var isResourceFork: Bool { - fileOptions[.isResourceFork] - } - - var isLocked: Bool { - fileOptions[.isLocked] - } - - var isOrphanFolder: Bool { - guard let linkedItem else { - return false - } - return isFolder && !linkedItem.isValidFile - } - - var isNewerThanLinked: Bool { - guard let linkedItem else { - return false - } - return type == .changed && linkedItem.type == .old - } - - // MARK: - Counter Accessors - - var olderFiles: Int { - summary.olderFiles - } - - var changedFiles: Int { - summary.changedFiles - } - - var orphanFiles: Int { - summary.orphanFiles - } - - var matchedFiles: Int { - summary.matchedFiles - } - - var subfoldersSize: Int64 { - summary.subfoldersSize - } - - var mismatchingTags: Int { - summary.mismatchingTags - } - - var mismatchingLabels: Int { - summary.mismatchingLabels - } - - var mismatchingFolderMetadata: MismatchingFolderMetadata { - summary.mismatchingFolderMetadata - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Clone.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+Clone.swift deleted file mode 100644 index 011e692..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Clone.swift +++ /dev/null @@ -1,145 +0,0 @@ -// -// CompareItem+Clone.swift -// VisualDiffer -// -// Created by davide ficano on 31/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension CompareItem { - func duplicate() -> CompareItem { - let dup = CompareItem( - path: path, - attrs: nil, - fileExtraOptions: [], - parent: parent - ) - - dup.fileOptions = fileOptions - - dup.fileSize = fileSize - dup.fileModificationDate = fileModificationDate - - dup.addOlderFiles(olderFiles) - dup.addChangedFiles(changedFiles) - dup.addOrphanFiles(orphanFiles) - dup.addMatchedFiles(matchedFiles) - dup.addSubfoldersSize(subfoldersSize) - - // attribute `file` can be null but it can represent a folder - dup.linkedItemIsFolder(isFolder) - dup.linkedItem = linkedItem - - return dup - } - - func cloneValidFiles(_ parent: CompareItem?) -> CompareItem? { - guard isValidFile else { - return nil - } - - let clone = duplicate() - clone.parent = parent - clone.linkedItem = nil - - for item in children where item.isValidFile { - if item.isFolder { - if let child = item.cloneValidFiles(clone) { - clone.add(child: child) - } - } else { - let newItem = item.duplicate() - newItem.parent = clone - newItem.linkedItem = nil - clone.add(child: newItem) - } - } - - return clone - } - - func duplicateAsOrphan( - withPath path: String, - withParent parent: CompareItem?, - fileExtraOptions: FileExtraOptions, - recursive: Bool - ) -> CompareItem { - let dupItem = CompareItem( - path: path, - attrs: try? FileManager.default.attributesOfItem(atPath: path), - fileExtraOptions: fileExtraOptions, - parent: parent - ) - let dupLinkedItem = CompareItem( - path: nil, - attrs: nil, - fileExtraOptions: [], - parent: parent?.linkedItem - ) - dupItem.linkedItem = dupLinkedItem - dupLinkedItem.linkedItem = dupItem - - dupLinkedItem.linkedItemIsFolder(dupItem.isFolder) - - var summary = CompareSummary() - summary.orphanFiles = orphanFiles + matchedFiles + olderFiles + changedFiles - dupItem.setSummary(summary) - dupItem.addSubfoldersSize(subfoldersSize) - - if recursive { - if let url = dupItem.toUrl() { - for item in children where item.isValidFile { - if let fileName = item.fileName { - let childPath = url.appendingPathComponent(fileName).osPath - let child = item.duplicateAsOrphan( - withPath: childPath, - withParent: dupItem, - fileExtraOptions: fileExtraOptions, - recursive: true - ) - dupItem.add(child: child) - if let dupLinkedItem = dupItem.linkedItem, - let childLinkedItem = child.linkedItem { - dupLinkedItem.add(child: childLinkedItem) - } - } - } - } - } - return dupItem - } - - func mark(asOrphan recursive: Bool) { - guard let linkedItem else { - return - } - - if isValidFile { - var summary = CompareSummary() - summary.orphanFiles = orphanFiles + matchedFiles + olderFiles + changedFiles - summary.subfoldersSize = subfoldersSize - setSummary(summary) - - linkedItem.path = nil - linkedItem.setAttributes(nil, fileExtraOptions: []) - - linkedItem.linkedItemIsFolder(isFolder) - linkedItem.addSubfoldersSize(-linkedItem.subfoldersSize) - } else { - parent?.remove(child: self) - linkedItem.parent?.remove(child: linkedItem) - - if let vi = visibleItem { - parent?.visibleItem?.remove(vi) - } - if let vi = linkedItem.visibleItem { - linkedItem.visibleItem?.remove(vi) - } - } - if recursive { - for item in children.reversed() { - item.mark(asOrphan: true) - } - } - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Comparison.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+Comparison.swift deleted file mode 100644 index 5cb4bf2..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Comparison.swift +++ /dev/null @@ -1,220 +0,0 @@ -// -// CompareItem+Comparison.swift -// VisualDiffer -// -// Created by davide ficano on 21/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension CompareItem { - func compareInfo( - leftOrphanFiles: inout [CompareItem], - rightOrphanFiles: inout [CompareItem], - matchesFiles: inout [CompareItem], - newerLeftFiles: inout [CompareItem], - newerRightFiles: inout [CompareItem] - ) { - for item in children { - if item.isFiltered { - continue - } - if item.isFolder { - item.compareInfo( - leftOrphanFiles: &leftOrphanFiles, - rightOrphanFiles: &rightOrphanFiles, - matchesFiles: &matchesFiles, - newerLeftFiles: &newerLeftFiles, - newerRightFiles: &newerRightFiles - ) - } else { - if let linkedItem = item.linkedItem { - if item.orphanFiles > 0 { - leftOrphanFiles.append(item) - } else if linkedItem.orphanFiles > 0 { - rightOrphanFiles.append(linkedItem) - } else if item.matchedFiles > 0 { - matchesFiles.append(item) - } else if item.changedFiles > 0, linkedItem.olderFiles > 0 { - newerLeftFiles.append(item) - } else if linkedItem.changedFiles > 0, item.olderFiles > 0 { - newerRightFiles.append(linkedItem) - } else { - newerLeftFiles.append(item) - } - } - } - } - } - - func applyComparison( - fileFilters: NSPredicate?, - comparator: ItemComparator?, - recursive: Bool - ) { - guard let srcRight = linkedItem else { - return - } - var leftFileCount = CompareSummary() - var rightFileCount = CompareSummary() - - var children: [CompareItem] - - if isFile { - children = [self] - } else { - children = self.children - leftFileCount.mismatchingFolderMetadata = mismatchingFolderMetadata - rightFileCount.mismatchingFolderMetadata = srcRight.mismatchingFolderMetadata - } - - for left in children { - guard let right = left.linkedItem else { - continue - } - let isFiltered = isFiltered( - leftItem: left, - rightItem: right, - fileFilters: fileFilters - ) - - if left.isFolder { - if recursive { - left.applyComparison( - fileFilters: fileFilters, - comparator: comparator, - recursive: true - ) - } - } - if isFiltered { - left.setSummary(CompareSummary()) - right.setSummary(CompareSummary()) - left.type = .orphan - right.type = .orphan - } - if !isFiltered, let comparator { - comparator.compare(left, right) - } - - leftFileCount += left.summary - rightFileCount += right.summary - leftFileCount.subfoldersSize += left.fileSize - rightFileCount.subfoldersSize += right.fileSize - - left.isFiltered = isFiltered - right.isFiltered = isFiltered - } - setSummary(leftFileCount) - srcRight.setSummary(rightFileCount) - } - - private func isFiltered( - leftItem: CompareItem, - rightItem: CompareItem, - fileFilters: NSPredicate? - ) -> Bool { - if let parent = leftItem.parent, parent.isFiltered { - return true - } - if let fileFilters { - return leftItem.evaluate(filter: fileFilters) || rightItem.evaluate(filter: fileFilters) - } - return false - } - - func determineFileTypeWhen( - followSymLinks: Bool, - isFile: inout Bool, - isFolder: inout Bool - ) { - if self.isFile { - isFile = true - } else if self.isFolder { - if !followSymLinks, isSymbolicLink { - isFile = true - } else { - isFolder = true - } - } - } - - func compare( - _ other: CompareItem, - followSymLinks: Bool, - comparator: CompareItemComparison - ) -> ComparisonResult { - var isFile1 = false - var isFile2 = false - var isFolder1 = false - var isFolder2 = false - - determineFileTypeWhen(followSymLinks: followSymLinks, isFile: &isFile1, isFolder: &isFolder1) - other.determineFileTypeWhen(followSymLinks: followSymLinks, isFile: &isFile2, isFolder: &isFolder2) - - // check if invalid files have path otherwise isn't necessary to compare - let bothInvalidWithPath = !isValidFile && !other.isValidFile && (path != nil || other.path != nil) - - if (isFolder1 && isFolder2) || (isFile1 && isFile2) || bothInvalidWithPath { - return comparator(self, other) - } - // Place directories before files - return isFolder1 ? .orderedAscending : .orderedDescending - } - - func compare( - forAlign rhs: CompareItem, - followSymLinks: Bool, - insensitiveCompare: Bool - ) -> ComparisonResult { - compare( - rhs, - followSymLinks: followSymLinks - ) { - guard let lhsFileName = $0.fileName else { - return .orderedAscending - } - guard let rhsFileName = $1.fileName else { - return .orderedDescending - } - return insensitiveCompare - ? lhsFileName.localizedCaseInsensitiveCompare(rhsFileName) - : lhsFileName.localizedCompare(rhsFileName) - } - } - - func compare( - forList other: CompareItem, - followSymLinks: Bool - ) -> ComparisonResult { - compare( - other, - followSymLinks: followSymLinks - ) { - guard let lhsFileName = $0.fileName else { - return .orderedAscending - } - guard let rhsFileName = $1.fileName else { - return .orderedDescending - } - return lhsFileName.localizedCompare(rhsFileName) - } - } - - func evaluate(filter: NSPredicate) -> Bool { - guard isValidFile else { - return false - } - var dict = [String: Any]() - - dict["fileName"] = fileName - dict["pathRelativeToRoot"] = pathRelativeToRoot - - // only the files use modification date and the size - if isFile { - dict["fileObjectModificationDate"] = fileModificationDate - dict["fileSize"] = NSNumber(value: fileSize) - } - - return filter.evaluate(with: dict) - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Description.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+Description.swift deleted file mode 100644 index ea5889e..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Description.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// CompareItem+Description.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension CompareItem { - override public var description: String { - String( - format: "Path %@, isFileValid %d, isFolder %d, isFiltered %d, isDisp %d, subs , type %@, older %ld, changed %ld, orphan %ld, matched %ld, tags = %ld, labels = %ld, linkedItem %@", - path ?? "", - isValidFile, - isFolder, - isFiltered, - isDisplayed, - // self.mutableSubfolders, - type.description, - summary.olderFiles, - summary.changedFiles, - summary.orphanFiles, - summary.matchedFiles, - summary.mismatchingTags, - summary.mismatchingLabels, - linkedItem?.path ?? "" - ) - } - - // periphery:ignore - func shortDescription() -> String { - String( - format: "O %ld C%ld H%ld M%ld SB%ld %@ %@", - olderFiles, - changedFiles, - orphanFiles, - matchedFiles, - children.count, - type.description, - fileName ?? "" - ) - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+FilterConfig.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+FilterConfig.swift deleted file mode 100644 index a834e06..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+FilterConfig.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// CompareItem+FilterConfig.swift -// VisualDiffer -// -// Created by davide ficano on 27/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension CompareItem { - @discardableResult - func removeVisibleItems( - filterConfig: FilterConfig, - recursive: Bool = false - ) -> Bool { - removeVisibleItems( - showFilteredFiles: filterConfig.showFilteredFiles, - displayOptions: filterConfig.displayOptions, - hideEmptyFolders: filterConfig.hideEmptyFolders, - followSymLinks: filterConfig.followSymLinks, - recursive: recursive - ) - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Path.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+Path.swift deleted file mode 100644 index a0142e2..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+Path.swift +++ /dev/null @@ -1,81 +0,0 @@ -// -// CompareItem+Path.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension CompareItem { - var pathRelativeToRoot: String? { - guard let currentPath = path else { - return nil - } - var parent = parent - var rootPath = currentPath - - while let p = parent, let parentPath = p.path { - rootPath = parentPath - parent = p.parent - } - // skip path separator - let start = currentPath.index(currentPath.startIndex, offsetBy: rootPath.count + 1) - return String(currentPath[start...]) - } - - static func find( - withPath path: String, - from root: CompareItem - ) -> CompareItem? { - guard root.isValidFile else { - return nil - } - if root.path == path { - return root - } - for item in root.children where item.isValidFile { - if item.path == path { - return item - } - - if item.isFolder { - if let retval = find(withPath: path, from: item) { - return retval - } - } - } - return nil - } - - func findChildFileNameIndex(_ fileName: String, typeIsFile: Bool) -> Int { - var index = 0 - - for item in children { - if item.fileName == fileName, - item.isFile == typeIsFile { - return index - } - index += 1 - } - return NSNotFound - } - - func buildDestinationPath( - from srcBaseUrl: URL, - to destBaseUrl: URL - ) -> URL { - guard let srcUrl = toUrl() else { - fatalError("Path is not present on \(self)") - } - let linkedUrl = linkedItem?.toUrl() - return URL.buildDestinationPath(srcUrl, linkedUrl, srcBaseUrl, destBaseUrl) - } - - func toUrl() -> URL? { - if let path { - URL(filePath: path, directoryHint: isFolder ? .isDirectory : .notDirectory) - } else { - nil - } - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem+VisibleItem.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem+VisibleItem.swift deleted file mode 100644 index 7f8c499..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem+VisibleItem.swift +++ /dev/null @@ -1,233 +0,0 @@ -// -// CompareItem+VisibleItem.swift -// VisualDiffer -// -// Created by davide ficano on 30/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension DisplayOptions { - func showsOnlyMismatchesOrOrphans() -> Bool { - // .showAll contains also the flag .onlyMismatches - // so we can't simply check .onlyMismatches but we must be sure - // .showAll is turned off at all - !contains(.showAll) && (contains(.onlyMismatches) || contains(.onlyOrphans)) - } -} - -extension CompareItem { - func removeVisibleItems( - showFilteredFiles: Bool, - displayOptions: DisplayOptions, - hideEmptyFolders: Bool, - followSymLinks: Bool, - recursive: Bool - ) -> Bool { - var isRemovable = false - var removed = false - let leftItem = self - guard let rightItem = leftItem.linkedItem else { - return false - } - - if leftItem.isFolder { - if recursive { - for item in leftItem.children { - _ = item.removeVisibleItems( - showFilteredFiles: showFilteredFiles, - displayOptions: displayOptions, - hideEmptyFolders: hideEmptyFolders, - followSymLinks: followSymLinks, - recursive: true - ) - } - } - if !followSymLinks, leftItem.isSymbolicLink || rightItem.isSymbolicLink { - let dontFollowSymbolicLinks = displayOptions.union(.dontFollowSymlinks) - let isDisplayable = dontFollowSymbolicLinks.isDisplayable( - leftItem, - rightItem: rightItem - ) - leftItem.isDisplayed = isDisplayable - rightItem.isDisplayed = isDisplayable - isRemovable = !isDisplayable - } else { - // folders have the flag always set to true - leftItem.isDisplayed = true - rightItem.isDisplayed = true - - var hideOrphanFolders = false - - if !hideEmptyFolders, - displayOptions.showsOnlyMismatchesOrOrphans(), - leftItem.orphanFolders == 0, rightItem.orphanFolders == 0, - leftItem.isFolder, leftItem.summary.containsOnlyMatches(), - rightItem.isFolder, rightItem.summary.containsOnlyMatches() { - hideOrphanFolders = true - } else if displayOptions.contains(.noOrphansFolders) { - hideOrphanFolders = !leftItem.isValidFile || !rightItem.isValidFile - } - if hideOrphanFolders { - isRemovable = true - } else { - if let viLeft = leftItem.visibleItem, - let viRight = rightItem.visibleItem { - let isEmpty = viLeft.childrenAllFiltered && viRight.childrenAllFiltered - isRemovable = hideEmptyFolders && isEmpty - } - } - } - } else { - let isDisplayable = displayOptions.isDisplayable(leftItem, rightItem: rightItem) - leftItem.isDisplayed = isDisplayable - rightItem.isDisplayed = isDisplayable - isRemovable = !leftItem.isDisplayed - } - if isRemovable { - if showFilteredFiles { - leftItem.isFiltered = true - rightItem.isFiltered = true - } else { - if let vi = leftItem.visibleItem { - leftItem.parent?.visibleItem?.remove(vi) - } - if let vi = rightItem.visibleItem { - rightItem.parent?.visibleItem?.remove(vi) - } - removed = true - } - } - return removed - } - - func refresh( - filterConfig: FilterConfig, - comparator: ItemComparator - ) { - guard let destRoot = linkedItem, - isFile, - destRoot.isFile else { - return - } - - var srcCount = CompareSummary() - var destCount = CompareSummary() - - var srcDiffSize = fileSize - var destDiffSize = destRoot.fileSize - let fm = FileManager.default - - if let path { - setAttributes(try? fm.attributesOfItem(atPath: path), fileExtraOptions: filterConfig.fileExtraOptions) - } - if let path = destRoot.path { - destRoot.setAttributes( - try? fm.attributesOfItem(atPath: path), - fileExtraOptions: filterConfig.fileExtraOptions - ) - } - - srcDiffSize = fileSize - srcDiffSize - destDiffSize = destRoot.fileSize - destDiffSize - - srcCount += summary - destCount += destRoot.summary - - comparator.compare(self, destRoot) - removeVisibleItems(filterConfig: filterConfig) - - srcCount -= summary - destCount -= destRoot.summary - - // if nothing has been changed don't try to update parents - if srcCount.olderFiles == 0, srcCount.changedFiles == 0, - srcCount.orphanFiles == 0, srcCount.matchedFiles == 0, - destCount.olderFiles == 0, destCount.changedFiles == 0, - destCount.orphanFiles == 0, destCount.matchedFiles == 0, - destCount.subfoldersSize == 0, - srcDiffSize == 0, destDiffSize == 0 { - return - } - - var parent = parent - while let item = parent { - item.addOlderFiles(-srcCount.olderFiles) - item.addChangedFiles(-srcCount.changedFiles) - item.addOrphanFiles(-srcCount.orphanFiles) - item.addMatchedFiles(srcCount.matchedFiles) - item.addSubfoldersSize(srcDiffSize) - - if let destItem = item.linkedItem { - destItem.addOlderFiles(-destCount.olderFiles) - destItem.addChangedFiles(-destCount.changedFiles) - destItem.addOrphanFiles(-destCount.orphanFiles) - destItem.addMatchedFiles(destCount.matchedFiles) - destItem.addSubfoldersSize(destDiffSize) - } - - item.removeVisibleItems(filterConfig: filterConfig) - - parent = item.parent - } - } - - @discardableResult - func filterVisibleItems( - showFilteredFiles: Bool, - hideEmptyFolders: Bool, - recursive: Bool - ) -> VisibleItem { - var leftVisible: VisibleItem - var rightVisible: VisibleItem? - - if let vi = visibleItem { - leftVisible = vi - rightVisible = vi.linkedItem - } else { - leftVisible = VisibleItem.createLinked(self) - rightVisible = leftVisible.linkedItem - } - - guard let rightVisible else { - return leftVisible - } - - leftVisible.removeAll() - rightVisible.removeAll() - - for left in children { - if showFilteredFiles || !left.isFiltered { - var newDestLeft = left.visibleItem - var addItem = true - - if left.isFolder { - if recursive { - newDestLeft = left.filterVisibleItems( - showFilteredFiles: showFilteredFiles, - hideEmptyFolders: hideEmptyFolders, - recursive: true - ) - } - - if hideEmptyFolders, let newDestLeft { - addItem = !newDestLeft.children.isEmpty - } - } - - if addItem { - if newDestLeft == nil { - newDestLeft = VisibleItem.createLinked(left) - } - if let newDestLeft { - leftVisible.add(newDestLeft) - if let linkedItem = newDestLeft.linkedItem { - rightVisible.add(linkedItem) - } - } - } - } - } - - return leftVisible - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareItem.swift b/Sources/Features/FoldersCompare/CompareItem/CompareItem.swift deleted file mode 100644 index 8fb49ed..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareItem.swift +++ /dev/null @@ -1,388 +0,0 @@ -// -// CompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -typealias CompareItemComparison = (CompareItem, CompareItem) -> ComparisonResult - -public class CompareItem: NSObject { - struct FileOptions: FlagSet { - var rawValue: Int - - static let isFile = FileOptions(rawValue: 1 << 0) - static let isFolder = FileOptions(rawValue: 1 << 1) - static let isSymbolicLink = FileOptions(rawValue: 1 << 2) - static let isPackage = FileOptions(rawValue: 1 << 3) - static let isResourceFork = FileOptions(rawValue: 1 << 4) - static let isLocked = FileOptions(rawValue: 1 << 5) - static let isValidFile = FileOptions(rawValue: 1 << 6) - } - - var linkedItem: CompareItem? - var visibleItem: VisibleItem? - var parent: CompareItem? - - var fileModificationDate: Date? - var fileSize: Int64 = 0 - var type: CompareChangeType - - private(set) var children = [CompareItem]() - private(set) var orphanFolders: Int = 0 - - // MARK: - Path - - @objc var path: String? { - didSet { - cachedFileName = nil - } - } - - private var cachedFileName: String? - - var fileName: String? { - if cachedFileName == nil, - let path { - let url = URL(filePath: path, directoryHint: isFolder ? .isDirectory : .notDirectory) - cachedFileName = url.lastPathComponent - } - return cachedFileName - } - - var fileOptions: FileOptions = [] - - var isFiltered: Bool = false - var isDisplayed: Bool = false - - private(set) var summary: CompareSummary = .init() - - func setSummary(_ summary: CompareSummary) { - self.summary = summary - updateType() - } - - // MARK: - init - - init( - path: String?, - attrs: [FileAttributeKey: Any]?, - fileExtraOptions: FileExtraOptions, - parent: CompareItem? - ) { - self.path = path - self.parent = parent - type = .orphan - - super.init() - - setAttributes(attrs, fileExtraOptions: fileExtraOptions) - summary = CompareSummary() - } - - func setAttributes( - _ input: [FileAttributeKey: Any]?, - fileExtraOptions: FileExtraOptions - ) { - guard let input else { - fileOptions = [] - - summary = CompareSummary() - type = .orphan - - fileSize = 0 - fileModificationDate = nil - - return - } - - let fileType = input.fileAttributeType - - // if the object is a symlink then we use the destination file attributes - // so we refer to destFileAttrs instead of the original input - var destFileAttrs = input - fileOptions[.isLocked] = (input[.immutable] as? NSNumber)?.boolValue ?? false - fileOptions[.isSymbolicLink] = fileType == .typeSymbolicLink - - let url: URL? = if let path { - // at this time we don't know if path is a directory or file, so force .notDirectory - URL(filePath: path, directoryHint: .notDirectory) - } else { - nil - } - - if fileOptions[.isSymbolicLink] { - let fileManager = FileManager.default - if let symLinkDest = url?.resolvingSymlinksInPath().osPath, - let symLinkAttrs = try? fileManager.attributesOfItem(atPath: symLinkDest) { - let symLinkFileType = symLinkAttrs.fileAttributeType - fileOptions[.isFolder] = symLinkFileType == .typeDirectory - destFileAttrs = symLinkAttrs - } - } else { - fileOptions[.isFolder] = fileType == .typeDirectory - } - fileOptions[.isFile] = !fileOptions[.isFolder] - fileModificationDate = destFileAttrs[.modificationDate] as? Date - if fileOptions[.isFolder] { - // this is necessary because the returned value can be > 0 also for directories - // but we don't care about its real internal size - fileSize = 0 - - if let path { - fileOptions[.isPackage] = NSWorkspace.shared.isFilePackage(atPath: path) - } else { - fileOptions[.isPackage] = false - } - fileOptions[.isResourceFork] = false - } else { - fileOptions[.isResourceFork] = false - var forkSize: Int64 = 0 - if fileExtraOptions.contains(.resourceFork), - let size = try? url?.resourceForkSize() { - forkSize = Int64(size) - fileOptions[.isResourceFork] = true - } - if fileOptions[.isResourceFork] { - fileSize = forkSize - } else { - fileSize = destFileAttrs[.size] as? Int64 ?? 0 - } - - fileOptions[.isPackage] = false - } - fileOptions[.isValidFile] = true - } - - // The linked item on other side represent a folder - // this allows to treat this object as a folder when necessary for example when items are expanded - // If set to YES isFolderObject will return YES - func linkedItemIsFolder(_ isFolder: Bool) { - fileOptions[.isFolder] = isFolder - fileOptions[.isFile] = !isFolder - } - - // MARK: - Children access methods - - func add(child: CompareItem) { - children.append(child) - } - - func insert(child: CompareItem, at index: Int) { - children.insert(child, at: index) - } - - func remove(child: CompareItem) { - if let index = children.firstIndex(where: { $0 === child }) { - children.remove(at: index) - } - } - - // periphery:ignore - func removeChild(at index: Int) { - children.remove(at: index) - } - - func replaceChild(at index: Int, with child: CompareItem) { - children[index] = child - } - - func child(at index: Int) -> CompareItem { - children[index] - } - - func sortChildren(using cmptr: CompareItemComparison) { - children.sort { - cmptr($0, $1) == .orderedAscending - } - } - - // MARK: - Counter setters - - private func updateType() { - if isFile { - if summary.olderFiles > 0 { - type = .old - } else if summary.changedFiles > 0 { - type = .changed - } else if summary.orphanFiles > 0 { - type = .orphan - } else { - type = .same - } - } else if isFolder { - if summary.hasMetadataTags { - type = .mismatchingTags - } else if summary.hasMetadataLabels { - type = .mismatchingLabels - } else { - type = .orphan - } - } - } - - func addOlderFiles(_ delta: Int) { - if delta != 0 { - // file can only have one count and it must be 1 - if isFile { - summary.olderFiles = delta < 0 ? 0 : 1 - - summary.changedFiles = 0 - summary.orphanFiles = 0 - summary.matchedFiles = 0 - } else { - summary.olderFiles += delta - } - - updateType() - } - } - - func addChangedFiles(_ delta: Int) { - if delta != 0 { - if isFile { - summary.changedFiles = delta < 0 ? 0 : 1 - - summary.olderFiles = 0 - summary.orphanFiles = 0 - summary.matchedFiles = 0 - } else { - summary.changedFiles += delta - } - - updateType() - } - } - - func addOrphanFiles(_ delta: Int) { - if delta != 0 { - if isFile { - summary.orphanFiles = delta < 0 ? 0 : 1 - summary.olderFiles = 0 - summary.changedFiles = 0 - summary.matchedFiles = 0 - } else { - summary.orphanFiles += delta - } - - updateType() - } - } - - func addMatchedFiles(_ delta: Int) { - if delta != 0 { - if isFile { - summary.matchedFiles = delta < 0 ? 0 : 1 - summary.olderFiles = 0 - summary.changedFiles = 0 - summary.orphanFiles = 0 - } else { - summary.matchedFiles += delta - } - - updateType() - } - } - - func addMismatchingTags(_ delta: Int) { - if delta != 0 { - if isFile { - summary.mismatchingTags = delta < 0 ? 0 : 1 - } else { - summary.mismatchingTags += delta - } - - updateType() - } - } - - func addMismatchingLabels(_ delta: Int) { - if delta != 0 { - if isFile { - summary.mismatchingLabels = delta < 0 ? 0 : 1 - } else { - summary.mismatchingLabels += delta - } - - updateType() - } - } - - func setMismatchingFolderMetadataTags(_ hasMetadata: Bool) { - if hasMetadata { - summary.mismatchingFolderMetadata.insert(.tags) - } else { - summary.mismatchingFolderMetadata.remove(.tags) - } - updateType() - } - - func setMismatchingFolderMetadataLabels(_ hasMetadata: Bool) { - if hasMetadata { - summary.mismatchingFolderMetadata.insert(.labels) - } else { - summary.mismatchingFolderMetadata.remove(.labels) - } - updateType() - } - - func addSubfoldersSize(_ delta: Int64) { - summary.subfoldersSize += delta - } - - func addOrphanFolders(_ delta: Int) { - guard delta != 0 else { - return - } - - var parent: CompareItem? = parent - - while let p = parent { - p.orphanFolders += delta - parent = p.parent - } - } - - // MARK: Counter computing - - func computeCounts(_ countHolder: inout CompareSummary, filteredSummary: inout CompareSummary) { - guard isValidFile else { - return - } - - let updateFilteredCount = isFiltered || !isDisplayed - var counter = CompareSummary() - - if isFile { - let total = summary.olderFiles + summary.changedFiles + summary.orphanFiles + summary.matchedFiles - - // Filtered files have all count set to zero, not displayed files have count set correctly - // so we determine how to increment the usedCounter - if total == 0 { - counter.orphanFiles += 1 - } else { - counter += summary - } - counter.subfoldersSize += fileSize - } else { - counter.folders += 1 - for subfolder in children { - subfolder.computeCounts(&countHolder, filteredSummary: &filteredSummary) - } - } - - if updateFilteredCount { - filteredSummary += counter - } else { - countHolder += counter - } - } - - func invalidate() { - path = nil - setAttributes(nil, fileExtraOptions: []) - type = .orphan - isFiltered = false - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/CompareSummary.swift b/Sources/Features/FoldersCompare/CompareItem/CompareSummary.swift deleted file mode 100644 index 9080456..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/CompareSummary.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// CompareSummary.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/24. -// Copyright (c) 2024 visualdiffer.com -// - -struct MismatchingFolderMetadata: OptionSet { - let rawValue: Int - - static let tags = MismatchingFolderMetadata(rawValue: 1 << 0) - static let labels = MismatchingFolderMetadata(rawValue: 1 << 1) -} - -public struct CompareSummary: Sendable { - var olderFiles: Int = 0 - var changedFiles: Int = 0 - var orphanFiles: Int = 0 - var matchedFiles: Int = 0 - var folders: Int = 0 - var subfoldersSize: Int64 = 0 - var mismatchingTags: Int = 0 - var mismatchingLabels: Int = 0 - // The info is relative only for folders = 0 - var mismatchingFolderMetadata: MismatchingFolderMetadata = [] -} - -public extension CompareSummary { - static func += (lhs: inout CompareSummary, rhs: CompareSummary) { - lhs.olderFiles += rhs.olderFiles - lhs.changedFiles += rhs.changedFiles - lhs.orphanFiles += rhs.orphanFiles - lhs.subfoldersSize += rhs.subfoldersSize - lhs.matchedFiles += rhs.matchedFiles - lhs.folders += rhs.folders - lhs.mismatchingTags += rhs.mismatchingTags - lhs.mismatchingLabels += rhs.mismatchingLabels - - if rhs.mismatchingFolderMetadata.contains(.tags) { - lhs.mismatchingTags += 1 - } - - if rhs.mismatchingFolderMetadata.contains(.labels) { - lhs.mismatchingLabels += 1 - } - } - - static func -= (lhs: inout CompareSummary, rhs: CompareSummary) { - lhs.olderFiles -= rhs.olderFiles - lhs.changedFiles -= rhs.changedFiles - lhs.orphanFiles -= rhs.orphanFiles - lhs.subfoldersSize -= rhs.subfoldersSize - lhs.matchedFiles -= rhs.matchedFiles - lhs.folders -= rhs.folders - lhs.mismatchingTags -= rhs.mismatchingTags - lhs.mismatchingLabels -= rhs.mismatchingLabels - - if rhs.mismatchingFolderMetadata.contains(.tags) { - lhs.mismatchingTags -= 1 - } - - if rhs.mismatchingFolderMetadata.contains(.labels) { - lhs.mismatchingLabels -= 1 - } - } -} - -public extension CompareSummary { - @inline(__always) var hasMetadataTags: Bool { - mismatchingFolderMetadata.contains(.tags) - } - - @inline(__always) var hasMetadataLabels: Bool { - mismatchingFolderMetadata.contains(.labels) - } - - @inline(__always) func containsDifferences() -> Bool { - olderFiles > 0 - || changedFiles > 0 - || orphanFiles > 0 - || mismatchingTags > 0 - || mismatchingLabels > 0 - || hasMetadataTags - || hasMetadataLabels - } - - @inline(__always) func containsOnlyMatches() -> Bool { - olderFiles == 0 - && changedFiles == 0 - && orphanFiles == 0 - && mismatchingTags == 0 - && mismatchingLabels == 0 - && !hasMetadataTags - && !hasMetadataLabels - && matchedFiles > 0 - } -} - -extension CompareSummary: CustomStringConvertible { - public var description: String { - String( - format: "old = %ld, chg = %ld, add = %ld, subs = %lld, mch = %ld, fol %ld, tag = %ld, lbl = %ld ft = %ld", - olderFiles, - changedFiles, - orphanFiles, - subfoldersSize, - matchedFiles, - folders, - mismatchingTags, - mismatchingLabels, - mismatchingFolderMetadata.rawValue - ) - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/DisplayOptions+Helper.swift b/Sources/Features/FoldersCompare/CompareItem/DisplayOptions+Helper.swift deleted file mode 100644 index 70b4bdc..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/DisplayOptions+Helper.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// DisplayOptions+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 27/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -public extension DisplayOptions { - init(number: NSNumber?) { - self.init(rawValue: number?.intValue ?? 0) - } - - func toNumber() -> NSNumber { - NSNumber(value: rawValue) - } -} - -public extension DisplayOptions { - var onlyMethodFlags: Self { - intersection(.fileTypeMask) - } - - var withoutMethodFlags: Self { - subtracting(.fileTypeMask) - } - - func changeWithoutMethod(_ flags: Self) -> Self { - withoutMethodFlags.union(flags) - } - - func changeWithoutMethod(_ flags: Int) -> Self { - withoutMethodFlags.union(.init(rawValue: flags)) - } -} - -public extension DisplayOptions { - /** - * Determine if left and right can be displayed based on displayFilters - * @param leftItem CompareItem - * @param rightItem CompareItem - * @param displayFilters the display filter value - * @returns true if displayable, false otherwise - */ - func isDisplayable( - _ leftItem: CompareItem, - rightItem: CompareItem - ) -> Bool { - if contains(.showAll) { - return true - } - if contains(.onlyMismatches) { - if leftItem.isFile { - return leftItem.summary.containsDifferences() || rightItem.summary.containsDifferences() - } else if contains(.dontFollowSymlinks) { - return leftItem.isValidFile != rightItem.isValidFile - } else if leftItem.isFolder { - return leftItem.summary.hasMetadataTags || leftItem.summary.hasMetadataLabels - || rightItem.summary.hasMetadataTags || rightItem.summary.hasMetadataLabels - } - return leftItem.type == .orphan - } - if contains(.noOrphan) { - if leftItem.isFile { - return (leftItem.olderFiles > 0 || leftItem.changedFiles > 0 || leftItem.matchedFiles > 0) - && (rightItem.olderFiles > 0 || rightItem.changedFiles > 0 || rightItem.matchedFiles > 0) - } else if contains(.dontFollowSymlinks) { - return leftItem.isValidFile && rightItem.isValidFile - } - return leftItem.type == .orphan - } - if contains(.onlyOrphans) { - if leftItem.isFile { - return leftItem.orphanFiles > 0 || rightItem.orphanFiles > 0 - } else if contains(.dontFollowSymlinks) { - return leftItem.isValidFile != rightItem.isValidFile - } - return leftItem.type == .orphan - } - if contains(.onlyMatches) { - if contains(.dontFollowSymlinks) { - return leftItem.isValidFile == rightItem.isValidFile - } - // no need to check also the right side - // because it has the same informations - return leftItem.matchedFiles > 0 - } - return true - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/DisplayOptions.swift b/Sources/Features/FoldersCompare/CompareItem/DisplayOptions.swift deleted file mode 100644 index 36313da..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/DisplayOptions.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// DisplayOptions.swift -// VisualDiffer -// -// Created by davide ficano on 27/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -public struct DisplayOptions: OptionSet, Sendable { - public let rawValue: Int - - public static let offset = DisplayOptions(rawValue: 0x100) - public static let onlyMatches = DisplayOptions(rawValue: offset.rawValue | 1 << 0) - public static let onlyLeftSideNewer = DisplayOptions(rawValue: offset.rawValue | 1 << 1) - public static let onlyLeftSideOrphans = DisplayOptions(rawValue: offset.rawValue | 1 << 2) - public static let onlyRightSideNewer = DisplayOptions(rawValue: offset.rawValue | 1 << 3) - public static let onlyRightSideOrphans = DisplayOptions(rawValue: offset.rawValue | 1 << 4) - public static let mismatchesButNoOrphans: DisplayOptions = [ - DisplayOptions(rawValue: 1 << 5), - .offset, - .onlyLeftSideNewer, - .onlyRightSideNewer, - ] - - public static let leftNewerAndLeftOrphans: DisplayOptions = [ - .onlyLeftSideNewer, - .onlyLeftSideOrphans, - ] - public static let rightNewerAndRightOrphans: DisplayOptions = [ - .onlyRightSideNewer, - .onlyRightSideOrphans, - ] - - public static let onlyMismatches: DisplayOptions = [ - .leftNewerAndLeftOrphans, - .rightNewerAndRightOrphans, - .mismatchesButNoOrphans, - ] - public static let noOrphan: DisplayOptions = [ - .mismatchesButNoOrphans, - .onlyMatches, - ] - public static let onlyOrphans: DisplayOptions = [ - .onlyLeftSideOrphans, - .onlyRightSideOrphans, - ] - - public static let showAll: DisplayOptions = [ - .noOrphan, - .onlyOrphans, - ] - - // Mask with all file flags - public static let fileTypeMask: DisplayOptions = [ - .onlyMatches, - .onlyLeftSideNewer, - .onlyLeftSideOrphans, - .onlyRightSideNewer, - .onlyRightSideOrphans, - .mismatchesButNoOrphans, - ] - - public static let dontFollowSymlinks = DisplayOptions(rawValue: 0x200) - public static let noOrphansFolders = DisplayOptions(rawValue: 1 << 10) - - public init(rawValue: Int) { - self.rawValue = rawValue - } -} diff --git a/Sources/Features/FoldersCompare/CompareItem/FileExtraOptions.swift b/Sources/Features/FoldersCompare/CompareItem/FileExtraOptions.swift deleted file mode 100644 index f96599c..0000000 --- a/Sources/Features/FoldersCompare/CompareItem/FileExtraOptions.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// FileExtraOptions.swift -// VisualDiffer -// -// Created by davide ficano on 03/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -public struct FileExtraOptions: FlagSet, Sendable { - public let rawValue: Int - - static let resourceFork = FileExtraOptions(rawValue: 1 << 0) - - public init(rawValue: Int) { - self.rawValue = rawValue - } -} diff --git a/Sources/Features/FoldersCompare/Components/CompareItemTableCellView.swift b/Sources/Features/FoldersCompare/Components/CompareItemTableCellView.swift deleted file mode 100644 index 6480d45..0000000 --- a/Sources/Features/FoldersCompare/Components/CompareItemTableCellView.swift +++ /dev/null @@ -1,174 +0,0 @@ -// -// CompareItemTableCellView.swift -// VisualDiffer -// -// Created by davide ficano on 22/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -class CompareItemTableCellView: NSView { - lazy var text: NSTextField = createTextField() - var icon: NSImageView? - - init(icon hasIcon: Bool) { - if hasIcon { - icon = Self.createImageView() - } - - super.init(frame: .zero) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(text) - - if let icon { - addSubview(icon) - setupConstraints(icon: icon) - } else { - setupConstraints() - } - } - - private func setupConstraints(icon: NSImageView) { - NSLayoutConstraint.activate([ - icon.centerYAnchor.constraint(equalTo: centerYAnchor), - icon.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 2), - - text.topAnchor.constraint(equalTo: topAnchor, constant: 2), - // Align the textField to the bottom of the superview (view) - text.bottomAnchor.constraint(equalTo: bottomAnchor), - // is relative to icon - text.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 2), - text.trailingAnchor.constraint(equalTo: trailingAnchor), - ]) - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - text.topAnchor.constraint(equalTo: topAnchor, constant: 2), - // Align the textField to the bottom of the superview (view) - text.bottomAnchor.constraint(equalTo: bottomAnchor), - // is relative to super view - text.leadingAnchor.constraint(equalTo: leadingAnchor), - text.trailingAnchor.constraint(equalTo: trailingAnchor), - ]) - } - - private func createTextField() -> NSTextField { - let view = NSTextField() - - view.allowsExpansionToolTips = true - view.lineBreakMode = .byTruncatingTail - view.alignment = .left - view.isBordered = false - view.backgroundColor = NSColor.clear - view.refusesFirstResponder = true - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private static func createImageView() -> NSImageView { - let view = NSImageView() - - view.alignment = .left - view.imageScaling = .scaleProportionallyDown - view.refusesFirstResponder = true - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func fileName( - _ item: CompareItem, - font: NSFont, - isExpanded: Bool, - followSymLinks _: Bool, - hideEmptyFolders: Bool - ) { - if let fileName = resolvedFileName(item) { - text.stringValue = fileName - text.lineBreakMode = .byTruncatingMiddle - } - text.font = font - icon?.image = IconUtils.shared.icon( - for: item, - size: 16, - isExpanded: isExpanded, - hideEmptyFolders: hideEmptyFolders - ) - } - - func fileSize( - _ item: CompareItem, - font: NSFont, - columnWidth: CGFloat - ) { - if item.isValidFile { - text.frame = NSRect(x: 0, y: -2, width: 80, height: 17) - text.font = font - text.alignment = .right - - let size = item.isFile ? Int64(item.fileSize) : item.subfoldersSize - var strSize = FileSizeFormatter.default.string(from: NSNumber(value: size), showInBytes: true, showUnitForBytes: false) - // increment width by one digit to be sure it is enough large to contain the formatted number - let sizeCellWidth: CGFloat = if let cell = text.cell as? NSTextFieldCell, - let strSize { - cell.widthString("9") + cell.widthString(strSize) - } else { - 0 - } - - if columnWidth < sizeCellWidth { - strSize = FileSizeFormatter.default.string(from: NSNumber(value: size), showInBytes: false, showUnitForBytes: false) - } - text.stringValue = strSize ?? "\(size)" - } - } - - func fileDate( - _: CompareItem, - date: Date?, - font: NSFont, - dateFormat: String - ) { - text.font = font - - // includes seconds on time using the current locale time components position - if let date { - let localeFormat = DateFormatter() - - localeFormat.dateFormat = DateFormatter.dateFormat( - fromTemplate: dateFormat, - options: 0, - locale: Locale.current - ) - text.formatter = localeFormat - text.objectValue = date - } else { - text.formatter = nil - text.objectValue = nil - } - } - - private func resolvedFileName( - _ item: CompareItem - ) -> String? { - guard let fileName = item.fileName else { - return nil - } - if item.isSymbolicLink, - let path = item.path, - let destination = try? FileManager.default.destinationOfSymbolicLink(atPath: path) { - return "\(fileName) -> \(destination)" - } - return fileName - } -} diff --git a/Sources/Features/FoldersCompare/Components/CompareItemTableRowView.swift b/Sources/Features/FoldersCompare/Components/CompareItemTableRowView.swift deleted file mode 100644 index 3ca854b..0000000 --- a/Sources/Features/FoldersCompare/Components/CompareItemTableRowView.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// CompareItemTableRowView.swift -// VisualDiffer -// -// Created by davide ficano on 18/10/14. -// Copyright (c) 2014 visualdiffer.com -// - -class CompareItemTableRowView: NSTableRowView { - @objc var item: CompareItem? - - override func drawSelection(in dirtyRect: NSRect) { - guard let selectionColor = CommonPrefs.shared.folderColor(.selectedRow)?.background else { - return - } - - // if the enclosing tableview has the focus use the color without modification - if isEmphasized { - selectionColor.set() - } else { - selectionColor.blended(withFraction: 0.3, of: NSColor.gray)?.set() - } - dirtyRect.fill() - } - - // http://stackoverflow.com/questions/11127764/how-to-customize-disclosure-cell-in-view-based-nsoutlineview - override func didAddSubview(_ subview: NSView) { - super.didAddSubview(subview) - - guard let item, - let button = subview as? NSButton else { - return - } - - // hide disclosure button on invalid files - if button.identifier == NSOutlineView.disclosureButtonIdentifier { - if !item.isValidFile { - button.image = NSImage(size: NSSize(width: 16, height: 16)) - } - } - } - - override var interiorBackgroundStyle: NSView.BackgroundStyle { - // Change the background style because when row is selected the outline cell - // (eg disclosure triangle) is drawn white but we want it uses the - // same style used when it is not selected - .normal - } -} diff --git a/Sources/Features/FoldersCompare/Components/DisplayFiltersScopeBar.swift b/Sources/Features/FoldersCompare/Components/DisplayFiltersScopeBar.swift deleted file mode 100644 index 44a3cba..0000000 --- a/Sources/Features/FoldersCompare/Components/DisplayFiltersScopeBar.swift +++ /dev/null @@ -1,250 +0,0 @@ -// -// DisplayFiltersScopeBar.swift -// VisualDiffer -// -// Created by davide ficano on 10/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -enum DisplayFiltersScopeBarAttributeKey: String { - case filterFlagsDisplayFilters = "filterFlags" -} - -@objc enum DisplayFiltersScopeBarAction: Int { - case selectFilter - case showFiltered - case showEmptyFolders - case showNoOrphansFolders -} - -protocol DisplayFiltersScopeBarDelegate: AnyObject { - func displayFiltersScopeBar( - _ displayFiltersScopeBar: DisplayFiltersScopeBar, - action: DisplayFiltersScopeBarAction, - options: [DisplayFiltersScopeBarAttributeKey: Any]? - ) -} - -private let showFilteredId = "FilteredId" -private let showEmptyFoldersId = "EmptyFoldersId" -private let showNoOrphansFoldersId = "NoOrphansFoldersId" - -private enum ScopeGroupOptions: Int { - case displayFilters - case displayFolders - case displayFlags -} - -@MainActor @objc class DisplayFiltersScopeBar: MGScopeBar, @preconcurrency MGScopeBarDelegate { - private var groupItems = [[ScopeBarGroupKey: Any]]() - private var labels = [String: String]() - - var actionDelegate: DisplayFiltersScopeBarDelegate? - @objc var findView: FindText - - override init(frame frameRect: NSRect) { - findView = FindText(frame: NSRect(x: 0, y: 0, width: 400, height: 25)) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - fontSize = 11.0 - } - - func initScopeBar(_ actionDelegate: DisplayFiltersScopeBarDelegate) { - self.actionDelegate = actionDelegate - delegate = self - - groupItems.removeAll() - - groupItems.append([ - .selectionMode: MGScopeBarGroupSelectionMode.radio, - .items: [ - mkItem(String(format: "%ld", DisplayOptions.showAll.rawValue), NSLocalizedString("All", comment: "")), - mkItem(String(format: "%ld", DisplayOptions.onlyMismatches.rawValue), NSLocalizedString("Only Mismatches", comment: "")), - mkItem(String(format: "%ld", DisplayOptions.onlyMatches.rawValue), NSLocalizedString("Only Matches", comment: "")), - mkItem(String(format: "%ld", DisplayOptions.noOrphan.rawValue), NSLocalizedString("No Orphans", comment: "")), - mkItem(String(format: "%ld", DisplayOptions.onlyOrphans.rawValue), NSLocalizedString("Only Orphans", comment: "")), - ], - ]) - - // Folders related group - groupItems.append([ - .label: NSLocalizedString("Folders:", comment: ""), - .separator: true, - .selectionMode: MGScopeBarGroupSelectionMode.multiple, - .items: [ - mkItem(showEmptyFoldersId, NSLocalizedString("Empty", comment: "")), - mkItem(showNoOrphansFoldersId, NSLocalizedString("No Orphans", comment: "")), - ], - ]) - - // Filtered group - groupItems.append([ - .separator: true, - .selectionMode: MGScopeBarGroupSelectionMode.multiple, - .items: [ - mkItem(showFilteredId, NSLocalizedString("Filtered", comment: "")), - ], - ]) - - // Dictionary doesn't preserve order so we can't use it to fill the array - // So first fill the array then labels - labels.removeAll() - for group in groupItems { - if let groupItems = group[.items] as? [[ScopeBarItem: String]] { - for dict in groupItems { - if let identifier = dict[.identifier], - let name = dict[.name] { - labels[identifier] = name - } - } - } - } - - // scopeBar automatically select first item and then we set selected but this causes unnecessary folder reload - notifyDefaultSelections = false - smartResizeEnabled = true - - reloadData() - } - - private func mkItem(_ identifier: String, _ name: String) -> [ScopeBarItem: String] { - [ - .identifier: identifier, - .name: name, - ] - } - - // MARK: - MGScopeBarDelegate methods - - func numberOfGroups(in _: MGScopeBar) -> Int { - groupItems.count - } - - func scopeBar(_: MGScopeBar, itemIdentifiersForGroup groupNumber: Int) -> [Any] { - guard let items = groupItems[groupNumber][.items], - let itemIdentifiers = items as? [[ScopeBarItem: String]] else { - fatalError("Unexpected data format in groupItems") - } - return itemIdentifiers.compactMap { $0[.identifier] } - } - - func scopeBar(_: MGScopeBar, labelForGroup groupNumber: Int) -> String? { - groupItems[groupNumber][.label] as? String // might be nil, which is fine (nil means no label). - } - - func scopeBar(_: MGScopeBar, titleOfItem identifier: String, inGroup groupNumber: Int) -> String? { - if groupItems[groupNumber][.items] != nil { - return labels[identifier] - } - return nil - } - - func scopeBar(_: MGScopeBar, selectionModeForGroup groupNumber: Int) -> MGScopeBarGroupSelectionMode { - (groupItems[groupNumber][.selectionMode] as? MGScopeBarGroupSelectionMode) ?? .radio - } - - func scopeBar(_: MGScopeBar, showSeparatorBeforeGroup groupNumber: Int) -> Bool { - // Optional method. If not implemented, all groups except the first will have a separator before them. - groupItems[groupNumber][.separator] as? Bool ?? false - } - - func scopeBar(_: MGScopeBar, imageForItem _: String, inGroup _: Int) -> NSImage? { - nil - } - - func accessoryView(for _: MGScopeBar) -> NSView? { - findView - } - - func scopeBar(_: MGScopeBar, selectedStateChanged _: Bool, forItem identifier: String, inGroup groupNumber: Int) { - guard let actionDelegate, - let group = ScopeGroupOptions(rawValue: groupNumber) else { - return - } - switch group { - case .displayFilters: - if let filterValue = Int(identifier) { - actionDelegate.displayFiltersScopeBar( - self, - action: .selectFilter, - options: [.filterFlagsDisplayFilters: NSNumber(value: filterValue)] - ) - } - case .displayFlags: - if identifier == showFilteredId { - actionDelegate.displayFiltersScopeBar( - self, - action: .showFiltered, - options: nil - ) - } - case .displayFolders: - if identifier == showNoOrphansFoldersId { - actionDelegate.displayFiltersScopeBar( - self, - action: .showNoOrphansFolders, - options: nil - ) - } else if identifier == showEmptyFoldersId { - actionDelegate.displayFiltersScopeBar( - self, - action: .showEmptyFolders, - options: nil - ) - } - } - } - - // MARK: - Actions - - @objc func hideEmptyFolders(_ hideEmptyFolders: Bool, informDelegate _: Bool) { - // The logic to select the 'empty folders' button is inverted so we pass the negated value - setSelected( - !hideEmptyFolders, - forItem: showEmptyFoldersId, - inGroup: ScopeGroupOptions.displayFolders.rawValue, - informDelegate: false - ) - } - - @objc func showFilteredFiles(_ showFilteredFiles: Bool, informDelegate: Bool) { - setSelected( - showFilteredFiles, - forItem: showFilteredId, - inGroup: ScopeGroupOptions.displayFolders.rawValue, - informDelegate: informDelegate - ) - } - - func select(_ displayOptions: DisplayOptions, informDelegate: Bool) { - setSelected( - true, - forItem: String(format: "%ld", displayOptions.onlyMethodFlags.rawValue), - inGroup: ScopeGroupOptions.displayFilters.rawValue, - informDelegate: informDelegate - ) - } - - @objc func noOrphansFolders(_ noOrphansFolders: Bool, informDelegate: Bool) { - setSelected( - noOrphansFolders, - forItem: showNoOrphansFoldersId, - inGroup: ScopeGroupOptions.displayFolders.rawValue, - informDelegate: informDelegate - ) - } - - override func becomeFirstResponder() -> Bool { - findView.becomeFirstResponder() - } -} diff --git a/Sources/Features/FoldersCompare/Components/FolderPanelView.swift b/Sources/Features/FoldersCompare/Components/FolderPanelView.swift deleted file mode 100644 index bbde32d..0000000 --- a/Sources/Features/FoldersCompare/Components/FolderPanelView.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// FolderPanelView.swift -// VisualDiffer -// -// Created by davide ficano on 24/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FolderPanelView: TablePanelView { - override var pathViewDelegate: PathControlDelegate? { - willSet { - pathView.isSaveHidden = true - } - } - - init() { - super.init(treeView: FoldersOutlineView(frame: .zero), bottomBar: NSTextField(frame: .zero)) - treeView.addColumns() - } - - override func setupBottomBarConstraints() { - super.setupBottomBarConstraints() - - NSLayoutConstraint.activate([ - bottomBar.bottomAnchor.constraint(equalTo: bottomAnchor), - bottomBar.heightAnchor.constraint(equalToConstant: 22), - bottomBar.trailingAnchor.constraint(equalTo: trailingAnchor), - bottomBar.leadingAnchor.constraint(equalTo: leadingAnchor), - ]) - } - - override func setupBottomBar() { - bottomBar.centerVertically() - bottomBar.translatesAutoresizingMaskIntoConstraints = false - - bottomBar.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - } - - override func updateBottomBar() { - bottomBar.stringValue = treeView.getFileCountInfo().description - } - - static func createFolderPanel( - side: DisplaySide, - delegate: PathControlDelegate & FoldersOutlineViewDelegate & NSOutlineViewDataSource - ) -> FolderPanelView { - let view = FolderPanelView() - view.pathViewDelegate = delegate - view.side = side - - view.treeView.delegate = delegate - view.treeView.dataSource = delegate - view.treeView.setupColumnsSort() - - return view - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/ColoredFoldersManager.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/ColoredFoldersManager.swift deleted file mode 100644 index 796c214..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/ColoredFoldersManager.swift +++ /dev/null @@ -1,201 +0,0 @@ -// -// ColoredFoldersManager.swift -// VisualDiffer -// -// Created by davide ficano on 21/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ColoredFoldersManager: NSObject, @unchecked Sendable { - private let queue = DispatchQueue(label: "com.visualdiffer.colored.folders", attributes: .concurrent) - private var foldersColors: [String: NSImage] - - @objc static let shared = ColoredFoldersManager() - - override private init() { - foldersColors = Self.buildColoredFolders() - } - - func iconName( - _ item: CompareItem, - isExpanded: Bool, - hideEmptyFolders: Bool - ) -> String { - let openSuffix = isExpanded ? "-open" : "" - return String(format: "folder-%03d%@", icon(sequenceFor: item, hideEmptyFolders: hideEmptyFolders), openSuffix) - } - - func icon( - forFolder item: CompareItem, - size _: CGFloat, - isExpanded: Bool, - hideEmptyFolders: Bool - ) -> NSImage? { - let imageFileName = iconName(item, isExpanded: isExpanded, hideEmptyFolders: hideEmptyFolders) - return icon(folderName: imageFileName) - } - - @objc func refresh() { - queue.async(flags: .barrier) { - self.foldersColors = Self.buildColoredFolders() - } - } - - private func icon(folderName: String) -> NSImage? { - queue.sync { - foldersColors[folderName] - } - } - - private static func buildColoredFolders() -> [String: NSImage] { - let prefs = CommonPrefs.shared - guard let changedColor = prefs.changeTypeColor(.changed)?.text, - let olderColor = prefs.changeTypeColor(.old)?.text, - let sameColor = prefs.changeTypeColor(.same)?.text, - let newerColor = prefs.changeTypeColor(.newer)?.text, - let orphanColor = prefs.changeTypeColor(.orphan)?.text, - let filteredColor = prefs.changeTypeColor(.filtered)?.text, - let mismatchingTagsColor = prefs.changeTypeColor(.mismatchingTags)?.text, - let mismatchingLabelsColor = prefs.changeTypeColor(.mismatchingLabels)?.text else { - fatalError("Unable to get colors for colored folders") - } - - guard let maskFull = NSImage(named: "mask-full"), - let maskBackWhite = NSImage(named: "mask-back-white"), - let maskBack = NSImage(named: "mask-back"), - let maskFront = NSImage(named: "mask-front"), - let maskMiddle = NSImage(named: "mask-middle") else { - fatalError("Unable to build colored folders") - } - - let size = NSSize(width: 16.0, height: 16.0) - - let useSoftwareRenderer = false - - return [ - "folder-000-open": maskedWithFront(maskFront.monochromaticTint(sameColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-000": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(sameColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-001-open": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-001": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-010-open": maskedWithFront(maskFront.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-010": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-080-open": maskedWithFront(maskFront.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-080": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-100-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-100": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-101-open": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-101": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-011-open": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-011": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-110-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-110": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-111-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-111": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(changedColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-180-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-180": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-999-open": maskedWithFront(maskFront.monochromaticTint(filteredColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-999": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(filteredColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-888-open": maskedWithFront(maskFront.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-888": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-020-open": maskedWithFront(maskFront.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-020": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-030-open": maskedWithFront(maskFront.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBackWhite, size: size), - "folder-030": maskedWithFront(nil, middle: nil, back: maskFull.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-021-open": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-021": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-031-open": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-031": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-081-open": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-081": maskedWithFront(maskFront.monochromaticTint(olderColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-120-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-120": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-130-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-130": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-121-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-121": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(mismatchingTagsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-131-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-131": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(mismatchingLabelsColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - - "folder-181-open": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: maskMiddle.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), back: maskBackWhite, size: size), - "folder-181": maskedWithFront(maskFront.monochromaticTint(orphanColor, useSoftwareRenderer: useSoftwareRenderer), middle: nil, back: maskBack.monochromaticTint(newerColor, useSoftwareRenderer: useSoftwareRenderer), size: size), - ] - } - - private static func maskedWithFront( - _ frontImage: NSImage?, - middle middleImage: NSImage?, - back backImage: NSImage?, - size: NSSize - ) -> NSImage { - var composedImage: NSImage - - if let frontImage, let backImage { - composedImage = backImage.overImage(frontImage) - - if let middleImage { - let middleTintImage = middleImage - composedImage = composedImage.overImage(middleTintImage) - } - } else if let backImage { - composedImage = backImage - } else { - fatalError("Invalid Image combination frontImage \(String(describing: frontImage)), middleImage \(String(describing: middleImage)), backImage \(String(describing: backImage))") - } - composedImage.size = size - - return composedImage - } - - private func icon( - sequenceFor item: CompareItem, - hideEmptyFolders: Bool - ) -> Int { - if item.isFiltered { - return 999 - } - if item.isOrphanFolder { - return 100 - } - - let hasOrphanFolders = hideEmptyFolders ? true : item.orphanFolders == 0 - let hasTags = item.summary.hasMetadataTags || item.mismatchingTags != 0 - let hasLabels = item.summary.hasMetadataLabels || item.mismatchingLabels != 0 - let hasNewerFiles = item.changedFiles != 0 && (item.linkedItem?.olderFiles ?? 0) != 0 - - let changed = if hasNewerFiles { - 80 - } else if item.changedFiles != 0 { - 10 - } else if hasTags { - 20 - } else if hasLabels { - 30 - } else { - 0 - } - - return (item.orphanFiles == 0 && hasOrphanFolders ? 0 : 100) - + changed - + (item.olderFiles == 0 ? 0 : 1) - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/CompareItem+LeafPath.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/CompareItem+LeafPath.swift deleted file mode 100644 index 37c2ed0..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/CompareItem+LeafPath.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// CompareItem+LeafPath.swift -// VisualDiffer -// -// Created by davide ficano on 06/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension CompareItem { - /** - * Eliminate all ancestor elements from the provided array. - * The resulting array will contain only leaf paths. - */ - @objc static func findLeafPaths(_ items: [CompareItem]) -> [CompareItem] { - // The items must be sorted to find correctly the leaves - let arr = items.sorted { - let lhs = $0.path ?? "" - let rhs = $1.path ?? "" - return lhs.localizedCompare(rhs) == .orderedAscending - } - - var leaves = [CompareItem]() - - for (i, item) in arr.enumerated() { - var isLeaf = true - - if (i + 1) < arr.count { - if item.isAncestor(of: arr[i + 1]) { - isLeaf = false - } - } - if isLeaf { - leaves.append(item) - } - } - - return leaves - } - - func isAncestor(of child: CompareItem) -> Bool { - guard let parentUrl = toUrl(), - let childUrl = child.toUrl() else { - return false - } - - // pathComponents returns leading "/" and any extra trailing "/" - // e.g. /a//b/c/// returns ["/", "a", "b", "c", "/"] so we filter out any "/" manually - let parentComponents = parentUrl.pathComponents.filter { $0 != "/" } - let childComponents = childUrl.pathComponents.filter { $0 != "/" } - - return childComponents.starts(with: parentComponents) - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/CompareItem+NSTableCellView.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/CompareItem+NSTableCellView.swift deleted file mode 100644 index 367e6a3..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/CompareItem+NSTableCellView.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// CompareItem+NSTableCellView.swift -// VisualDiffer -// -// Created by davide ficano on 24/06/20. -// Copyright (c) 2020 visualdiffer.com -// - -extension NSUserInterfaceItemIdentifier.Folders { - static let cellName = NSUserInterfaceItemIdentifier("cellName") - static let cellSize = NSUserInterfaceItemIdentifier("cellSize") - static let cellModified = NSUserInterfaceItemIdentifier("cellModified") -} - -extension CompareItem { - func compareChangeType( - _ cellIdentifier: NSUserInterfaceItemIdentifier?, - followSymLinks: Bool - ) -> CompareChangeType { - if !isValidFile { - return .unknown - } - - var type = CompareChangeType.unknown - - if isFiltered { - type = .filtered - } else if isFile { - if isNewerThanLinked { - type = .newer - } else if orphanFiles == 0, olderFiles == 0, changedFiles == 0 { - // only differs for metadata so choose the appropriate status - if summary.hasMetadataTags || mismatchingTags > 0 { - type = .mismatchingTags - } else if summary.hasMetadataLabels || mismatchingLabels > 0 { - type = .mismatchingLabels - } else { - type = self.type - } - } else { - type = self.type - } - } else if isFolder { - if cellIdentifier == .Folders.cellSize { - type = .subFoldersSize - } else { - if isSymbolicLink, !followSymLinks { - type = (linkedItem?.isValidFile ?? false) ? .same : .orphan - } else { - if summary.hasMetadataTags { - type = .mismatchingTags - } else if summary.hasMetadataLabels { - type = .mismatchingLabels - } else { - type = .folder - } - } - } - } - - return type - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Clipboard.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Clipboard.swift deleted file mode 100644 index 76c633f..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Clipboard.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// FoldersOutlineView+Clipboard.swift -// VisualDiffer -// -// Created by davide ficano on 28/12/20. -// Copyright (c) 2020 visualdiffer.com -// - -@objc extension FoldersOutlineView { - func copySelectedAsFileNames() { - var paths = [String]() - - enumerateSelectedValidFiles { item, _ in - if let fileName = item.fileName { - paths.append(fileName) - } - } - if !paths.isEmpty { - paths.append("") - } - - NSPasteboard.general.copy(lines: paths) - } - - func copySelectedAsFullPaths() { - var paths = [String]() - - enumerateSelectedValidFiles { item, _ in - if let path = item.path { - paths.append(path) - } - } - if !paths.isEmpty { - paths.append("") - } - - NSPasteboard.general.copy(lines: paths) - } - - func copySelectedAsUrls() { - var urls = [URL]() - - enumerateSelectedValidFiles { item, _ in - if let path = item.toUrl() { - urls.append(path) - } - } - - NSPasteboard.general.copy(urls: urls) - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Columns.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Columns.swift deleted file mode 100644 index 4e22c9a..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Columns.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// FoldersOutlineView+Columns.swift -// VisualDiffer -// -// Created by davide ficano on 26/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersOutlineView { - func addColumns() { - var column = NSTableColumn(identifier: .Folders.cellName) - column.isEditable = false - column.width = 180 - column.minWidth = 100 - column.maxWidth = 1000 - column.resizingMask = [.autoresizingMask, .userResizingMask] - column.title = NSLocalizedString("Name", comment: "") - column.headerCell.alignment = .left - column.headerCell.lineBreakMode = .byTruncatingTail - - addTableColumn(column) - - column = NSTableColumn(identifier: .Folders.cellSize) - column.isEditable = false - column.width = 80 - column.minWidth = 40 - column.maxWidth = 1000 - column.resizingMask = [.autoresizingMask, .userResizingMask] - column.title = NSLocalizedString("Size", comment: "") - column.headerCell.alignment = .right - column.headerCell.lineBreakMode = .byTruncatingTail - - addTableColumn(column) - - column = NSTableColumn(identifier: .Folders.cellModified) - column.isEditable = false - column.width = 250 - column.minWidth = 100 - column.maxWidth = CGFLOAT_MAX - column.resizingMask = [.autoresizingMask, .userResizingMask] - column.title = NSLocalizedString("Modified", comment: "") - column.headerCell.alignment = .left - column.headerCell.lineBreakMode = .byTruncatingTail - - addTableColumn(column) - } - - func adjustColumnsWidths( - _ font: NSFont, - dateFormatTemplate: String - ) { - var totalWidth = 0.0 - - for col in tableColumns { - totalWidth += col.width - } - - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = DateFormatter.dateFormat( - fromTemplate: dateFormatTemplate, - options: 0, - locale: Locale.current - ) - - let textAttrs = [NSAttributedString.Key.font: font] - let dateCellWidth = dateFormatter.widthOfLongestDateStringWithLevel(attrs: textAttrs) - + NSScroller.scrollerWidth(for: .regular, scrollerStyle: .legacy) - - let fileSizeFormatter = FileSizeFormatter(showInBytes: true, showUnitForBytes: false) - let sizeCellWidth = (fileSizeFormatter.string(from: NSNumber(value: 999_999_999_999)) as? NSString)? - .size(withAttributes: textAttrs).width ?? 0 - - tableColumn(withIdentifier: .Folders.cellName)?.width = totalWidth - dateCellWidth - sizeCellWidth - tableColumn(withIdentifier: .Folders.cellSize)?.width = sizeCellWidth - tableColumn(withIdentifier: .Folders.cellModified)?.width = dateCellWidth - - outlineTableColumn = tableColumns[0] - } - - func setupColumnsSort() { - tableColumn(withIdentifier: .Folders.cellName)? - .sortDescriptorPrototype = NSSortDescriptor( - key: "fileName", - ascending: true, - selector: #selector(NSString.compare(_:)) - ) - tableColumn(withIdentifier: .Folders.cellSize)? - .sortDescriptorPrototype = NSSortDescriptor( - key: "fileSize", - ascending: true, - selector: #selector(NSString.compare(_:)) - ) - tableColumn(withIdentifier: .Folders.cellModified)? - .sortDescriptorPrototype = NSSortDescriptor( - key: "fileModificationDate", - ascending: true, - selector: #selector(NSString.compare(_:)) - ) - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Enumerate.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Enumerate.swift deleted file mode 100644 index e8a8a77..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Enumerate.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// FoldersOutlineView+Enumerate.swift -// VisualDiffer -// -// Created by davide ficano on 13/02/21. -// Copyright (c) 2021 visualdiffer.com -// - -extension FoldersOutlineView { - func enumerateSelectedValidFiles(block: (_ fs: CompareItem, _ stop: inout Bool) -> Void) { - enumerateSelectedFiles(true, block: block) - } - - func enumerateSelectedFiles(_ onlyValid: Bool, block: (_ fs: CompareItem, _ stop: inout Bool) -> Void) { - var stop = false - - for row in selectedRowIndexes { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - let skip = onlyValid && !item.isValidFile - if !skip { - block(item, &stop) - - if stop { - break - } - } - } - } - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+ExternalApp.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+ExternalApp.swift deleted file mode 100644 index 8b0d358..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+ExternalApp.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// FoldersOutlineView+ExternalApp.swift -// VisualDiffer -// -// Created by davide ficano on 28/12/20. -// Copyright (c) 2020 visualdiffer.com -// - -extension FoldersOutlineView { - func showSelectedInFinder() { - var paths = [String]() - var selectionHasNotVisibleFiles = false - - enumerateSelectedValidFiles { item, _ in - if let path = item.path, - let fileName = item.fileName { - paths.append(path) - if !selectionHasNotVisibleFiles, fileName.hasPrefix(".") { - selectionHasNotVisibleFiles = true - } - } - } - if selectionHasNotVisibleFiles { - NSAlert.showModalInfo( - messageText: NSLocalizedString("Some selected file(s) may not be visible in Finder", comment: ""), - informativeText: NSLocalizedString("Finder doesn't show files starting with a . (period) character", comment: ""), - suppressPropertyName: CommonPrefs.Name.confirmShowInFinderNotVisibleFiles.rawValue - ) - } - NSWorkspace.shared.show(inFinder: paths) - } - - @objc func openSelected(with application: URL) { - do { - let editor = OpenEditor(attributes: openEditorFromSelectedFiles()) - try editor.open(withApplication: application) - } catch { - if let window { - NSAlert(error: error).beginSheetModal(for: window) - } - } - } - - @objc func openSelectedWithOther() { - do { - let editor = OpenEditor(attributes: openEditorFromSelectedFiles()) - try editor.browseApplicationAndLaunch() - } catch { - if let window { - NSAlert(error: error).beginSheetModal(for: window) - } - } - } - - private func openEditorFromSelectedFiles() -> [OpenEditorAttribute] { - var editorData = [OpenEditorAttribute]() - - enumerateSelectedValidFiles { item, _ in - if item.isFile, let path = item.path { - editorData.append(OpenEditorAttribute(path: path)) - } - } - - return editorData - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+FileCount.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+FileCount.swift deleted file mode 100644 index 6a34b0b..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+FileCount.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// FoldersOutlineView+FileCount.swift -// VisualDiffer -// -// Created by davide ficano on 27/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct FileCountInfo { - let fileCount: Int - let size: Int64 - let selectionContainsFiles: Bool -} - -extension FileCountInfo: CustomStringConvertible { - var description: String { - if selectionContainsFiles { - return String.localizedStringWithFormat( - NSLocalizedString("Selected %ld files, %@", comment: "Selected 3 files, 4.1GB"), - fileCount, - FileSizeFormatter.default.string(from: NSNumber(value: size)) ?? "" - ) - } - return String.localizedStringWithFormat( - NSLocalizedString("%ld files, %@", comment: "3 files, 4.1GB"), - fileCount, - FileSizeFormatter.default.string(from: NSNumber(value: size)) ?? "" - ) - } -} - -extension FoldersOutlineView { - func getFileCountInfo() -> FileCountInfo { - var fileCount = 0 - var size = Int64(0) - - for row in selectedRowIndexes { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - - if item.isValidFile, item.isFile { - fileCount += 1 - size += Int64(item.fileSize) - } - } - } - let selectionContainsFiles = fileCount != 0 - - if !selectionContainsFiles { - for row in 0 ..< numberOfRows { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - - if item.isValidFile, item.isFile { - fileCount += 1 - size += Int64(item.fileSize) - } - } - } - } - return FileCountInfo( - fileCount: fileCount, - size: size, - selectionContainsFiles: selectionContainsFiles - ) - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Menu.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Menu.swift deleted file mode 100644 index bb526ad..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+Menu.swift +++ /dev/null @@ -1,194 +0,0 @@ -// -// FoldersOutlineView+Menu.swift -// VisualDiffer -// -// Created by davide ficano on 18/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -@objc protocol FoldersOutlineViewContextMenu: NSObjectProtocol { - @MainActor func compareFiles(_ sender: AnyObject?) - @MainActor func compareFolders(_ sender: AnyObject?) - @MainActor func copyFileNames(_ sender: AnyObject?) - @MainActor func copyFiles(_ sender: AnyObject?) - @MainActor func copyFullPaths(_ sender: AnyObject?) - @MainActor func deleteFiles(_ sender: AnyObject?) - @MainActor func excludeByExt(_ sender: AnyObject?) - @MainActor func excludeByName(_ sender: AnyObject?) - @MainActor func expandSelectedSubfolders(_ sender: AnyObject?) - @MainActor func moveFiles(_ sender: AnyObject?) - @MainActor func popupOpenWithApp(_ sender: AnyObject?) - @MainActor func setAsBaseFolder(_ sender: AnyObject?) - @MainActor func setAsBaseFolderOtherSide(_ sender: AnyObject?) - @MainActor func setAsBaseFoldersBothSides(_ sender: AnyObject?) - @MainActor func showInFinder(_ sender: AnyObject?) - @MainActor func togglePreviewPanel(_ sender: AnyObject?) -} - -public extension FoldersOutlineView { - // MARK: - Menu Definition - - override class var defaultMenu: NSMenu? { - let theMenu = NSMenu(title: NSLocalizedString("Contextual Menu", comment: "")) - theMenu.autoenablesItems = false - theMenu.addItem( - withTitle: NSLocalizedString("Expand all Subfolders", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.expandSelectedSubfolders), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Set as Base Folder", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.setAsBaseFolder), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Set as Base Folder on the Other Side", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.setAsBaseFolderOtherSide), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Set as Base Folders Both Sides", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.setAsBaseFoldersBothSides), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Compare Files", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.compareFiles), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Compare Folders", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.compareFolders), - keyEquivalent: "" - ) - - theMenu.addItem( - withTitle: NSLocalizedString("Copy...", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.copyFiles), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Delete...", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.deleteFiles), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Move...", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.moveFiles), - keyEquivalent: "" - ) - - theMenu.addItem(NSMenuItem.separator()) - - theMenu.addItem( - withTitle: NSLocalizedString("Exclude Items", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.excludeByName), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Exclude by Ext", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.excludeByExt), - keyEquivalent: "" - ) - - theMenu.addItem(NSMenuItem.separator()) - - theMenu.addItem( - withTitle: NSLocalizedString("Copy Paths", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.copyFullPaths), - keyEquivalent: "" - ) - let copyFileNamesItem = theMenu.addItem( - withTitle: NSLocalizedString("Copy File Names", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.copyFileNames), - keyEquivalent: "" - ) - copyFileNamesItem.keyEquivalentModifierMask = .option - copyFileNamesItem.isAlternate = true - - theMenu.addItem( - withTitle: NSLocalizedString("Show in Finder", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.showInFinder), - keyEquivalent: "" - ) - theMenu.addItem( - withTitle: NSLocalizedString("Open With", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.popupOpenWithApp), - keyEquivalent: "" - ) - let quickLookItem = theMenu.addItem( - withTitle: NSLocalizedString("Quick Look", comment: ""), - action: #selector(FoldersOutlineViewContextMenu.togglePreviewPanel), - keyEquivalent: "" - ) - quickLookItem.image = NSImage(named: NSImage.quickLookTemplateName) - - return theMenu - } - - override func menu(for event: NSEvent) -> NSMenu? { - let wherePoint = convert(event.locationInWindow, from: nil) - let row = row(at: wherePoint) - - // if mouse isn't outside any row simply doesn't show menu - if row < 0 { - return nil - } - - // highlight the view containing the menu - superview?.window?.makeFirstResponder(superview) - - let indexes = selectedRowIndexes - if !indexes.contains(row) { - selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false) - } - let theMenu = Self.defaultMenu - - if let theMenu, - let delegate = delegate as? TableViewContextMenuDelegate { - var lastVisibleItem: NSMenuItem? - var hasVisibleItemBeforeSeparator = false - var hasVisibleItems = false - - for item in theMenu.items { - var hide = false - - if item.isSeparatorItem { - item.isHidden = !hasVisibleItemBeforeSeparator - hasVisibleItemBeforeSeparator = false - lastVisibleItem = item - } else { - let isValid = delegate.tableView(self, menuItem: item, hideMenuItem: &hide) - if isValid { - item.isHidden = false - item.isEnabled = true - } else { - item.isHidden = hide - item.isEnabled = false - } - if !item.isHidden { - lastVisibleItem = item - hasVisibleItemBeforeSeparator = true - hasVisibleItems = true - } - } - } - - // hide last item if it's a separator - if let lastVisibleItem, - lastVisibleItem.isSeparatorItem { - lastVisibleItem.isHidden = true - } - if !hasVisibleItems { - theMenu.addItem( - withTitle: NSLocalizedString("No actions for current selection", comment: ""), - action: nil, - keyEquivalent: "" - ) - .isEnabled = false - } - } - - return theMenu - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+VisibleItem.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+VisibleItem.swift deleted file mode 100644 index c1d7445..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView+VisibleItem.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// FoldersOutlineView+VisibleItem.swift -// VisualDiffer -// -// Created by davide ficano on 13/02/21. -// Copyright (c) 2021 visualdiffer.com -// - -@objc extension FoldersOutlineView { - func getSelectedVisibleItems(_ includesSelected: Bool) -> [VisibleItem] { - var arr = [VisibleItem]() - - // At index 0 there is the last selected row - if selectedRow >= 0 { - if let vi = item(atRow: selectedRow) as? VisibleItem { - arr.append(vi) - } - } - - if includesSelected { - for row in selectedRowIndexes where row != selectedRow { - if let vi = item(atRow: row) as? VisibleItem { - arr.append(vi) - } - } - } - - return arr - } - - @discardableResult - func select( - visibleItems items: [VisibleItem], - scrollToFirst: Bool = false, - center: Bool = false, - selectLinked: Bool = false - ) -> Bool { - var indexes = IndexSet() - - for vi in items { - let row = row(forItem: vi) - if row >= 0 { - indexes.insert(row) - } - } - if scrollToFirst, let row = indexes.first { - scrollTo(row: row, center: center) - } - selectRowIndexes(indexes, byExtendingSelection: false) - if selectLinked { - linkedView?.selectRowIndexes(indexes, byExtendingSelection: false) - } - return !indexes.isEmpty - } - - func restoreSelectionAndFocusPosition(_ selectedVisibleItems: [VisibleItem]) { - if selectedVisibleItems.isEmpty { - return - } - var indexes = IndexSet() - let focusVI = selectedVisibleItems[0] - - // start from 1 to skip focus item - for vi in selectedVisibleItems.dropFirst() { - let row = row(forItem: vi) - if row >= 0 { - indexes.insert(row) - } - } - - let focusItem = focusVI.item - var focusRow = row(forItem: focusVI) - - if focusRow < 0 { - let count = numberOfRows - for row in 0 ..< count { - guard let vi = item(atRow: row) as? VisibleItem else { - continue - } - let res = URL.compare(path: vi.item.toUrl(), with: focusItem.toUrl()) - - if res == .orderedSame || res == .orderedDescending { - focusRow = row - if res == .orderedDescending, row > 0 { - focusRow -= 1 - } - indexes.insert(focusRow) - break - } - } - } else { - indexes.insert(focusRow) - } - - scrollRowToVisible(focusRow) - selectRowIndexes(indexes, byExtendingSelection: false) - linkedView?.selectRowIndexes(indexes, byExtendingSelection: false) - } - - func expandParents(of child: VisibleItem) { - var parents = [VisibleItem]() - - var parent = child.item.parent - - while parent?.parent != nil { - if let vi = parent?.visibleItem { - parents.append(vi) - } - parent = parent?.parent - } - - for parent in parents.reversed() { - expandItem(parent) - } - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView.swift deleted file mode 100644 index 2c31444..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineView.swift +++ /dev/null @@ -1,277 +0,0 @@ -// -// FoldersOutlineView.swift -// VisualDiffer -// -// Created by davide ficano on 18/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -import Quartz - -@MainActor protocol FoldersOutlineViewDelegate: NSOutlineViewDelegate { - func foldersOutlineView(_ view: FoldersOutlineView, doubleClickFileObject clickedRow: Int) - func selectionChanged(in view: FoldersOutlineView) - func setLastUsedViewResponder(_ view: FoldersOutlineView) -} - -public class FoldersOutlineView: NSOutlineView, @preconcurrency DisplayPositionable, ViewLinkable { - private var _selectionInfo: FolderSelectionInfo? - - var selectionInfo: FolderSelectionInfo { - if _selectionInfo == nil { - _selectionInfo = FolderSelectionInfo(view: self) - } - // swiftlint:disable:next force_unwrapping - return _selectionInfo! - } - - private var lockExpand = false - - var linkedView: FoldersOutlineView? - var side: DisplaySide = .left - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - autoresizesOutlineColumn = false - indentationPerLevel = 16 - indentationMarkerFollowsCell = true - autosaveExpandedItems = false - - floatsGroupRows = true - allowsColumnReordering = true - allowsColumnResizing = true - - focusRingType = .none - allowsExpansionToolTips = true - columnAutoresizingStyle = .firstColumnOnlyAutoresizingStyle - autosaveTableColumns = false - rowSizeStyle = .custom - autoresizingMask = [.width, .height] - intercellSpacing = NSSize(width: 3, height: 2) - allowsMultipleSelection = true - - // Needed by drop - registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL]) - setDraggingSourceOperationMask(.every, forLocal: true) - setDraggingSourceOperationMask(.every, forLocal: false) - - doubleAction = #selector(handleDoubleClick) - - NotificationCenter.default.addObserver( - self, - selector: #selector(tableSelectionChanged), - name: NSOutlineView.selectionDidChangeNotification, - object: self - ) - } - - @objc func tableSelectionChanged(_ notification: Notification) { - _selectionInfo = nil - - if let delegate = delegate as? FoldersOutlineViewDelegate, - let view = notification.object as? FoldersOutlineView { - delegate.selectionChanged(in: view) - } - } - - // MARK: - Keys and mouse handlers - - func handleEnterKeypressed(_ selectedRow: Int) { - var view = self - guard var itemRow = view.item(atRow: selectedRow) as? VisibleItem else { - return - } - var item = itemRow.item - - if !item.isValidFile { - guard let linkedView = view.linkedView, - let linkedItemRow = linkedView.item(atRow: selectedRow) as? VisibleItem else { - return - } - view = linkedView - itemRow = linkedItemRow - item = itemRow.item - } - if item.isFile { - (delegate as? FoldersOutlineViewDelegate)?.foldersOutlineView(view, doubleClickFileObject: selectedRow) - } else if item.isFolder { - if view.isItemExpanded(itemRow) { - view.collapseItem(itemRow) - } else { - view.expandItem(itemRow) - } - } - } - - @objc func handleDoubleClick(_ sender: AnyObject) { - if clickedRow != -1 { // make sure double click was not in table header - handleEnterKeypressed(sender.clickedRow) - } - } - - override public func keyDown(with event: NSEvent) { - guard let str = event.charactersIgnoringModifiers, - !str.isEmpty else { - super.keyDown(with: event) - return - } - let key = str[str.startIndex].asciiValue ?? 0 - - if key == NSCarriageReturnCharacter || key == NSEnterCharacter { - handleEnterKeypressed(selectedRow) - } else if event.keyCode == KeyCode.forwardDeleteCharacter { - // see http://www.evernote.com/shard/s106/sh/1ed6fa73-10bd-445e-a086-0c8fcdf1af43/0285d57405aa350a98fcb5c6ffc09067 - (delegate as? FoldersOutlineViewContextMenu)?.deleteFiles(nil) - } else if str == " " { - QLPreviewPanel.toggle() - } else { - super.keyDown(with: event) - } - } - - override public func draggingSession(_: NSDraggingSession, sourceOperationMaskFor _: NSDraggingContext) -> NSDragOperation { - // this is necessary otherwise dragging files to finder move them instead of copy - .copy - } - - // MARK: - Menu Actions - - func selectBy(type: CompareChangeType) { - var indexes = IndexSet() - - for row in 0 ..< numberOfRows { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - if item.isFile, item.isValidFile, item.type == type { - indexes.insert(row) - } - } - } - selectRowIndexes(indexes, byExtendingSelection: false) - } - - func selectAll(files: Bool, folders: Bool, byExtendingSelection: Bool) { - var indexes = IndexSet() - - for row in 0 ..< numberOfRows { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - if item.isValidFile, files && item.isFile || (folders && item.isFolder) { - indexes.insert(row) - } - } - } - selectRowIndexes(indexes, byExtendingSelection: byExtendingSelection) - } - - func invertSelection() { - var indexes = IndexSet() - - for row in 0 ..< numberOfRows { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - if item.isValidFile, !isRowSelected(row) { - indexes.insert(row) - } - } - } - selectRowIndexes(indexes, byExtendingSelection: false) - } - - func selectedItems() -> [CompareItem] { - let indexes = selectedRowIndexes - var arr = [CompareItem]() - arr.reserveCapacity(indexes.count) - - for row in indexes { - if let vi = item(atRow: row) as? VisibleItem { - let item = vi.item - if item.isValidFile { - arr.append(item) - } - } - } - - return CompareItem.findLeafPaths(arr) - } - - override public func becomeFirstResponder() -> Bool { - (delegate as? FoldersOutlineViewDelegate)?.setLastUsedViewResponder(self) - return super.becomeFirstResponder() - } - - // MARK: - Expand/Collapse notifiers - - override public func expandItem(_ item: Any?, expandChildren: Bool) { - guard let delegate = delegate as? OutlineViewItemDelegate else { - return - } - if lockExpand { - return - } - - super.expandItem(item, expandChildren: expandChildren) - - let vi = item as? VisibleItem - - lockExpand = true - linkedView?.expandItem(vi?.linkedItem, expandChildren: expandChildren) - lockExpand = false - - delegate.itemDidExpand(vi, outlineView: self) - } - - override public func collapseItem(_ item: Any?, collapseChildren: Bool) { - guard let delegate = delegate as? OutlineViewItemDelegate else { - return - } - if lockExpand { - return - } - super.collapseItem(item, collapseChildren: collapseChildren) - - let vi = item as? VisibleItem - - lockExpand = true - linkedView?.collapseItem(vi?.linkedItem, collapseChildren: collapseChildren) - lockExpand = false - - delegate.itemDidCollapse(vi, outlineView: self) - } - - override public func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner _: Any?) -> NSView? { - // the owner is not used - var view = super.makeView(withIdentifier: identifier, owner: nil) - - if view != nil { - return view - } - if identifier == .Folders.cellName { - view = CompareItemTableCellView(icon: true) - } else if identifier == .Folders.cellSize { - view = CompareItemTableCellView(icon: false) - } else if identifier == .Folders.cellModified { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "dd/MM/yy HH:mm" - dateFormatter.dateStyle = .short - dateFormatter.timeStyle = .short - - let cellView = CompareItemTableCellView(icon: false) - cellView.text.formatter = dateFormatter - view = cellView - } - view?.identifier = identifier - - return view - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineViewFindTextDelegate.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineViewFindTextDelegate.swift deleted file mode 100644 index 3853d45..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/FoldersOutlineViewFindTextDelegate.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// FoldersOutlineViewFindTextDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 23/11/20. -// Copyright (c) 2020 visualdiffer.com -// - -@MainActor class FoldersOutlineViewFindTextDelegate: @preconcurrency FindTextDelegate { - let view: FoldersOutlineView - private var fileNames: [VisibleItem] - - init(view: FoldersOutlineView) { - fileNames = [] - self.view = view - } - - func find(findText _: FindText, moveToMatchIndex index: Int) -> Bool { - let vi = fileNames[index] - view.expandParents(of: vi) - - if view.select(visibleItems: [vi], scrollToFirst: true, center: true, selectLinked: true) { - return true - } - fileNames.remove(at: index) - return false - } - - func find(findText _: FindText, searchPattern pattern1: String) -> Bool { - let pathPattern = URL(filePath: pattern1).standardizingPath - let globPattern = pathPattern.convertGlobMetaCharsToRegexpMetaChars() - let re = try? NSRegularExpression( - pattern: globPattern, - options: .caseInsensitive - ) - - guard let re, - let firstChild = view.dataSource?.outlineView?(view, child: 0, ofItem: nil) as? VisibleItem, - let rootVisibleItem = firstChild.item.parent?.visibleItem else { - return false - } - - // enable the search full path if the pattern contains a path separator - let searchFullPath = pathPattern.contains("/") - rootVisibleItem.findFileName(regex: re, searchFullPath: searchFullPath, items: &fileNames) - - return true - } - - func numberOfMatches(in _: FindText) -> Int { - fileNames.count - } - - func clearMatches(in _: FindText) { - fileNames.removeAll() - } -} diff --git a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/IconUtils+CompareItemIconUtils.swift b/Sources/Features/FoldersCompare/Components/FoldersOutlineView/IconUtils+CompareItemIconUtils.swift deleted file mode 100644 index 40c7e78..0000000 --- a/Sources/Features/FoldersCompare/Components/FoldersOutlineView/IconUtils+CompareItemIconUtils.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// IconUtils+CompareItemIconUtils.swift -// VisualDiffer -// -// Created by davide ficano on 08/11/14. -// Copyright (c) 2014 visualdiffer.com -// - -@MainActor extension IconUtils { - func icon( - for item: CompareItem, - size: CGFloat, - isExpanded: Bool, - hideEmptyFolders: Bool - ) -> NSImage? { - var icon: NSImage? - - if item.isValidFile, - let url = item.toUrl() { - if item.isLocked { - if item.isFolder { - let name = ColoredFoldersManager.shared.iconName( - item, - isExpanded: isExpanded, - hideEmptyFolders: hideEmptyFolders - ) - let url = URL(filePath: name) - icon = self.icon(forLockedFile: url, size: size) - } else { - icon = self.icon(forLockedFile: url, size: size) - } - } else if item.isSymbolicLink { - if item.isFolder { - let name = ColoredFoldersManager.shared.iconName( - item, - isExpanded: isExpanded, - hideEmptyFolders: hideEmptyFolders - ) - let url = URL(filePath: name) - icon = self.icon(forSymbolicLink: url, size: size) - } else { - icon = self.icon(forSymbolicLink: url, size: size) - } - } else { - if item.isFolder { - icon = ColoredFoldersManager.shared.icon( - forFolder: item, - size: size, - isExpanded: isExpanded, - hideEmptyFolders: hideEmptyFolders - ) - } else { - // get the icon from path because for some files (eg resource forks) - // the file type should be irrelevant - // This means caching every single path - icon = self.icon(forFile: url, size: size) - } - } - } - return icon - } -} diff --git a/Sources/Features/FoldersCompare/Controller/Comparator/MainThreadComparatorDelegateBridge.swift b/Sources/Features/FoldersCompare/Controller/Comparator/MainThreadComparatorDelegateBridge.swift deleted file mode 100644 index c43ec72..0000000 --- a/Sources/Features/FoldersCompare/Controller/Comparator/MainThreadComparatorDelegateBridge.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// MainThreadComparatorDelegateBridge.swift -// VisualDiffer -// -// Created by davide ficano on 24/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -/** - * This bridge is used to call on main thread all ItemComparatorDelegate methods - */ -class MainThreadComparatorDelegateBridge: ItemComparatorDelegate { - private weak var controller: FoldersWindowController? - - init(_ controller: FoldersWindowController) { - self.controller = controller - } - - func isRunning(_: ItemComparator) -> Bool { - guard let controller else { - return false - } - - return DispatchQueue.main.sync { - controller.running - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Copy/CopyFileOperationExecutor.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Copy/CopyFileOperationExecutor.swift deleted file mode 100644 index ad9361f..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Copy/CopyFileOperationExecutor.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// CopyFileOperationExecutor.swift -// VisualDiffer -// -// Created by davide ficano on 14/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class CopyFileOperationExecutor: FileOperationExecutor, @unchecked Sendable { - var title = NSLocalizedString("Copy", comment: "") - var summary = NSLocalizedString("Copy selected files and folders", comment: "") - var image: NSImage? - var progressLabel = NSLocalizedString("Copying", comment: "") - var prefName: CommonPrefs.Name? = .confirmCopy - - // periphery:ignore - private let side: DisplaySide - - private let items: [CompareItem] - - var operationOnSingleItem: Bool { - items.count == 1 && items[0].isFile - } - - private let srcBaseDir: String - private let destBaseDir: String - - init( - srcBaseDir: String, - destBaseDir: String, - items: [CompareItem], - side: DisplaySide - ) { - self.srcBaseDir = srcBaseDir - self.destBaseDir = destBaseDir - self.side = side - self.items = items - - image = NSImage(named: side == .left ? VDImageNameCopyRight : VDImageNameCopyLeft) - } - - func execute(_ manager: FileOperationManagerAction, payload _: Sendable?) { - for item in items { - manager.copy( - item, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir - ) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Delete/DeleteFileOperationExecutor.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Delete/DeleteFileOperationExecutor.swift deleted file mode 100644 index f8821ac..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Delete/DeleteFileOperationExecutor.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// DeleteFileOperationExecutor.swift -// VisualDiffer -// -// Created by davide ficano on 14/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DeleteFileOperationExecutor: FileOperationExecutor, @unchecked Sendable { - var title = NSLocalizedString("Delete", comment: "") - var summary = NSLocalizedString("Delete selected files and folders", comment: "") - var image: NSImage? - var progressLabel = NSLocalizedString("Deleting", comment: "") - var prefName: CommonPrefs.Name? = .confirmDelete - - private let items: [CompareItem] - - // delete operation doesn't use it - var operationOnSingleItem = false - - private let srcBaseDir: String - - init( - srcBaseDir: String, - items: [CompareItem] - ) { - self.srcBaseDir = srcBaseDir - self.items = items - - image = NSImage(named: VDImageNameDelete) - } - - func execute(_ manager: FileOperationManagerAction, payload _: Sendable?) { - for item in items { - manager.delete( - item, - srcBaseDir: srcBaseDir - ) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileOperationExecutor.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/FileOperationExecutor.swift deleted file mode 100644 index 84b7c98..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileOperationExecutor.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// FileOperationExecutor.swift -// VisualDiffer -// -// Created by davide ficano on 14/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -public protocol FileOperationExecutor: Sendable { - associatedtype TPayload: Sendable - - var title: String { get } - var summary: String { get } - var image: NSImage? { get } - var progressLabel: String { get } - var prefName: CommonPrefs.Name? { get } - - var operationOnSingleItem: Bool { get } - - func execute(_ manager: FileOperationManagerAction, payload: TPayload?) -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSummaryView.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSummaryView.swift deleted file mode 100644 index 7a58bcf..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSummaryView.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// FileSummaryView.swift -// VisualDiffer -// -// Created by davide ficano on 07/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -@objc class FileSummaryView: NSStackView { - @objc let folderTotalText: NSTextField - @objc let fileTotalText: NSTextField - @objc let sizeTotalText: NSTextField - @objc let operationDescription: NSTextField - @objc let filteredFilesInSelectionText: NSTextField - @objc let checkboxFilteredFiles: NSButton - - init() { - operationDescription = NSTextField.labelWithTitle("") - operationDescription.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) - - folderTotalText = NSTextField.hintWithTitle("") - fileTotalText = NSTextField.hintWithTitle("") - sizeTotalText = NSTextField.hintWithTitle("") - - checkboxFilteredFiles = NSButton( - checkboxWithTitle: NSLocalizedString("Include Filtered Files", comment: ""), - target: nil, - action: nil - ) - checkboxFilteredFiles.translatesAutoresizingMaskIntoConstraints = false - - filteredFilesInSelectionText = NSTextField.hintWithTitle( - NSLocalizedString("Selection already contains Filtered Files", comment: "") - ) - filteredFilesInSelectionText.controlSize = .mini - filteredFilesInSelectionText.font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .mini)) - - super.init(frame: .zero) - - orientation = .vertical - alignment = .leading - spacing = 10 - translatesAutoresizingMaskIntoConstraints = false - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addArrangedSubview(operationDescription) - addArrangedSubview(folderTotalText) - addArrangedSubview(fileTotalText) - addArrangedSubview(sizeTotalText) - addArrangedSubview(checkboxFilteredFiles) - addArrangedSubview(filteredFilesInSelectionText) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - filteredFilesInSelectionText.leadingAnchor.constraint(equalTo: checkboxFilteredFiles.leadingAnchor, constant: 20), - filteredFilesInSelectionText.topAnchor.constraint(equalTo: checkboxFilteredFiles.bottomAnchor, constant: 4), - ]) - } - - @objc func setupCheckboxFilteredFiles( - _ includesFilteredFiles: Bool, - hasFilteredInSelection: Bool - ) { - if includesFilteredFiles { - checkboxFilteredFiles.isHidden = false - checkboxFilteredFiles.state = .on - checkboxFilteredFiles.isEnabled = !hasFilteredInSelection - } else { - checkboxFilteredFiles.isHidden = true - checkboxFilteredFiles.state = .off - checkboxFilteredFiles.isEnabled = false - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSystemController.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSystemController.swift deleted file mode 100644 index 8062b83..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSystemController.swift +++ /dev/null @@ -1,355 +0,0 @@ -// -// FileSystemController.swift -// VisualDiffer -// -// Created by davide ficano on 18/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -public protocol FileSystemControllerDelegate: AnyObject { - func fileSystem( - _ fileSystem: FileSystemController, - restoreSelection selectedVisibleItems: [VisibleItem], - errors: [any Error]? - ) -} - -public class FileSystemController: NSWindowController { - private var includesFilteredFiles = false - private var hasFilteredInSelection = false - - var mainStackView: NSStackView - - private(set) var operationSummary: OperationSummaryView - private(set) var fileSummary: FileSummaryView - - private(set) var filtersWarningText: NSTextField - - private let checkboxSuppressDialog: NSButton - - private(set) var standardButtons: StandardButtons - - var fileCount = CompareSummary() - var fileFilteredCount = CompareSummary() - - var totalFiles = 0 - var totalFolders = 0 - var totalSize = Int64(0) - - let executor: TExecutor - - var selectedVisibleItems: [VisibleItem] - var callerWindow: NSWindow? - var delegate: FileSystemControllerDelegate? - - var progressIndicatorController: ProgressIndicatorController? - var fileOperationManager: FileOperationManager - - init( - executor: TExecutor, - fileOperationManager: FileOperationManager, - view: FoldersOutlineView, - progressIndicatorController: ProgressIndicatorController, - filteredFileVisible: Bool - ) { - filtersWarningText = NSTextField.labelWithTitle(NSLocalizedString( - "Filtered files will be skipped.\nTo include them select 'Show Filtered Files' from View menu", comment: "" - )) - - standardButtons = StandardButtons( - primaryTitle: NSLocalizedString("OK", comment: ""), - secondaryTitle: NSLocalizedString("Cancel", comment: ""), - target: nil, - action: nil - ) - - checkboxSuppressDialog = NSButton( - checkboxWithTitle: NSLocalizedString("Do not show this message again", comment: ""), - target: nil, - action: nil - ) - operationSummary = OperationSummaryView() - fileSummary = FileSummaryView() - mainStackView = NSStackView(frame: .zero) - - self.executor = executor - self.fileOperationManager = fileOperationManager - self.progressIndicatorController = progressIndicatorController - self.progressIndicatorController?.operationDescription = executor.progressLabel - - selectedVisibleItems = view.getSelectedVisibleItems(false) - - super.init(window: nil) - - hasFilteredInSelection = computeCountsForItems(view.selectedItems()) - - var showFiltered = filteredFileVisible - if hasFilteredInSelection { - showFiltered = true - } - includesFilteredFiles = showFiltered || CommonPrefs.shared.bool(forKey: .confirmIncludeFilteredItems) - window = createWindow() - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func computeCountsForItems(_ items: [CompareItem]) -> Bool { - var foundFilteredRoot = false - - for item in items { - item.computeCounts(&fileCount, filteredSummary: &fileFilteredCount) - if item.isFiltered { - foundFilteredRoot = true - } - } - - return foundFilteredRoot - } - - func createWindow() -> NSWindow { - let styleMask: NSWindow.StyleMask = [.titled, .closable] - - let view = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - view.hasShadow = true - view.isRestorable = true - view.minSize = NSSize(width: 480, height: 300) - view.maxSize = NSSize(width: 480, height: 300) - - return view - } - - // MARK: - Setup Views - - func setupViews() { - setupMainStackView() - setupStandardButtons() - setupOperationSummaryView() - setupCheckboxSuppressDialog() - setupFiltersWarningText() - - if let contentView = window?.contentView { - contentView.addSubview(mainStackView) - contentView.addSubview(standardButtons) - } - - setupConstraints() - - setupTitle() - fileSummary.setupCheckboxFilteredFiles(includesFilteredFiles, hasFilteredInSelection: hasFilteredInSelection) - updateCount(nil) - } - - func setupMainStackView() { - mainStackView.addArrangedSubviews([ - operationSummary, - filtersWarningText, - checkboxSuppressDialog, - ]) - - mainStackView.orientation = .vertical - mainStackView.alignment = .leading - mainStackView.spacing = 10 - mainStackView.translatesAutoresizingMaskIntoConstraints = false - } - - func setupStandardButtons() { - standardButtons.primaryButton.target = self - standardButtons.primaryButton.action = #selector(closeSheet) - standardButtons.secondaryButton.target = self - standardButtons.secondaryButton.action = #selector(closeSheet) - } - - func setupOperationSummaryView() { - fileSummary.checkboxFilteredFiles.target = self - fileSummary.checkboxFilteredFiles.action = #selector(updateCount) - fileSummary.filteredFilesInSelectionText.isHidden = !hasFilteredInSelection - - operationSummary.addArrangedSubview(fileSummary) - } - - func setupCheckboxSuppressDialog() { - checkboxSuppressDialog.target = self - checkboxSuppressDialog.action = #selector(updateSuppressWarning) - checkboxSuppressDialog.translatesAutoresizingMaskIntoConstraints = false - // the value is negated - checkboxSuppressDialog.state = if let prefName = executor.prefName { - CommonPrefs.shared.bool(forKey: prefName) ? .off : .on - } else { - .off - } - } - - func setupFiltersWarningText() { - filtersWarningText.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) - filtersWarningText.isHidden = includesFilteredFiles - } - - func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - NSLayoutConstraint.activate([ - mainStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - mainStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - mainStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - // MARK: - Sheet methods - - func beginSheetModal(for callerWindow: NSWindow) { - guard let window else { - return - } - if itemsCount() == 0 { - return - } - self.callerWindow = callerWindow - - if !willShowConfirmDialog() { - execute() - return - } - - callerWindow.beginSheet(window) { - self.sheetEnd($0) - } - } - - @objc func closeSheet(_ sender: AnyObject) { - guard let sender = sender as? NSButton, - let window, - let sheetParent = window.sheetParent else { - return - } - let tag = NSApplication.ModalResponse(sender.tag) - sheetParent.endSheet(window, returnCode: tag) - } - - @objc func updateCount(_: AnyObject?) { - includesFilteredFiles = fileSummary.checkboxFilteredFiles.state == .on - recalcTotals(includesFilteredFiles) - - fileSummary.fileTotalText.stringValue = String.localizedStringWithFormat(NSLocalizedString("%ld files", comment: ""), totalFiles) - fileSummary.folderTotalText.stringValue = String.localizedStringWithFormat(NSLocalizedString("%ld folders", comment: ""), totalFolders) - fileSummary.sizeTotalText.stringValue = FileSizeFormatter.default.string(from: NSNumber(value: totalSize)) ?? "0" - } - - @objc func updateSuppressWarning(_: AnyObject) { - guard let prefName = executor.prefName else { - return - } - let isSuppressed = checkboxSuppressDialog.state == .off - - CommonPrefs.shared.set(isSuppressed, forKey: prefName) - } - - private func recalcTotals(_ includesFiltered: Bool) { - totalFiles = fileCount.olderFiles + fileCount.changedFiles + fileCount.orphanFiles + fileCount.matchedFiles - totalFolders = fileCount.folders - totalSize = fileCount.subfoldersSize - - if includesFiltered { - totalFiles += fileFilteredCount.olderFiles - + fileFilteredCount.changedFiles - + fileFilteredCount.orphanFiles - + fileFilteredCount.matchedFiles - totalFolders += fileFilteredCount.folders - totalSize += fileFilteredCount.subfoldersSize - } - } - - func updateUIAfterExecute() { - progressIndicatorController?.endSheetAfterCompletion() - delegate?.fileSystem( - self, - restoreSelection: selectedVisibleItems, - errors: progressIndicatorController?.errors - ) - } - - func prepareExecute() { - guard let callerWindow else { - fatalError("Caller window is nil") - } - - recalcTotals(includesFilteredFiles) - - progressIndicatorController?.beginSheetModal( - for: callerWindow, - processingItemsCount: totalFiles, - totalSize: totalSize, - singleItem: executor.operationOnSingleItem - ) - - fileOperationManager.includesFiltered = includesFilteredFiles - - progressIndicatorController?.startRun() - } - - func execute() { - prepareExecute() - - let capturedManager = fileOperationManager - DispatchQueue.global(qos: .userInitiated).async { - self.executor.execute(capturedManager, payload: nil) - - DispatchQueue.main.async { - self.updateUIAfterExecute() - } - } - } - - func sheetEnd(_ returnCode: NSApplication.ModalResponse) { - if returnCode == .cancel { - return - } - execute() - } - - // MARK: - Confirmation handling methods - - func willShowConfirmDialog() -> Bool { - let isOptionPressed = NSApp.currentEvent?.modifierFlags.contains(.option) ?? false - if isOptionPressed { - return true - } - - if let prefName = executor.prefName { - return CommonPrefs.shared.bool(forKey: prefName) - } - return true - } - - // MARK: - Helper Methods - - func setupTitle() { - fileSummary.operationDescription.stringValue = executor.summary - standardButtons.primaryButton.title = executor.title - operationSummary.icon.image = executor.image - } - - func itemsCount() -> Int { - var count = fileCount.olderFiles + fileCount.changedFiles + fileCount.orphanFiles + fileCount.matchedFiles - count += fileCount.folders - count += fileFilteredCount.olderFiles - + fileFilteredCount.changedFiles - + fileFilteredCount.orphanFiles - + fileFilteredCount.matchedFiles - count += fileFilteredCount.folders - - return count - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSystemTestHelper.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSystemTestHelper.swift deleted file mode 100644 index 9366bf1..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/FileSystemTestHelper.swift +++ /dev/null @@ -1,450 +0,0 @@ -// -// FileSystemTestHelper.swift -// VisualDiffer -// -// Created by davide ficano on 25/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -// swiftlint:disable identifier_name force_unwrapping force_try file_length -#if DEBUG - - private let fakeFile = "12345678901234567890" - - private let headerPattern = - """ - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: %@, - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: %@, - isRightCaseSensitive: %@ - ) - let filterConfig = FilterConfig( - showFilteredFiles: %@, - hideEmptyFolders: %@, - followSymLinks: %@, - skipPackages: %@, - traverseFilteredFolders: %@, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: %@ - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - """ - - private let readFolderPattern = - """ - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - """ - - class FileSystemTestHelper { - var strFolders = "// create folders\n" - var strFiles = "// create files\n" - var strAssert = "" - var strAssertVisibleItems = "// VisibleItems\n" - var leftPos = 0 - var rightPos = 0 - var childNum = 1 - - var header = "" - - private static let comparatorStrings = [ - ComparatorOptions.timestamp.rawValue: ".timestamp", - ComparatorOptions.size.rawValue: ".size", - ComparatorOptions.content.rawValue: ".content", - ComparatorOptions.contentTimestamp.rawValue: ".contentTimestamp", - ComparatorOptions.asText.rawValue: ".asText", - ComparatorOptions.finderLabel.rawValue: ".finderLabel", - ComparatorOptions.finderTags.rawValue: ".finderTags", - ComparatorOptions.filename.rawValue: ".filename", - ComparatorOptions.alignFileSystemCase.rawValue: ".alignFileSystemCase", - ComparatorOptions.alignMatchCase.rawValue: ".alignMatchCase", - ComparatorOptions.alignIgnoreCase.rawValue: ".alignIgnoreCase", - ] - private static let displayFiltersStrings = [ - DisplayOptions.onlyMatches.rawValue: ".onlyMatches", - DisplayOptions.onlyLeftSideNewer.rawValue: ".onlyLeftSideNewer", - DisplayOptions.onlyLeftSideOrphans.rawValue: ".onlyLeftSideOrphans", - DisplayOptions.onlyRightSideNewer.rawValue: ".onlyRightSideNewer", - DisplayOptions.onlyRightSideOrphans.rawValue: ".onlyRightSideOrphans", - DisplayOptions.mismatchesButNoOrphans.rawValue: ".mismatchesButNoOrphans", - DisplayOptions.leftNewerAndLeftOrphans.rawValue: ".leftNewerAndLeftOrphans", - DisplayOptions.rightNewerAndRightOrphans.rawValue: ".rightNewerAndRightOrphans", - DisplayOptions.onlyMismatches.rawValue: ".onlyMismatches", - DisplayOptions.noOrphan.rawValue: ".noOrphan", - DisplayOptions.onlyOrphans.rawValue: ".onlyOrphans", - DisplayOptions.showAll.rawValue: ".showAll", - DisplayOptions.dontFollowSymlinks.rawValue: ".dontFollowSymlinks", - ] - - private var comparatorFlags: ComparatorOptions = [] - - init(sessionDiff: SessionDiff) { - comparatorFlags = sessionDiff.comparatorOptions - - leftPos = URL(filePath: sessionDiff.leftPath!).deletingLastPathComponent().osPath.count + 1 - rightPos = URL(filePath: sessionDiff.rightPath!).deletingLastPathComponent().osPath.count + 1 - header = Self.formatHeader(sessionDiff) - } - - private static func formatHeader(_ sessionDiff: SessionDiff) -> String { - let leftURL = URL(filePath: sessionDiff.leftPath!) - let rightURL = URL(filePath: sessionDiff.rightPath!) - - return String( - format: headerPattern, - Self.stringify(flag: sessionDiff.comparatorOptions.rawValue, stringNumberDictionary: Self.comparatorStrings), - Self.bool2String(try! leftURL.volumeSupportsCaseSensitive()), - Self.bool2String(try! rightURL.volumeSupportsCaseSensitive()), - Self.bool2String(false), - Self.bool2String(true), - Self.bool2String(sessionDiff.followSymLinks), - Self.bool2String(sessionDiff.skipPackages), - Self.bool2String(sessionDiff.traverseFilteredFolders), - Self.stringify(flag: sessionDiff.displayOptions.rawValue, stringNumberDictionary: Self.displayFiltersStrings) - ) - } - - private static func bool2String(_ value: Bool) -> String { value ? "true" : "false" } - - private static func stringify( - flag: Int, - stringNumberDictionary: [Int: String] - ) -> String { - var orFlags = [String]() - - if let str = stringNumberDictionary[flag] { - orFlags.append(str) - } else { - var currentFlag = 1 - var flag = flag - - while flag != 0 { - if (currentFlag & flag) != 0, let value = stringNumberDictionary[currentFlag] { - orFlags.append(value) - flag &= ~currentFlag - } - currentFlag <<= 1 - } - } - let stringFlags = orFlags.joined(separator: ", ") - - if orFlags.count > 1 { - return "[\(stringFlags)]" - } - return stringFlags - } - - @MainActor static func createTestCode( - _ view: FoldersOutlineView, - sessionDiff: SessionDiff - ) { - guard let vi = view.dataSource?.outlineView?(view, child: 0, ofItem: nil) as? VisibleItem, - let fs = vi.item.parent else { - return - } - let testHelper = FileSystemTestHelper(sessionDiff: sessionDiff) - testHelper.generateTest(fs) - testHelper.generateTest(fs.visibleItem!) - testHelper.copyTestToClipboard() - } - - // MARK: - CompareItem generators - - private func generateTest(_ root: CompareItem) { - childNum = 1 - generateTest(root, parentVarName: "rootL", index: 0) - } - - private func generateTest( - _ root: CompareItem, - parentVarName: String, - index: Int - ) { - createFolders(root) - createAssert(root, parentVarName: parentVarName, outString: &strAssert, index: index) - - let parentName = String(format: "child%ld", childNum) - for (index, fs) in root.children.enumerated() { - childNum += 1 - - if fs.isFolder { - generateTest(fs, parentVarName: parentName, index: index) - } else { - createFolders(fs) - createAssert(fs, parentVarName: parentName, outString: &strAssert, index: index) - } - } - } - - // MARK: - VisibleItem generators - - private func generateTest(_ root: VisibleItem) { - childNum = 1 - - generateTest(root, parentVarName: "vi", index: 0) - } - - private func generateTest( - _ root: VisibleItem, - parentVarName: String, - index: Int - ) { - let childVarName = String(format: "childVI%ld", childNum) - - // root element for simplicity is assigned to child and printed out - if parentVarName == "vi" { - strAssertVisibleItems.append(String( - format: "let %@ = vi // %@ <--> %@\n", - childVarName, - root.item.fileName!, - root.item.linkedItem!.fileName! - )) - } else { - strAssertVisibleItems.append(String( - format: "let %@ = %@.children[%ld] // %@ <--> %@\n", - childVarName, - parentVarName, - index, - root.item.parent!.fileName!, - root.item.linkedItem!.parent!.fileName! - )) - } - strAssertVisibleItems.append(String(format: "assertArrayCount(%@.children, %ld)\n", childVarName, root.children.count)) - createAssert(root, parentVarName: childVarName, outString: &strAssertVisibleItems, index: index) - - for (index, vi) in root.children.enumerated() { - childNum += 1 - - generateTest(vi, parentVarName: childVarName, index: index) - } - } - - // MARK: - Assert generator methods - - private func createAssert( - _ fsOrVi: AnyObject, - parentVarName: String, - outString: inout String, - index: Int - ) { - var fs: CompareItem - var stringFormat = "" - - if let vi = fsOrVi as? VisibleItem { - fs = vi.item - stringFormat = "let child%ld = %@.item // %@ <-> %@\n" - outString.append(String( - format: stringFormat, - childNum, - parentVarName, - fs.parent?.fileName ?? "nil", - fs.parent?.linkedItem?.fileName ?? "nil" - )) - } else if let fsOrVi = fsOrVi as? CompareItem { - fs = fsOrVi - if parentVarName == "rootL" { - stringFormat = "let child%ld = %@ // %@ <-> %@\n" - outString.append(String( - format: stringFormat, - childNum, - parentVarName, - fs.fileName ?? "nil", - fs.linkedItem?.fileName ?? "nil" - )) - } else { - stringFormat = "let child%ld = %@.children[%ld] // %@ <-> %@\n" - outString.append(String( - format: stringFormat, - childNum, - parentVarName, - index, - fs.parent?.fileName ?? "nil", - fs.parent?.linkedItem?.fileName ?? "nil" - )) - } - } else { - return - } - - createAssert(fs, outString: &outString, linked: false) - createAssert(fs.linkedItem!, outString: &outString, linked: true) - - outString.append("\n") - } - - private func createAssert( - _ fs: CompareItem, - outString: inout String, - linked: Bool - ) { - let fileName = if let fileName = fs.fileName { - String(format: "\"%@\"", fileName) - } else { - "nil" - } - - let linkedString = linked ? ".linkedItem" : "" - - outString.append(String( - format: "assertItem(child%ld%@, %ld, %ld, %ld, %ld, %ld, %@, .%@, %lld)\n", - childNum, - linkedString, - fs.olderFiles, - fs.changedFiles, - fs.orphanFiles, - fs.matchedFiles, - fs.children.count, - fileName, - fs.type.description, - fs.isFolder ? fs.subfoldersSize : fs.fileSize - )) - if fs.isValidFile, fs.isFolder { - let linkedStringForceUnwrapping = linked ? ".linkedItem!" : "" - outString.append(String( - format: "#expect(child%ld%@.orphanFolders == %ld, \"OrphanFolder: Expected count %ld found \\(child%ld%@.orphanFolders)\")\n", - childNum, - linkedStringForceUnwrapping, - fs.orphanFolders, - fs.orphanFolders, - childNum, - linkedStringForceUnwrapping - )) - } - - if comparatorFlags.contains(.finderTags) { - outString.append(String(format: "assertFolderTags(child%ld%@, %@, %@)\n", childNum, linkedString, Self.bool2String(fs.summary.hasMetadataTags), fileName)) - outString.append(String(format: "assertMismatchingTags(child%ld%@, %ld, %@)\n", childNum, linkedString, fs.mismatchingTags, fileName)) - } - if comparatorFlags.contains(.finderLabel) { - outString.append(String(format: "assertFolderLabels(child%ld%@, %@, %@)\n", childNum, linkedString, Self.bool2String(fs.summary.hasMetadataLabels), fileName)) - outString.append(String(format: "assertMismatchingLabels(child%ld%@, %ld, %@)\n", childNum, linkedString, fs.mismatchingLabels, fileName)) - addAssertLabelsOnDisk(&outString, fs: fs, linked: linked, index: childNum) - } - } - - private func addAssertLabelsOnDisk( - _ outString: inout String, - fs: CompareItem, - linked: Bool, - index: Int - ) { - guard fs.isValidFile else { - return - } - let path = fs.path! - if let labelNumber = URL(filePath: path).labelNumber() { - let linkedString = linked ? ".linkedItem" : "" - let startIndex = path.index(path.startIndex, offsetBy: linked ? rightPos : leftPos) - let subPath = String(path[startIndex ..< path.endIndex]) - outString.append(String( - format: "assertResourceFileLabels(child%ld%@, %ld, appendFolder(\"%@\"))\n", - index, - linkedString, - labelNumber, - subPath - )) - } - } - - // MARK: - Folder, size, metadata - - private func createFolders(_ fs: CompareItem) { - appendCreateFoldersExpressions(fs, pathIndex: leftPos) - appendCreateFoldersExpressions(fs.linkedItem!, pathIndex: rightPos) - } - - private func appendCreateFoldersExpressions( - _ fs: CompareItem, - pathIndex: Int - ) { - guard fs.isValidFile, - let path = fs.path else { - return - } - let startIndex = path.index(path.startIndex, offsetBy: pathIndex) - let subPath = String(path[startIndex ..< path.endIndex]) - if fs.isFolder { - strFolders.append(String(format: "try createFolder(\"%@\")\n", subPath)) - } else { - if fs.olderFiles > 0 { - strFiles.append(String(format: "try createFile(\"%@\", \"%@\")\n", subPath, fakeFileBySize(fs.fileSize, subPath))) - strFiles.append(String(format: "try setFileTimestamp(\"%@\", \"2001-03-24 10:45:32 +0600\")\n", subPath)) - } else { - strFiles.append(String(format: "try createFile(\"%@\", \"%@\")\n", subPath, fakeFileBySize(fs.fileSize, subPath))) - } - } - if comparatorFlags.contains(.finderTags) { - addTags(fs, subPath: subPath) - } - if comparatorFlags.contains(.finderLabel) { - addLabels(fs, subPath: subPath) - } - } - - private func fakeFileBySize(_ size: Int64, _ path: String) -> String { - assert(size < fakeFile.count, "File size (\(size)) is greater then fakeFile length (\(fakeFile.count)) for \(path)") - - return String(fakeFile[.. NSImageView { - let view = NSImageView() - - view.alignment = .left - view.imageScaling = .scaleNone - view.imageAlignment = .alignCenter - view.translatesAutoresizingMaskIntoConstraints = false - - view.widthAnchor.constraint(equalToConstant: 90).isActive = true - - return view - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/CompletionIndicator.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/CompletionIndicator.swift deleted file mode 100644 index bc95bd0..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/CompletionIndicator.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// CompletionIndicator.swift -// VisualDiffer -// -// Created by davide ficano on 18/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class CompletionIndicator: NSStackView { - lazy var progress: NSProgressIndicator = .bar() - lazy var text: NSTextField = createCompletedText() - - init() { - super.init(frame: .zero) - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addArrangedSubviews([ - progress, - text, - ]) - orientation = .vertical - spacing = 1 - alignment = .leading - translatesAutoresizingMaskIntoConstraints = false - } - - private func createCompletedText() -> NSTextField { - let view = NSTextField.hintWithTitle("") - view.lineBreakMode = .byTruncatingHead - - // Make sure it doesn't grow based on content - view.setContentHuggingPriority(.defaultLow, for: .horizontal) - view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - - return view - } - - func reset(maxValue: Double) { - progress.doubleValue = 0.0 - progress.minValue = 0.0 - progress.maxValue = maxValue - progress.displayIfNeeded() - - text.stringValue = "" - } - - func update( - completedBytes: Int64, - totalBytes: Int64, - throughput: Int64 - ) { - progress.doubleValue = Double(completedBytes) - - guard completedBytes > 0 else { - text.stringValue = "" - return - } - - let sizeFormatter = FileSizeFormatter.default - let completeStr = sizeFormatter.string(from: NSNumber(value: completedBytes)) ?? "N/A" - let totalStr = sizeFormatter.string(from: NSNumber(value: totalBytes)) ?? "N/A" - - text.stringValue = if throughput > 0 { - String( - format: NSLocalizedString("Copied %@ of %@ at %@/s", comment: ""), - completeStr, - totalStr, - sizeFormatter.string(from: NSNumber(value: throughput)) ?? "N/A" - ) - } else { - String( - format: NSLocalizedString("Copied %@ of %@", comment: ""), - completeStr, - totalStr - ) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ErrorsView.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ErrorsView.swift deleted file mode 100644 index 7a3ee3b..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ErrorsView.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// ErrorsView.swift -// VisualDiffer -// -// Created by davide ficano on 10/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ErrorsView: NSStackView { - private(set) var errors: [NSError] = [] - - private lazy var errorTextContainer: NSScrollView = createScrollView() - private lazy var errorsText: NSTextView = createErrorTextView() - - private lazy var title: NSTextField = .hintWithTitle(NSLocalizedString("Problems", comment: "")) - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - orientation = .vertical - alignment = .leading - spacing = 4 - translatesAutoresizingMaskIntoConstraints = false - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - collapse() - isHidden = true - - addArrangedSubview(title) - addArrangedSubview(errorTextContainer) - - // gives the text a default height - errorsText.heightAnchor.constraint(greaterThanOrEqualToConstant: 100).isActive = true - } - - @objc func updateErrors(_ errorMessage: String) { - // Show errors if it is the first time we are called - if errors.isEmpty { - isHidden = false - expand() - } - - let attributes = [ - NSAttributedString.Key.foregroundColor: errorsText.textColor ?? NSColor.white, - ] - - errorsText.append(text: errorMessage + "\n", attributes: attributes) - } - - func addError(_ error: NSError, forPath path: String) { - let errorMessage = error.format(withPath: path) - performSelector(onMainThread: #selector(updateErrors), with: errorMessage, waitUntilDone: true) - - errors.append(error) - title.stringValue = String(format: NSLocalizedString("Problems (%lu)", comment: ""), errors.count) - } - - func expand() { - errorTextContainer.isHidden = false - } - - func collapse() { - errorTextContainer.isHidden = true - } - - private func createErrorTextView() -> NSTextView { - let view = NSTextView(frame: .zero) - - view.isEditable = true - view.isSelectable = true - view.isRichText = true - view.autoresizingMask = [.width, .height] - - // This causes the text to appear a bit scrolled on the right, I can't find a valid solution - view.disableWordWrap() - - return view - } - - private func createScrollView() -> NSScrollView { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - - view.documentView = errorsText - - return view - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/FileOperationManagerDelegateImpl.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/FileOperationManagerDelegateImpl.swift deleted file mode 100644 index 80b2dc6..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/FileOperationManagerDelegateImpl.swift +++ /dev/null @@ -1,197 +0,0 @@ -// -// FileOperationManagerDelegateImpl.swift -// VisualDiffer -// -// Created by davide ficano on 31/03/16. -// Copyright (c) 2016 visualdiffer.com -// - -class FileOperationManagerDelegateImpl: FileOperationManagerDelegate { - private(set) weak var progressIndicatorController: ProgressIndicatorController? - - init(progressIndicatorController: ProgressIndicatorController) { - self.progressIndicatorController = progressIndicatorController - } - - func waitPause(for _: FileOperationManager) { - guard let pic = progressIndicatorController else { - return - } - - DispatchQueue.main.sync { - pic.waitPause() - } - } - - func isRunning(_: FileOperationManager) -> Bool { - guard let pic = progressIndicatorController else { - return false - } - - return DispatchQueue.main.sync { - pic.isRunning() - } - } - - func fileManager( - _: FileOperationManager, - canReplaceFromPath fromPath: String, - fromAttrs: [FileAttributeKey: Any]?, - toPath: String, - toAttrs: [FileAttributeKey: Any]? - ) -> Bool { - guard let pic = progressIndicatorController else { - return false - } - - let fromAttributes = fromAttrs?.toFileAttributes() - let toAttributes = toAttrs?.toFileAttributes() - - return DispatchQueue.main.sync { - pic.canReplace( - fromPath: fromPath, - fromAttrs: fromAttributes, - toPath: toPath, - toAttrs: toAttributes - ) - } - } - - func fileManager(_: FileOperationManager, initForItem item: CompareItem) { - guard let pic = progressIndicatorController else { - return - } - let path = item.path ?? "" - - DispatchQueue.main.async { - pic.updateItem(path: path) - } - } - - func fileManager(_: FileOperationManager, updateForItem item: CompareItem) { - guard let pic = progressIndicatorController else { - return - } - - let path = item.path ?? "" - let isFile = item.isFile - let fileSize = item.fileSize - - DispatchQueue.main.async { - pic.updateItem( - path: path, - isFile: isFile, - fileSize: fileSize - ) - } - } - - func fileManager(_: FileOperationManager, addError error: any Error, forItem item: CompareItem) { - guard let pic = progressIndicatorController else { - return - } - let path = item.path ?? "" - - DispatchQueue.main.async { - pic.add(error: error as NSError, forPath: path) - } - } - - func fileManager(_: FileOperationManager, startBigFileOperationForItem item: CompareItem) { - guard let pic = progressIndicatorController else { - return - } - let fileSize = item.fileSize - - DispatchQueue.main.async { - pic.prepare(with: fileSize) - } - } - - func isBigFileOperationCancelled(_: FileOperationManager) -> Bool { - guard let pic = progressIndicatorController else { - return false - } - - return DispatchQueue.main.sync { - pic.fileOpCancelled - } - } - - func isBigFileOperationCompleted(_: FileOperationManager) -> Bool { - guard let pic = progressIndicatorController else { - return false - } - - return DispatchQueue.main.sync { - pic.fileOpCompleted - } - } - - func fileManager(_: FileOperationManager, setCancelled cancelled: Bool) { - guard let pic = progressIndicatorController else { - return - } - - DispatchQueue.main.async { - pic.fileOpCancelled = cancelled - // passing 0 to updateBytesCompleted ensures the UI is cleared - pic.update( - completedBytes: 0, - totalBytes: 0, - throughput: 0 - ) - } - } - - func fileManager(_: FileOperationManager, setCompleted completed: Bool) { - guard let pic = progressIndicatorController else { - return - } - - DispatchQueue.main.async { - pic.fileOpCompleted = completed - // passing 0 to updateBytesCompleted ensures the UI is cleared - pic.update( - completedBytes: 0, - totalBytes: 0, - throughput: 0 - ) - } - } - - func fileManager( - _: FileOperationManager, - updateBytesCompleted bytesCompleted: Double, - totalBytes: Double, - throughput: Double - ) { - guard let pic = progressIndicatorController else { - return - } - - DispatchQueue.main.async { - pic.update( - completedBytes: Int64(bytesCompleted), - totalBytes: Int64(totalBytes), - throughput: Int64(throughput) - ) - } - } -} - -extension ProgressIndicatorController { - func canReplace( - fromPath: String, - fromAttrs: FileAttributes?, - toPath: String, - toAttrs: FileAttributes? - ) -> Bool { - canReplace( - fromPath: fromPath, - fromAttrs: fromAttrs?.toFileAttributeKeys(), - toPath: toPath, - toAttrs: toAttrs?.toFileAttributeKeys() - ) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ProgressIndicatorController.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ProgressIndicatorController.swift deleted file mode 100644 index 20c866c..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ProgressIndicatorController.swift +++ /dev/null @@ -1,381 +0,0 @@ -// -// ProgressIndicatorController.swift -// VisualDiffer -// -// Created by davide ficano on 12/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -class ProgressIndicatorController: NSWindowController { - var sizeLeft: Int64 = 0 { - didSet { - updateSizeLeftDisplay() - } - } - - var operationOnSingleItem = false - - var operationDescription: String { - get { descriptionTitle.stringValue } - set { descriptionTitle.stringValue = newValue } - } - - var fileOpCompleted = false - var fileOpCancelled = false - - var errors: [NSError] { - errorView.errors - } - - private var running = false - private var isPaused = false - private var yesToAll = false - private var noToAll = false - - private let pauseCondition = NSCondition() - - // MARK: - Views - - private let errorView = ErrorsView(frame: .zero) - - private lazy var descriptionTitle: NSTextField = { - let label = NSTextField.labelWithTitle("") - label.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) - - return label - }() - - private lazy var itemPath: NSTextField = { - let view = NSTextField.hintWithTitle("") - view.lineBreakMode = .byTruncatingMiddle - - // Make sure it doesn't grow based on content - view.setContentHuggingPriority(.defaultLow, for: .horizontal) - view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - - return view - }() - - private lazy var mainStack: NSStackView = { - let stack = NSStackView(views: [ - descriptionTitle, - itemPath, - completionIndicator, - processingItemsIndicator, - itemsLeftView, - sizeLeftView, - ]) - stack.orientation = .vertical - stack.spacing = 1 - stack.alignment = .leading - stack.translatesAutoresizingMaskIntoConstraints = false - - stack.setCustomSpacing(14, after: itemPath) - stack.setCustomSpacing(4, after: processingItemsIndicator) - - return stack - }() - - private let itemsLeftView = ReplaceInfoView( - label: NSLocalizedString("Items left", comment: ""), - labelWidth: 60 - ) - private let sizeLeftView = ReplaceInfoView( - label: NSLocalizedString("Size left", comment: ""), - labelWidth: 60 - ) - - private let completionIndicator = CompletionIndicator() - private let processingItemsIndicator = NSProgressIndicator.bar() - - private lazy var stopButton: NSButton = .cancelButton( - title: NSLocalizedString("Stop", comment: ""), - target: self, - action: #selector(stop) - ) - - init() { - super.init(window: Self.createWindow()) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - let formatter = NumberFormatter() - formatter.numberStyle = .decimal - itemsLeftView.text.formatter = formatter - - completionIndicator.widthAnchor.constraint(equalTo: mainStack.widthAnchor).isActive = true - completionIndicator.heightAnchor.constraint(equalToConstant: 30).isActive = true - - if let contentView = window?.contentView { - contentView.addSubview(mainStack) - contentView.addSubview(errorView) - contentView.addSubview(stopButton) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - - NSLayoutConstraint.activate([ - mainStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - mainStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - mainStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - - errorView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - errorView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - errorView.topAnchor.constraint(equalTo: mainStack.bottomAnchor, constant: 4), - errorView.bottomAnchor.constraint(equalTo: stopButton.topAnchor, constant: -20), - - stopButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - stopButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20), - stopButton.widthAnchor.constraint(equalToConstant: 100), - ]) - } - - private static func createWindow() -> NSWindow { - let styleMask: NSWindow.StyleMask = [.titled, .closable, .resizable] - - let view = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - view.hasShadow = true - view.isRestorable = true - view.minSize = NSSize(width: 480, height: 300) - - return view - } - - // MARK: - Sheet - - func beginSheetModal( - for callerWindow: NSWindow, - processingItemsCount: Int, - totalSize: Int64, - singleItem: Bool - ) { - setProcessingItemsCount(processingItemsCount) - sizeLeft = totalSize - operationOnSingleItem = singleItem - - if let window { - callerWindow.beginSheet(window, completionHandler: nil) - } - } - - func endSheet() { - // call orderOut only if the sheet wasn't already closed - if let window, - window.isVisible { - window.sheetParent?.endSheet(window) - } - stopRun() - } - - // MARK: - Start/Stop, wait and running - - @objc func stop(_ sender: AnyObject) { - guard let sender = sender as? NSButton else { - return - } - if !running { - closeSheet(sender) - return - } - pauseCondition.lock() - isPaused = true - - let retVal = NSAlert.showModalConfirm( - messageText: NSLocalizedString("Are you sure to stop the operation?", comment: ""), - informativeText: NSLocalizedString("If the operation takes a long time to run, you can stop it, but the results could be inaccurate", comment: ""), - suppressPropertyName: CommonPrefs.Name.confirmStopLongOperation.rawValue - ) - - if retVal { - sender.isEnabled = false - stopRun() - } else { - // The operation has been completed while - // the alert was on screen so dismiss the panel - if !running { - closeSheet(sender) - } - } - isPaused = false - pauseCondition.signal() - pauseCondition.unlock() - } - - private func closeSheet(_ sender: AnyObject) { - if let window { - window.endSheet(window) - window.orderOut(sender) - } - } - - func startRun() { - running = true - } - - func stopRun() { - running = false - fileOpCancelled = true - } - - func isRunning() -> Bool { - running - } - - func waitPause() { - pauseCondition.lock() - while isPaused { - pauseCondition.wait() - } - pauseCondition.unlock() - } - - // MARK: - UI progression components update - - func updateItem(path: String) { - itemPath.stringValue = path - } - - func updateItem( - path: String, - isFile: Bool, - fileSize: Int64 - ) { - updateItem(path: path) - incrementProcessingItems() - if isFile { - sizeLeft -= fileSize - } - } - - func update( - completedBytes: Int64, - totalBytes: Int64, - throughput: Int64 - ) { - completionIndicator.update( - completedBytes: completedBytes, - totalBytes: totalBytes, - throughput: throughput - ) - } - - // MARK: - File replace cornfirmation code - - func canReplace( - fromPath: String, - fromAttrs: [FileAttributeKey: Any]?, - toPath: String, - toAttrs: [FileAttributeKey: Any]? - ) -> Bool { - let confirmReplace = ConfirmReplace( - yesToAll: yesToAll, - noToAll: noToAll, - confirmHandler: replaceHandler - ) - return confirmReplace.canReplace( - fromPath: fromPath, - fromAttrs: fromAttrs, - toPath: toPath, - toAttrs: toAttrs - ) - } - - func replaceHandler( - confirmReplace _: ConfirmReplace, - replaceInfo: [ReplaceFileAttributeKey: Any] - ) -> Bool { - let alert = NSAlert() - alert.replaceFile( - from: replaceInfo, - operationOnSingleItem: operationOnSingleItem - ) - - switch alert.runModal().replaceFile { - case .cancel: - stopRun() - return false - case .noToAll: - fileOpCancelled = true - noToAll = true - return false - case .no: - fileOpCancelled = true - return false - case .yesToAll: - yesToAll = true - return true - case .yes: - return true - default: - return false - } - } - - func prepare(with fileSize: Int64) { - fileOpCompleted = false - fileOpCancelled = false - - completionIndicator.reset(maxValue: Double(fileSize)) - } - - func turnButtonToClose() { - stopRun() - stopButton.title = NSLocalizedString("Close", comment: "") - stopButton.isEnabled = true - } - - func endSheetAfterCompletion() { - if errors.isEmpty { - endSheet() - } else { - turnButtonToClose() - } - } - - // MARK: - Error Helpers - - func add(error: NSError, forPath path: String) { - errorView.addError(error, forPath: path) - } - - // MARK: - Text Helpers - - func resetProcessingItems() { - processingItemsIndicator.doubleValue = 0 - } - - func incrementProcessingItems() { - let newValue = processingItemsIndicator.doubleValue + 1 - processingItemsIndicator.doubleValue = newValue - itemsLeftView.text.stringValue = String(format: "%ld", Int(processingItemsIndicator.maxValue - newValue)) - } - - func setProcessingItemsCount(_ newValue: Int) { - processingItemsIndicator.maxValue = Double(newValue) - itemsLeftView.text.stringValue = String(format: "%ld", newValue) - } - - private func updateSizeLeftDisplay() { - sizeLeftView.text.stringValue = sizeLeft > 0 - ? FileSizeFormatter.default.string(from: NSNumber(value: sizeLeft)) ?? "0" - : "0" - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/ConfirmReplace.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/ConfirmReplace.swift deleted file mode 100644 index 53dfe4e..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/ConfirmReplace.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// ConfirmReplace.swift -// VisualDiffer -// -// Created by davide ficano on 18/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ConfirmReplace { - let yesToAll: Bool - let noToAll: Bool - let confirmHandler: (ConfirmReplace, [ReplaceFileAttributeKey: Any]) -> Bool - - init( - yesToAll: Bool, - noToAll: Bool, - confirmHandler: @escaping (ConfirmReplace, [ReplaceFileAttributeKey: Any]) -> Bool - ) { - self.yesToAll = yesToAll - self.noToAll = noToAll - self.confirmHandler = confirmHandler - } - - func canReplace( - fromPath: String, - fromAttrs: [FileAttributeKey: Any]?, - toPath: String, - toAttrs: [FileAttributeKey: Any]? - ) -> Bool { - if yesToAll { - return true - } - - var localFromAttrs = fromAttrs - var localToAttrs = toAttrs - - do { - if localToAttrs == nil { - localToAttrs = try FileManager.default.attributesOfItem(atPath: toPath) - } - } catch let error as NSError { - // file doesn't exist - if error.code == NSFileReadNoSuchFileError { - return true - } - } - - if localFromAttrs == nil { - localFromAttrs = try? FileManager.default.attributesOfItem(atPath: fromPath) - } - - let toDate = localToAttrs?[.modificationDate] as? Date - let fromDate = localFromAttrs?[.modificationDate] as? Date - - if let toDate, - let fromDate, - fromDate.compare(toDate) != .orderedAscending { - return true - } - - if noToAll { - return false - } - - var replaceInfo = [ReplaceFileAttributeKey: Any]() - replaceInfo[.toPath] = toPath - replaceInfo[.fromPath] = fromPath - - if let toDate { - replaceInfo[.toDate] = toDate - } - if let size = localToAttrs?[.size] { - replaceInfo[.toSize] = size - } - if let fromDate { - replaceInfo[.fromDate] = fromDate - } - if let size = localFromAttrs?[.size] { - replaceInfo[.fromSize] = size - } - - return confirmHandler(self, replaceInfo) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/NSAlert+ReplaceFile.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/NSAlert+ReplaceFile.swift deleted file mode 100644 index 9c283ac..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/NSAlert+ReplaceFile.swift +++ /dev/null @@ -1,201 +0,0 @@ -// -// NSAlert+ReplaceFile.swift -// VisualDiffer -// -// Created by davide ficano on 12/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -@MainActor private struct ViewItemConfiguration { - let key: ReplaceFileAttributeKey - let view: NSTextField - let indent: CGFloat - - init(key: ReplaceFileAttributeKey, view: NSTextField, indent: CGFloat = 0) { - self.key = key - self.indent = indent - self.view = view - } - - static let configurations: [ViewItemConfiguration] = [ - ViewItemConfiguration( - key: .toTitle, - view: NSTextField.hintWithTitle( - NSLocalizedString("Would you like to replace the existing file?", comment: "") - ) - ), - - ViewItemConfiguration( - key: .toPath, - view: createPathTextField(), - indent: 30 - ), - - ViewItemConfiguration( - key: .toSize, - view: createNumberTextField(), - indent: 30 - ), - - ViewItemConfiguration( - key: .toDate, - view: createDateTextField(), - indent: 30 - ), - - ViewItemConfiguration( - key: .fromTitle, - view: NSTextField.hintWithTitle(NSLocalizedString("With this one?", comment: "")) - ), - - ViewItemConfiguration( - key: .fromPath, - view: createPathTextField(), - indent: 30 - ), - - ViewItemConfiguration( - key: .fromSize, - view: createNumberTextField(), - indent: 30 - ), - - ViewItemConfiguration( - key: .fromDate, - view: createDateTextField(), - indent: 30 - ), - ] -} - -extension NSAlert { - func replaceFile( - from info: [ReplaceFileAttributeKey: Any], - operationOnSingleItem: Bool - ) { - setupIcon(info: info) - setupButtons(operationOnSingleItem: operationOnSingleItem) - setupMessages(info: info) - setupAccessoryView(info: info) - } - - func addButton(replaceFile: NSApplication.ModalResponse.ReplaceFile) { - let button = addButton(withTitle: replaceFile.title) - - button.keyEquivalent = replaceFile.keyEquivalent - button.tag = replaceFile.rawValue - button.toolTip = String.localizedStringWithFormat(NSLocalizedString("Press '%@'", comment: ""), replaceFile.keyDescription) - } - - private func setupButtons(operationOnSingleItem: Bool) { - // Don't use three buttons otherwise NSAlert doesn't space equally buttons - let buttons: [NSApplication.ModalResponse.ReplaceFile] = operationOnSingleItem - ? [.no, .yes] - : [.cancel, .noToAll, .no, .yesToAll, .yes] - - for button in buttons { - addButton(replaceFile: button) - } - } - - private func setupIcon(info: [ReplaceFileAttributeKey: Any]) { - if let toPath = info.toPath { - icon = NSWorkspace.shared.icon(forFile: toPath) - } - } - - private func setupMessages(info: [ReplaceFileAttributeKey: Any]) { - let name = if let fromPath = info.fromPath { - URL(filePath: fromPath).lastPathComponent - } else { - "" - } - messageText = NSLocalizedString("Replace existing file?", comment: "") - informativeText = String(format: NSLocalizedString("This folder contains a newer file named %@", comment: ""), name) - } - - private func setupAccessoryView(info: [ReplaceFileAttributeKey: Any]) { - accessoryView = createAccessoryView(createReplaceDetailView(info)) - } - - private func createAccessoryView(_ contentView: NSView) -> NSView { - let contentRect = contentView.frame - - let view = NSView(frame: contentRect) - view.addSubview(contentView) - - NSLayoutConstraint.activate([ - contentView.topAnchor.constraint(equalTo: view.topAnchor), - contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - view.widthAnchor.constraint(equalToConstant: contentRect.size.width), - ]) - - return view - } - - private func createReplaceDetailView(_ info: [ReplaceFileAttributeKey: Any]) -> NSStackView { - let stack = NSStackView(frame: NSRect(x: 0, y: 0, width: 480, height: 200)) - - stack.orientation = .vertical - stack.spacing = 12 - stack.alignment = .leading - stack.translatesAutoresizingMaskIntoConstraints = false - - for item in ViewItemConfiguration.configurations { - stack.addArrangedSubview(item.view) - - if item.indent > 0 { - item.view.leadingAnchor.constraint( - equalTo: stack.leadingAnchor, - constant: item.indent - ).isActive = true - } - - if let obj = info[item.key] { - item.view.objectValue = obj - } - } - - return stack - } -} - -@MainActor private func createPathTextField() -> NSTextField { - let view = NSTextField.hintWithTitle("") - - view.lineBreakMode = .byTruncatingMiddle - view.translatesAutoresizingMaskIntoConstraints = false - - return view -} - -@MainActor private func createNumberTextField() -> NSTextField { - let formatter = NumberFormatter() - formatter.numberStyle = .decimal - - let view = NSTextField.hintWithTitle("") - view.formatter = formatter - view.translatesAutoresizingMaskIntoConstraints = false - - return view -} - -@MainActor private func createDateTextField() -> NSTextField { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .medium - - let view = NSTextField.hintWithTitle("") - view.formatter = formatter - view.translatesAutoresizingMaskIntoConstraints = false - - return view -} - -// Private keys for internal use only -private extension ReplaceFileAttributeKey { - static let toTitle = ReplaceFileAttributeKey(rawValue: "toTitle") - static let fromTitle = ReplaceFileAttributeKey(rawValue: "fromTitle") -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/NSApplication.ModalResponse+ReplaceFile.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/NSApplication.ModalResponse+ReplaceFile.swift deleted file mode 100644 index a0a3a2f..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/NSApplication.ModalResponse+ReplaceFile.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// NSApplication.ModalResponse+ReplaceFile.swift -// VisualDiffer -// -// Created by davide ficano on 19/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSApplication.ModalResponse { - enum ReplaceFile: Int { - case cancel = 1000 - case noToAll = 1001 - // swiftlint:disable:next identifier_name - case no = 1002 - case yesToAll = 1003 - case yes = 1004 - - var title: String { - switch self { - case .cancel: NSLocalizedString("Cancel", comment: "") - case .noToAll: NSLocalizedString("No to All", comment: "") - case .no: NSLocalizedString("No", comment: "") - case .yesToAll: NSLocalizedString("Yes to All", comment: "") - case .yes: NSLocalizedString("Yes", comment: "") - } - } - - var keyEquivalent: String { - switch self { - case .cancel, .no: KeyEquivalent.escape - case .noToAll: "o" - case .yesToAll: "a" - case .yes: "y" - } - } - - var keyDescription: String { - keyEquivalent == KeyEquivalent.escape ? "Escape" : keyEquivalent - } - - // periphery:ignore - var modalResponse: NSApplication.ModalResponse { - NSApplication.ModalResponse(rawValue: rawValue) - } - } - - var replaceFile: ReplaceFile? { - ReplaceFile(rawValue: rawValue) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/ReplaceFileAttributeKey.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/ReplaceFileAttributeKey.swift deleted file mode 100644 index 8a1f11a..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceFile/ReplaceFileAttributeKey.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// ReplaceFileAttributeKey.swift -// VisualDiffer -// -// Created by davide ficano on 19/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -// periphery:ignore:all - -struct ReplaceFileAttributeKey: Hashable, RawRepresentable { - let rawValue: String - - static let fromPath = ReplaceFileAttributeKey(rawValue: "fromPath") - static let fromSize = ReplaceFileAttributeKey(rawValue: "fromSize") - static let fromDate = ReplaceFileAttributeKey(rawValue: "fromDate") - static let toPath = ReplaceFileAttributeKey(rawValue: "toPath") - static let toSize = ReplaceFileAttributeKey(rawValue: "toSize") - static let toDate = ReplaceFileAttributeKey(rawValue: "toDate") -} - -extension [ReplaceFileAttributeKey: Any] { - var fromPath: String? { self[.fromPath] as? String } - var fromSize: Int? { self[.fromSize] as? Int } - var fromDate: Date? { self[.fromDate] as? Date } - var toPath: String? { self[.toPath] as? String } - var toSize: Int? { self[.toSize] as? Int } - var toDate: Date? { self[.toDate] as? Date } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceInfoView.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceInfoView.swift deleted file mode 100644 index 1b59cf6..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Progress/ReplaceInfoView.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// ReplaceInfoView.swift -// VisualDiffer -// -// Created by davide ficano on 12/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -class ReplaceInfoView: NSStackView { - let label: NSTextField - let text: NSTextField - - init(label labelText: String, labelWidth: CGFloat) { - label = NSTextField.hintWithTitle(labelText) - label.font = NSFont.boldSystemFont(ofSize: NSFont.smallSystemFontSize) - label.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true - - text = NSTextField.hintWithTitle("") - - super.init(frame: .zero) - - addArrangedSubviews([ - label, - text, - ]) - orientation = .horizontal - spacing = 4 - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncFileController.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncFileController.swift deleted file mode 100644 index 89228bc..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncFileController.swift +++ /dev/null @@ -1,328 +0,0 @@ -// -// SyncFileController.swift -// VisualDiffer -// -// Created by davide ficano on 31/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -class SyncFileController: FileSystemController { - private lazy var syncOptionsView: NSStackView = createOptionsView() - - private let operationDescription: NSTextField = { - let view = NSTextField.labelWithTitle("") - view.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) - - return view - }() - - private lazy var checkboxUseSelection: NSButton = { - let view = NSButton( - checkboxWithTitle: NSLocalizedString("Use selection", comment: ""), - target: self, - action: #selector(updateCount) - ) - view.state = .on - - return view - }() - - private lazy var checkboxSyncBothSides: NSButton = .init( - checkboxWithTitle: NSLocalizedString("Sync both sides", comment: ""), - target: self, - action: #selector(updateCount) - ) - - private lazy var scrollView: NSScrollView = createScrollView() - private lazy var treeView: SyncOutlineView = .init(items: itemsToSync) - - private lazy var itemsToSync: SyncItemsInfo = { - let view = SyncItemsInfo() - view.nodes = DescriptionOutlineNode(text: "", isContainer: true) - - return view - }() - - private var createEmptyFolders = false - - private var view: FoldersOutlineView - private let root: CompareItem - - init( - executor: SyncFileOperationExecutor, - fileOperationManager: FileOperationManager, - view: FoldersOutlineView, - progressIndicatorController: ProgressIndicatorController - ) { - self.view = view - - guard let vi = view.dataSource?.outlineView?(view, child: 0, ofItem: nil) as? VisibleItem, - let root = vi.item.parent else { - fatalError("Unable to get root") - } - self.root = root - - createEmptyFolders = true - super.init( - executor: executor, - fileOperationManager: fileOperationManager, - view: view, - progressIndicatorController: progressIndicatorController, - filteredFileVisible: false - ) - } - - override func createWindow() -> NSWindow { - let styleMask: NSWindow.StyleMask = [.titled, .closable, .resizable] - - let view = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - view.hasShadow = true - view.isRestorable = true - view.minSize = NSSize(width: 480, height: 300) - - return view - } - - override func setupStandardButtons() { - super.setupStandardButtons() - - standardButtons.primaryButton.title = NSLocalizedString("Sync", comment: "") - } - - override func setupOperationSummaryView() { - operationSummary.addArrangedSubview(syncOptionsView) - } - - override func setupMainStackView() { - mainStackView.addArrangedSubviews([ - operationSummary, - scrollView, - ]) - - mainStackView.orientation = .vertical - mainStackView.alignment = .leading - mainStackView.spacing = 10 - mainStackView.translatesAutoresizingMaskIntoConstraints = false - } - - override func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - NSLayoutConstraint.activate([ - mainStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - mainStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - mainStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - mainStackView.bottomAnchor.constraint(equalTo: standardButtons.topAnchor, constant: -20), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - private func createScrollView() -> NSScrollView { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = treeView - - return view - } - - private func createOptionsView() -> NSStackView { - let view = NSStackView(views: [ - operationDescription, - checkboxUseSelection, - checkboxSyncBothSides, - ]) - view.orientation = .vertical - view.alignment = .leading - view.spacing = 4 - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - override func setupTitle() { - let syncBothSides = checkboxSyncBothSides.state == .on - - if syncBothSides { - operationSummary.icon.image = NSImage(named: VDImageNameSyncBoth) - operationDescription.stringValue = NSLocalizedString("Copy newer and orphan files to the other side", comment: "") - } else { - if view.side == .left { - operationSummary.icon.image = NSImage(named: VDImageNameSyncRight) - operationDescription.stringValue = NSLocalizedString("Copy newer and orphan files to right", comment: "") - } else { - operationSummary.icon.image = NSImage(named: VDImageNameSyncLeft) - operationDescription.stringValue = NSLocalizedString("Copy newer and orphan files to left", comment: "") - } - } - } - - override func setupViews() { - super.setupViews() - - if !executor.hasSelectedItems { - checkboxUseSelection.state = .off - checkboxUseSelection.isEnabled = false - } - } - - @objc override func updateCount(_: AnyObject?) { - setupTitle() - fillItemsToSync() - - treeView.reloadData() - } - - private func fillItemsToSync() { - let useSelection = checkboxUseSelection.state == .on - let syncBothSides = checkboxSyncBothSides.state == .on - - executor.prepareSyncItemsInfo( - items: itemsToSync, - withSelection: useSelection, - syncBothSides: syncBothSides, - createEmptyFolders: createEmptyFolders, - view: view - ) - guard let nodes = itemsToSync.nodes, - nodes.children.isEmpty else { - return - } - let text = if view.side == .left { - NSLocalizedString("No files to copy on the right", comment: "") - } else { - NSLocalizedString("No files to copy on the left", comment: "") - } - - nodes.children.append(DescriptionOutlineNode(text: text, isContainer: false)) - } - - override func prepareExecute() { - guard let callerWindow else { - fatalError("Caller window is nil") - } - - executor.syncSelection = checkboxUseSelection.state == .on - executor.syncBothSides = checkboxSyncBothSides.state == .on - - fileOperationManager.includesFiltered = false - - guard let progressIndicatorController else { - return - } - progressIndicatorController.beginSheetModal( - for: callerWindow, - processingItemsCount: 0, - totalSize: 0, - singleItem: false - ) - - let itemsInfo = executor.itemsInfo - progressIndicatorController.operationDescription = NSLocalizedString("Syncing", comment: "") - progressIndicatorController.resetProcessingItems() - progressIndicatorController.setProcessingItemsCount(itemsInfo.nodes?.items?.count ?? 0) - progressIndicatorController.sizeLeft = itemsInfo.totalSize - - progressIndicatorController.startRun() - } - - override func execute() { - guard let rootPath = root.path, - let rootLinkedPath = root.linkedItem?.path else { - return - } - prepareExecute() - - let capturedManager = fileOperationManager - DispatchQueue.global(qos: .userInitiated).async { - self.performExecution( - fileOperationManager: capturedManager, - srcBaseDir: rootPath, - destBaseDir: rootLinkedPath - ) - - DispatchQueue.main.async { - self.updateUIAfterExecute() - } - } - } - - nonisolated func performExecution( - fileOperationManager: FileOperationManager, - srcBaseDir: String, - destBaseDir: String - ) { - sync( - fileOperationManager: fileOperationManager, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - copyDestFiles: false - ) - - if executor.syncBothSides { - // pay attention src and dest are inverted because we are copying from dest versus src - sync( - fileOperationManager: fileOperationManager, - srcBaseDir: destBaseDir, - destBaseDir: srcBaseDir, - copyDestFiles: true - ) - } - } - - nonisolated func sync( - fileOperationManager: FileOperationManager, - srcBaseDir: String, - destBaseDir: String, - copyDestFiles: Bool - ) { - let payload = SyncFileOperationExecutor.Payload( - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - copyDestFiles: copyDestFiles, - copyEmptyFolders: false - ) - executor.execute(fileOperationManager, payload: payload) - - let items = copyDestFiles ? executor.itemsInfo.linkedInfo : executor.itemsInfo - let emptyFoldersCount = items?.emptyFoldersNodes?.items?.count ?? 0 - - if emptyFoldersCount == 0 { - return - } - DispatchQueue.main.async { - if let progressIndicatorController = self.progressIndicatorController { - progressIndicatorController.operationDescription = NSLocalizedString("Creating empty folders", comment: "") - progressIndicatorController.resetProcessingItems() - progressIndicatorController.setProcessingItemsCount(emptyFoldersCount) - progressIndicatorController.sizeLeft = 0 - } - } - - let emptyFoldersPayload = SyncFileOperationExecutor.Payload( - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - copyDestFiles: copyDestFiles, - copyEmptyFolders: true - ) - executor.execute(fileOperationManager, payload: emptyFoldersPayload) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncFileOperationExecutor.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncFileOperationExecutor.swift deleted file mode 100644 index 59c542c..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncFileOperationExecutor.swift +++ /dev/null @@ -1,319 +0,0 @@ -// -// SyncFileOperationExecutor.swift -// VisualDiffer -// -// Created by davide ficano on 14/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class SyncFileOperationExecutor: FileOperationExecutor, @unchecked Sendable { - typealias TPayload = Payload - - struct Payload: Sendable { - let srcBaseDir: String - let destBaseDir: String - let copyDestFiles: Bool - let copyEmptyFolders: Bool - } - - var title = NSLocalizedString("Sync", comment: "") - var summary = NSLocalizedString("Copy newer and orphan files to the other side", comment: "") - var image: NSImage? - var progressLabel = "" - var prefName: CommonPrefs.Name? - - // delete operation doesn't use it - var operationOnSingleItem = false - - private(set) var side: DisplaySide - - private var selectedItems: SyncItemsInfo - private var allItems: SyncItemsInfo - - var syncSelection = false - var syncBothSides = false - - var hasSelectedItems: Bool { - let nodesCount = selectedItems.nodes?.items?.count ?? 0 - let emptyNodeCount = selectedItems.emptyFoldersNodes?.items?.count ?? 0 - - return nodesCount != 0 || emptyNodeCount != 0 - } - - var itemsInfo: SyncItemsInfo { - syncSelection ? selectedItems : allItems - } - - init(side: DisplaySide) { - self.side = side - - selectedItems = SyncItemsInfo() - selectedItems.linkedInfo = SyncItemsInfo() - selectedItems.linkedInfo?.linkedInfo = selectedItems - - allItems = SyncItemsInfo() - allItems.linkedInfo = SyncItemsInfo() - allItems.linkedInfo?.linkedInfo = allItems - } - - func execute(_ manager: FileOperationManagerAction, payload: Payload?) { - guard let payload else { - fatalError("Missing payload") - } - let srcBaseDir = payload.srcBaseDir - let destBaseDir = payload.destBaseDir - let items = payload.copyDestFiles ? itemsInfo.linkedInfo : itemsInfo - let nodes = payload.copyEmptyFolders ? items?.emptyFoldersNodes : items?.nodes - - guard let nodes = nodes?.items else { - return - } - - for item in nodes { - manager.copy( - item, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir - ) - } - } -} - -extension SyncFileOperationExecutor { - /** - * Build the tree putting items in the correct order - * 1. Folders to create on left (if any) - * 2. Folders to create on right (if any) - * 3. Files to copy on left (if any) - * 4. Files to copy on right (if any) - */ - func buildTree( - srcItemsInfo: SyncItemsInfo, - destItemsInfo: SyncItemsInfo, - syncBothSides: Bool, - createEmptyFolders: Bool - ) { - if side == .left { - if createEmptyFolders { - if syncBothSides, - let emptyFoldersNodes = srcItemsInfo.linkedInfo?.emptyFoldersNodes { - destItemsInfo.add(emptyFoldersNodes) - } - if let emptyFoldersNodes = srcItemsInfo.emptyFoldersNodes { - destItemsInfo.add(emptyFoldersNodes) - } - } - if syncBothSides, - let nodes = srcItemsInfo.linkedInfo?.nodes { - destItemsInfo.add(nodes) - } - if let nodes = srcItemsInfo.nodes { - destItemsInfo.add(nodes) - } - } else { - if createEmptyFolders { - if let emptyFoldersNodes = srcItemsInfo.emptyFoldersNodes { - destItemsInfo.add(emptyFoldersNodes) - } - if syncBothSides { - if let emptyFoldersNodes = srcItemsInfo.linkedInfo?.emptyFoldersNodes { - destItemsInfo.add(emptyFoldersNodes) - } - } - } - if let nodes = srcItemsInfo.nodes { - destItemsInfo.add(nodes) - } - if syncBothSides { - if let nodes = srcItemsInfo.linkedInfo?.nodes { - destItemsInfo.add(nodes) - } - } - } - } - - @MainActor func prepareSyncItemsInfo( - items itemsToSync: SyncItemsInfo, - withSelection useSelection: Bool, - syncBothSides: Bool, - createEmptyFolders: Bool, - view: FoldersOutlineView - ) { - itemsToSync.removeAll() - - if useSelection { - if selectedItems.nodes?.items == nil { - fillSyncItemsInfo( - selectedItems, - srcArray: view.selectedItems(), - foldersView: view - ) - } - if syncBothSides { - if selectedItems.linkedInfo?.nodes?.items == nil { - var arr = [CompareItem]() - for item in view.selectedItems() { - if let linkedItem = item.linkedItem { - arr.append(linkedItem) - } - } - if let linkedInfo = selectedItems.linkedInfo, - let linkedView = view.linkedView { - fillSyncItemsInfo( - linkedInfo, - srcArray: arr, - foldersView: linkedView - ) - } - } - } - itemsToSync.totalSize = selectedItems.totalSize - itemsToSync.nodes?.items = selectedItems.nodes?.items - - buildTree( - srcItemsInfo: selectedItems, - destItemsInfo: itemsToSync, - syncBothSides: syncBothSides, - createEmptyFolders: createEmptyFolders - ) - } else { - if allItems.nodes?.items == nil { - if let vi = view.dataSource?.outlineView?(view, child: 0, ofItem: nil) as? VisibleItem, - let root = vi.item.parent { - fillSyncItemsInfo( - allItems, - srcArray: [root], - foldersView: view - ) - } - } - if syncBothSides { - if allItems.linkedInfo?.nodes?.items == nil { - if let linkedView = view.linkedView, - let vi = linkedView.dataSource?.outlineView?(linkedView, child: 0, ofItem: nil) as? VisibleItem, - let root = vi.item.parent, - let linkedInfo = allItems.linkedInfo { - fillSyncItemsInfo( - linkedInfo, - srcArray: [root], - foldersView: linkedView - ) - } - } - } - itemsToSync.totalSize = allItems.totalSize - itemsToSync.nodes?.items = allItems.nodes?.items - - buildTree( - srcItemsInfo: allItems, - destItemsInfo: itemsToSync, - syncBothSides: syncBothSides, - createEmptyFolders: createEmptyFolders - ) - } - } - - @MainActor func fillSyncItemsInfo( - _ syncItems: SyncItemsInfo, - srcArray: [CompareItem], - foldersView: FoldersOutlineView - ) { - var files = [CompareItem]() - var emptyFolders = [CompareItem]() - syncItems.totalSize = 0 - - for item in srcArray { - syncItems.totalSize += getSyncableList( - item, - files: &files, - emptyFoldersList: &emptyFolders - ) - } - let vi = foldersView.dataSource?.outlineView?(foldersView, child: 0, ofItem: nil) as? VisibleItem - let rootPath = vi?.item.parent?.path ?? "" - - // copy direction is the inverse of displayPosition - let direction: DisplaySide = foldersView.side == .left ? .right : .left - var text = String.localizedStringWithFormat( - NSLocalizedString("Copy %ld files (%@) to %lu", comment: "Copy 3 files (10.4MB) to left/right"), - files.count, - FileSizeFormatter.default.string(from: NSNumber(value: syncItems.totalSize)) ?? "0", - direction.rawValue - ) - syncItems.nodes = DescriptionOutlineNode( - relativePath: text, - items: files, - rootPath: rootPath - ) - - text = String.localizedStringWithFormat( - NSLocalizedString("Create %ld empty folders on %lu", comment: "Create 1 empty folder on left/right"), - emptyFolders.count, - direction.rawValue - ) - syncItems.emptyFoldersNodes = DescriptionOutlineNode( - relativePath: text, - items: emptyFolders, - rootPath: rootPath - ) - } - - /** - * emptyFoldersList contains also filtered folders - */ - func getSyncableList( - _ root: CompareItem, - files: inout [CompareItem], - emptyFoldersList: inout [CompareItem] - ) -> Int64 { - if !root.isValidFile { - return 0 - } - // if called directly on a file or an empty folder create a temp array - // otherwise this element is skipped - let subfolders = root.children.isEmpty ? [root] : root.children - - var size: Int64 = 0 - var filteredCount = 0 - - for item in subfolders where item.isValidFile { - if item.isFiltered || !item.isDisplayed { - filteredCount += 1 - continue - } - if item.isFile { - if item.type == .orphan || item.linkedItem?.type == .old { - size += item.fileSize - files.append(item) - } - } else { - if item.children.isEmpty { - if let linkedItem = item.linkedItem, !linkedItem.isValidFile { - emptyFoldersList.append(item) - } - } else { - let listCount = files.count - let emptyFoldersCount = emptyFoldersList.count - - size += getSyncableList(item, files: &files, emptyFoldersList: &emptyFoldersList) - if listCount == files.count, - emptyFoldersCount == emptyFoldersList.count, - let linkedItem = item.linkedItem, - !linkedItem.isValidFile { - emptyFoldersList.append(item) - } - } - } - } - - // root contains only filtered objects so it is an "empty" folder - if root.isFolder, - let linkedItem = root.linkedItem, - !linkedItem.isValidFile { - if filteredCount == root.children.count { - emptyFoldersList.append(root) - } - } - return size - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncItemsInfo.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncItemsInfo.swift deleted file mode 100644 index 120bf2e..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncItemsInfo.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// SyncItemsInfo.swift -// VisualDiffer -// -// Created by davide ficano on 31/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -class SyncItemsInfo: NSObject { - @objc var totalSize: Int64 = 0 - @objc var nodes: DescriptionOutlineNode? - @objc var emptyFoldersNodes: DescriptionOutlineNode? - @objc var linkedInfo: SyncItemsInfo? - - @objc func removeAll() { - nodes?.children.removeAll() - } - - @objc func add(_ syncNode: DescriptionOutlineNode) { - guard let syncDataSource = syncNode.items, - let nodes else { - return - } - if !syncDataSource.isEmpty { - nodes.children.append(syncNode) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncOutlineView.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncOutlineView.swift deleted file mode 100644 index 54855e5..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Sync/SyncOutlineView.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// SyncOutlineView.swift -// VisualDiffer -// -// Created by davide ficano on 06/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -@objc class SyncOutlineView: NSOutlineView, NSOutlineViewDataSource, NSOutlineViewDelegate { - var items: SyncItemsInfo - - @objc init(items: SyncItemsInfo) { - self.items = items - super.init(frame: .zero) - - dataSource = self - delegate = self - controlSize = .small - headerView = nil - - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("FileName")) - - column.isEditable = false - column.width = 180 - column.minWidth = 100 - column.maxWidth = 1000 - column.resizingMask = [.autoresizingMask, .userResizingMask] - - addTableColumn(column) - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func outlineView(_: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - guard let node = item as? DescriptionOutlineNode ?? items.nodes else { - return 0 - } - - return node.children.count - } - - func outlineView(_: NSOutlineView, isItemExpandable item: Any) -> Bool { - guard let node = item as? DescriptionOutlineNode else { - return false - } - - return node.isContainer - } - - func outlineView(_: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { - guard let node = item as? DescriptionOutlineNode ?? items.nodes else { - fatalError("Item must be DescriptionOutlineNode") - } - - return node.children[index] - } - - func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { - guard let item = item as? DescriptionOutlineNode, - let identifier = tableColumn?.identifier else { - return nil - } - let cell = outlineView.makeView( - withIdentifier: identifier, - owner: self - ) as? NSTableCellView ?? createCell(identifier) - cell.textField?.stringValue = item.text - - return cell - } - - private func createCell(_ identifier: NSUserInterfaceItemIdentifier) -> NSTableCellView { - let cell = NSTableCellView(frame: NSRect(x: 0, y: 0, width: 100, height: 20)) - cell.identifier = identifier - - let textField = NSTextField(frame: cell.bounds) - textField.autoresizingMask = [.width, .height] - - textField.isBezeled = false - textField.drawsBackground = false - textField.isEditable = false - textField.isSelectable = false - textField.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - textField.lineBreakMode = .byTruncatingMiddle - - cell.addSubview(textField) - cell.textField = textField - - return cell - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/DatePickersStackView.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/DatePickersStackView.swift deleted file mode 100644 index 9b0e90f..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/DatePickersStackView.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// DatePickersStackView.swift -// VisualDiffer -// -// Created by davide ficano on 04/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DatePickersStackView: NSStackView, NSDatePickerCellDelegate { - enum PickerType { - case date - case time - } - - private var isDateChanging = false - - private let textualPicker: NSDatePicker - private let graphicalPicker: NSDatePicker - - var dateValue: Date { - get { - textualPicker.dateValue - } - - set { - textualPicker.dateValue = newValue - graphicalPicker.dateValue = newValue - } - } - - var isEnabled: Bool { - get { - textualPicker.isEnabled - } - - set { - textualPicker.isEnabled = newValue - graphicalPicker.isEnabled = newValue - } - } - - private(set) var type: PickerType - - init(type: DatePickersStackView.PickerType) { - self.type = type - - textualPicker = NSDatePicker() - graphicalPicker = NSDatePicker() - - super.init(frame: .zero) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - textualPicker.delegate = self - graphicalPicker.delegate = self - - switch type { - case .date: - textualPicker.datePickerStyle = .textFieldAndStepper - textualPicker.datePickerElements = .yearMonthDay - - graphicalPicker.datePickerStyle = .clockAndCalendar - graphicalPicker.datePickerElements = .yearMonthDay - case .time: - textualPicker.datePickerStyle = .textFieldAndStepper - textualPicker.datePickerElements = .hourMinuteSecond - - graphicalPicker.datePickerStyle = .clockAndCalendar - graphicalPicker.datePickerElements = .hourMinuteSecond - } - - addArrangedSubview(textualPicker) - addArrangedSubview(graphicalPicker) - - orientation = .vertical - alignment = .centerX - spacing = 10 - translatesAutoresizingMaskIntoConstraints = false - } - - func components(calendar: NSCalendar) -> DateComponents { - switch type { - case .date: - calendar.components([.day, .month, .year], from: textualPicker.dateValue) - case .time: - calendar.components([.hour, .minute, .second], from: textualPicker.dateValue) - } - } - - func datePickerCell( - _ datePickerCell: NSDatePickerCell, - validateProposedDateValue proposedDateValue: - AutoreleasingUnsafeMutablePointer, - timeInterval _: UnsafeMutablePointer? - ) { - if isDateChanging { - return - } - isDateChanging = true - - // sync graphical and textual pickers - let date = proposedDateValue.pointee as Date - if datePickerCell === graphicalPicker.cell { - textualPicker.dateValue = date - } else if datePickerCell === textualPicker.cell { - graphicalPicker.dateValue = date - } - isDateChanging = false - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchController.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchController.swift deleted file mode 100644 index 6fd5b5a..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchController.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// TouchController.swift -// VisualDiffer -// -// Created by davide ficano on 11/03/12. -// Copyright (c) 2012 visualdiffer.com -// - -class TouchController: FileSystemController { - enum TouchDateFromSource: Int { - case otherSide - case userSelection - } - - private let dateFromOtherSideButton: NSButton - private let dateFromUserSelection: NSButton - - private let applyToAllFolderContentsButton: NSButton - - private let pickers: TouchPickersStackView - - /** - * return nil when TouchDateFromSourceOtherSide is selected otherwise the Date - */ - var buildTouchDate: Date? { - if dateFromOtherSideButton.state == .on { - return nil - } - return pickers.touchDate - } - - override init( - executor: TouchFileOperationExecutor, - fileOperationManager: FileOperationManager, - view: FoldersOutlineView, - progressIndicatorController: ProgressIndicatorController, - filteredFileVisible: Bool - ) { - dateFromOtherSideButton = NSButton( - radioButtonWithTitle: NSLocalizedString("Copy Date from the other side", comment: ""), - target: nil, - action: nil - ) - - dateFromUserSelection = NSButton( - radioButtonWithTitle: NSLocalizedString("Set Date to", comment: ""), - target: nil, - action: nil - ) - - applyToAllFolderContentsButton = NSButton( - checkboxWithTitle: NSLocalizedString("Apply to all folder contents", comment: ""), - target: nil, - action: nil - ) - - pickers = TouchPickersStackView() - - super.init( - executor: executor, - fileOperationManager: fileOperationManager, - view: view, - progressIndicatorController: progressIndicatorController, - filteredFileVisible: filteredFileVisible - ) - - setupViews() - } - - override func createWindow() -> NSWindow { - let window = super.createWindow() - - window.setContentSize(NSSize(width: 450, height: 480)) - window.minSize = NSSize(width: 450, height: 480) - window.maxSize = NSSize(width: 450, height: 480) - - return window - } - - override func setupViews() { - super.setupViews() - - dateFromOtherSideButton.target = self - dateFromOtherSideButton.action = #selector(radioButton) - dateFromOtherSideButton.translatesAutoresizingMaskIntoConstraints = false - dateFromOtherSideButton.tag = TouchDateFromSource.otherSide.rawValue - - dateFromUserSelection.target = self - dateFromUserSelection.action = #selector(radioButton) - dateFromUserSelection.translatesAutoresizingMaskIntoConstraints = false - dateFromUserSelection.tag = TouchDateFromSource.userSelection.rawValue - dateFromUserSelection.state = .on - - fileSummary.sizeTotalText.isHidden = true - applyToAllFolderContentsButton.isHidden = fileCount.folders == 0 && fileFilteredCount.folders == 0 - - pickers.touchDate = Date() - } - - override func setupMainStackView() { - mainStackView.addArrangedSubviews([ - operationSummary, - createSeparator(), - dateFromOtherSideButton, - dateFromUserSelection, - pickers, - applyToAllFolderContentsButton, - filtersWarningText, - ]) - - mainStackView.orientation = .vertical - mainStackView.alignment = .leading - mainStackView.spacing = 10 - mainStackView.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - pickers.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor), - pickers.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor), - ]) - } - - func createSeparator() -> NSBox { - let view = NSBox(frame: .zero) - - view.title = "" - view.boxType = .separator - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - override func prepareExecute() { - guard let callerWindow else { - fatalError("Caller window is nil") - } - - progressIndicatorController?.beginSheetModal( - for: callerWindow, - processingItemsCount: totalFiles + totalFolders, - totalSize: 0, - singleItem: false - ) - - fileOperationManager.includesFiltered = fileSummary.checkboxFilteredFiles.state == .on - executor.touchDate = buildTouchDate - executor.includeSubfolders = applyToAllFolderContentsButton.state == .on - - progressIndicatorController?.startRun() - } - - // MARK: - Action Methods - - @objc func radioButton(_ sender: AnyObject) { - guard let sender = sender as? NSButton, - let source = TouchDateFromSource(rawValue: sender.tag) else { - return - } - pickers.isEnabled = switch source { - case .otherSide: - false - case .userSelection: - true - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchFileOperationExecutor.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchFileOperationExecutor.swift deleted file mode 100644 index 8c36b6f..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchFileOperationExecutor.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// TouchFileOperationExecutor.swift -// VisualDiffer -// -// Created by davide ficano on 14/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class TouchFileOperationExecutor: FileOperationExecutor, @unchecked Sendable { - var title = NSLocalizedString("Run", comment: "") - var summary = NSLocalizedString("Set Modification Date for files and folders", comment: "") - var image: NSImage? - var progressLabel = NSLocalizedString("Setting modification date", comment: "") - var prefName: CommonPrefs.Name? - - private let items: [CompareItem] - - // delete operation doesn't use it - var operationOnSingleItem = false - - var touchDate: Date? - var includeSubfolders = false - - init(items: [CompareItem]) { - self.items = items - - image = NSImage(named: VDImageNameDateTime) - } - - func execute(_ manager: FileOperationManagerAction, payload _: Sendable?) { - for item in items { - manager.touch( - item, - includeSubfolders: includeSubfolders, - touch: touchDate - ) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchPickersStackView.swift b/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchPickersStackView.swift deleted file mode 100644 index 846ab58..0000000 --- a/Sources/Features/FoldersCompare/Controller/FileSystemController/Touch/TouchPickersStackView.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// TouchPickersStackView.swift -// VisualDiffer -// -// Created by davide ficano on 04/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -class TouchPickersStackView: NSStackView { - private let datePickers: DatePickersStackView - private let timePickers: DatePickersStackView - - var isEnabled: Bool { - get { - datePickers.isEnabled - } - - set { - datePickers.isEnabled = newValue - timePickers.isEnabled = newValue - } - } - - var touchDate: Date? { - get { - guard let gregorian = NSCalendar(calendarIdentifier: .gregorian) else { - return nil - } - var dateComponents = datePickers.components(calendar: gregorian) - let timeComponents = timePickers.components(calendar: gregorian) - - dateComponents.hour = timeComponents.hour - dateComponents.minute = timeComponents.minute - dateComponents.second = timeComponents.second - - return gregorian.date(from: dateComponents) - } - - set { - if let newValue { - datePickers.dateValue = newValue - timePickers.dateValue = newValue - } - } - } - - init() { - datePickers = DatePickersStackView(type: .date) - timePickers = DatePickersStackView(type: .time) - - super.init(frame: .zero) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - orientation = .horizontal - alignment = .centerY - spacing = 10 - translatesAutoresizingMaskIntoConstraints = false - - addArrangedSubview(datePickers) - addArrangedSubview(timePickers) - - // move timePickers to the top so textual fields are aligned - NSLayoutConstraint.activate([ - timePickers.topAnchor.constraint(equalTo: topAnchor), - ]) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FolderReader/FolderReader+Detached.swift b/Sources/Features/FoldersCompare/Controller/FolderReader/FolderReader+Detached.swift deleted file mode 100644 index 5271da0..0000000 --- a/Sources/Features/FoldersCompare/Controller/FolderReader/FolderReader+Detached.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// FolderReader+Detached.swift -// VisualDiffer -// -// Created by davide ficano on 23/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FolderReader { - /** - * Identical to .start() but runs in separated detached thread - */ - func startDetached( - leftRoot: CompareItem?, - rightRoot: CompareItem?, - leftPath: URL, - rightPath: URL - ) { - DispatchQueue.global(qos: .userInitiated).async { - self.start( - withLeftRoot: leftRoot, - rightRoot: rightRoot, - leftPath: leftPath, - rightPath: rightPath - ) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FolderReader/MainThreadFolderReaderDelegateBridge.swift b/Sources/Features/FoldersCompare/Controller/FolderReader/MainThreadFolderReaderDelegateBridge.swift deleted file mode 100644 index 3783c43..0000000 --- a/Sources/Features/FoldersCompare/Controller/FolderReader/MainThreadFolderReaderDelegateBridge.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// MainThreadFolderReaderDelegateBridge.swift -// VisualDiffer -// -// Created by davide ficano on 22/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -/** - * This bridge is used to call on main thread all FolderReaderDelegate methods - */ -class MainThreadFolderReaderDelegateBridge: FolderReaderDelegate { - private weak var controller: FoldersWindowController? - - init(_ controller: FoldersWindowController) { - self.controller = controller - } - - func isRunning(_: FolderReader) -> Bool { - guard let controller else { - return false - } - - return DispatchQueue.main.sync { - controller.running - } - } - - func progress(_ folderReader: FolderReader, status: FolderReaderStatus) { - guard let controller else { - return - } - - switch status { - case let .will(startAt): - DispatchQueue.main.sync { controller.will(startAt: startAt) } - case let .did(endAt, startedAt): - DispatchQueue.main.sync { controller.did(endAt: endAt, startedAt: startedAt) } - case let .rootFoldersDidRead(folderCount): - DispatchQueue.main.sync { - controller.rootFoldersDidRead(folderReader: folderReader, foldersOnRoot: folderCount) - } - case let .willTraverse(item): - DispatchQueue.main.async { controller.willTraverse(item) } - case let .didTraverse(item): - DispatchQueue.main.async { controller.didTraverse(folderReader: folderReader, item) } - } - } - - func folderReader(_: FolderReader, handleError error: any Error, forPath path: URL) -> Bool { - guard let controller else { - return false - } - - DispatchQueue.main.async { - _ = controller.handleError(error: error, forPath: path) - } - - return true - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FolderSelectionInfo.swift b/Sources/Features/FoldersCompare/Controller/FolderSelectionInfo.swift deleted file mode 100644 index 93432a7..0000000 --- a/Sources/Features/FoldersCompare/Controller/FolderSelectionInfo.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// FolderSelectionInfo.swift -// VisualDiffer -// -// Created by davide ficano on 18/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -struct SelectionType: OptionSet { - let rawValue: Int - - static let nullfile = SelectionType(rawValue: 1 << 0) - static let folder = SelectionType(rawValue: 1 << 1) - static let file = SelectionType(rawValue: 1 << 2) -} - -@MainActor struct FolderSelectionInfo: @preconcurrency CustomDebugStringConvertible { - private(set) var selType: SelectionType = [] - private(set) var nullFilesCount = 0 - private(set) var foldersCount = 0 - private(set) var filesCount = 0 - private(set) var hasValidPaths = false - private(set) var hasMultipleSel = false - - let view: FoldersOutlineView - private(set) var foldersIndexes: IndexSet - private(set) var filesIndexes: IndexSet - - // contains all valid folders and files indexes - private(set) var validObjectsIndexes: [Int] - - init(view: FoldersOutlineView) { - self.view = view - let indexes = view.selectedRowIndexes - var folders = IndexSet() - var files = IndexSet() - hasMultipleSel = !indexes.isEmpty - - validObjectsIndexes = [] - - // Determine if all selected items aren't files - for row in indexes { - guard let item = (view.item(atRow: row) as? VisibleItem)?.item else { - continue - } - - if !item.isValidFile { - nullFilesCount += 1 - } else if item.isFolder { - foldersCount += 1 - folders.insert(row) - validObjectsIndexes.append(row) - } else if item.isFile { - filesCount += 1 - files.insert(row) - validObjectsIndexes.append(row) - } - if item.path != nil { - hasValidPaths = true - } - } - if nullFilesCount > 0 { - selType.insert(.nullfile) - } - if foldersCount > 0 { - selType.insert(.folder) - } - if filesCount > 0 { - selType.insert(.file) - } - foldersIndexes = folders - filesIndexes = files - } - - var debugDescription: String { - String( - format: "selType %ld, nullFilesCount %d, foldersCount %d, folders %@, filesCount %d, files %@, hasMultipleSel %d", - selType.rawValue, - nullFilesCount, - foldersCount, - foldersIndexes.description, - filesCount, - filesIndexes.description, - hasMultipleSel - ) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+BaseFolder.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+BaseFolder.swift deleted file mode 100644 index e67d025..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+BaseFolder.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// FoldersWindowController+BaseFolder.swift -// VisualDiffer -// -// Created by davide ficano on 20/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController { - @objc func setAsBaseFolder(_ sender: AnyObject?) { - if let sender = sender as? NSMenuItem, - isPathControlMenu(sender.tag) { - let leftUrl = leftPanelView.pathView.pathControl.clickedPath - var leftPath: String? - var rightPath: String? - - if let leftUrl { - leftPath = leftUrl.osPath - rightPath = leftVisibleItems?.item.linkedItem?.path - } else { - leftPath = leftVisibleItems?.item.path - rightPath = rightPanelView.pathView.pathControl.clickedPath?.osPath - } - setBaseFolders(leftPath, rightPath: rightPath) - return - } - - let row = lastUsedView.selectedRow - - if row < 0 { - return - } - var leftItem: CompareItem? - var rightItem: CompareItem? - if lastUsedView.side == .left { - if let vi = lastUsedView.item(atRow: row) as? VisibleItem { - let item = vi.item - leftItem = item - rightItem = rightItemOriginal - } - } else { - if let vi = lastUsedView.item(atRow: row) as? VisibleItem { - let item = vi.item - leftItem = leftItemOriginal - rightItem = item - } - } - setBaseFolders(leftItem, right: rightItem) - } - - @objc func setAsBaseFoldersBothSides(_: AnyObject?) { - var leftItem: CompareItem? - var rightItem: CompareItem? - let indexes = lastUsedView.selectedRowIndexes - - if indexes.count == 2 { - if let first = indexes.first, - let vi = lastUsedView.item(atRow: first) as? VisibleItem { - leftItem = vi.item - } - if let last = indexes.last, - let vi = lastUsedView.item(atRow: last) as? VisibleItem { - rightItem = vi.item - } - } else { - let baseView = if lastUsedView.side == .left { - lastUsedView - } else { - lastUsedView.linkedView - } - - if let baseView { - if let vi = baseView.item(atRow: baseView.selectedRow) as? VisibleItem { - leftItem = vi.item - } - if let linkedView = baseView.linkedView, - let vi = linkedView.item(atRow: linkedView.selectedRow) as? VisibleItem { - rightItem = vi.item - } - } - } - setBaseFolders(leftItem, right: rightItem) - } - - @objc func setAsBaseFolderOtherSide(_ sender: AnyObject?) { - if let sender = sender as? NSMenuItem, - isPathControlMenu(sender.tag) { - let leftUrl = leftPanelView.pathView.pathControl.clickedPath - var leftPath: String? - var rightPath: String? - - if let leftUrl { - leftPath = leftVisibleItems?.item.path - rightPath = leftUrl.osPath - } else { - leftPath = rightPanelView.pathView.pathControl.clickedPath?.osPath - rightPath = leftVisibleItems?.item.linkedItem?.path - } - setBaseFolders(leftPath, rightPath: rightPath) - return - } - - let row = lastUsedView.selectedRow - - if row < 0 { - return - } - var leftItem: CompareItem? - var rightItem: CompareItem? - if lastUsedView.side == .left { - leftItem = leftItemOriginal - if let vi = lastUsedView.item(atRow: row) as? VisibleItem { - rightItem = vi.item - } - } else { - if let vi = lastUsedView.item(atRow: row) as? VisibleItem { - leftItem = vi.item - } - rightItem = rightItemOriginal - } - setBaseFolders(leftItem, right: rightItem) - } - - // MARK: - Internal Helpers - - // TODO: check clone - private func setBaseFolders( - _ leftItem: CompareItem?, - right rightItem: CompareItem? - ) { - guard let leftItem, - let rightItem else { - return - } - if leftItem.isSymbolicLink || rightItem.isSymbolicLink { - setBaseFolders(leftItem.path, rightPath: rightItem.path) - } else { - leftItemOriginal = leftItem.cloneValidFiles(nil) - rightItemOriginal = rightItem.cloneValidFiles(nil) - leftItemOriginal?.linkedItem = rightItemOriginal - rightItemOriginal?.linkedItem = leftItemOriginal - - let refreshInfo = RefreshInfo( - initState: false, - realign: true, - expandAllFolders: sessionDiff.expandAllFolders - ) - reloadAll(refreshInfo) - - sessionDiff.leftPath = leftItem.path - sessionDiff.rightPath = rightItem.path - synchronizeWindowTitleWithDocumentName() - } - } - - private func setBaseFolders(_ leftPath: String?, rightPath: String?) { - guard let leftPath, - let rightPath else { - return - } - reloadAll(RefreshInfo( - initState: true, - expandAllFolders: sessionDiff.expandAllFolders - )) - - sessionDiff.leftPath = leftPath - sessionDiff.rightPath = rightPath - synchronizeWindowTitleWithDocumentName() - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Comparison.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Comparison.swift deleted file mode 100644 index 915de52..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Comparison.swift +++ /dev/null @@ -1,300 +0,0 @@ -// -// FoldersWindowController+Comparison.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -import UserNotifications - -extension CompareItem: @unchecked Sendable {} - -public extension FoldersWindowController { - // MARK: - Reload folders - - func reloadAll(_ refreshInfo: RefreshInfo) { - if running { - return - } - - guard let leftPath = sessionDiff.resolvePath( - for: .left, - chooseFileType: .folder, - alwaysResolveSymlinks: CommonPrefs.shared.alwaysResolveSymlinks - ), - let rightPath = sessionDiff.resolvePath( - for: .right, - chooseFileType: .folder, - alwaysResolveSymlinks: CommonPrefs.shared.alwaysResolveSymlinks - ) else { - return - } - - // release previously started bookmarks - SecureBookmark.shared.stopAccessing(url: leftSecureURL) - SecureBookmark.shared.stopAccessing(url: rightSecureURL) - - leftSecureURL = SecureBookmark.shared.secure(fromBookmark: leftPath, startSecured: true) - rightSecureURL = SecureBookmark.shared.secure(fromBookmark: rightPath, startSecured: true) - - if refreshInfo.refreshFolders { - leftItemOriginal = nil - rightItemOriginal = nil - } - - syncFolders(refreshInfo) - } - - /** - * refreshComparison is forced to YES if refreshLeftFolders or refreshRightFolders are YES - * refreshDisplay is forced to YES if refreshComparison is YES - */ - func syncFolders(_ refreshInfo: RefreshInfo) { - guard let leftPath = sessionDiff.leftPath, - let rightPath = sessionDiff.rightPath else { - return - } - let comparatorDelegateBridge = MainThreadComparatorDelegateBridge(self) - - let folderReaderComparator = sessionDiff.comparator( - withDelegate: comparatorDelegateBridge, - bufferSize: CommonPrefs.shared.comparatorBinaryBufferSize - ) - comparator = folderReaderComparator - let folderDelegateBridge = MainThreadFolderReaderDelegateBridge(self) - let filterConfig = FilterConfig( - from: sessionDiff, - showFilteredFiles: showFilteredFiles, - hideEmptyFolders: hideEmptyFolders - ) - - let folderReader = FolderReader( - with: folderDelegateBridge, - comparator: folderReaderComparator, - filterConfig: filterConfig, - refreshInfo: refreshInfo - ) - - folderReader.startDetached( - leftRoot: leftItemOriginal, - rightRoot: rightItemOriginal, - leftPath: URL(filePath: leftPath, directoryHint: .isDirectory), - rightPath: URL(filePath: rightPath, directoryHint: .isDirectory) - ) - } - - // MARK: - Comparison actions - - @objc func startComparison() { - reloadAll(RefreshInfo( - initState: true, - expandAllFolders: sessionDiff.expandAllFolders - )) - } - - @objc func showEmptyFolders(_: AnyObject?) { - hideEmptyFolders.toggle() - - scopeBar.hideEmptyFolders(hideEmptyFolders, informDelegate: false) - - CommonPrefs.shared.hideEmptyFolders = hideEmptyFolders - - reloadAll(RefreshInfo(initState: false)) - } - - @objc func noOrphansFolders(_: AnyObject?) { - let displayOptions = sessionDiff.displayOptions.toggled(.noOrphansFolders) - - sessionDiff.displayOptions = displayOptions - - scopeBar.noOrphansFolders( - displayOptions.contains(.noOrphansFolders), - informDelegate: false - ) - - reloadAll(RefreshInfo(initState: false)) - } - - @objc func selectComparison(_ sender: AnyObject?) { - guard let tag = sender?.selectedItem?.tag as? Int else { - return - } - var compareFlags = sessionDiff.comparatorOptions.withoutMethodFlags - compareFlags.insert(ComparatorOptions(rawValue: tag)) - sessionDiff.comparatorOptions = compareFlags - - let refreshInfo = RefreshInfo( - initState: false, - refreshComparison: true, - expandAllFolders: sessionDiff.expandAllFolders - ) - - reloadAll(refreshInfo) - } - - @objc func refresh(_: AnyObject) { - reloadAll(RefreshInfo( - initState: true, - expandAllFolders: sessionDiff.expandAllFolders - )) - } - - @objc func toggleFilteredFiles(_: AnyObject?) { - showFilteredFiles.toggle() - - scopeBar.showFilteredFiles(showFilteredFiles, informDelegate: false) - - reloadAll(RefreshInfo(initState: false)) - } - - // MARK: - FolderReaderDelegate Bridge - - func isRunning() -> Bool { - running - } - - func will(startAt _: Date) { - PowerAssertion.shared.setDisableSystemSleep(true, with: NSLocalizedString("Reading and comparing folders", comment: "")) - running = true - - setProgressHidden(false) - progressView.updateMessage(sessionDiff.leftPath ?? "") - leftPanelView.pathView.isEnabled = false - rightPanelView.pathView.isEnabled = false - - scopeBar.setEnabledAllGroups(false) - } - - func did(endAt: Date, startedAt: Date) { - PowerAssertion.shared.setDisableSystemSleep(false, with: NSLocalizedString("Reading and comparing folders", comment: "")) - running = false - - let elapsedTime = endAt.timeIntervalSinceReferenceDate - startedAt.timeIntervalSinceReferenceDate - - didComparisonCompleted(elapsedTime.format()) - } - - func rootFoldersDidRead(folderReader: FolderReader, foldersOnRoot: Int) { - leftItemOriginal = folderReader.leftRoot - rightItemOriginal = folderReader.rightRoot - leftVisibleItems = leftItemOriginal?.visibleItem - - // reloadItem requires the parent to be already loaded so we refresh the root here - // queue the operation to avoid deadlock and serialize execution - sortBySessionColumn() - - progressView.setProgress(position: 0, maxValue: Double(foldersOnRoot)) - - leftView.reloadData() - leftView.linkedView?.reloadData() - - if folderReader.refreshInfo.expandAllFolders { - leftView.expandItem(nil, expandChildren: true) - } - } - - func handleError(error: any Error, forPath path: URL) -> Bool { - log(error: (error as NSError).format(withPath: path.osPath)) - - return true - } - - func willTraverse(_ item: CompareItem) { - progressView.updateMessage(item.path ?? item.linkedItem?.path ?? "") - } - - func didTraverse(folderReader: FolderReader, _ item: CompareItem) { - sortBySessionColumn() - - progressView.advanceProgress() - - leftView.reloadItem(item.visibleItem, reloadChildren: true) - leftView.linkedView?.reloadItem(item.visibleItem?.linkedItem, reloadChildren: true) - if folderReader.refreshInfo.expandAllFolders { - leftView.expandItem(item.visibleItem, expandChildren: true) - } - } - - func didComparisonCompleted(_ elapsedTimeText: String) { - let leftSelection = leftView.getSelectedVisibleItems(true) - let rightSelection = rightView.getSelectedVisibleItems(true) - - leftView.reloadData() - rightView.reloadData() - - leftView.select(visibleItems: leftSelection) - rightView.select(visibleItems: rightSelection) - -// selectFirstRow(leftSelection: leftSelection, -// rightSelection: rightSelection) - - updateBottomBar(leftView) - updateBottomBar(rightView) - updateStatusBar() - - scopeBar.setEnabledAllGroups(true) - - setProgressHidden(true) - - leftPanelView.pathView.isEnabled = true - rightPanelView.pathView.isEnabled = true - - // force toolbar to enable items - window?.toolbar?.validateVisibleItems() - - consoleView.log(info: String(format: NSLocalizedString("Comparison completed in %@", comment: ""), elapsedTimeText)) - showCompareCompleteNotification(elapsedTimeText) - } - - func showCompareCompleteNotification(_ text: String) { - guard let document = document as? VDDocument else { - return - } - - let content = UNMutableNotificationContent() - content.title = NSLocalizedString("Folder comparison completed", comment: "") - content.body = String(format: NSLocalizedString("'%@' completed in %@", comment: ""), document.displayName, text) - content.sound = UNNotificationSound.default - - let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) - // ensure the identifier is unique per document - let request = UNNotificationRequest( - identifier: document.uuid, - content: content, - trigger: trigger - ) - - let center = UNUserNotificationCenter.current() - center.add(request, withCompletionHandler: nil) - NSApp.requestUserAttention(.informationalRequest) - } - - // periphery:ignore - private func selectFirstRow( - leftSelection: [VisibleItem], - rightSelection: [VisibleItem] - ) { - let suggestedRow = leftSelection.isEmpty ? -1 : leftView.row(forItem: leftSelection[0]) - let firstVisibleRow = leftView.ensureRowVisibility(suggestedRow: suggestedRow) - - if leftSelection.isEmpty { - leftView.selectRowIndexes(IndexSet(integer: firstVisibleRow), byExtendingSelection: false) - } - if rightSelection.isEmpty { - rightView.selectRowIndexes(IndexSet(integer: firstVisibleRow), byExtendingSelection: false) - } - } - - private func setProgressHidden(_ hidden: Bool) { - if hidden { - differenceCounters.isHidden = false - statusbarText.isHidden = false - progressView.isHidden = true - } else { - differenceCounters.isHidden = true - statusbarText.isHidden = true - progressView.isHidden = false - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+ConsoleViewDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+ConsoleViewDelegate.swift deleted file mode 100644 index c7e5a86..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+ConsoleViewDelegate.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// FoldersWindowController+ConsoleViewDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 08/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController: @preconcurrency ConsoleViewDelegate { - // MARK: - Delegate - - func hide(console: ConsoleView) { - consoleSplitter.toggleSubview(at: 1) - console.focus() - } - - // MARK: - ConsoleSplitView - - func log(error: String) { - showConsoleView() - consoleView.log(error: error) - } - - func showConsoleView() { - consoleSplitter.expandSubview(at: 1) - consoleView.focus() - } - - @objc func toggleLogConsole(_: AnyObject?) { - hide(console: consoleView) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+DisplayFiltersScopeBarDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+DisplayFiltersScopeBarDelegate.swift deleted file mode 100644 index fa8c41b..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+DisplayFiltersScopeBarDelegate.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// FoldersWindowController+DisplayFiltersScopeBarDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 10/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController: @preconcurrency DisplayFiltersScopeBarDelegate { - func displayFiltersScopeBar(_: DisplayFiltersScopeBar, action: DisplayFiltersScopeBarAction, options: [DisplayFiltersScopeBarAttributeKey: Any]?) { - switch action { - case .selectFilter: - select(displayOptions: options?[.filterFlagsDisplayFilters] as? NSNumber) - case .showFiltered: - toggleFilteredFiles(nil) - case .showEmptyFolders: - showEmptyFolders(nil) - case .showNoOrphansFolders: - noOrphansFolders(nil) - } - } - - func select(displayOptions newFlags: NSNumber?) { - guard let newFlags = newFlags?.intValue else { - return - } - let displayOptions = sessionDiff.displayOptions.changeWithoutMethod(newFlags) - sessionDiff.displayOptions = displayOptions - let refreshInfo = RefreshInfo( - initState: false, - expandAllFolders: sessionDiff.expandAllFolders - ) - reloadAll(refreshInfo) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Document.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Document.swift deleted file mode 100644 index af76e02..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Document.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// FoldersWindowController+Document.swift -// VisualDiffer -// -// Created by davide ficano on 12/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController: DiffOpenerDelegate { - override open var document: AnyObject? { - didSet { - // create a shortcut to sessionDiff held by document - if let sessionDiff = (document as? VDDocument)?.sessionDiff { - self.sessionDiff = sessionDiff - setupUIState() - } - } - } - - public func addChildDocument(_ document: VDDocument) { - sessionChildren.append(document) - document.parentSession = self - } - - public func removeChildDocument(_ document: VDDocument) { - let index = sessionChildren.firstIndex { $0 === document } - if let index { - sessionChildren.remove(at: index) - document.parentSession = nil - } - } - - func removeAllChildrenDocuments() { - for document in sessionChildren { - document.parentSession = nil - } - sessionChildren.removeAll() - } - - public func nextDifferenceFiles(from leftPath: String?, rightPath: String?, block: DiffOpenerDelegateBlock) { - findDifference(from: leftPath, rightPath: rightPath, findNext: true, block: block) - } - - public func prevDifferenceFiles(from leftPath: String?, rightPath: String?, block: DiffOpenerDelegateBlock) { - findDifference(from: leftPath, rightPath: rightPath, findNext: false, block: block) - } - - func findDifference(from leftPath: String?, rightPath: String?, findNext: Bool, block: DiffOpenerDelegateBlock) { - var item: CompareItem? - - if let leftPath, !leftPath.isEmpty { - if let leftItemOriginal, - let leftSessionPath = sessionDiff.leftPath, - leftPath.hasPrefix(leftSessionPath) { - item = CompareItem.find(withPath: leftPath, from: leftItemOriginal) - } else if let rightItemOriginal, - let rightSessionPath = sessionDiff.rightPath, - leftPath.hasPrefix(rightSessionPath) { - item = CompareItem.find(withPath: leftPath, from: rightItemOriginal)?.linkedItem - } - } else if let rightPath, !rightPath.isEmpty { - if let leftItemOriginal, - let leftSessionPath = sessionDiff.leftPath, - rightPath.hasPrefix(leftSessionPath) { - item = CompareItem.find(withPath: rightPath, from: leftItemOriginal) - } else if let rightItemOriginal, - let rightSessionPath = sessionDiff.rightPath, - rightPath.hasPrefix(rightSessionPath) { - item = CompareItem.find(withPath: rightPath, from: rightItemOriginal)?.linkedItem - } - } - - var foundItem: (CompareItem, Int)? - - if let item, let parent = item.parent, parent.visibleItem != nil, let vi = item.visibleItem { - foundItem = findNearest( - vi, - parentPath: parent.path, - direction: findNext ? 1 : -1, - limitToCurrentFolder: false - ) - } - - if let (item, row) = foundItem, block(item.path, item.linkedItem?.path) { - leftView.scrollRowToVisible(row) - let indexes = IndexSet(integer: row) - leftView.selectRowIndexes(indexes, byExtendingSelection: false) - leftView.linkedView?.selectRowIndexes(indexes, byExtendingSelection: false) - } - } - - private func findNearest( - _ vi: VisibleItem, - parentPath: String?, - direction: Int, - limitToCurrentFolder: Bool - ) -> (CompareItem, Int)? { - var row = leftView.row(forItem: vi) - - if row < 0 { - return nil - } - while true { - row += direction - guard let vi = leftView.item(atRow: row) as? VisibleItem else { - break - } - let fs1 = vi.item - if limitToCurrentFolder, fs1.isFolder, fs1.path != parentPath { - break - } - if fs1.isFile, fs1.type != .same { - return (fs1, row) - } - } - return nil - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Exclude.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Exclude.swift deleted file mode 100644 index 1e287a8..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Exclude.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// FoldersWindowController+Exclude.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension FoldersWindowController { - @objc func excludeByName(_: AnyObject?) { - var arr = [String]() - - if let exclusionFileFilters = sessionDiff.exclusionFileFilters, - !exclusionFileFilters.isEmpty { - arr.append(exclusionFileFilters) - } - - let fsi = lastUsedView.selectionInfo - let containsFolders = fsi.foldersCount > 0 - - var predicateTemplate: NSPredicate - var propertyKey: (CompareItem) -> String? - if containsFolders { - predicateTemplate = NSPredicate(format: "pathRelativeToRoot == $name") - propertyKey = { $0.pathRelativeToRoot } - } else { - predicateTemplate = NSPredicate(format: "fileName LIKE $name") - propertyKey = { $0.fileName } - } - lastUsedView.enumerateSelectedValidFiles { item, _ in - if let value = propertyKey(item) { - let bindVariables = ["name": value] - let result = predicateTemplate.withSubstitutionVariables(bindVariables) - arr.append(result.description) - } - } - sessionDiff.exclusionFileFilters = arr.joined(separator: " OR ") - reloadAll(RefreshInfo(initState: false)) - } - - @objc func excludeByExt(_: AnyObject?) { - var arr = [String]() - - if let exclusionFileFilters = sessionDiff.exclusionFileFilters, - !exclusionFileFilters.isEmpty { - arr.append(exclusionFileFilters) - } - let predicateTemplate = NSPredicate(format: "fileName ENDSWITH $name") - lastUsedView.enumerateSelectedValidFiles { item, _ in - // use extension - if let path = item.toUrl() { - let pathExtension = String(format: ".%@", path.pathExtension) - let bindVariables = ["name": pathExtension] - let result = predicateTemplate.withSubstitutionVariables(bindVariables) - arr.append(result.description) - } - } - sessionDiff.exclusionFileFilters = arr.joined(separator: " OR ") - reloadAll(RefreshInfo(initState: false)) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FileSystemControllerDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FileSystemControllerDelegate.swift deleted file mode 100644 index b05e11e..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FileSystemControllerDelegate.swift +++ /dev/null @@ -1,163 +0,0 @@ -// -// FoldersWindowController+FileSystemControllerDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 19/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController: @preconcurrency FileSystemControllerDelegate { - @objc func copyFiles(_: AnyObject?) { - guard let vi = lastUsedView.dataSource?.outlineView?(lastUsedView, child: 0, ofItem: nil) as? VisibleItem, - let root = vi.item.parent, - let srcBaseDir = root.path, - let destBaseDir = root.linkedItem?.path else { - return - } - let executor = CopyFileOperationExecutor( - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - items: lastUsedView.selectedItems(), - side: lastUsedView.side - ) - run(executor) - } - - @objc func deleteFiles(_: AnyObject?) { - guard let vi = lastUsedView.dataSource?.outlineView?(lastUsedView, child: 0, ofItem: nil) as? VisibleItem, - let root = vi.item.parent, - let srcBaseDir = root.path else { - return - } - let executor = DeleteFileOperationExecutor( - srcBaseDir: srcBaseDir, - items: lastUsedView.selectedItems() - ) - run(executor) - } - - @objc func moveFiles(_: AnyObject?) { - guard let vi = lastUsedView.dataSource?.outlineView?(lastUsedView, child: 0, ofItem: nil) as? VisibleItem, - let root = vi.item.parent, - let srcBaseDir = root.path, - let destBaseDir = root.linkedItem?.path else { - return - } - let executor = MoveFileOperationExecutor( - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - items: lastUsedView.selectedItems(), - side: lastUsedView.side - ) - run(executor) - } - - @objc func syncFiles(_: AnyObject?) { - guard let window else { - return - } - - let pic = ProgressIndicatorController() - progressIndicatorController = pic - let delegate = FileOperationManagerDelegateImpl(progressIndicatorController: pic) - let executor = SyncFileOperationExecutor(side: lastUsedView.side) - let fileSystemController = SyncFileController( - executor: executor, - fileOperationManager: createLocalFileManager(delegate: delegate), - view: lastUsedView, - progressIndicatorController: pic - ) - - fileSystemController.delegate = self - fileSystemController.beginSheetModal(for: window) - } - - @objc func setModificationDate(_: AnyObject) { - guard let window else { - return - } - - let pic = ProgressIndicatorController() - progressIndicatorController = pic - let delegate = FileOperationManagerDelegateImpl(progressIndicatorController: pic) - let executor = TouchFileOperationExecutor(items: lastUsedView.selectedItems()) - let fileSystemController = TouchController( - executor: executor, - fileOperationManager: createLocalFileManager(delegate: delegate), - view: lastUsedView, - progressIndicatorController: pic, - filteredFileVisible: showFilteredFiles - ) - fileSystemController.delegate = self - fileSystemController.beginSheetModal(for: window) - } - - // MARK: - File Operations Delegate - - @MainActor public func fileSystem( - _: FileSystemController, - restoreSelection selectedVisibleItems: [VisibleItem], - errors: [any Error]? - ) { - // reload data before working on selection to prevent errors - // on not updated rows count (see bug 0000063) - leftView.reloadData() - rightView.reloadData() - - // Deselect all items otherwise after removing items from tree - // the visible items will contain incorrect selection - leftView.deselectAll(nil) - rightView.deselectAll(nil) - - updateBottomBar(leftView) - updateBottomBar(rightView) - updateStatusBar() - - lastUsedView.restoreSelectionAndFocusPosition(selectedVisibleItems) - - let suggestedRow = selectedVisibleItems.isEmpty ? -1 : leftView.row(forItem: selectedVisibleItems[0]) - leftView.ensureRowVisibility(suggestedRow: suggestedRow) - showErrorsAfterFileSystemOperation(errors) - } - - // MARK: - Internal Helpers - - private func showErrorsAfterFileSystemOperation(_: [any Error]?) { - // Bouncing Dock Icon on complete - NSApp.requestUserAttention(.informationalRequest) - } - - private func run(_ executor: some FileOperationExecutor) { - guard let window else { - return - } - let pic = ProgressIndicatorController() - progressIndicatorController = pic - let delegate = FileOperationManagerDelegateImpl(progressIndicatorController: pic) - let fileSystemController = FileSystemController( - executor: executor, - fileOperationManager: createLocalFileManager(delegate: delegate), - view: lastUsedView, - progressIndicatorController: pic, - filteredFileVisible: showFilteredFiles - ) - fileSystemController.delegate = self - fileSystemController.beginSheetModal(for: window) - } - - private func createLocalFileManager(delegate: FileOperationManagerDelegate) -> FileOperationManager { - guard let comparator else { - fatalError("Comparator not found") - } - let config = FilterConfig( - from: sessionDiff, - showFilteredFiles: showFilteredFiles, - hideEmptyFolders: hideEmptyFolders - ) - return FileOperationManager( - filterConfig: config, - comparator: comparator, - delegate: delegate - ) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FoldersOutlineView.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FoldersOutlineView.swift deleted file mode 100644 index 232674b..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FoldersOutlineView.swift +++ /dev/null @@ -1,352 +0,0 @@ -// -// FoldersWindowController+FoldersOutlineView.swift -// VisualDiffer -// -// Created by davide ficano on 08/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -extension FoldersWindowController: NSOutlineViewDelegate, - NSOutlineViewDataSource, - FoldersOutlineViewDelegate, - OutlineViewItemDelegate { - @objc var leftView: FoldersOutlineView { - leftPanelView.treeView - } - - @objc var rightView: FoldersOutlineView { - rightPanelView.treeView - } - - // MARK: - NSOutlineView delegates messages - - public func outlineView(_: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - // left and right are symmetric so subfolders count is identical and we don't need to check the outlineView - guard let children = item as? VisibleItem ?? leftVisibleItems else { - return 0 - } - - return children.children.count - } - - public func outlineView(_: NSOutlineView, isItemExpandable item: Any) -> Bool { - // left and right are symmetric so expandable status is identical and we don't need to check the outlineView - guard let child = (item as? VisibleItem)?.item else { - return false - } - - if child.isFolder { - if child.isSymbolicLink { - return sessionDiff.followSymLinks - } - return true - } - return false - } - - public func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { - guard let leftVisibleItems, - let vi = outlineView == leftView ? leftVisibleItems : leftVisibleItems.linkedItem else { - fatalError("leftVisibleItems can't be nil") - } - - let children = item as? VisibleItem ?? vi - let arr = children.children - - if index >= arr.count { - #if DEBUG - if let badItem = vi.item.path != nil ? vi.item : vi.linkedItem?.item { - Logger.ui.error("Saved from array out of bound for index \(index) path \(badItem.path ?? "") subs count \(badItem.visibleItem?.children.count ?? 0)") - } - #endif - return arr.last ?? children - } - - return arr[index] - } - - public func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { - guard let tableColumn, - let itemCompare = (item as? VisibleItem)?.item, - let result = outlineView.makeView( - withIdentifier: tableColumn.identifier, - owner: nil - ) as? CompareItemTableCellView else { - return nil - } - - result.text.stringValue = "" - - if tableColumn.identifier == .Folders.cellName { - result.fileName( - itemCompare, - font: currentFont, - isExpanded: outlineView.isItemExpanded(item), - followSymLinks: sessionDiff.followSymLinks, - hideEmptyFolders: hideEmptyFolders - ) - } else if tableColumn.identifier == .Folders.cellSize { - result.fileSize( - itemCompare, - font: currentFont, - columnWidth: tableColumn.width - ) - } else if tableColumn.identifier == .Folders.cellModified { - result.fileDate( - itemCompare, - date: itemCompare.fileModificationDate, - font: currentFont, - dateFormat: CommonPrefs.shared.folderViewDateFormat as String - ) - } - - // No matter if the row is selected, its color is taken from the status - let type = itemCompare.compareChangeType( - tableColumn.identifier, - followSymLinks: sessionDiff.followSymLinks - ) - if type != .unknown { - result.text.textColor = CommonPrefs.shared.changeTypeColor(type)?.text - } - return result - } - - public func outlineView(_: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { - // draw the selection - let result = CompareItemTableRowView(frame: .zero) - result.item = (item as? VisibleItem)?.item - - return result - } - - public func outlineViewColumnDidResize(_ notification: Notification) { - // don't resize other view if alternate key is down - let isOptionDown = NSApp.currentEvent?.modifierFlags.contains(.option) ?? false - if dontResizeColumns || isOptionDown { - return - } - - guard let column = notification.userInfo?["NSTableColumn"] as? NSTableColumn, - let view = (notification.object as? FoldersOutlineView)?.linkedView as? FoldersOutlineView else { - return - } - - dontResizeColumns = true - view.tableColumn(withIdentifier: column.identifier)?.width = column.width - dontResizeColumns = false - } - - public func outlineView(_: NSOutlineView, didAdd rowView: NSTableRowView, forRow _: Int) { - // folders background is not changed so check if this is a file (or a filtered file/folder) - if let item = (rowView as? CompareItemTableRowView)?.item, - item.isValidFile, item.isFile || item.isFiltered { - let type = item.compareChangeType( - nil, - followSymLinks: sessionDiff.followSymLinks - ) - if type != .unknown { - if let color = CommonPrefs.shared.changeTypeColor(type)?.background { - rowView.backgroundColor = color - } - } - } - } - - public func outlineView(_ outlineView: NSOutlineView, sortDescriptorsDidChange _: [NSSortDescriptor]) { - if leftVisibleItems == nil { - return - } - if running { - return - } - if outlineView.sortDescriptors.isEmpty { - return - } - - guard let folderView = outlineView as? FoldersOutlineView else { - return - } - - // remove the sort indicator from the other side - folderView.linkedView?.sortDescriptors = [] - - sessionDiff.updateSortColumn( - from: outlineView.sortDescriptors[0], - side: outlineView == leftView ? .left : .right - ) - - sortBySessionColumn() - - leftView.reloadData() - rightView.reloadData() - } - - @objc func sortBySessionColumn() { - guard let leftVisibleItems else { - return - } - - guard let vi = sessionDiff.currentSortSide == .left ? leftVisibleItems : leftVisibleItems.linkedItem else { - return - } - - let ascending = sessionDiff.isCurrentSortAscending - - switch sessionDiff.currentSortColumn { - case .name: - vi.sort(byFileName: ascending, ignoreCase: true, followSymLinks: sessionDiff.followSymLinks) - case .size: - vi.sort(byFileSize: ascending, ignoreCase: true) - case .modificationDate: - vi.sort(byDate: ascending, ignoreCase: true) - } - } - - // MARK: - Drag&Drop - - public func outlineView(_ outlineView: NSOutlineView, validateDrop info: any NSDraggingInfo, proposedItem _: Any?, proposedChildIndex _: Int) -> NSDragOperation { - let pasteboard = info.draggingPasteboard - var result: NSDragOperation = [] - - if (info.draggingSource as? FoldersOutlineView) === outlineView { - // The drag is originating from ourselves, discard it - return result - } - outlineView.setDropItem(nil, dropChildIndex: NSOutlineViewDropOnItemIndex) - - if pasteboard.availableType(from: [.fileURL]) == nil { - return result - } - guard let arr = pasteboard.readObjects(forClasses: [NSURL.self]) as? [URL] else { - return result - } - let path1 = arr[0].osPath - var isDir = ObjCBool(false) - let isValidPath1 = FileManager.default.fileExists(atPath: path1, isDirectory: &isDir) && isDir.boolValue - - if arr.count < 2 { - if isValidPath1 { - result = .copy - } - } else { - let path2 = arr[1].osPath - let isValidPath2 = FileManager.default.fileExists(atPath: path2, isDirectory: &isDir) && isDir.boolValue - if isValidPath1, isValidPath2 { - result = .copy - } - } - - return result - } - - public func outlineView(_ outlineView: NSOutlineView, acceptDrop info: any NSDraggingInfo, item _: Any?, childIndex _: Int) -> Bool { - guard let view = outlineView as? FoldersOutlineView else { - return false - } - - let pasteboard = info.draggingPasteboard - - if pasteboard.availableType(from: [.fileURL]) == nil { - return false - } - guard let arr = pasteboard.readObjects(forClasses: [NSURL.self]) as? [URL] else { - return false - } - if arr.count < 2 { - if let path = arr.last?.osPath { - if view.side == .left { - sessionDiff.leftPath = path - } else { - sessionDiff.rightPath = path - } - } - } else { - sessionDiff.leftPath = arr[0].osPath - sessionDiff.rightPath = arr[1].osPath - } - - reloadAll(RefreshInfo( - initState: true, - expandAllFolders: sessionDiff.expandAllFolders - )) - - synchronizeWindowTitleWithDocumentName() - - return true - } - - public func outlineView(_: NSOutlineView, pasteboardWriterForItem item: Any) -> (any NSPasteboardWriting)? { - guard let vi = item as? VisibleItem, - let url = vi.item.toUrl() else { - return nil - } - - return url as NSURL - } - - // MARK: - FoldersOutlineViewDelegate - - public func foldersOutlineView(_ view: FoldersOutlineView, doubleClickFileObject clickedRow: Int) { - guard let itemRow = view.item(atRow: clickedRow) as? VisibleItem else { - return - } - let leftItem = view.side == .left ? itemRow.item : itemRow.item.linkedItem - guard let leftItem, - let rightItem = leftItem.linkedItem else { - return - } - - do { - if let document = try VDDocumentController.shared.openDifferDocument( - leftUrl: leftItem.toUrl(), - rightUrl: rightItem.toUrl() - ) { - addChildDocument(document) - } - } catch { - NSAlert(error: error).runModal() - } - } - - public func selectionChanged(in view: FoldersOutlineView) { - // we can't use outlineViewSelectionDidChange because it is called - // before the notification declared into outline subclass - // So we use our notification sent from outline subclass - updateBottomBar(view) - updateStatusBar() - previewPanel?.reloadData() - } - - public func setLastUsedViewResponder(_ view: FoldersOutlineView) { - lastUsedView = view - - if let toolbarItems = window?.toolbar?.visibleItems { - for item in toolbarItems { - updateToolbarButton(item) - } - } - } - - // MARK: - OutlineViewExpandItemDelegate implementation - - public func itemDidExpand(_ item: Any?, outlineView: NSOutlineView) { - let row = outlineView.row(forItem: item) - // View based TableView must be informed to redraw the view - // otherwise the folder image doesn't toggle its expanded/collapsed status - // For view based TableView the outlineView.reloadItem() method seems not be called (is it a bug???) - // so we reload using [outlineView reloadDataForRowIndexes:columnIndexes] - outlineView.reloadData( - forRowIndexes: IndexSet(integer: row), - columnIndexes: IndexSet(integersIn: 0 ..< outlineView.numberOfColumns) - ) - if let outlineView = outlineView as? FoldersOutlineView { - updateBottomBar(outlineView) - } - } - - public func itemDidCollapse(_ item: Any?, outlineView: NSOutlineView) { - itemDidExpand(item, outlineView: outlineView) - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FoldersOutlineViewContextMenu.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FoldersOutlineViewContextMenu.swift deleted file mode 100644 index 239e222..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+FoldersOutlineViewContextMenu.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// FoldersWindowController+FoldersOutlineViewContextMenu.swift -// VisualDiffer -// -// Created by davide ficano on 19/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController: FoldersOutlineViewContextMenu { - @objc func compareFiles(_: AnyObject?) { - let leftSelItems = leftView.selectedItems() - let rightSelItems = rightView.selectedItems() - var leftItem: CompareItem? - var rightItem: CompareItem? - - switch leftSelItems.count { - case 2: - leftItem = leftSelItems[0] - rightItem = leftSelItems[1] - case 1: - leftItem = leftSelItems.last - rightItem = rightSelItems.last - case 0: - leftItem = rightSelItems[0] - rightItem = rightSelItems[1] - default: - return - } - do { - _ = try VDDocumentController.shared.openDifferDocument( - leftUrl: leftItem?.toUrl(), - rightUrl: rightItem?.toUrl() - ) - } catch { - NSAlert(error: error).runModal() - } - } - - @objc func compareFolders(_ sender: AnyObject?) { - compareFiles(sender) - } - - @objc func copyFileNames(_: AnyObject?) { - lastUsedView.copySelectedAsFileNames() - } - - @objc func copy(_ sender: AnyObject?) { - copyFullPaths(sender) - } - - @objc func copyFullPaths(_: AnyObject?) { - lastUsedView.copySelectedAsFullPaths() - } - - @objc func expandSelectedSubfolders(_: AnyObject?) { - lastUsedView.expandSelectedSubfolders() - - // expand also selected rows on linkedView that may be different for those selected on this view - lastUsedView.linkedView?.expandSelectedSubfolders() - } - - @objc func popupOpenWithApp(_: AnyObject?) { - // Make Cocoa happy otherwise without action the menuitem is always grayed - } - - @objc func showInFinder(_: AnyObject?) { - lastUsedView.showSelectedInFinder() - } -} - -extension FoldersOutlineView { - func expandSelectedSubfolders() { - for row in selectedRowIndexes.reversed() { - let item = item(atRow: row) - expandItem(item, expandChildren: true) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Menu.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Menu.swift deleted file mode 100644 index 5033abc..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Menu.swift +++ /dev/null @@ -1,399 +0,0 @@ -// -// FoldersWindowController+Menu.swift -// VisualDiffer -// -// Created by davide ficano on 13/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController { - // swiftlint:disable:next function_body_length - private static func editMenu() -> NSMenu { - let menu = NSMenu(title: NSLocalizedString("Edit", comment: "")) - - menu.addItem( - withTitle: NSLocalizedString("Cut", comment: ""), - action: #selector(NSText.cut(_:)), - keyEquivalent: "x" - ) - menu.addItem( - withTitle: NSLocalizedString("Copy Paths", comment: ""), - action: #selector(copy(_:)), - keyEquivalent: "c" - ) - - let copyFileNames = NSMenuItem( - title: NSLocalizedString("Copy File Names", comment: ""), - action: #selector(copyFileNames), - keyEquivalent: "c" - ) - copyFileNames.isAlternate = true - copyFileNames.keyEquivalentModifierMask = [.option, .command] - menu.addItem(copyFileNames) - - // TODO: copy urls no longer work for some sandbox problem so we disable it entirely - // menu.addItem(withTitle: NSLocalizedString("Copy URLs", comment: ""), action:#selector(copyUrls), keyEquivalent:"u") - menu.addItem( - withTitle: NSLocalizedString("Paste", comment: ""), - action: #selector(NSText.paste), - keyEquivalent: "v" - ) - menu.addItem( - withTitle: NSLocalizedString("Delete", comment: ""), - action: #selector(NSText.delete), - keyEquivalent: "" - ) - - menu.addItem(NSMenuItem.separator()) - menu.addItem( - withTitle: NSLocalizedString("Select All", comment: ""), - action: #selector(selectAll), - keyEquivalent: "a" - ) - - let selectAllBothSides = NSMenuItem( - title: NSLocalizedString("Select All Both Sides", comment: ""), - action: #selector(selectAllBothSides), - keyEquivalent: "a" - ) - selectAllBothSides.isAlternate = true - selectAllBothSides.keyEquivalentModifierMask = [.option, .command] - menu.addItem(selectAllBothSides) - - menu.addItem( - withTitle: NSLocalizedString("Select All Files", comment: ""), - action: #selector(selectAllFiles), - keyEquivalent: "e" - ) - - let selectAllFilesBoth = NSMenuItem( - title: NSLocalizedString("Select All Files Both Sides", comment: ""), - action: #selector(selectAllFiles), - keyEquivalent: "e" - ) - selectAllFilesBoth.tag = SelectionSide.both.rawValue - selectAllFilesBoth.isAlternate = true - selectAllFilesBoth.keyEquivalentModifierMask = [.option, .command] - menu.addItem(selectAllFilesBoth) - - menu.addItem( - withTitle: NSLocalizedString("Select All Folders", comment: ""), - action: #selector(selectAllFolders), - keyEquivalent: "t" - ) - - let selectAllFoldersBoth = NSMenuItem( - title: NSLocalizedString("Select All Folders Both Sides", comment: ""), - action: #selector(selectAllFolders), - keyEquivalent: "t" - ) - selectAllFoldersBoth.tag = SelectionSide.both.rawValue - selectAllFoldersBoth.isAlternate = true - selectAllFoldersBoth.keyEquivalentModifierMask = [.option, .command] - menu.addItem(selectAllFoldersBoth) - - // Select Newer submenu - let selectNewerItem = NSMenuItem( - title: NSLocalizedString("Select Newer", comment: ""), - action: nil, - keyEquivalent: "" - ) - let selectNewerSub = NSMenu(title: NSLocalizedString("Select Newer", comment: "")) - selectNewerSub.addItem( - withTitle: NSLocalizedString("Left Side", comment: ""), - action: #selector(selectNewer), - keyEquivalent: "1" - ).tag = SelectionSide.left.rawValue - selectNewerSub.addItem( - withTitle: NSLocalizedString("Right Side", comment: ""), - action: #selector(selectNewer), - keyEquivalent: "2" - ).tag = SelectionSide.right.rawValue - selectNewerSub.addItem( - withTitle: NSLocalizedString("Both Sides", comment: ""), - action: #selector(selectNewer), - keyEquivalent: "3" - ).tag = SelectionSide.both.rawValue - menu.setSubmenu(selectNewerSub, for: selectNewerItem) - menu.addItem(selectNewerItem) - - // Select Orphans submenu - let selectOrphansItem = NSMenuItem( - title: NSLocalizedString("Select Orphans", comment: ""), - action: nil, - keyEquivalent: "" - ) - let selectOrphansSub = NSMenu(title: NSLocalizedString("Select Orphans", comment: "")) - selectOrphansSub.addItem( - withTitle: NSLocalizedString("Left Side", comment: ""), - action: #selector(selectOrphans), - keyEquivalent: "4" - ).tag = SelectionSide.left.rawValue - selectOrphansSub.addItem( - withTitle: NSLocalizedString("Right Side", comment: ""), - action: #selector(selectOrphans), - keyEquivalent: "5" - ).tag = SelectionSide.right.rawValue - selectOrphansSub.addItem( - withTitle: NSLocalizedString("Both Sides", comment: ""), - action: #selector(selectOrphans), - keyEquivalent: "6" - ).tag = SelectionSide.both.rawValue - menu.setSubmenu(selectOrphansSub, for: selectOrphansItem) - menu.addItem(selectOrphansItem) - - menu.addItem( - withTitle: NSLocalizedString("Invert Selection", comment: ""), - action: #selector(invertSelection), - keyEquivalent: "i" - ) - - let invertBoth = NSMenuItem( - title: NSLocalizedString("Invert Selection Both Sides", comment: ""), - action: #selector(invertSelection), - keyEquivalent: "i" - ) - invertBoth.tag = SelectionSide.both.rawValue - invertBoth.isAlternate = true - invertBoth.keyEquivalentModifierMask = [.option, .command] - menu.addItem(invertBoth) - - menu.addItem(NSMenuItem.separator()) - - // Find submenu - let findItem = NSMenuItem( - title: NSLocalizedString("Find", comment: ""), - action: nil, - keyEquivalent: "" - ) - let findSub = NSMenu(title: NSLocalizedString("Find", comment: "")) - findSub.addItem( - withTitle: NSLocalizedString("Find...", comment: ""), - action: #selector(find), - keyEquivalent: "f" - ) - findSub.addItem( - withTitle: NSLocalizedString("Find Next", comment: ""), - action: #selector(findNext), - keyEquivalent: "g" - ) - findSub.addItem( - withTitle: NSLocalizedString("Find Previous", comment: ""), - action: #selector(findPrevious), - keyEquivalent: "G" - ) - menu.setSubmenu(findSub, for: findItem) - menu.addItem(findItem) - - return menu - } - - private static func actionsMenu() -> NSMenu { - let menu = NSMenu(title: NSLocalizedString("Actions", comment: "")) - - menu.addItem( - withTitle: NSLocalizedString("Set as Base Folder", comment: ""), - action: #selector(setAsBaseFolder), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Set as Base Folder on the Other Side", comment: ""), - action: #selector(setAsBaseFolderOtherSide), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Set as Base Folder Both Sides", comment: ""), - action: #selector(setAsBaseFoldersBothSides), - keyEquivalent: "" - ) - - menu.addItem(NSMenuItem.separator()) - - menu.addItem( - withTitle: NSLocalizedString("Compare Files", comment: ""), - action: #selector(compareFiles), - keyEquivalent: "" - ) - let copyFiles = NSMenuItem( - title: NSLocalizedString("Copy Files...", comment: ""), - action: #selector(copyFiles), - keyEquivalent: KeyEquivalent.f5 - ) - copyFiles.keyEquivalentModifierMask = [] - menu.addItem(copyFiles) - - let syncFiles = NSMenuItem( - title: NSLocalizedString("Sync Files...", comment: ""), - action: #selector(syncFiles), - keyEquivalent: KeyEquivalent.f6 - ) - syncFiles.keyEquivalentModifierMask = [] - menu.addItem(syncFiles) - - menu.addItem( - withTitle: NSLocalizedString("Delete Files...", comment: ""), - action: #selector(deleteFiles), - keyEquivalent: KeyEquivalent.deleteBackspace - ) - menu.addItem( - withTitle: NSLocalizedString("Move Files...", comment: ""), - action: #selector(moveFiles), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Set Modification Date...", comment: ""), - action: #selector(setModificationDate), - keyEquivalent: "" - ) - - menu.addItem(NSMenuItem.separator()) - - menu.addItem( - withTitle: NSLocalizedString("Copy Filenames", comment: ""), - action: #selector(copyFileNames), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Show in Finder", comment: ""), - action: #selector(showInFinder), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Open With", comment: ""), - action: #selector(popupOpenWithApp), - keyEquivalent: "" - ) - - let quickLook = NSMenuItem( - title: NSLocalizedString("Quick Look", comment: ""), - action: #selector(togglePreviewPanel), - keyEquivalent: "y" - ) - quickLook.image = NSImage(named: NSImage.quickLookTemplateName) - menu.addItem(quickLook) - - return menu - } - - private static func viewMenu() -> NSMenu { - let menu = NSMenu(title: NSLocalizedString("View", comment: "")) - - menu.addItem( - withTitle: NSLocalizedString("Set Left Read-Only", comment: ""), - action: #selector(setLeftReadOnly), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Set Right Read-Only", comment: ""), - action: #selector(setRightReadOnly), - keyEquivalent: "" - ) - menu.addItem(NSMenuItem.separator()) - - menu.addItem( - withTitle: NSLocalizedString("Show Filtered Files", comment: ""), - action: #selector(toggleFilteredFiles), - keyEquivalent: "" - ) - menu.addItem( - withTitle: NSLocalizedString("Show Empty Folders", comment: ""), - action: #selector(showEmptyFolders), - keyEquivalent: "" - ) - - menu.addItem( - withTitle: NSLocalizedString("Expand Selected Subfolders", comment: ""), - action: #selector(expandSelectedSubfolders), - keyEquivalent: "*" - ) - let expandAllItem = menu.addItem( - withTitle: NSLocalizedString("Expand All", comment: ""), - action: #selector(expandAllFolders), - keyEquivalent: "+" - ) - expandAllItem.keyEquivalentModifierMask = .control - - let collapseAllItem = menu.addItem( - withTitle: NSLocalizedString("Collapse All", comment: ""), - action: #selector(collapseAllFolders), - keyEquivalent: "-" - ) - collapseAllItem.keyEquivalentModifierMask = .control - - menu.addItem( - withTitle: NSLocalizedString("Swap Sides", comment: ""), - action: #selector(swapSides), - keyEquivalent: "" - ) - - menu.addItem( - withTitle: NSLocalizedString("Refresh", comment: ""), - action: #selector(refresh), - keyEquivalent: "r" - ) - menu.addItem(NSMenuItem.separator()) - - // Font submenu - let fontItem = NSMenuItem( - title: NSLocalizedString("Font", comment: ""), - action: nil, - keyEquivalent: "" - ) - let fontSub = NSMenu(title: NSLocalizedString("Font", comment: "")) - fontSub.addItem( - withTitle: NSLocalizedString("Larger", comment: ""), - action: #selector(zoomLargerFont), - keyEquivalent: "+" - ) - fontSub.addItem( - withTitle: NSLocalizedString("Smaller", comment: ""), - action: #selector(zoomSmallerFont), - keyEquivalent: "-" - ) - fontSub.addItem(NSMenuItem.separator()) - fontSub.addItem( - withTitle: NSLocalizedString("Reset", comment: ""), - action: #selector(zoomResetFont), - keyEquivalent: "0" - ) - menu.setSubmenu(fontSub, for: fontItem) - menu.addItem(fontItem) - - let logConsole = NSMenuItem( - title: NSLocalizedString("Show Log Console", comment: ""), - action: #selector(toggleLogConsole), - keyEquivalent: "l" - ) - logConsole.keyEquivalentModifierMask = [.option, .command] - menu.addItem(logConsole) - - let toolbarItem = NSMenuItem( - title: NSLocalizedString("Show Toolbar", comment: ""), - action: #selector(NSWindow.toggleToolbarShown(_:)), - keyEquivalent: "t" - ) - toolbarItem.keyEquivalentModifierMask = [.option, .command] - menu.addItem(toolbarItem) - - menu.addItem( - withTitle: NSLocalizedString("Customize Toolbar…", comment: ""), - action: #selector(NSWindow.runToolbarCustomizationPalette(_:)), - keyEquivalent: "" - ) - return menu - } - - @objc static func switchMenu() { - @MainActor enum StaticMenus { - static let edit = FoldersWindowController.editMenu() - static let actions = FoldersWindowController.actionsMenu() - static let view = FoldersWindowController.viewMenu() - } - guard let mainMenu = NSApp.mainMenu else { - return - } - mainMenu.item(withTag: MainMenu.edit.rawValue)?.submenu = StaticMenus.edit - mainMenu.item(withTag: MainMenu.actions.rawValue)?.submenu = StaticMenus.actions - mainMenu.item(withTag: MainMenu.view.rawValue)?.submenu = StaticMenus.view - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+MenuDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+MenuDelegate.swift deleted file mode 100644 index 108044f..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+MenuDelegate.swift +++ /dev/null @@ -1,230 +0,0 @@ -// -// FoldersWindowController+MenuDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 11/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Quartz - -extension FoldersWindowController: NSMenuDelegate, - NSMenuItemValidation, - TableViewContextMenuDelegate { - // swiftlint:disable:next function_body_length - public func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - if running { - return false - } - if isPathControlMenu(menuItem.tag) { - return true - } - let action = menuItem.action - let fsi = lastUsedView.selectionInfo - - if action == #selector(toggleFilteredFiles) { - if showFilteredFiles { - menuItem.title = NSLocalizedString("Hide Filtered Files", comment: "") - } else { - menuItem.title = NSLocalizedString("Show Filtered Files", comment: "") - } - return true - } else if action == #selector(setLeftReadOnly) { - menuItem.state = sessionDiff.leftReadOnly ? .on : .off - return true - } else if action == #selector(setRightReadOnly) { - menuItem.state = sessionDiff.rightReadOnly ? .on : .off - return true - } else if action == #selector(expandSelectedSubfolders) { - return fsi.validateExpandSelectedSubfolders() - } else if action == #selector(showInFinder) { - return fsi.validateShowInFinder() - } else if action == #selector(copyFiles) { - switch fsi.view.side { - case .left: - menuItem.title = NSLocalizedString("Copy to Right...", comment: "") - case .right: - menuItem.title = NSLocalizedString("Copy to Left...", comment: "") - } - return fsi.validateCopyFiles(sessionDiff) - } else if action == #selector(deleteFiles) { - return fsi.validateDeleteFiles(sessionDiff) - } else if action == #selector(copyFullPaths) - || action == #selector(copyFileNames) - || action == #selector(copy(_:)) - || action == #selector(copyUrls) { - return fsi.validateClipboardCopy() - } else if action == #selector(setAsBaseFolder) { - return fsi.validateSetAsBaseFolder() - } else if action == #selector(setAsBaseFolderOtherSide) { - return fsi.validateSetAsBaseFolderOtherSide() - } else if action == #selector(setAsBaseFoldersBothSides) { - return fsi.validateSetAsBaseFoldersBothSides() - } else if action == #selector(compareFiles) { - return fsi.validateCompareFiles() - } else if action == #selector(excludeByName) { - var fileName: String? = "" - if !fsi.validateExclude(byName: &fileName) { - return false - } - if let fileName { - menuItem.title = String(format: NSLocalizedString("Exclude '%@'", comment: ""), fileName) - } - return true - } else if action == #selector(excludeByExt) { - var excludedExt: String? = "" - let isValid = fsi.validateExclude(byExt: &excludedExt) - if isValid, - let excludedExt { - menuItem.title = String(format: NSLocalizedString("Exclude all '*.%@'", comment: ""), excludedExt) - } - return isValid - } else if action == #selector(syncFiles) { - switch fsi.view.side { - case .left: - menuItem.title = NSLocalizedString("Sync to Right...", comment: "") - case .right: - menuItem.title = NSLocalizedString("Sync to Left...", comment: "") - } - return fsi.validateSyncFiles(sessionDiff) - } else if action == #selector(showEmptyFolders) { - if hideEmptyFolders { - menuItem.title = NSLocalizedString("Show Empty Folders", comment: "") - } else { - menuItem.title = NSLocalizedString("Hide Empty Folders", comment: "") - } - return true - } else if action == #selector(popupOpenWithApp) { - var selectedPath: String? = "" - let isValid = fsi.validateOpen(withApp: &selectedPath) - - if isValid { - let url: URL? = if let selectedPath { - URL(filePath: selectedPath) - } else { - nil - } - menuItem.menu?.setSubmenu( - NSMenu.appsMenuForFile( - url, - openAppAction: #selector(openWithApp), - openOtherAppAction: #selector(openWithOther) - ), - for: menuItem - ) - } - return isValid - } else if action == #selector(findNext) { - return scopeBar.findView.hasMatches - } else if action == #selector(findPrevious) { - return scopeBar.findView.hasMatches - } else if action == #selector(compareFolders) { - return fsi.validateCompareFolders() - } else if action == #selector(moveFiles) { - switch fsi.view.side { - case .left: - menuItem.title = NSLocalizedString("Move to Right...", comment: "") - case .right: - menuItem.title = NSLocalizedString("Move to Left...", comment: "") - } - return fsi.validateMoveFiles(sessionDiff) - } else if action == #selector(setModificationDate) { - return fsi.validateFileTouch(sessionDiff) - } else if action == #selector(toggleLogConsole) { - if consoleSplitter.hasSubviewCollapsed { - menuItem.title = NSLocalizedString("Show Log Console", comment: "") - } else { - menuItem.title = NSLocalizedString("Hide Log Console", comment: "") - } - return true - } else if action == #selector(togglePreviewPanel) { - if QLPreviewPanel.sharedPreviewPanelExists(), QLPreviewPanel.shared().isVisible { - menuItem.title = NSLocalizedString("Close Quick Look", comment: "") - } else { - menuItem.title = NSLocalizedString("Quick Look", comment: "") - } - return fsi.validatePreviewPanel() - } - return true - } - - // MARK: - Menu Delegate - - public func menuNeedsUpdate(_ menu: NSMenu) { - if menu.identifier == .Folders.openWithToolbarMenu { - // preserve root item - let root = menu.item(at: 0) - menu.removeAllItems() - if let root { - menu.addItem(root) - } - let row = lastUsedView.selectedRow - if row >= 0, - let vi = lastUsedView.item(atRow: row) as? VisibleItem { - menu.addMenuItemsForFile( - vi.item.toUrl(), - openAppAction: #selector(openWithApp), - openOtherAppAction: #selector(openWithOther) - ) - } - } - } - - // MARK: - TableViewContextMenuDelegate - - func tableView(_ tableView: NSTableView, menuItem: NSMenuItem, hideMenuItem hide: inout Bool) -> Bool { - if running { - return false - } - guard let folderView = tableView as? FoldersOutlineView else { - return false - } - let action = menuItem.action - - hide = true - - let isValid = validateMenuItem(menuItem) - if action == #selector(copyFiles) { - // make menu visible but disabled only if it's readonly - if !isValid { - let isReadOnly = switch folderView.side { - case .left: - sessionDiff.rightReadOnly - case .right: - sessionDiff.leftReadOnly - } - if isReadOnly { - hide = false - } - } - } else if action == #selector(deleteFiles) { - // make menu visible but disabled only if it's readonly - if !isValid { - let isReadOnly = switch folderView.side { - case .left: - sessionDiff.leftReadOnly - case .right: - sessionDiff.rightReadOnly - } - if isReadOnly { - hide = false - } - } - } else if action == #selector(moveFiles) { - // make menu visible but disabled only if it's readonly - if !isValid { - let isReadOnly = switch folderView.side { - case .left: - sessionDiff.rightReadOnly - case .right: - sessionDiff.leftReadOnly - } - if isReadOnly { - hide = false - } - } - } - - return isValid - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+NSToolbarDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+NSToolbarDelegate.swift deleted file mode 100644 index fa081bd..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+NSToolbarDelegate.swift +++ /dev/null @@ -1,355 +0,0 @@ -// -// FoldersWindowController+NSToolbarDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 31/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSToolbarItem.Identifier { - enum Folders { - static let comparisonList = NSToolbarItem.Identifier("ComparisonList") - static let comparison = NSToolbarItem.Identifier("Comparison") - static let exclusionFilters = NSToolbarItem.Identifier("ExclusionFilters") - static let refresh = NSToolbarItem.Identifier("Refresh") - static let expandAllFolders = NSToolbarItem.Identifier("ExpandAllFolders") - static let collapseAllFolders = NSToolbarItem.Identifier("CollapseAllFolders") - static let copy = NSToolbarItem.Identifier("Copy") - static let move = NSToolbarItem.Identifier("Move") - static let sync = NSToolbarItem.Identifier("Sync") - static let touch = NSToolbarItem.Identifier("Touch") - static let sessionPreferences = NSToolbarItem.Identifier("SessionPreferences") - static let nextDifference = NSToolbarItem.Identifier("NextDifference") - static let prevDifference = NSToolbarItem.Identifier("PrevDifference") - static let openWith = NSToolbarItem.Identifier("OpenWith") - static let showInFinder = NSToolbarItem.Identifier("ShowInFinder") - } -} - -extension NSUserInterfaceItemIdentifier { - enum Folders { - static let openWithToolbarMenu = NSUserInterfaceItemIdentifier("FolderOpenWithToolbarMenuIdentifier") - } -} - -extension FoldersWindowController: NSToolbarDelegate, NSToolbarItemValidation { - public func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - [ - .Folders.comparison, - .Folders.expandAllFolders, - .Folders.collapseAllFolders, - .Folders.refresh, - .space, - .Folders.copy, - .Folders.move, - .space, - .Folders.sync, - .Folders.touch, - .flexibleSpace, - .Folders.exclusionFilters, - .Folders.sessionPreferences, - ] - } - - public func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - [ - .Folders.comparisonList, - .Folders.comparison, - .space, - .flexibleSpace, - .Folders.exclusionFilters, - .Folders.refresh, - .Folders.expandAllFolders, - .Folders.collapseAllFolders, - .Folders.copy, - .Folders.move, - .Folders.sync, - .Folders.touch, - .Folders.sessionPreferences, - .Folders.nextDifference, - .Folders.prevDifference, - .Folders.openWith, - .Folders.showInFinder, - ] - } - - public func toolbarWillAddItem(_ notification: Notification) { - guard let item = notification.userInfo?["item"] as? NSToolbarItem else { - return - } - updateToolbarButton(item) - } - - // swiftlint:disable:next function_body_length - public func toolbar(_: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar _: Bool) -> NSToolbarItem? { - if itemIdentifier == .Folders.comparisonList { - let cell = ComparatorPopUpButtonCell(textCell: "", pullsDown: false) - - let popupButton = NSPopUpButton(frame: .zero, pullsDown: false) - popupButton.cell = cell - popupButton.bezelStyle = .texturedRounded - popupButton.setButtonType(.momentaryPushIn) - popupButton.alignment = .left - popupButton.lineBreakMode = .byTruncatingTail - popupButton.state = .off - popupButton.imagePosition = .noImage - popupButton.imageScaling = .scaleProportionallyDown - popupButton.target = self - popupButton.action = #selector(selectComparison) - popupButton.select(popupButton.menu?.item(withTag: comparatorMethod.rawValue)) - - let item = CustomValidationToolbarItem(itemIdentifier: itemIdentifier) - item.label = NSLocalizedString("Comparison", comment: "") - item.paletteLabel = NSLocalizedString("Comparison", comment: "") - item.view = popupButton - - return item - } else if itemIdentifier == .Folders.comparison { - let menuItem = NSMenuItem() - menuItem.state = .on - menuItem.image = NSImage(named: VDImageNameComparisonMethod) - menuItem.isHidden = true - let cell = ComparatorPopUpButtonCell(textCell: "", pullsDown: true) - cell.menu?.insertItem(menuItem, at: 0) - cell.arrowPosition = .arrowAtCenter - - let popupButton = NSPopUpButton(frame: .zero, pullsDown: true) - popupButton.cell = cell - popupButton.image = NSImage(named: VDImageNameComparisonMethod) - popupButton.imagePosition = .imageOnly - popupButton.bezelStyle = .texturedRounded - popupButton.setButtonType(.momentaryPushIn) - popupButton.alignment = .center - popupButton.lineBreakMode = .byTruncatingTail - popupButton.state = .on - popupButton.isBordered = true - popupButton.imageScaling = .scaleProportionallyDown - popupButton.target = self - popupButton.action = #selector(selectComparison) - popupButton.select(popupButton.menu?.item(withTag: comparatorMethod.rawValue)) - - let item = CustomValidationToolbarItem(itemIdentifier: itemIdentifier) - item.label = NSLocalizedString("Comparison", comment: "") - item.paletteLabel = NSLocalizedString("Comparison", comment: "") - item.toolTip = comparatorMethod.description - item.view = popupButton - - return item - } else if itemIdentifier == .Folders.exclusionFilters { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Exclusion Filters", comment: ""), - tooltip: NSLocalizedString("Edit Exclusion File Filters", comment: ""), - image: NSImage(named: VDImageNameFilter), - target: self, - action: #selector(openFileFilters) - ) - } else if itemIdentifier == .Folders.refresh { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Refresh", comment: ""), - tooltip: NSLocalizedString("Refresh", comment: ""), - image: NSImage(named: VDImageNameRefresh), - target: self, - action: #selector(refresh) - ) - } else if itemIdentifier == .Folders.expandAllFolders { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Expand All", comment: ""), - tooltip: NSLocalizedString("Expand All Folders", comment: ""), - image: NSImage(named: VDImageNameExpand), - target: self, - action: #selector(expandAllFolders) - ) - } else if itemIdentifier == .Folders.collapseAllFolders { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Collapse All", comment: ""), - tooltip: NSLocalizedString("Collapse All Folders", comment: ""), - image: NSImage(named: VDImageNameCollapse), - target: self, - action: #selector(collapseAllFolders) - ) - } else if itemIdentifier == .Folders.copy { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Copy Files", comment: ""), - tooltip: NSLocalizedString("Copy Files", comment: ""), - image: NSImage(named: VDImageNameCopyRight), - target: self, - action: #selector(copyFiles) - ) - } else if itemIdentifier == .Folders.move { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Move Files", comment: ""), - tooltip: NSLocalizedString("Move Files", comment: ""), - image: NSImage(named: VDImageNameMoveRight), - target: self, - action: #selector(moveFiles) - ) - } else if itemIdentifier == .Folders.sync { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Sync Files", comment: ""), - tooltip: NSLocalizedString("Copy newer and orphan files", comment: ""), - image: NSImage(named: VDImageNameSyncRight), - target: self, - action: #selector(syncFiles) - ) - } else if itemIdentifier == .Folders.touch { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Set Date", comment: ""), - tooltip: NSLocalizedString("Change date/time", comment: ""), - image: NSImage(named: VDImageNameDateTime), - target: self, - action: #selector(setModificationDate) - ) - } else if itemIdentifier == .Folders.sessionPreferences { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Session Settings", comment: ""), - tooltip: NSLocalizedString("Edit Session Settings", comment: ""), - image: NSImage(named: VDImageNamePreferences), - target: self, - action: #selector(openSessionSettingsSheet) - ) - } else if itemIdentifier == .Folders.nextDifference { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Next Difference", comment: ""), - tooltip: NSLocalizedString("Go to next difference", comment: ""), - image: NSImage(named: VDImageNameNext), - target: self, - action: #selector(nextDifference) - ) - } else if itemIdentifier == .Folders.prevDifference { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Prev Difference", comment: ""), - tooltip: NSLocalizedString("Go to previous difference", comment: ""), - image: NSImage(named: VDImageNamePrev), - target: self, - action: #selector(previousDifference) - ) - } else if itemIdentifier == .Folders.openWith { - let popupButton = NSPopUpButton( - identifier: .Folders.openWithToolbarMenu, - menuTitle: NSLocalizedString("ToolbarOpenWith", comment: ""), - menuImage: NSImage(named: VDImageNameOpenWith) - ) - popupButton.target = self - popupButton.action = #selector(popupOpenWithApp) - popupButton.menu?.delegate = self - - let item = CustomValidationToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Open With", comment: ""), - tooltip: NSLocalizedString("Open using the selected application", comment: ""), - image: NSImage(named: VDImageNameFinder), - target: nil, - action: nil - ) - item.view = popupButton - - return item - } else if itemIdentifier == .Folders.showInFinder { - return NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Show in Finder", comment: ""), - tooltip: NSLocalizedString("Show in Finder", comment: ""), - image: NSImage(named: VDImageNameFinder), - target: self, - action: #selector(showInFinder) - ) - } - - return nil - } - - open func validateToolbarItem(_ item: NSToolbarItem) -> Bool { - if running { - return false - } - - let fsi = lastUsedView.selectionInfo - - if item.itemIdentifier == .Folders.copy { - return fsi.validateCopyFiles(sessionDiff) - } - if item.itemIdentifier == .Folders.sync { - return fsi.validateSyncFiles(sessionDiff) - } - if item.itemIdentifier == .Folders.move { - return fsi.validateMoveFiles(sessionDiff) - } - if item.itemIdentifier == .Folders.touch { - return fsi.validateFileTouch(sessionDiff) - } - if item.itemIdentifier == .Folders.showInFinder { - return fsi.validateShowInFinder() - } - if item.itemIdentifier == .Folders.openWith { - var path: String? - return fsi.validateOpen(withApp: &path) - } - return true - } - - func updateToolbarButton(_ item: NSToolbarItem) { - switch lastUsedView.side { - case .left: - if item.itemIdentifier == .Folders.copy { - item.image = NSImage(named: VDImageNameCopyRight) - } else if item.itemIdentifier == .Folders.sync { - item.image = NSImage(named: VDImageNameSyncRight) - } else if item.itemIdentifier == .Folders.move { - item.image = NSImage(named: VDImageNameMoveRight) - } - case .right: - if item.itemIdentifier == .Folders.copy { - item.image = NSImage(named: VDImageNameCopyLeft) - } else if item.itemIdentifier == .Folders.sync { - item.image = NSImage(named: VDImageNameSyncLeft) - } else if item.itemIdentifier == .Folders.move { - item.image = NSImage(named: VDImageNameMoveLeft) - } - } - } - - func updateToolbarTooltip() { - guard let toolbar = window?.toolbar, - let visibleItems = toolbar.visibleItems else { - return - } - for item in visibleItems { - // swiftlint:disable:next for_where - if item.itemIdentifier == .Folders.comparison { - item.toolTip = comparatorMethod.description - } - } - } - - func updateComparisonToolbarItems(_ method: ComparatorOptions) { - // The Objc version of VD used `popupButton.bind(.selectedTag, ... withKeyPath: "comparatorMethod",` - // and `comparatorMethod` was declared `@objc dynamic` which is not compatible - // with the Swift version of `ComparatorOptions` so we update by hand - - let comparisonItems: [NSToolbarItem.Identifier] = [ - .Folders.comparison, - .Folders.comparisonList, - ] - - window? - .toolbar? - .visibleItems? - .filter { comparisonItems.contains($0.itemIdentifier) } - .forEach { - if let popupButton = $0.view as? NSPopUpButton { - popupButton.select(popupButton.menu?.item(withTag: method.rawValue)) - } - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+NSWindowDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+NSWindowDelegate.swift deleted file mode 100644 index 78f2459..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+NSWindowDelegate.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// FoldersWindowController+NSWindowDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 12/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController: NSWindowDelegate { - public func windowDidBecomeMain(_: Notification) { - Self.switchMenu() - } - - public func windowWillClose(_: Notification) { - // release previously started bookmarks - SecureBookmark.shared.stopAccessing(url: leftSecureURL) - SecureBookmark.shared.stopAccessing(url: rightSecureURL) - - removeObservers() - - removeAllChildrenDocuments() - } - - public func window(_: NSWindow, willUseFullScreenPresentationOptions proposedOptions: NSApplication.PresentationOptions = []) -> NSApplication.PresentationOptions { - [proposedOptions, .autoHideToolbar] - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Navigation.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Navigation.swift deleted file mode 100644 index fe134f7..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Navigation.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// FoldersWindowController+Navigation.swift -// VisualDiffer -// -// Created by davide ficano on 16/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController { - @objc func previousDifference(_: AnyObject) { - let options: DifferenceNavigator = [.previous, CommonPrefs.shared.folderDifferenceNavigatorOptions] - var didWrap = false - - if lastUsedView.moveToDifference(options: options, didWrap: &didWrap) != nil, didWrap { - scopeBar.findView.showWrapWindow() - } - } - - @objc func nextDifference(_: AnyObject) { - let options: DifferenceNavigator = [.next, CommonPrefs.shared.folderDifferenceNavigatorOptions] - var didWrap = false - - if lastUsedView.moveToDifference(options: options, didWrap: &didWrap) != nil, didWrap { - scopeBar.findView.showWrapWindow() - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+PathControlDelegate.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+PathControlDelegate.swift deleted file mode 100644 index 723a303..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+PathControlDelegate.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// FoldersWindowController+PathControlDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 08/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -let pathControlMenu = 0x1000 - -extension FoldersWindowController: PathControlDelegate { - func pathControl(_ pathControl: PathControl, willContextMenu menu: NSMenu) { - // if the click is outside any cell do not add menus - if pathControl.clickedPathItem == nil { - return - } - menu.addItem(NSMenuItem.separator()) - // set tag to pathControlMenu otherwise will be used the selected item on view - menu.addItem( - withTitle: NSLocalizedString("Set as Base Folder", comment: ""), - action: #selector(setAsBaseFolder), - keyEquivalent: "" - ).tag = pathControlMenu - menu.addItem( - withTitle: NSLocalizedString("Set as Base Folder on the Other Side", comment: ""), - action: #selector(setAsBaseFolderOtherSide), - keyEquivalent: "" - ).tag = pathControlMenu - } - - func pathControl(_: PathControl, chosenUrl _: URL) { - // no need to check witch path is changed (left or right) because - // the binding value has already set sessionDiff.Path - reloadAll(RefreshInfo( - initState: true, - expandAllFolders: sessionDiff.expandAllFolders - )) - - synchronizeWindowTitleWithDocumentName() - } - - public func pathControl(_: NSPathControl, willDisplay openPanel: NSOpenPanel) { - openPanel.canChooseDirectories = true - openPanel.canChooseFiles = false - } - - @objc func isPathControlMenu(_ tag: Int) -> Bool { - (tag & pathControlMenu) == pathControlMenu - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+QLPreviewPanel.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+QLPreviewPanel.swift deleted file mode 100644 index 97d1f5f..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+QLPreviewPanel.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// FoldersWindowController+QLPreviewPanel.swift -// VisualDiffer -// -// Created by davide ficano on 11/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Quartz - -// swiftlint:disable implicitly_unwrapped_optional -extension FoldersWindowController: @preconcurrency QLPreviewPanelDataSource, - @preconcurrency QLPreviewPanelDelegate { - @objc func togglePreviewPanel(_: AnyObject?) { - QLPreviewPanel.toggle() - } - - override open func acceptsPreviewPanelControl(_: QLPreviewPanel!) -> Bool { - true - } - - override open func beginPreviewPanelControl(_ panel: QLPreviewPanel!) { - // This document is now responsible of the preview panel - // It is allowed to set the delegate, data source and refresh panel. - MainActor.assumeIsolated { - previewPanel = panel - panel.delegate = self - panel.dataSource = self - } - } - - override open func endPreviewPanelControl(_: QLPreviewPanel!) { - // This document loses its responsibility on the preview panel - // Until the next call to -beginPreviewPanelControl: it must not - // change the panel's delegate, data source or refresh it. - MainActor.assumeIsolated { - previewPanel = nil - } - } - - // MARK: - Quick Look panel data source - - public func numberOfPreviewItems(in _: QLPreviewPanel!) -> Int { - lastUsedView.selectionInfo.validObjectsIndexes.count - } - - public func previewPanel(_: QLPreviewPanel!, previewItemAt index: Int) -> (any QLPreviewItem)! { - let row = lastUsedView.selectionInfo.validObjectsIndexes[index] - return lastUsedView.item(atRow: row) as? VisibleItem - } - - public func previewPanel(_: QLPreviewPanel!, handle event: NSEvent!) -> Bool { - // redirect all key down events to the table view - if event.type == .keyDown { - lastUsedView.keyDown(with: event) - return true - } - return false - } - - // This delegate method provides the rect on screen from which the panel will zoom. - public func previewPanel(_: QLPreviewPanel!, sourceFrameOnScreenFor item: (any QLPreviewItem)!) -> NSRect { - let index = lastUsedView.row(forItem: item) - - if index == NSNotFound { - return .zero - } - - var iconRect = lastUsedView.frameOfCell(atColumn: 0, row: index) - - // check that the icon rect is visible on screen - let visibleRect = lastUsedView.visibleRect - - if !visibleRect.intersects(iconRect) { - return .zero - } - - // convert icon rect to screen coordinates - iconRect = lastUsedView.convert(iconRect, to: nil) - if let window = lastUsedView.window { - iconRect = window.convertToScreen(iconRect) - } - - return iconRect - } -} - -// swiftlint:enable implicitly_unwrapped_optional diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Select.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Select.swift deleted file mode 100644 index c50fc88..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+Select.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// FoldersWindowController+Select.swift -// VisualDiffer -// -// Created by davide ficano on 13/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -public struct SelectionSide: OptionSet, Sendable { - public let rawValue: Int - - public static let left = SelectionSide(rawValue: 1 << 0) - public static let right = SelectionSide(rawValue: 1 << 1) - public static let both: SelectionSide = [.left, right] - - public init(rawValue: Int) { - self.rawValue = rawValue - } - - public init(menuItem: NSMenuItem) { - rawValue = menuItem.tag - } -} - -extension FoldersWindowController { - @objc func selectNewer(_ sender: AnyObject) { - guard let sender = sender as? NSMenuItem else { - return - } - let side = SelectionSide(menuItem: sender) - - if side.contains(.left) { - leftView.selectBy(type: .changed) - } - if side.contains(.right) { - rightView.selectBy(type: .changed) - } - } - - @objc func selectOrphans(_ sender: AnyObject) { - guard let sender = sender as? NSMenuItem else { - return - } - let side = SelectionSide(menuItem: sender) - - if side.contains(.left) { - leftView.selectBy(type: .orphan) - } - if side.contains(.right) { - rightView.selectBy(type: .orphan) - } - } - - @objc func selectAllBothSides(_ sender: AnyObject) { - leftView.selectAll(sender) - rightView.selectAll(sender) - } - - @objc func selectAllFiles(_ sender: AnyObject) { - guard let sender = sender as? NSMenuItem else { - return - } - let side = SelectionSide(menuItem: sender) - - let isShiftDown = NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false - - if side.contains(.both) { - leftView.selectAll(files: true, folders: false, byExtendingSelection: isShiftDown) - rightView.selectAll(files: true, folders: false, byExtendingSelection: isShiftDown) - } else { - // use the lastUsedView not a specific side - lastUsedView.selectAll(files: true, folders: false, byExtendingSelection: isShiftDown) - } - } - - @objc func selectAllFolders(_ sender: AnyObject) { - guard let sender = sender as? NSMenuItem else { - return - } - let side = SelectionSide(menuItem: sender) - - let isShiftDown = NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false - - if side.contains(.both) { - leftView.selectAll(files: false, folders: true, byExtendingSelection: isShiftDown) - rightView.selectAll(files: false, folders: true, byExtendingSelection: isShiftDown) - } else { - // use the lastUsedView not a specific side - lastUsedView.selectAll(files: false, folders: true, byExtendingSelection: isShiftDown) - } - } - - @objc func invertSelection(_ sender: AnyObject) { - guard let sender = sender as? NSMenuItem else { - return - } - let side = SelectionSide(menuItem: sender) - - if side.contains(.both) { - leftView.invertSelection() - rightView.invertSelection() - } else { - // use the lastUsedView not a specific side - lastUsedView.invertSelection() - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+SessionPreferences.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+SessionPreferences.swift deleted file mode 100644 index 48eac7b..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+SessionPreferences.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// FoldersWindowController+SessionPreferences.swift -// VisualDiffer -// -// Created by davide ficano on 27/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController { - @objc func openSessionSettingsSheet(_: AnyObject) { - guard let window else { - return - } - sessionPreferencesSheet.beginSheet( - window, - sessionDiff: sessionDiff, - selectedTab: .comparison - ) { - self.updateSessionPreferences($0) - } - } - - @objc func openFileFilters(_: AnyObject) { - guard let window else { - return - } - sessionPreferencesSheet.beginSheet( - window, - sessionDiff: sessionDiff, - selectedTab: .filters - ) { - self.updateSessionPreferences($0) - } - } - - private func updateSessionPreferences(_ returnCode: NSApplication.ModalResponse) { - if returnCode == .OK { - sessionPreferencesSheet.updateSessionDiff(sessionDiff) - updateScopeBar() - reloadAll(RefreshInfo( - initState: true, - expandAllFolders: sessionDiff.expandAllFolders - )) - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+UICreation.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+UICreation.swift deleted file mode 100644 index c495665..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+UICreation.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// FoldersWindowController+UICreation.swift -// VisualDiffer -// -// Created by davide ficano on 04/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FoldersWindowController { - func createFolderPanelsSplitView() -> NSSplitView { - let view = NSSplitView(frame: NSRect(x: 0, y: 0, width: 1, height: 0)) - - view.dividerStyle = .thin - view.isVertical = true - - return view - } - - func createProgressView() -> ProgressBarView { - let view = ProgressBarView(frame: .zero) - view.setStop(action: #selector(stopRefresh), target: self) - - return view - } - - func createStatusbar() -> NSStackView { - let spacerView = NSView() - spacerView.translatesAutoresizingMaskIntoConstraints = false - spacerView.setContentHuggingPriority(.init(1), for: .horizontal) - - let spacerWidth = spacerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) - spacerWidth.priority = .defaultLow - spacerWidth.isActive = true - - differenceCounters.setContentHuggingPriority(.defaultHigh, for: .horizontal) - progressView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - - let view = NSStackView(views: [ - differenceCounters, - progressView, - spacerView, - statusbarText, - ]) - - view.orientation = .horizontal - view.translatesAutoresizingMaskIntoConstraints = false - view.alignment = .centerY - view.distribution = .fill - view.spacing = 10 - - return view - } - - func createStatusbarText() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.drawsBackground = false - view.isBezeled = false - view.isBordered = false - view.isEditable = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.controlSize = .small - view.alignment = .right - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func createDisplayFiltersScopeBar() -> DisplayFiltersScopeBar { - let view = DisplayFiltersScopeBar(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - view.initScopeBar(self) - view.reloadData() - - return view - } - - func createConsoleView() -> ConsoleView { - let view = ConsoleView(frame: NSRect(x: 0, y: 0, width: 1, height: 0)) - view.delegate = self - - return view - } - - func createConsoleSplitter() -> DualPaneSplitView { - let frame = window?.contentView?.bounds ?? .zero - let view = DualPaneSplitView(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - view.firstViewSize = frame.height * 3 / 4 - - return view - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+UISetup.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController+UISetup.swift deleted file mode 100644 index dbe6449..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController+UISetup.swift +++ /dev/null @@ -1,299 +0,0 @@ -// -// FoldersWindowController+UISetup.swift -// VisualDiffer -// -// Created by davide ficano on 10/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -import UserNotifications - -extension FoldersWindowController { - @objc func initAllViews() { - setupWindowLayout() - setupFoldersLayout() - - leftPanelView.treeView.nextKeyView = rightView - lastUsedView = leftPanelView.treeView - - updateTreeViewFont() - - adjustTableColumnsWidth() - - // must be called after setting up all views - setupWindow() - } - - func setupWindowLayout() { - guard let window, - let contentView = window.contentView else { - return - } - - contentView.addSubview(consoleSplitter) - contentView.addSubview(scopeBar) - contentView.addSubview(statusbar) - - setupConstraints() - } - - func setupFoldersLayout() { - leftPanelView.setLinkPanels(rightPanelView) - } - - func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - var leadingMargin: CGFloat = 6 - var trailingMargin: CGFloat = 6 - if #available(macOS 26, *) { - leadingMargin = 16 - trailingMargin = 16 - } - - NSLayoutConstraint.activate([ - scopeBar.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - scopeBar.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - scopeBar.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 1), - scopeBar.heightAnchor.constraint(equalToConstant: 25), - - statusbar.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: leadingMargin), - statusbar.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -trailingMargin), - statusbar.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -2), - statusbar.heightAnchor.constraint(equalToConstant: 20), - - consoleSplitter.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - consoleSplitter.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - consoleSplitter.topAnchor.constraint(equalTo: scopeBar.bottomAnchor), - consoleSplitter.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -22), - ]) - } - - @objc func updateTreeViewFont() { - let font = treeViewFont() - leftPanelView.treeView.updateFont(font, reloadData: true) - rightPanelView.treeView.updateFont(font, reloadData: true) - } - - func treeViewFont() -> NSFont { - currentFont = CommonPrefs.shared.folderListingFont - if fontZoomFactor != 0 { - currentFont = NSFontManager.shared.convert( - currentFont, - toSize: currentFont.pointSize + fontZoomFactor - ) - } - - return currentFont - } - - func adjustTableColumnsWidth() { - dontResizeColumns = true - - leftView.adjustColumnsWidths(currentFont, dateFormatTemplate: CommonPrefs.shared.folderViewDateFormat) - rightView.adjustColumnsWidths(currentFont, dateFormatTemplate: CommonPrefs.shared.folderViewDateFormat) - - dontResizeColumns = false - } - - func setupWindow() { - guard let window else { - return - } - window.delegate = self - window.toolbar = NSToolbar(identifier: "FoldersToolbar", delegate: self) - window.makeFirstResponder(leftPanelView.treeView) - - window.collectionBehavior = [window.collectionBehavior, .fullScreenPrimary] - } - - /** - * Setup elements requiring the sessionDiff is correctly defined, this method must be called after setDocument - */ - @objc func setupUIState() { - setupObservers() - - comparatorMethod = sessionDiff.comparatorOptions.onlyMethodFlags - - hideEmptyFolders = CommonPrefs.shared.hideEmptyFolders - - window?.setFrameAutosaveName(String(format: "%lx%lx", sessionDiff.leftPath?.hash ?? 0, sessionDiff.rightPath?.hash ?? 0)) - - scopeBar.findView.delegate = FoldersOutlineViewFindTextDelegate(view: leftView) - updateScopeBar() - setupSortDescriptors() - setupConsoleSplitter() - - leftPanelView.bindControls() - rightPanelView.bindControls() - } - - func setupObservers() { - // a register for those notifications on the synchronized content view. - NotificationCenter.default.addObserver( - self, - selector: #selector(fontDidChange), - name: .prefsChanged, - object: nil - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(refreshCompareItem), - name: .fileSaved, - object: nil - ) - - NotificationCenter.default.addObserver( - self, - selector: #selector(appAppearanceDidChange), - name: .appAppearanceDidChange, - object: nil - ) - - let comparatorOptionsObservation = sessionDiff.observeComparatorOptions { flags in - Task { @MainActor in - self.comparatorMethod = flags.onlyMethodFlags - } - } - - observations.append(contentsOf: [comparatorOptionsObservation]) - - UNUserNotificationCenter.current().delegate = self - } - - func removeObservers() { - NotificationCenter.default.removeObserver( - self, - name: .prefsChanged, - object: nil - ) - NotificationCenter.default.removeObserver( - self, - name: .fileSaved, - object: nil - ) - NotificationCenter.default.removeObserver( - self, - name: .appAppearanceDidChange, - object: nil - ) - - observations.forEach { $0.invalidate() } - - // don't show notification if this window is closing - UNUserNotificationCenter.current().removeAllPendingNotificationRequests() - } - - @objc func updateScopeBar() { - let displayOptions = sessionDiff.displayOptions - - scopeBar.select(displayOptions, informDelegate: false) - scopeBar.showFilteredFiles(showFilteredFiles, informDelegate: false) - scopeBar.hideEmptyFolders(hideEmptyFolders, informDelegate: false) - scopeBar.noOrphansFolders(displayOptions.contains(.noOrphansFolders), informDelegate: false) - } - - func setupSortDescriptors() { - let view = sessionDiff.currentSortSide == .left ? leftView : rightView - view.sortDescriptors = [sessionDiff.columnSortDescriptor()] - } - - func setupConsoleSplitter() { - consoleSplitter.delegate = consoleDelegate - consoleSplitter.collapseSubview(at: 1) - } - - @objc func appAppearanceDidChange(_: Notification) { - leftView.reloadData() - rightView.reloadData() - - updateStatusBar() - } - - @objc func refreshCompareItem(_ notification: Notification) { - guard let userInfo = notification.userInfo, - let leftItemOriginal, - let rightItemOriginal, - let sessionLeftPath = sessionDiff.leftPath, - let sessionRightPath = sessionDiff.rightPath else { - return - } - - var item: CompareItem? - - if let leftPath = userInfo[FileSavedKey.leftPath] as? String { - if leftPath.hasPrefix(sessionLeftPath) { - item = CompareItem.find(withPath: leftPath, from: leftItemOriginal) - } - if item == nil, leftPath.hasPrefix(sessionRightPath) { - item = CompareItem.find(withPath: leftPath, from: rightItemOriginal) - } - } - - if let rightPath = userInfo[FileSavedKey.rightPath] as? String { - if item == nil, rightPath.hasPrefix(sessionLeftPath) { - item = CompareItem.find(withPath: rightPath, from: leftItemOriginal) - } - if item == nil, rightPath.hasPrefix(sessionRightPath) { - item = CompareItem.find(withPath: rightPath, from: rightItemOriginal) - } - } - - guard let item, - let comparator else { - return - } - let filterConfig = FilterConfig( - from: sessionDiff, - showFilteredFiles: showFilteredFiles, - hideEmptyFolders: hideEmptyFolders - ) - item.refresh( - filterConfig: filterConfig, - comparator: comparator - ) - - leftView.reloadData() - rightView.reloadData() - } - - @objc func updateBottomBar(_ view: FoldersOutlineView) { - if view.side == .left { - leftPanelView.updateBottomBar() - } else { - rightPanelView.updateBottomBar() - } - } - - @objc func updateStatusBar() { - let selInfo = lastUsedView.selectionInfo - let item = if selInfo.foldersCount == 1, - let row = selInfo.foldersIndexes.first, - let vi = leftView.item(atRow: row) as? VisibleItem { - vi.item - } else { - leftItemOriginal - } - guard let item else { - return - } - let items = DiffCountersItem.diffCounter( - withItem: item, - options: sessionDiff.comparatorOptions - ) - differenceCounters.update(counters: items) - } - - @objc func fontDidChange(notification: Notification) { - guard let userInfo = notification.userInfo, - let target = userInfo[PrefChangedKey.target] as? PrefChangedKey.Target else { - return - } - - if target == .folder { - fontZoomFactor = 0 - } - } -} diff --git a/Sources/Features/FoldersCompare/Controller/FoldersWindowController.swift b/Sources/Features/FoldersCompare/Controller/FoldersWindowController.swift deleted file mode 100644 index 3da7896..0000000 --- a/Sources/Features/FoldersCompare/Controller/FoldersWindowController.swift +++ /dev/null @@ -1,282 +0,0 @@ -// -// FoldersWindowController.swift -// VisualDiffer -// -// Created by davide ficano on 09/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -import Quartz -import UserNotifications -import os.log - -// the heights are in flipped coordinates -private let splitViewMinHeight: CGFloat = 160.0 -private let splitViewMaxHeight: CGFloat = 80.0 - -public class FoldersWindowController: NSWindowController, - UNUserNotificationCenterDelegate { - var observations: [NSKeyValueObservation] = [] - - // contain the directories read CompareItem - var leftItemOriginal: CompareItem? - var rightItemOriginal: CompareItem? - - // contain the CompareItem to display - var leftVisibleItems: VisibleItem? - // swiftlint:disable:next implicitly_unwrapped_optional - @objc dynamic var sessionDiff: SessionDiff! - var dontResizeColumns = false - var running = false - var previewPanel: QLPreviewPanel? - - var hideEmptyFolders = false - var showFilteredFiles = false - - // swiftlint:disable:next implicitly_unwrapped_optional - var lastUsedView: FoldersOutlineView! - - // ComparatorPopUpButtonCell uses the tag property to select item but - // sessionDiff.comparatorFlags bitmask should not match the tag value, so - // sessionDiff.comparatorFlags is bit-masked with comparatorMethod in selection action methods - var comparatorMethod: ComparatorOptions = [] { - didSet { - window?.subtitle = comparatorMethod.description - - updateComparisonToolbarItems(comparatorMethod) - updateToolbarTooltip() - } - } - - // swiftlint:disable:next implicitly_unwrapped_optional - var currentFont: NSFont! - - var comparator: ItemComparator? - lazy var sessionPreferencesSheet: SessionPreferencesWindow = .init() - - var sessionChildren: [VDDocument] - - var leftSecureURL: URL? - var rightSecureURL: URL? - - var fontZoomFactor: CGFloat = 0 { - didSet { - if fontZoomFactor < 0 || fontZoomFactor > 10 { - fontZoomFactor = oldValue - } - updateStatusText() - updateTreeViewFont() - } - } - - // MARK: - View Creation - - lazy var consoleSplitter: DualPaneSplitView = { - let view = createConsoleSplitter() - - let folderPanels = createFolderPanelsSplitView() - folderPanels.addArrangedSubview(leftPanelView) - folderPanels.addArrangedSubview(rightPanelView) - - view.addArrangedSubview(folderPanels) - view.addArrangedSubview(consoleView) - - return view - }() - - lazy var consoleView: ConsoleView = createConsoleView() - - // hold it to be sure it isn't deallocated when used from another element - // the FileSystemController uses it but is released before - // periphery:ignore - var progressIndicatorController: ProgressIndicatorController? - - var consoleDelegate = DualPaneSplitViewDelegate( - collapsableSubViewIndex: 1, - minSize: splitViewMinHeight, - maxSize: splitViewMaxHeight - ) - - lazy var leftPanelView: FolderPanelView = .createFolderPanel( - side: .left, - delegate: self - ) - - lazy var rightPanelView: FolderPanelView = .createFolderPanel( - side: .right, - delegate: self - ) - - lazy var scopeBar: DisplayFiltersScopeBar = createDisplayFiltersScopeBar() - - lazy var differenceCounters: DifferenceCounters = .init(frame: .zero) - - lazy var progressView: ProgressBarView = createProgressView() - - lazy var statusbar: NSStackView = createStatusbar() - - lazy var statusbarText: NSTextField = createStatusbarText() - - init() { - sessionChildren = [] - let window = WindowCancelOperation.createWindow() - super.init(window: window) - shouldCascadeWindows = false - initAllViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - public nonisolated func userNotificationCenter(_: UNUserNotificationCenter, willPresent _: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - if CommonPrefs.shared.showNotificationWhenWindowIsOnFront { - completionHandler([.list, .sound]) - } - } - - @objc func openWithApp(_ sender: AnyObject) { - guard let path = sender.representedObject as? String else { - return - } - let application = URL(filePath: path) - lastUsedView.openSelected(with: application) - } - - @objc func openWithOther(_: AnyObject) { - lastUsedView.openSelectedWithOther() - } - - @objc func copyUrls(_: AnyObject) { - lastUsedView.copySelectedAsUrls() - } - - @objc func expandAllFolders(_: AnyObject) { - let selectedVisibleItems = lastUsedView.getSelectedVisibleItems(false) - - // views are in sync so it's sufficient to expand only one side to propagate to the other - leftView.expandItem(nil, expandChildren: true) - leftView.reloadData() - rightView.reloadData() - - lastUsedView.restoreSelectionAndFocusPosition(selectedVisibleItems) - } - - @objc func collapseAllFolders(_: AnyObject) { - let selectedVisibleItems = lastUsedView.getSelectedVisibleItems(false) - - // views are in sync so it's sufficient to expand only one side to propagate to the other - leftView.collapseItem(nil, collapseChildren: true) - leftView.reloadData() - rightView.reloadData() - - lastUsedView.restoreSelectionAndFocusPosition(selectedVisibleItems) - } - - @objc func swapSides(_: AnyObject) { - leftVisibleItems?.swap() - - let path = sessionDiff.leftPath - sessionDiff.leftPath = sessionDiff.rightPath - sessionDiff.rightPath = path - - let readOnly = sessionDiff.leftReadOnly - sessionDiff.leftReadOnly = sessionDiff.rightReadOnly - sessionDiff.rightReadOnly = readOnly - - leftView.reloadData() - rightView.reloadData() - } - - @objc func compareInfo(_: AnyObject) { - guard let window else { - return - } - let folderCompareInfoWindow = FolderCompareInfoWindow.createSheet() - - folderCompareInfoWindow.leftRoot = leftItemOriginal - folderCompareInfoWindow.comparatorOptions = sessionDiff.comparatorOptions - folderCompareInfoWindow.selectedItems = lastUsedView.selectedItems() - - folderCompareInfoWindow.beginSheetModal(for: window) - } - - // MARK: - Find Methods - - @objc func find(_: AnyObject) { - window?.makeFirstResponder(scopeBar) - } - - @objc func findPrevious(_: AnyObject) { - scopeBar.findView.moveToMatch(false) - } - - @objc func findNext(_: AnyObject) { - scopeBar.findView.moveToMatch(true) - } - - // MARK: - Read only - - @objc func setLeftReadOnly(_: AnyObject) { - sessionDiff.leftReadOnly.toggle() - } - - @objc func setRightReadOnly(_: AnyObject) { - sessionDiff.rightReadOnly.toggle() - } - - // MARK: - Refresh - - @objc func stopRefresh(_: AnyObject) { - let retVal = NSAlert.showModalConfirm( - messageText: NSLocalizedString("Are you sure to stop the operation?", comment: ""), - informativeText: NSLocalizedString("If the operation takes a long time to run, you can stop it, but the results could be inaccurate", comment: ""), - suppressPropertyName: CommonPrefs.Name.confirmStopLongOperation.rawValue - ) - if retVal { - showConsoleView() - consoleView.log(warning: NSLocalizedString("Stopped comparison", comment: "")) - running = false - progressView.waitStopMessage = NSLocalizedString("Stopping comparison can take a while, please wait...", comment: "") - progressView.stop() - } - } - - func updateStatusText() { - if fontZoomFactor == 0 { - statusbarText.stringValue = "" - } else { - statusbarText.stringValue = "\(100 + Int(fontZoomFactor) * 10)%" - } - } - - // MARK: - Zoom Font - - @objc func zoomLargerFont(_: AnyObject) { - fontZoomFactor += 1 - } - - @objc func zoomSmallerFont(_: AnyObject) { - fontZoomFactor -= 1 - } - - @objc func zoomResetFont(_: AnyObject) { - fontZoomFactor = 0 - } - - #if DEBUG - override public func keyDown(with event: NSEvent) { - // Cmd + F12 pressed, create the test code - if event.modifierFlags.contains(.command), - event.charactersIgnoringModifiers?.unicodeScalars.first?.value == UInt32(NSF12FunctionKey) { - FileSystemTestHelper.createTestCode(leftView, sessionDiff: sessionDiff) - Logger.ui.info("Unit Test generated and copied on clipboard") - - return - } - - super.keyDown(with: event) - } - #endif -} diff --git a/Sources/Features/FoldersCompare/Controller/OutlineViewItemDelegate.swift b/Sources/Features/FoldersCompare/Controller/OutlineViewItemDelegate.swift deleted file mode 100644 index 5a55b2f..0000000 --- a/Sources/Features/FoldersCompare/Controller/OutlineViewItemDelegate.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// OutlineViewItemDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 14/07/11. -// Copyright (c) 2011 visualdiffer.com -// - -/** - * Used to inform when expansion/collapse is done on FoldersOutlineView - * This is necessary to minimize the times the updates to UI (bottom bar) are called - * The views in sync expand and collapse items and must take care to disable notification when necessary - */ -protocol OutlineViewItemDelegate: AnyObject { - @MainActor func itemDidExpand(_ item: Any?, outlineView: NSOutlineView) - @MainActor func itemDidCollapse(_ item: Any?, outlineView: NSOutlineView) -} diff --git a/Sources/Features/FoldersCompare/Extensions/CommonPrefs+ConsoleLog.swift b/Sources/Features/FoldersCompare/Extensions/CommonPrefs+ConsoleLog.swift deleted file mode 100644 index 1aa4d8a..0000000 --- a/Sources/Features/FoldersCompare/Extensions/CommonPrefs+ConsoleLog.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// CommonPrefs+ConsoleLog.swift -// VisualDiffer -// -// Created by davide ficano on 18/07/20. -// Copyright (c) 2020 visualdiffer.com -// - -enum LogLevel: Int { - case info = 1 - case warning - case error - - var color: String { - switch self { - case .info: - "info" - case .warning: - "warning" - case .error: - "error" - } - } -} - -extension CommonPrefs.Name { - static let consoleLogFont = CommonPrefs.Name(rawValue: "consoleLogFont") - static let consoleLogColorsMap = CommonPrefs.Name(rawValue: "consoleLogColorsMap") -} - -extension PrefChangedKey.Target { - static let consoleLog = PrefChangedKey.Target(rawValue: "consoleLog") -} - -extension CommonPrefs { - var consoleLogFont: NSFont { - get { - if let font = font(forKey: .consoleLogFont) { - return font - } - let fontManager = NSFontManager.shared - if let font = fontManager.font(withFamily: "Menlo", traits: .boldFontMask, weight: 0, size: 11.0) { - return font - } - return NSFont.systemFont(ofSize: 11.0) - } - - set { - save(newValue, forKey: .consoleLogFont) - - NotificationCenter.default.postPrefsChanged(userInfo: [ - PrefChangedKey.target: PrefChangedKey.Target.consoleLog, - PrefChangedKey.font: true, - ]) - } - } - - func consoleLogColors(_ level: LogLevel) -> ColorSet { - if let scheme = colorSchemeMap[CommonPrefs.Name.consoleLogColorsMap.rawValue], - let levelColor = scheme[level.color] { - return levelColor - } - return switch level { - case .info: ColorSet(text: .textColor, background: .textBackgroundColor) - case .warning: ColorSet(text: .yellow, background: .textBackgroundColor) - case .error: ColorSet(text: .red, background: .textBackgroundColor) - } - } -} diff --git a/Sources/Features/FoldersCompare/Extensions/DiffCountersItem+CompareItem.swift b/Sources/Features/FoldersCompare/Extensions/DiffCountersItem+CompareItem.swift deleted file mode 100644 index b51fa6b..0000000 --- a/Sources/Features/FoldersCompare/Extensions/DiffCountersItem+CompareItem.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// DiffCountersItem+CompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 10/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -extension DiffCountersItem { - static func diffCounter( - withItem compareItem: CompareItem, - options: ComparatorOptions - ) -> [DiffCountersItem] { - let leftOrphans = compareItem.orphanFiles - let rightOrphans = compareItem.linkedItem?.orphanFiles ?? 0 - let matching = compareItem.matchedFiles - var changed = 0 - var leftNewer = 0 - var rightNewer = 0 - - if options.contains(.timestamp) { - let linkedOlder = compareItem.linkedItem?.olderFiles ?? 0 - let linkedChanged = compareItem.linkedItem?.changedFiles ?? 0 - - if compareItem.changedFiles > 0, linkedOlder > 0 { - leftNewer = linkedOlder - } - if linkedChanged > 0, compareItem.olderFiles > 0 { - rightNewer = compareItem.olderFiles - } - if leftNewer == 0, rightNewer == 0, compareItem.changedFiles > 0 { - changed = compareItem.changedFiles - } - } else { - changed = compareItem.changedFiles - } - - var items = [DiffCountersItem]() - - add( - fileCount: leftOrphans, - format: NSLocalizedString("%ld %lu orphans", comment: "4 left/right orphans"), - side: .left, - type: .orphan, - items: &items - ) - add( - fileCount: leftNewer, - format: NSLocalizedString("%ld %lu newer", comment: "4 left/right newer"), - side: .left, - type: .newer, - items: &items - ) - add( - fileCount: rightOrphans, - format: NSLocalizedString("%ld %lu orphans", comment: "4 left/right orphans"), - side: .right, - type: .orphan, - items: &items - ) - add( - fileCount: rightNewer, - format: NSLocalizedString("%ld %lu newer", comment: "4 left/right newer"), - side: .right, - type: .newer, - items: &items - ) - add( - fileCount: changed, - format: NSLocalizedString("%ld changed", comment: ""), - side: .left, - type: .changed, - items: &items - ) - add( - fileCount: matching, - format: NSLocalizedString("%ld same", comment: ""), - side: .left, - type: .same, - items: &items - ) - if compareItem.summary.hasMetadataTags || compareItem.mismatchingTags > 0 { - add( - fileCount: compareItem.mismatchingTags + (compareItem.summary.hasMetadataTags ? 1 : 0), - format: NSLocalizedString("%ld mismatching tags", comment: ""), - side: .left, - type: .mismatchingTags, - items: &items - ) - } - if compareItem.summary.hasMetadataLabels || compareItem.mismatchingLabels > 0 { - add( - fileCount: compareItem.mismatchingLabels + (compareItem.summary.hasMetadataLabels ? 1 : 0), - format: NSLocalizedString("%ld mismatching labels", comment: ""), - side: .left, - type: .mismatchingLabels, - items: &items - ) - } - if let item = items.last, compareItem.parent != nil { - let fileName = compareItem.isValidFile ? compareItem.fileName : compareItem.linkedItem?.fileName - item.text = String.localizedStringWithFormat( - NSLocalizedString("%@ for '%@'", comment: ""), - item.text, - fileName ?? "<>" - ) as NSString - } - - return items - } - - private static func add( - fileCount: Int, - format: String, - side: DisplaySide, - type: CompareChangeType, - items: inout [DiffCountersItem] - ) { - guard fileCount > 0 else { - return - } - let text = String.localizedStringWithFormat(format, fileCount, side.rawValue) - if let color = CommonPrefs.shared.changeTypeColor(type)?.text { - items.append(diffCounterItem(withText: text, color: color)) - } - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/CompareItem+Metadata.swift b/Sources/Features/FoldersCompare/FileManager/CompareItem+Metadata.swift deleted file mode 100644 index 66845e3..0000000 --- a/Sources/Features/FoldersCompare/FileManager/CompareItem+Metadata.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// CompareItem+Metadata.swift -// VisualDiffer -// -// Created by davide ficano on 15/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -extension CompareItem { - /** - * update the metadata counters - * @param parentCount the counter containing the parent values - * @param fileObjectCount the counter for current prcessed CompareItem, - * it's used only if CompareItem points to a file object - */ - func updateMetadata( - with parentCount: inout CompareSummary, - fileObjectCount: inout CompareSummary - ) { - if isFile { - addMismatchingTags(-mismatchingTags) - addMismatchingLabels(-mismatchingLabels) - - parentCount.mismatchingTags += fileObjectCount.mismatchingTags - parentCount.mismatchingLabels += fileObjectCount.mismatchingLabels - fileObjectCount.mismatchingTags = 0 - fileObjectCount.mismatchingLabels = 0 - } else { - if summary.hasMetadataTags { - parentCount.mismatchingTags += 1 - } - if summary.hasMetadataLabels { - parentCount.mismatchingLabels += 1 - } - setMismatchingFolderMetadataTags(false) - setMismatchingFolderMetadataLabels(false) - addMismatchingTags(-mismatchingTags) - addMismatchingLabels(-mismatchingLabels) - } - } - - func copyMetadata(toPath destPath: inout URL) throws { - guard let url = toUrl() else { - return - } - - if summary.hasMetadataTags { - try url.copyTags(to: &destPath) - } - if summary.hasMetadataLabels { - try url.copyLabel(to: &destPath) - } - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/CopyCompareItem.swift b/Sources/Features/FoldersCompare/FileManager/CopyCompareItem.swift deleted file mode 100644 index d72db14..0000000 --- a/Sources/Features/FoldersCompare/FileManager/CopyCompareItem.swift +++ /dev/null @@ -1,399 +0,0 @@ -// -// CopyCompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 11/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -import os.log - -// swiftlint:disable function_parameter_count -class CopyCompareItem: NSObject { - let operationManager: FileOperationManager - private let fm = FileManager.default - private let deleteCompareItem: DeleteCompareItem - private(set) var bigFileSizeThreshold: UInt64 - private var bigFileManager: BigFileFileOperationManager - - init( - operationManager: FileOperationManager, - bigFileSizeThreshold: UInt64 - ) { - self.operationManager = operationManager - self.bigFileSizeThreshold = bigFileSizeThreshold - deleteCompareItem = DeleteCompareItem(operationManager: operationManager) - bigFileManager = BigFileFileOperationManager( - operationManager, - delegate: operationManager.delegate - ) - } - - func copy( - srcRoot: CompareItem, - srcBaseDir: URL, - destBaseDir: URL - ) { - guard srcRoot.isValidFile else { - return - } - guard let volumeType = srcBaseDir.volumeType() else { - operationManager.delegate.fileManager( - operationManager, - addError: FileError.unknownVolumeType, - forItem: srcRoot - ) - return - } - var srcCount = CompareSummary() - var destCount = CompareSummary() - - doCopy( - srcRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) - - var parent = srcRoot.parent - while let item = parent, - let fsUrl = item.toUrl() { - let destFullPath = URL.buildDestinationPath(fsUrl, nil, srcBaseDir, destBaseDir) - - item.addOlderFiles(-srcCount.olderFiles) - item.addChangedFiles(-srcCount.changedFiles) - item.addOrphanFiles(-srcCount.orphanFiles) - // matched file count increases - item.addMatchedFiles(srcCount.matchedFiles) - if item.isOrphanFolder { - item.addOrphanFolders(-1) - } - item.addMismatchingTags(-srcCount.mismatchingTags) - item.addMismatchingLabels(-srcCount.mismatchingLabels) - - if let destItem = item.linkedItem, - let attrs = try? fm.attributesOfItem(atPath: destFullPath.osPath) { - destItem.path = destFullPath.osPath - destItem.setAttributes(attrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - destItem.addOlderFiles(-destCount.olderFiles) - destItem.addChangedFiles(-destCount.changedFiles) - destItem.addOrphanFiles(-destCount.orphanFiles) - // matched file count increases - destItem.addMatchedFiles(destCount.matchedFiles) - destItem.addSubfoldersSize(destCount.subfoldersSize) - - destItem.addMismatchingTags(-destCount.mismatchingTags) - destItem.addMismatchingLabels(-destCount.mismatchingLabels) - } - - item.removeVisibleItems(filterConfig: operationManager.filterConfig) - - parent = item.parent - } - } - - @discardableResult - private func doCopy( - _ srcRoot: CompareItem, - srcBaseDir: URL, - destBaseDir: URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType: String - ) -> Bool { - let delegate = operationManager.delegate - - delegate.waitPause(for: operationManager) - - guard delegate.isRunning(operationManager) else { - return false - } - - guard srcRoot.isValidFile else { - return true - } - let isFiltered = srcRoot.isFiltered || !srcRoot.isDisplayed - if !operationManager.includesFiltered, isFiltered { - return true - } - guard let srcRootPath = srcRoot.path, - let destRoot = srcRoot.linkedItem else { - return true - } - - var retVal = true - var destFullPath = srcRoot.buildDestinationPath(from: srcBaseDir, to: destBaseDir) - - do { - let srcAttrs = try fm.attributesOfItem(atPath: srcRootPath) - if srcRoot.isFile { - var skipFile = false - try copySingleFile( - srcRoot, - destRoot: destRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - skipFile: &skipFile, - volumeType: volumeType - ) - if skipFile { - return true - } - } else { - try operationManager.createDestinationDirectory( - srcRoot, - destRoot: destRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - destFullPath: destFullPath - ) - retVal = copySubfolders( - srcRoot, - destRoot: destRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - destFullPath: &destFullPath, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - volumeType: volumeType - ) - } - try copyAttributes( - srcRoot, - destRoot: destRoot, - attributes: srcAttrs, - destFullPath: destFullPath, - volumeType: volumeType - ) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - return retVal - } - - func copySingleFile( - _ srcRoot: CompareItem, - destRoot: CompareItem, - srcBaseDir: URL, - destBaseDir: URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - skipFile: inout Bool, - volumeType: String - ) throws { - skipFile = false - guard let srcRootPath = srcRoot.path else { - return - } - let delegate = operationManager.delegate - var srcCount = srcRoot.summary - var destCount = destRoot.summary - var fileSize: Int64 = 0 - do { - let srcAttrs = try fm.attributesOfItem(atPath: srcRootPath) - let destFullPath = srcRoot.buildDestinationPath(from: srcBaseDir, to: destBaseDir) - var destAttrs: [FileAttributeKey: Any]? - var destError: NSError? - do { - let pathAttrs = try fm.attributesOfItem(atPath: destFullPath.osPath) - destAttrs = pathAttrs - fileSize = pathAttrs[.size] as? Int64 ?? 0 - } catch let error as NSError { - if error.code != NSFileReadNoSuchFileError { - destError = error - } - } - if !delegate.fileManager( - operationManager, - canReplaceFromPath: srcRootPath, - fromAttrs: srcAttrs, - toPath: destFullPath.osPath, - toAttrs: destAttrs - ) { - skipFile = true - return - } - let lastPathTimestamps = try createDirectory( - atPath: destBaseDir, - srcBaseDir: srcBaseDir, - namesFrom: srcRoot, - options: operationManager.comparator.options.directoryOptions - ) - if let destAttrs { - destRoot.path = destFullPath.osPath - destRoot.setAttributes(destAttrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - deleteCompareItem.doDelete( - destRoot, - baseDir: destBaseDir, - informDelegate: false - ) - } - delegate.fileManager(operationManager, initForItem: srcRoot) - if let destError { - throw destError - } - try copyItem( - srcRoot, - isBigFile: srcRoot.fileSize >= bigFileSizeThreshold, - destFullPath: destFullPath, - lastPathTimestamps: lastPathTimestamps, - volumeType: volumeType - ) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - - #if DEBUG && __VD_SLOW_OP__ - simulateSlowOperation("copy") - #endif - - srcRoot.addMatchedFiles(1) - srcRoot.updateMetadata( - with: &parentSrcCount, - fileObjectCount: &srcCount - ) - - destRoot.addMatchedFiles(1) - destRoot.updateMetadata( - with: &parentDestCount, - fileObjectCount: &destCount - ) - - if srcCount.matchedFiles == 0 { - parentSrcCount += srcCount - parentDestCount += destCount - parentSrcCount.matchedFiles += 1 - parentDestCount.matchedFiles += 1 - } - parentDestCount.subfoldersSize += srcRoot.fileSize - fileSize - delegate.fileManager(operationManager, updateForItem: srcRoot) - } - - func copyItem( - _ srcRoot: CompareItem, - isBigFile: Bool, - destFullPath: URL, - lastPathTimestamps: PathTimestamps?, - volumeType: String - ) throws { - guard let srcUrl = srcRoot.toUrl() else { - return - } - #if DEBUG && __VD_FAKE_FS_OP__ - Logger.debug.info("Fake copy, no files are really copied - \(path.localPath)") - #else - if isBigFile { - try bigFileManager.copy(srcRoot, destFullPath: destFullPath.osPath) - } else { - try fm.copyItem(at: srcUrl, to: destFullPath) - } - if let lastPathTimestamps { - try fm.setFileAttributes( - lastPathTimestamps.timestamps, - ofItemAtPath: destFullPath.deletingLastPathComponent(), - volumeType: volumeType - ) - } - #endif - } - - func copySubfolders( - _ srcRoot: CompareItem, - destRoot _: CompareItem, - srcBaseDir: URL, - destBaseDir: URL, - destFullPath: inout URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType: String - ) -> Bool { - var srcCount = CompareSummary() - var destCount = CompareSummary() - var retVal = true - - for item in srcRoot.children { - // swiftlint:disable:next for_where - if !doCopy( - item, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) { - retVal = false - break - } - } - do { - try srcRoot.copyMetadata(toPath: &destFullPath) - } catch { - operationManager.delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - - if srcRoot.isOrphanFolder { - srcRoot.addOrphanFolders(-1) - } - srcRoot.addOlderFiles(-srcCount.olderFiles) - srcRoot.addChangedFiles(-srcCount.changedFiles) - srcRoot.addOrphanFiles(-srcCount.orphanFiles) - // matched file count increases - srcRoot.addMatchedFiles(srcCount.matchedFiles) - - parentSrcCount += srcCount - var srcCountDummy = CompareSummary() - srcRoot.updateMetadata(with: &parentSrcCount, fileObjectCount: &srcCountDummy) - - if let srcRootLinked = srcRoot.linkedItem { - srcRootLinked.addOlderFiles(-destCount.olderFiles) - srcRootLinked.addChangedFiles(-destCount.changedFiles) - srcRootLinked.addOrphanFiles(-destCount.orphanFiles) - srcRootLinked.addSubfoldersSize(destCount.subfoldersSize) - - // matched file count increases - srcRootLinked.addMatchedFiles(destCount.matchedFiles) - - parentDestCount += destCount - var destCountDummy = CompareSummary() - srcRootLinked.updateMetadata(with: &parentDestCount, fileObjectCount: &destCountDummy) - } - return retVal - } - - func copyAttributes( - _ srcRoot: CompareItem, - destRoot: CompareItem, - attributes: [FileAttributeKey: Any], - destFullPath: URL, - volumeType: String - ) throws { - guard let fsSrcPath = (srcRoot.path as? NSString)?.fileSystemRepresentation else { - throw FolderManagerError.nilPath - } - let fsDestPath = (destFullPath.osPath as NSString).fileSystemRepresentation - - copyfile(fsSrcPath, fsDestPath, nil, copyfile_flags_t(COPYFILE_METADATA)) - - // set the timestamps at the end so we are sure they are not 'overwritten' - // For example modification date for folders changes after a file is copied into it - try fm.setFileAttributes( - operationManager.timestampAttributesFrom(attributes), - ofItemAtPath: destFullPath, - volumeType: volumeType - ) - // the delete above reset path and file to nil - // here we assign again the copied values - let destAttrs = try fm.attributesOfItem(atPath: destFullPath.osPath) - - destRoot.path = destFullPath.osPath - destRoot.setAttributes(destAttrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - - srcRoot.removeVisibleItems(filterConfig: operationManager.filterConfig) - } -} - -// swiftlint:enable function_parameter_count diff --git a/Sources/Features/FoldersCompare/FileManager/DeleteCompareItem.swift b/Sources/Features/FoldersCompare/FileManager/DeleteCompareItem.swift deleted file mode 100644 index 294cc64..0000000 --- a/Sources/Features/FoldersCompare/FileManager/DeleteCompareItem.swift +++ /dev/null @@ -1,374 +0,0 @@ -// -// DeleteCompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 11/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -import os.log - -class DeleteCompareItem { - struct DeleteResult { - var srcRoot: CompareItem? - var destRoot: CompareItem? - } - - let operationManager: FileOperationManager - private let fm = FileManager.default - private let delegate: FileOperationManagerDelegate - - init(operationManager: FileOperationManager) { - self.operationManager = operationManager - delegate = operationManager.delegate - } - - func delete( - _ srcRoot: CompareItem, - baseDir: URL - ) { - if !srcRoot.isValidFile { - return - } - - var srcCount = CompareSummary() - var destCount = CompareSummary() - - doDelete( - srcRoot, - baseDir: baseDir, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - informDelegate: true - ) - - var parent = srcRoot.parent - - while let item = parent { - item.addOlderFiles(-srcCount.olderFiles) - item.addChangedFiles(-srcCount.changedFiles) - item.addOrphanFiles(-srcCount.orphanFiles) - item.addMatchedFiles(-srcCount.matchedFiles) - item.addSubfoldersSize(-srcCount.subfoldersSize) - item.addMismatchingTags(-srcCount.mismatchingTags) - item.addMismatchingLabels(-srcCount.mismatchingLabels) - - if let destItem = item.linkedItem { - destItem.addOlderFiles(destCount.olderFiles) - destItem.addChangedFiles(destCount.changedFiles) - destItem.addOrphanFiles(destCount.orphanFiles) - destItem.addMatchedFiles(destCount.matchedFiles) - destItem.addSubfoldersSize(destCount.subfoldersSize) - destItem.addMismatchingTags(destCount.mismatchingTags) - destItem.addMismatchingLabels(destCount.mismatchingLabels) - } - - item.removeVisibleItems(filterConfig: operationManager.filterConfig) - - parent = item.parent - } - } - - @discardableResult - func doDelete( - _ srcRoot: CompareItem, - baseDir: URL, - informDelegate: Bool - ) -> Bool { - var parentSrcCount = CompareSummary() - var parentDestCount = CompareSummary() - - return doDelete( - srcRoot, - baseDir: baseDir, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - informDelegate: informDelegate - ) - } - - @discardableResult - func doDelete( - _ srcRoot: CompareItem, - baseDir: URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - informDelegate: Bool - ) -> Bool { - if informDelegate { - delegate.waitPause(for: operationManager) - - if !delegate.isRunning(operationManager) { - return false - } - } - if !srcRoot.isValidFile { - return true - } - let isFiltered = srcRoot.isFiltered || !srcRoot.isDisplayed - if !operationManager.includesFiltered, isFiltered { - return true - } - - let result = if srcRoot.isFile { - deleteFile( - srcRoot, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - informDelegate: informDelegate - ) - } else { - deleteFolder( - srcRoot, - baseDir: baseDir, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - informDelegate: informDelegate - ) - } - if let result { - updateParentTimeStamp(result) - updateFilteredStatus(result) - } - - return true - } - - private func updateParentTimeStamp(_ result: DeleteResult) { - // The parent's modification time changes after a delete so refresh attributes - if let parent = result.srcRoot?.parent, - let path = parent.path, - let attrs = try? fm.attributesOfItem(atPath: path) { - parent.setAttributes(attrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - } - } - - private func updateFilteredStatus(_ result: DeleteResult) { - if let srcRoot = result.srcRoot, - let destRoot = result.destRoot, - destRoot.isFiltered { - // check if file is yet filtered after deleting - let isFiltered = if let predicate = operationManager.filterConfig.predicate { - destRoot.evaluate(filter: predicate) - } else { - false - } - srcRoot.isFiltered = isFiltered - destRoot.isFiltered = isFiltered - } - } - - private func deleteFolder( - _ srcRoot: CompareItem, - baseDir: URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - informDelegate: Bool - ) -> DeleteResult? { - var srcCount = CompareSummary() - var destCount = CompareSummary() - - // interate in reverse order because the collection changes while running - for item in srcRoot.children.reversed() { - // swiftlint:disable:next for_where - if !doDelete( - item, - baseDir: baseDir, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - informDelegate: informDelegate - ) { - Logger.fs.warning("Stopped at folder process") - break - } - } - - var result: DeleteResult? - - if operationManager.canRemoveDirectory(srcRoot) { - if let path = srcRoot.path { - do { - try fm.removeItem(atPath: path) - } catch { - if informDelegate { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - } - makeFolderInvalid( - srcRoot, - parentCount: &parentSrcCount, - itemCount: &srcCount - ) - if let destRoot = srcRoot.linkedItem { - makeFolderOrphan( - destRoot, - parentCount: &parentDestCount, - itemCount: &destCount - ) - if destRoot.isValidFile { - result = DeleteResult(srcRoot: srcRoot, destRoot: destRoot) - } else { - makeLinkedFilesInvalid(srcRoot, destRoot: destRoot) - result = DeleteResult() - } - } - } - } else { - updateFolderCounters( - srcRoot, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - srcCount: &srcCount, - destCount: &destCount - ) - } - return result - } - - private func makeFolderInvalid( - _ srcRoot: CompareItem, - parentCount: inout CompareSummary, - itemCount: inout CompareSummary - ) { - parentCount += itemCount - if srcRoot.summary.hasMetadataTags { - parentCount.mismatchingTags += 1 - } - if srcRoot.summary.hasMetadataLabels { - parentCount.mismatchingLabels += 1 - } - if srcRoot.isOrphanFolder { - srcRoot.addOrphanFolders(-1) - } else { - srcRoot.linkedItem?.addOrphanFolders(1) - } - srcRoot.path = nil - srcRoot.setAttributes(nil, fileExtraOptions: []) - - // refresh isFolder - srcRoot.linkedItemIsFolder(true) - } - - private func makeFolderOrphan( - _ destRoot: CompareItem, - parentCount: inout CompareSummary, - itemCount: inout CompareSummary - ) { - destRoot.addOlderFiles(itemCount.olderFiles) - destRoot.addChangedFiles(itemCount.changedFiles) - destRoot.addOrphanFiles(itemCount.orphanFiles) - destRoot.addMatchedFiles(itemCount.matchedFiles) - - parentCount += itemCount - destRoot.setMismatchingFolderMetadataTags(false) - destRoot.setMismatchingFolderMetadataLabels(false) - } - - private func updateFolderCounters( - _ srcRoot: CompareItem, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - srcCount: inout CompareSummary, - destCount: inout CompareSummary - ) { - srcRoot.addOlderFiles(-srcCount.olderFiles) - srcRoot.addChangedFiles(-srcCount.changedFiles) - srcRoot.addOrphanFiles(-srcCount.orphanFiles) - srcRoot.addMatchedFiles(-srcCount.matchedFiles) - srcRoot.addSubfoldersSize(-srcCount.subfoldersSize) - parentSrcCount += srcCount - - if let destRoot = srcRoot.linkedItem { - destRoot.addOlderFiles(destCount.olderFiles) - destRoot.addChangedFiles(destCount.changedFiles) - destRoot.addOrphanFiles(destCount.orphanFiles) - destRoot.addMatchedFiles(destCount.matchedFiles) - parentDestCount += destCount - } - } - - private func deleteFile( - _ srcRoot: CompareItem, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - informDelegate: Bool - ) -> DeleteResult? { - guard let destRoot = srcRoot.linkedItem else { - return nil - } - if informDelegate { - delegate.fileManager(operationManager, initForItem: srcRoot) - } - - var result: DeleteResult? - - do { - try remove(item: srcRoot) - - parentSrcCount += srcRoot.summary - parentSrcCount.subfoldersSize += srcRoot.fileSize - - if destRoot.isValidFile { - makeFileOrphan(srcRoot, destRoot: destRoot, parentDestCount: &parentDestCount) - result = DeleteResult(srcRoot: srcRoot, destRoot: destRoot) - } else { - makeLinkedFilesInvalid(srcRoot, destRoot: destRoot) - result = DeleteResult(srcRoot: nil, destRoot: nil) - } - } catch { - if informDelegate { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - } - if informDelegate, - let result, - let srcRoot = result.srcRoot { - delegate.fileManager(operationManager, updateForItem: srcRoot) - } - - return result - } - - private func makeLinkedFilesInvalid( - _ srcRoot: CompareItem, - destRoot: CompareItem - ) { - // srcRoot and destRoot now are both invalid files (eg they both no longer exist) - // so we remove the folderStatus row and its visibleItem - srcRoot.parent?.remove(child: srcRoot) - destRoot.parent?.remove(child: destRoot) - - if let vi = srcRoot.visibleItem { - srcRoot.parent?.visibleItem?.remove(vi) - } - if let vi = destRoot.visibleItem { - destRoot.parent?.visibleItem?.remove(vi) - } - } - - private func makeFileOrphan( - _ srcRoot: CompareItem, - destRoot: CompareItem, - parentDestCount: inout CompareSummary - ) { - srcRoot.path = nil - srcRoot.setAttributes(nil, fileExtraOptions: []) - srcRoot.linkedItem = destRoot - srcRoot.linkedItemIsFolder(false) - - parentDestCount -= destRoot.summary - parentDestCount.orphanFiles += 1 - destRoot.addOrphanFiles(1) - } - - private func remove(item: CompareItem) throws { - guard let path = item.path else { - throw FolderManagerError.nilPath - } - #if __VD_FAKE_FS_OP__ - Logger.debug.info("Fake delete, no files are really deleted - \(path)") - #else - try fm.removeItem(atPath: path) - #endif - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/FileAttributes.swift b/Sources/Features/FoldersCompare/FileManager/FileAttributes.swift deleted file mode 100644 index 60520fb..0000000 --- a/Sources/Features/FoldersCompare/FileManager/FileAttributes.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// FileAttributes.swift -// VisualDiffer -// -// Created by davide ficano on 12/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -/** - * This is a wrapper used in async calls, it is Sendable - */ -struct FileAttributes: Sendable { - var modificationDate: Date? - var size: Int64? -} - -extension [FileAttributeKey: Any] { - func toFileAttributes() -> FileAttributes? { - FileAttributes( - modificationDate: self[.modificationDate] as? Date, - size: self[.size] as? Int64 - ) - } -} - -extension FileAttributes { - func toFileAttributeKeys() -> [FileAttributeKey: Any] { - var attrs: [FileAttributeKey: Any] = [:] - - if let modificationDate { - attrs[.modificationDate] = modificationDate - } - - if let size { - attrs[.size] = size - } - - return attrs - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/FileOperationManager+FileOperationManagerAction.swift b/Sources/Features/FoldersCompare/FileManager/FileOperationManager+FileOperationManagerAction.swift deleted file mode 100644 index f920625..0000000 --- a/Sources/Features/FoldersCompare/FileManager/FileOperationManager+FileOperationManagerAction.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// FileOperationManager+FileOperationManagerAction.swift -// VisualDiffer -// -// Created by davide ficano on 11/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -import os.log - -extension FileOperationManager: FileOperationManagerAction { - func copy(_ srcRoot: CompareItem, srcBaseDir: String, destBaseDir: String) { - let copyItem = CopyCompareItem( - operationManager: self, - bigFileSizeThreshold: defaultBigFileSizeThreshold - ) - - copyItem.copy( - srcRoot: srcRoot, - srcBaseDir: URL(filePath: srcBaseDir, directoryHint: .isDirectory), - destBaseDir: URL(filePath: destBaseDir, directoryHint: .isDirectory) - ) - } - - func move( - _ srcRoot: CompareItem, - srcBaseDir: String, - destBaseDir: String - ) { - let moveItem = MoveCompareItem( - operationManager: self, - bigFileSizeThreshold: defaultBigFileSizeThreshold - ) - - moveItem.move( - srcRoot: srcRoot, - srcBaseDir: URL(filePath: srcBaseDir, directoryHint: .isDirectory), - destBaseDir: URL(filePath: destBaseDir, directoryHint: .isDirectory) - ) - } - - func delete(_ srcRoot: CompareItem, srcBaseDir: String) { - let deleteItem = DeleteCompareItem(operationManager: self) - - deleteItem.delete(srcRoot, baseDir: URL(filePath: srcBaseDir, directoryHint: .isDirectory)) - } - - func touch(_ srcRoot: CompareItem, includeSubfolders: Bool, touch touchDate: Date?) { - let touchItem = TouchCompareItem(operationManager: self) - - touchItem.touch( - srcRoot: srcRoot, - includeSubfolders: includeSubfolders, - touchDate: touchDate - ) - } - - func rename(_ srcRoot: CompareItem, toName: String) { - let renameItem = RenameCompareItem(operationManager: self) - - renameItem.rename( - srcRoot: srcRoot, - toName: toName - ) - } -} - -#if DEBUG && __VD_SLOW_OP__ - func simulateSlowOperation(_ message: String, delay: TimeInterval = 2) { - Logger.debug.info("Simulating slow operation \(message)") - Thread.sleep(forTimeInterval: delay) - } -#endif diff --git a/Sources/Features/FoldersCompare/FileManager/FileOperationManager+Util.swift b/Sources/Features/FoldersCompare/FileManager/FileOperationManager+Util.swift deleted file mode 100644 index 0cc8b23..0000000 --- a/Sources/Features/FoldersCompare/FileManager/FileOperationManager+Util.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// FileOperationManager+Util.swift -// VisualDiffer -// -// Created by davide ficano on 09/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension FileOperationManager { - func canRemoveDirectory(_ item: CompareItem) -> Bool { - if !filterConfig.followSymLinks, item.isSymbolicLink { - // unlink folder without deleting files pointed to real path - return true - } - // if some error occurred while deleting files or - // directory isn't empty (can contain filtered files) it can't be deleted - if let path = item.path, - let dirEnum = FileManager.default.enumerator(atPath: path) { - return dirEnum.nextObject() == nil - } - return false - } - - func createSymLink( - atPath path: URL, - destinationOfSymLinkAtPath srcPath: URL - ) throws { - let canCreateSymLink = try overwriteSymLink(path) - - if canCreateSymLink { - // don't normalize relative path to absolute one - // this allow us to recreate relative path to destBaseDir - let real = try srcPath.destinationOfSymbolicLink() - try path.createSymbolicLink(withDestination: real) - } else { - throw FileError.createSymLink(path: path.osPath) - } - } - - /** - * check if we can "overwrite" the file with symlink - */ - func overwriteSymLink(_ url: URL) throws -> Bool { - do { - let urlPath = url.osPath - let cleanPath = urlPath.hasSuffix("/") ? String(urlPath.dropLast()) : urlPath - let attrs = try FileManager.default.attributesOfItem(atPath: cleanPath) - if let fileType = attrs[.type] as? String, - fileType == FileAttributeType.typeSymbolicLink.rawValue { - // should call doDelete - try FileManager.default.removeItem(at: url) - return true - } - } catch let error as NSError { - if error.domain == NSCocoaErrorDomain, - error.code == NSFileReadNoSuchFileError { - return true - } - } - return false - } - - func timestampAttributesFrom(_ srcAttrs: [FileAttributeKey: Any]) -> [FileAttributeKey: Any] { - var timestampAttrs = [FileAttributeKey: Any]() - let keys: [FileAttributeKey] = [.creationDate, .modificationDate] - - for key in keys { - if let value = srcAttrs[key] { - timestampAttrs[key] = value - } - } - - return timestampAttrs - } - - func createDestinationDirectory( - _ srcRoot: CompareItem, - destRoot _: CompareItem, - srcBaseDir: URL, - destBaseDir: URL, - destFullPath: URL - ) throws { - if !filterConfig.followSymLinks, srcRoot.isSymbolicLink { - guard let srcRootPath = srcRoot.toUrl() else { - throw FolderManagerError.nilPath - } - try createSymLink( - atPath: destFullPath, - destinationOfSymLinkAtPath: srcRootPath - ) - } else { - // Directory can be empty so we ensure it is created - try createDirectory( - atPath: destBaseDir, - srcBaseDir: srcBaseDir, - namesFrom: srcRoot, - options: comparator.options.directoryOptions - ) - } - } -} - -extension ComparatorOptions { - var directoryOptions: DirectoryOptions { - var options: DirectoryOptions = [] - - if contains(.finderLabel) { - options.insert(.copyLabels) - } - if contains(.finderTags) { - options.insert(.copyTags) - } - - return options - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/FileOperationManager.swift b/Sources/Features/FoldersCompare/FileManager/FileOperationManager.swift deleted file mode 100644 index 2a19444..0000000 --- a/Sources/Features/FoldersCompare/FileManager/FileOperationManager.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// FileOperationManager.swift -// VisualDiffer -// -// Created by davide ficano on 02/04/16. -// Copyright (c) 2016 visualdiffer.com -// - -@objc protocol FileOperationManagerDelegate: AnyObject { - func waitPause(for fileManager: FileOperationManager) - func isRunning(_ fileManager: FileOperationManager) -> Bool - func fileManager( - _ fileManager: FileOperationManager, - canReplaceFromPath fromPath: String, - fromAttrs: [FileAttributeKey: Any]?, - toPath: String, - toAttrs: [FileAttributeKey: Any]? - ) -> Bool - func fileManager(_ fileManager: FileOperationManager, initForItem item: CompareItem) - func fileManager(_ fileManager: FileOperationManager, updateForItem item: CompareItem) - func fileManager(_ fileManager: FileOperationManager, addError error: any Error, forItem item: CompareItem) - - @objc func fileManager(_ fileManager: FileOperationManager, startBigFileOperationForItem item: CompareItem) - @objc func isBigFileOperationCancelled(_ fileManager: FileOperationManager) -> Bool - @objc func isBigFileOperationCompleted(_ fileManager: FileOperationManager) -> Bool - @objc func fileManager(_ fileManager: FileOperationManager, setCancelled cancelled: Bool) - @objc func fileManager(_ fileManager: FileOperationManager, setCompleted completed: Bool) - @objc func fileManager(_ fileManager: FileOperationManager, updateBytesCompleted bytesCompleted: Double, totalBytes: Double, throughput: Double) -} - -public protocol FileOperationManagerAction: AnyObject { - func copy(_ srcRoot: CompareItem, srcBaseDir: String, destBaseDir: String) - func move( - _ srcRoot: CompareItem, - srcBaseDir: String, - destBaseDir: String - ) - func delete(_ srcRoot: CompareItem, srcBaseDir: String) - func touch(_ srcRoot: CompareItem, includeSubfolders: Bool, touch touchDate: Date?) - func rename(_ srcRoot: CompareItem, toName: String) -} - -@objc class FileOperationManager: NSObject, @unchecked Sendable { - /** - * Can be modified by callers, this allows file system operations to include filtered files while copying, moving - * The filterConfig.showFilteredFiles is then used to correctly updated the UI - */ - var includesFiltered: Bool - - let filterConfig: FilterConfig - let comparator: ItemComparator - @objc let delegate: FileOperationManagerDelegate - - init( - filterConfig: FilterConfig, - comparator: ItemComparator, - delegate: FileOperationManagerDelegate, - includesFiltered: Bool? = nil - ) { - self.includesFiltered = includesFiltered ?? filterConfig.showFilteredFiles - self.filterConfig = filterConfig - self.comparator = comparator - self.delegate = delegate - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/FolderManagerError.swift b/Sources/Features/FoldersCompare/FileManager/FolderManagerError.swift deleted file mode 100644 index bf5260d..0000000 --- a/Sources/Features/FoldersCompare/FileManager/FolderManagerError.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// FolderManagerError.swift -// VisualDiffer -// -// Created by davide ficano on 04/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -public enum FolderManagerError: Error { - case nilPath -} - -extension FolderManagerError: CustomStringConvertible { - public var description: String { - switch self { - case .nilPath: - "Path is not defined" - } - } -} diff --git a/Sources/Features/FoldersCompare/FileManager/MoveCompareItem.swift b/Sources/Features/FoldersCompare/FileManager/MoveCompareItem.swift deleted file mode 100644 index c5ce545..0000000 --- a/Sources/Features/FoldersCompare/FileManager/MoveCompareItem.swift +++ /dev/null @@ -1,464 +0,0 @@ -// -// MoveCompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 11/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -// swiftlint:disable function_parameter_count file_length -public class MoveCompareItem: NSObject { - let operationManager: FileOperationManager - private let fm = FileManager.default - private let deleteCompareItem: DeleteCompareItem - private(set) var bigFileSizeThreshold: UInt64 - private let delegate: FileOperationManagerDelegate - private var bigFileManager: BigFileFileOperationManager - - init( - operationManager: FileOperationManager, - bigFileSizeThreshold: UInt64 - ) { - self.operationManager = operationManager - self.bigFileSizeThreshold = bigFileSizeThreshold - - deleteCompareItem = DeleteCompareItem(operationManager: operationManager) - delegate = operationManager.delegate - bigFileManager = BigFileFileOperationManager( - operationManager, - delegate: operationManager.delegate - ) - } - - public func move( - srcRoot: CompareItem, - srcBaseDir: URL, - destBaseDir: URL - ) { - guard srcRoot.isValidFile else { - return - } - - guard let volumeType = destBaseDir.volumeType() else { - delegate.fileManager( - operationManager, - addError: FileError.unknownVolumeType, - forItem: srcRoot - ) - return - } - - var srcCount = CompareSummary() - var destCount = CompareSummary() - - _ = try? doMove( - srcRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) - - var parent = srcRoot.parent - while let item = parent, - let itemUrl = item.toUrl() { - let destUrl = URL.buildDestinationPath(itemUrl, nil, srcBaseDir, destBaseDir) - let destFullPath = destUrl.osPath - - item.addOlderFiles(-srcCount.olderFiles) - item.addChangedFiles(-srcCount.changedFiles) - item.addOrphanFiles(-srcCount.orphanFiles) - item.addMatchedFiles(-srcCount.matchedFiles) - item.addSubfoldersSize(-srcCount.subfoldersSize) - - item.addMismatchingTags(-srcCount.mismatchingTags) - item.addMismatchingLabels(-srcCount.mismatchingLabels) - - if let destItem = item.linkedItem, - let attrs = try? fm.attributesOfItem(atPath: destFullPath) { - destItem.path = destFullPath - destItem.setAttributes(attrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - destItem.addOlderFiles(-destCount.olderFiles) - destItem.addChangedFiles(-destCount.changedFiles) - destItem.addOrphanFiles(-destCount.orphanFiles) - destItem.addMatchedFiles(-destCount.matchedFiles) - destItem.addSubfoldersSize(-destCount.subfoldersSize) - - destItem.addMismatchingTags(-destCount.mismatchingTags) - destItem.addMismatchingLabels(-destCount.mismatchingLabels) - } - - item.removeVisibleItems(filterConfig: operationManager.filterConfig) - - parent = item.parent - } - } - - @discardableResult - func doMove( - _ srcRoot: CompareItem, - srcBaseDir: URL, - destBaseDir: URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType: String - ) throws -> Bool { - delegate.waitPause(for: operationManager) - - guard delegate.isRunning(operationManager) else { - return false - } - - guard srcRoot.isValidFile else { - return true - } - - guard let destRoot = srcRoot.linkedItem else { - throw FolderManagerError.nilPath - } - - let isFiltered = srcRoot.isFiltered || !srcRoot.isDisplayed - if !operationManager.includesFiltered, isFiltered { - return true - } - - guard let srcRootPath = srcRoot.path else { - throw FolderManagerError.nilPath - } - - var destAttrs: [FileAttributeKey: Any]? - var destFullPath = srcRoot.buildDestinationPath(from: srcBaseDir, to: destBaseDir) - - do { - let srcAttrs = try fm.attributesOfItem(atPath: srcRootPath) - - if srcRoot.isFile { - do { - destAttrs = try fm.attributesOfItem(atPath: destFullPath.osPath) - } catch {} - if !delegate.fileManager( - operationManager, - canReplaceFromPath: srcRootPath, - fromAttrs: srcAttrs, - toPath: destFullPath.osPath, - toAttrs: destAttrs - ) { - // skip this file - return true - } - try moveSingleFile( - srcRoot, - destRoot: destRoot, - srcAttrs: srcAttrs, - destAttrs: destAttrs, - destFullPath: destFullPath, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - volumeType: volumeType - ) - } else { - try operationManager.createDestinationDirectory( - srcRoot, - destRoot: destRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - destFullPath: destFullPath - ) - try moveSubfolders( - srcRoot, - destRoot: destRoot, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - destFullPath: &destFullPath, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - volumeType: volumeType - ) - - try deleteSourceDirectory( - srcRoot, - destRoot: destRoot, - attributes: srcAttrs, - destFullPath: destFullPath - ) - } - try copyAttributes( - srcRoot, - destRoot: destRoot, - attributes: srcAttrs, - destFullPath: destFullPath, - volumeType: volumeType - ) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - return true - } - - private func moveSingleFile( - _ srcRoot: CompareItem, - destRoot: CompareItem, - srcAttrs _: [FileAttributeKey: Any], - destAttrs: [FileAttributeKey: Any]?, - destFullPath: URL, - srcBaseDir: URL, - destBaseDir: URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType: String - ) throws { - var srcCount = srcRoot.summary - var destCount = destRoot.summary - - do { - let lastPathTimestamps = try createDirectory( - atPath: destBaseDir, - srcBaseDir: srcBaseDir, - namesFrom: srcRoot, - options: operationManager.comparator.options.directoryOptions - ) - if let destAttrs { - destRoot.path = destFullPath.osPath - destRoot.setAttributes(destAttrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - deleteCompareItem.doDelete( - destRoot, - baseDir: destBaseDir, - informDelegate: false - ) - } - delegate.fileManager(operationManager, initForItem: srcRoot) - - try moveItem( - srcRoot, - isBigFile: srcRoot.fileSize >= bigFileSizeThreshold, - destFullPath: destFullPath, - lastPathTimestamps: lastPathTimestamps, - volumeType: volumeType - ) - #if DEBUG && __VD_SLOW_OP__ - simulateSlowOperation("move") - #endif - updateParentCountersAfterMoveFile( - srcRoot, - destRoot: destRoot, - destAttrs: destAttrs, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount, - srcCount: &srcCount, - destCount: &destCount - ) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - delegate.fileManager(operationManager, updateForItem: srcRoot) - } - - func updateParentCountersAfterMoveFile( - _ srcRoot: CompareItem, - destRoot: CompareItem, - destAttrs: [FileAttributeKey: Any]?, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - srcCount: inout CompareSummary, - destCount: inout CompareSummary - ) { - let srcSize = srcRoot.fileSize - let destSize: Int64 = if let destAttrs, let size = (destAttrs[.size] as? NSNumber) { - size.int64Value - } else { - 0 - } - - parentDestCount.olderFiles += destCount.olderFiles - parentDestCount.changedFiles += destCount.changedFiles - parentDestCount.matchedFiles += destCount.matchedFiles - parentDestCount.subfoldersSize -= srcSize - destSize - parentDestCount.orphanFiles -= 1 - - parentSrcCount.olderFiles += srcCount.olderFiles - parentSrcCount.changedFiles += srcCount.changedFiles - parentSrcCount.matchedFiles += srcCount.matchedFiles - parentSrcCount.subfoldersSize += srcSize - parentSrcCount.orphanFiles += srcCount.orphanFiles - - // set all counters on destRoot to 0 except 'orphan' set to 1 - // because it's an orphan file - destRoot.addOrphanFiles(1) - destRoot.updateMetadata( - with: &parentDestCount, - fileObjectCount: &destCount - ) - - srcRoot.updateMetadata( - with: &parentSrcCount, - fileObjectCount: &srcCount - ) - - srcRoot.invalidate() - srcRoot.isFiltered = false - } - - func moveItem( - _ srcRoot: CompareItem, - isBigFile: Bool, - destFullPath: URL, - lastPathTimestamps: PathTimestamps?, - volumeType: String - ) throws { - #if DEBUG && __VD_FAKE_FS_OP__ - Logger.debug.info("Fake move, no files are really moved - \(srcRoot.path)") - #else - guard let url = srcRoot.toUrl() else { - throw FolderManagerError.nilPath - } - if isBigFile { - try bigFileManager.move(srcRoot, destFullPath: destFullPath.osPath) - } else { - try fm.moveItem(at: url, to: destFullPath) - } - if let lastPathTimestamps { - try fm.setFileAttributes( - lastPathTimestamps.timestamps, - ofItemAtPath: destFullPath.deletingLastPathComponent(), - volumeType: volumeType - ) - } - #endif - } - - func moveSubfolders( - _ srcRoot: CompareItem, - destRoot: CompareItem, - srcBaseDir: URL, - destBaseDir: URL, - destFullPath: inout URL, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType: String - ) throws { - var srcCount = CompareSummary() - var destCount = CompareSummary() - - do { - // interate in reverse order because the collection changes while running - for item in srcRoot.children.reversed() { - // swiftlint:disable:next for_where - if try doMove( - item, - srcBaseDir: srcBaseDir, - destBaseDir: destBaseDir, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) == false { - break - } - } - } catch {} - - do { - try srcRoot.copyMetadata(toPath: &destFullPath) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - - srcRoot.addOlderFiles(-srcCount.olderFiles) - srcRoot.addChangedFiles(-srcCount.changedFiles) - srcRoot.addOrphanFiles(-srcCount.orphanFiles) - srcRoot.addMatchedFiles(-srcCount.matchedFiles) - srcRoot.addSubfoldersSize(-srcCount.subfoldersSize) - parentSrcCount += srcCount - var dummySrcCount = CompareSummary() - srcRoot.updateMetadata( - with: &parentSrcCount, - fileObjectCount: &dummySrcCount - ) - - destRoot.addOlderFiles(-destCount.olderFiles) - destRoot.addChangedFiles(-destCount.changedFiles) - destRoot.addOrphanFiles(-destCount.orphanFiles) - destRoot.addMatchedFiles(-destCount.matchedFiles) - destRoot.addSubfoldersSize(-destCount.subfoldersSize) - parentDestCount += destCount - var dummyDestCount = CompareSummary() - destRoot.updateMetadata( - with: &parentDestCount, - fileObjectCount: &dummyDestCount - ) - } - - private func deleteSourceDirectory( - _ srcRoot: CompareItem, - destRoot: CompareItem, - attributes _: [FileAttributeKey: Any], - destFullPath: URL - ) throws { - guard operationManager.canRemoveDirectory(srcRoot) else { - return - } - - guard let srcPath = srcRoot.path else { - throw FolderManagerError.nilPath - } - let fsSrcPath = (srcPath as NSString).fileSystemRepresentation - let fsDestPath = (destFullPath.osPath as NSString).fileSystemRepresentation - - copyfile(fsSrcPath, fsDestPath, nil, copyfile_flags_t(COPYFILE_METADATA)) - - do { - try fm.removeItem(atPath: srcPath) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - if srcRoot.isOrphanFolder { - srcRoot.addOrphanFolders(-1) - srcRoot.linkedItem?.addOrphanFolders(1) - } else { - srcRoot.linkedItem?.addOrphanFolders(1) - } - srcRoot.invalidate() - - // refresh isFolderObject - srcRoot.linkedItemIsFolder(true) - - // destRoot is orphan set to default type - destRoot.type = .orphan - } - - func copyAttributes( - _ srcRoot: CompareItem, - destRoot: CompareItem, - attributes: [FileAttributeKey: Any]?, - destFullPath: URL, - volumeType: String - ) throws { - // set the timestamps at the end so we are sure they are not 'overwritten' - // For example modification date for folders changes after a file is copied into it - if let attributes { - try fm.setFileAttributes( - operationManager.timestampAttributesFrom(attributes), - ofItemAtPath: destFullPath, - volumeType: volumeType - ) - } - // update file information after move - destRoot.path = destFullPath.osPath - destRoot.setAttributes( - try? FileManager.default.attributesOfItem(atPath: destFullPath.osPath), - fileExtraOptions: operationManager.filterConfig.fileExtraOptions - ) - - if destRoot.isFiltered, - let filter = operationManager.filterConfig.predicate { - // check if file is yet filtered after moving - destRoot.isFiltered = destRoot.evaluate(filter: filter) - } - - srcRoot.removeVisibleItems(filterConfig: operationManager.filterConfig) - } -} - -// swiftlint:enable function_parameter_count file_length diff --git a/Sources/Features/FoldersCompare/FileManager/PathTimestamps.swift b/Sources/Features/FoldersCompare/FileManager/PathTimestamps.swift deleted file mode 100644 index 6ed0e8e..0000000 --- a/Sources/Features/FoldersCompare/FileManager/PathTimestamps.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// PathTimestamps.swift -// VisualDiffer -// -// Created by davide ficano on 03/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct DirectoryOptions: OptionSet { - let rawValue: Int - - static let copyLabels = DirectoryOptions(rawValue: 1 << 0) - static let copyTags = DirectoryOptions(rawValue: 1 << 1) -} - -struct PathTimestamps { - private(set) var timestamps = [FileAttributeKey: Any]() - - init() {} - - init(fromFileAttributes attrs: [FileAttributeKey: Any]) { - if let creationDate = attrs[.creationDate] as? Date { - timestamps[.creationDate] = creationDate - } - if let modificationDate = attrs[.modificationDate] as? Date { - timestamps[.modificationDate] = modificationDate - } - } - - func applyTo(itemAtPath path: URL) throws { - try FileManager.default.setAttributes(timestamps, ofItemAtPath: path.osPath) - } -} - -@discardableResult -func createDirectory( - atPath baseDestPath: URL, - srcBaseDir: URL, - namesFrom srcRoot: CompareItem, - options: DirectoryOptions -) throws -> PathTimestamps? { - var item: CompareItem? = srcRoot - - if srcRoot.isFile { - item = srcRoot.parent - } - - var directories: [CompareItem] = [] - - while let localItem = item, - let url = localItem.toUrl(), - url != srcBaseDir { - directories.insert(localItem, at: 0) - item = localItem.parent - } - - let fm = FileManager.default - var path = baseDestPath - var attrs: PathTimestamps? - - for fsDir in directories { - guard let fsPath = fsDir.path, - let fsFileName = fsDir.fileName else { - break - } - path = path.appending(path: fsFileName, directoryHint: fsDir.isFolder ? .isDirectory : .notDirectory) - - if fm.fileExists(atPath: path.osPath) { - continue - } - - try fm.createDirectory( - at: path, - withIntermediateDirectories: false, - attributes: nil - ) - try attrs?.applyTo(itemAtPath: path.deletingLastPathComponent()) - - if options.contains(.copyLabels) { - try fsDir.toUrl()?.copyLabel(to: &path) - } - - if options.contains(.copyTags) { - try fsDir.toUrl()?.copyTags(to: &path) - } - - attrs = try PathTimestamps(fromFileAttributes: fm.attributesOfItem(atPath: fsPath)) - } - - try attrs?.applyTo(itemAtPath: path) - - return attrs -} diff --git a/Sources/Features/FoldersCompare/FileManager/RenameCompareItem.swift b/Sources/Features/FoldersCompare/FileManager/RenameCompareItem.swift deleted file mode 100644 index 8444227..0000000 --- a/Sources/Features/FoldersCompare/FileManager/RenameCompareItem.swift +++ /dev/null @@ -1,348 +0,0 @@ -// -// RenameCompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 11/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -// swiftlint:disable function_parameter_count -class RenameCompareItem { - let operationManager: FileOperationManager - private let fm = FileManager.default - private let delegate: FileOperationManagerDelegate - - init(operationManager: FileOperationManager) { - self.operationManager = operationManager - delegate = operationManager.delegate - } - - func rename( - srcRoot: CompareItem, - toName: String - ) { - guard let srcUrl = srcRoot.toUrl(), - srcRoot.isValidFile else { - return - } - - guard let volumeType = srcUrl.volumeType() else { - operationManager.delegate.fileManager( - operationManager, - addError: FileError.unknownVolumeType, - forItem: srcRoot - ) - return - } - - var srcCount = CompareSummary() - var destCount = CompareSummary() - - _ = try? doRename( - srcRoot, - toName: toName, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) - - var parent = srcRoot.parent - - while let item = parent { - item.addOlderFiles(srcCount.olderFiles) - item.addChangedFiles(srcCount.changedFiles) - item.addMatchedFiles(srcCount.matchedFiles) - item.addOrphanFiles(srcCount.orphanFiles) - - if let destItem = item.linkedItem { - destItem.addOlderFiles(destCount.olderFiles) - destItem.addChangedFiles(destCount.changedFiles) - destItem.addMatchedFiles(destCount.matchedFiles) - destItem.addOrphanFiles(destCount.orphanFiles) - } - - item.removeVisibleItems(filterConfig: operationManager.filterConfig) - - parent = item.parent - } - } - - @discardableResult - func doRename( - _ srcRoot: CompareItem, - toName: String, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType _: String - ) throws -> Bool { - delegate.waitPause(for: operationManager) - - if !delegate.isRunning(operationManager) { - return false - } - - if !srcRoot.isValidFile { - return true - } - let isFiltered = srcRoot.isFiltered || !srcRoot.isDisplayed - if !operationManager.includesFiltered, isFiltered { - return true - } - - guard let srcUrl = srcRoot.toUrl() else { - throw FolderManagerError.nilPath - } - - guard let destRoot = srcRoot.linkedItem else { - throw FolderManagerError.nilPath - } - - let toPath = srcUrl - .deletingLastPathComponent() - .appendingPathComponent(toName) - - var renamedSrcRoot: CompareItem? - - do { - try fm.moveItem(at: srcUrl, to: toPath) - // search fileName on other side - let fileIndex = destRoot.parent?.findChildFileNameIndex( - toName, - typeIsFile: srcRoot.isFile - ) ?? NSNotFound - - if fileIndex == NSNotFound { - renamedSrcRoot = insertOrphan( - srcRoot: srcRoot, - toPath: toPath, - parentCount: &parentSrcCount - ) - } else { - if let item = destRoot.parent?.child(at: fileIndex) { - renamedSrcRoot = align( - srcRoot: srcRoot, - toPath: toPath, - item: item, - index: fileIndex, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount - ) - } - } - // all files now are orphans so decrement correctly parent counters - parentDestCount.orphanFiles += destRoot.matchedFiles + destRoot.olderFiles + destRoot.changedFiles - parentDestCount.matchedFiles -= destRoot.matchedFiles - parentDestCount.olderFiles -= destRoot.olderFiles - parentDestCount.changedFiles -= destRoot.changedFiles - - // must be called after duplicateAsOrphan because it resets - // srcRoot's path and attrs - destRoot.mark(asOrphan: true) - - if let parent = srcRoot.parent { - parent.filterVisibleItems( - showFilteredFiles: operationManager.filterConfig.showFilteredFiles, - hideEmptyFolders: operationManager.filterConfig.hideEmptyFolders, - recursive: true - ) - } - - // Start from renamedSrcRoot's parent because after rename both src and dest may be orphans - // and displayFilters can require to remove them - renamedSrcRoot?.parent?.removeVisibleItems( - filterConfig: operationManager.filterConfig, - recursive: true - ) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - delegate.fileManager(operationManager, updateForItem: srcRoot) - - return true - } - - private func insertOrphan( - srcRoot: CompareItem, - toPath: URL, - parentCount: inout CompareSummary - ) -> CompareItem { - // srcRoot and destRoot are both orphans - parentCount.orphanFiles += srcRoot.matchedFiles + srcRoot.olderFiles + srcRoot.changedFiles - parentCount.matchedFiles -= srcRoot.matchedFiles - parentCount.olderFiles -= srcRoot.olderFiles - parentCount.changedFiles -= srcRoot.changedFiles - - let renamedSrcRoot = srcRoot.duplicateAsOrphan( - withPath: toPath.osPath, - withParent: srcRoot.parent, - fileExtraOptions: operationManager.filterConfig.fileExtraOptions, - recursive: true - ) - - // find insertion point into parent - let insertPoint = findInsertionPoint(in: srcRoot.parent, for: renamedSrcRoot) - - renamedSrcRoot.parent?.insert(child: renamedSrcRoot, at: insertPoint) - if let linkedItem = renamedSrcRoot.linkedItem { - linkedItem.parent?.insert(child: linkedItem, at: insertPoint) - } - - return renamedSrcRoot - } - - private func findInsertionPoint(in parent: CompareItem?, for item: CompareItem) -> Int { - guard let parent else { - return 0 - } - - var insertPoint = 0 - for child in parent.children { - if let child = child.isValidFile ? child : child.linkedItem { - let result = child.compare(forList: item, followSymLinks: false) - if result == .orderedDescending { - break - } - } - insertPoint += 1 - } - return insertPoint - } - - private func align( - srcRoot: CompareItem, - toPath: URL, - item: CompareItem, - index: Int, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary - ) -> CompareItem { - if srcRoot.isOrphanFolder || srcRoot.isOrphanFile { - linkNewItem( - srcRoot: srcRoot, - toPath: toPath, - item: item, - index: index, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount - ) - } else { - makeOrphan( - srcRoot: srcRoot, - toPath: toPath, - item: item, - index: index, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount - ) - } - } - - private func linkNewItem( - srcRoot: CompareItem, - toPath: URL, - item: CompareItem, - index: Int, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary - ) -> CompareItem { - parentSrcCount.orphanFiles -= srcRoot.orphanFiles - parentDestCount.orphanFiles -= item.orphanFiles - - let renamedSrcRoot = srcRoot.duplicateAsOrphan( - withPath: toPath.osPath, - withParent: srcRoot.parent, - fileExtraOptions: operationManager.filterConfig.fileExtraOptions, - recursive: true - ) - - renamedSrcRoot.linkedItem = item - item.linkedItem = renamedSrcRoot - operationManager.comparator.alignItem( - renamedSrcRoot, - rightRoot: item, - alignConfig: AlignConfig(recursive: true, followSymLinks: operationManager.filterConfig.followSymLinks) - ) - - // TODO: expensive - renamedSrcRoot.applyComparison( - fileFilters: operationManager.filterConfig.predicate, - comparator: operationManager.comparator, - recursive: true - ) - - parentSrcCount.orphanFiles += renamedSrcRoot.orphanFiles - parentSrcCount.matchedFiles += renamedSrcRoot.matchedFiles - parentSrcCount.olderFiles += renamedSrcRoot.olderFiles - parentSrcCount.changedFiles += renamedSrcRoot.changedFiles - - parentDestCount.orphanFiles += item.orphanFiles - parentDestCount.matchedFiles += item.matchedFiles - parentDestCount.olderFiles += item.olderFiles - parentDestCount.changedFiles += item.changedFiles - // TODO: memory leak on srcRoot.linkedItem and item.linkedItem - // TODO: memory leak on replaced element - renamedSrcRoot.parent?.replaceChild(at: index, with: renamedSrcRoot) - - return renamedSrcRoot - } - - private func makeOrphan( - srcRoot: CompareItem, - toPath: URL, - item: CompareItem, - index: Int, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary - ) -> CompareItem { - // item is orphan so decrement parent - // the comparison done below will recompute the new counters - parentDestCount.orphanFiles -= item.orphanFiles - - // srcRoot and destRoot are both orphans - parentSrcCount.orphanFiles += srcRoot.matchedFiles + srcRoot.olderFiles + srcRoot.changedFiles - parentSrcCount.matchedFiles -= srcRoot.matchedFiles - parentSrcCount.olderFiles -= srcRoot.olderFiles - parentSrcCount.changedFiles -= srcRoot.changedFiles - - let renamedSrcRoot = srcRoot.duplicateAsOrphan( - withPath: toPath.osPath, - withParent: srcRoot.parent, - fileExtraOptions: operationManager.filterConfig.fileExtraOptions, - recursive: true - ) - - renamedSrcRoot.linkedItem = item - item.linkedItem = renamedSrcRoot - operationManager.comparator.alignItem( - renamedSrcRoot, - rightRoot: item, - alignConfig: AlignConfig(recursive: true, followSymLinks: operationManager.filterConfig.followSymLinks) - ) - - parentSrcCount.orphanFiles -= renamedSrcRoot.orphanFiles - - // TODO: expensive - renamedSrcRoot.applyComparison( - fileFilters: operationManager.filterConfig.predicate, - comparator: operationManager.comparator, - recursive: true - ) - - parentSrcCount.orphanFiles += renamedSrcRoot.orphanFiles - parentSrcCount.matchedFiles += renamedSrcRoot.matchedFiles - parentSrcCount.olderFiles += renamedSrcRoot.olderFiles - parentSrcCount.changedFiles += renamedSrcRoot.changedFiles - - parentDestCount.orphanFiles += item.orphanFiles - parentDestCount.matchedFiles += item.matchedFiles - parentDestCount.olderFiles += item.olderFiles - parentDestCount.changedFiles += item.changedFiles - - // TODO: memory leak on replaced element - renamedSrcRoot.parent?.replaceChild(at: index, with: renamedSrcRoot) - - return renamedSrcRoot - } -} - -// swiftlint:enable function_parameter_count diff --git a/Sources/Features/FoldersCompare/FileManager/TouchCompareItem.swift b/Sources/Features/FoldersCompare/FileManager/TouchCompareItem.swift deleted file mode 100644 index 43beee9..0000000 --- a/Sources/Features/FoldersCompare/FileManager/TouchCompareItem.swift +++ /dev/null @@ -1,276 +0,0 @@ -// -// TouchCompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 11/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -// swiftlint:disable function_parameter_count -class TouchCompareItem { - let operationManager: FileOperationManager - private let fm = FileManager.default - private let delegate: FileOperationManagerDelegate - - init(operationManager: FileOperationManager) { - self.operationManager = operationManager - delegate = operationManager.delegate - } - - func touch( - srcRoot: CompareItem, - includeSubfolders: Bool, - touchDate: Date? - ) { - guard let srcUrl = srcRoot.toUrl(), - srcRoot.isValidFile else { - return - } - - guard let volumeType = srcUrl.volumeType() else { - operationManager.delegate.fileManager( - operationManager, - addError: FileError.unknownVolumeType, - forItem: srcRoot - ) - return - } - - var srcCount = CompareSummary() - var destCount = CompareSummary() - let useComparator = operationManager.comparator.options.contains(.timestamp) - - doTouch( - srcRoot, - attrs: dateAttributes(touchDate), - includeSubfolders: includeSubfolders, - comparator: useComparator ? operationManager.comparator : nil, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) - - if useComparator { - var parent = srcRoot.parent - - while let item = parent { - item.addOlderFiles(srcCount.olderFiles) - item.addChangedFiles(srcCount.changedFiles) - item.addMatchedFiles(srcCount.matchedFiles) - - if let destItem = item.linkedItem { - destItem.addOlderFiles(destCount.olderFiles) - destItem.addChangedFiles(destCount.changedFiles) - destItem.addMatchedFiles(destCount.matchedFiles) - } - - item.removeVisibleItems(filterConfig: operationManager.filterConfig) - - parent = item.parent - } - } - } - - private func dateAttributes(_ date: Date?) -> [FileAttributeKey: Any]? { - guard let date else { - return nil - } - return [.modificationDate: date] - } - - @discardableResult - private func doTouch( - _ srcRoot: CompareItem, - attrs: [FileAttributeKey: Any]?, - includeSubfolders: Bool, - comparator: ItemComparator?, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary, - volumeType: String - ) -> Bool { - delegate.waitPause(for: operationManager) - - guard delegate.isRunning(operationManager) else { - return false - } - - guard srcRoot.isValidFile else { - return true - } - let isFiltered = srcRoot.isFiltered || !srcRoot.isDisplayed - if !operationManager.includesFiltered, isFiltered { - return true - } - guard let linkedItem = srcRoot.linkedItem else { - return true - } - guard let dateDict = buildTouchDateAttributes(attrs: attrs, item: linkedItem) else { - return true - } - - guard let srcRootPath = srcRoot.path else { - return true - } - delegate.fileManager(operationManager, initForItem: srcRoot) - do { - try fm.setFileAttributes( - dateDict, - ofItemAtPath: srcRootPath, - volumeType: volumeType - ) - #if DEBUG && __VD_SLOW_OP__ - simulateSlowOperation("touch") - #endif - let attrs = try fm.attributesOfItem(atPath: srcRootPath) - srcRoot.setAttributes(attrs, fileExtraOptions: operationManager.filterConfig.fileExtraOptions) - updateFileCounters(srcRoot, comparator, &parentSrcCount, &parentDestCount) - } catch { - delegate.fileManager(operationManager, addError: error, forItem: srcRoot) - } - delegate.fileManager(operationManager, updateForItem: srcRoot) - - touchFolders( - srcRoot, - volumeType: volumeType, - attrs: attrs, - includeSubfolders: includeSubfolders, - comparator: comparator, - parentSrcCount: &parentSrcCount, - parentDestCount: &parentDestCount - ) - - srcRoot.removeVisibleItems(filterConfig: operationManager.filterConfig) - - return true - } - - private func buildTouchDateAttributes( - attrs: [FileAttributeKey: Any]?, - item: CompareItem - ) -> [FileAttributeKey: Any]? { - if attrs != nil { - return attrs - } - if item.isValidFile, - let itemDate = item.fileModificationDate { - return [.modificationDate: itemDate] - } - return nil - } - - private func updateFileCounters( - _ item: CompareItem, - _ comparator: ItemComparator?, - _ parentSrcCount: inout CompareSummary, - _ parentDestCount: inout CompareSummary - ) { - guard item.isFile, - let linkedItem = item.linkedItem else { - return - } - - var srcCount = CompareSummary() - var destCount = CompareSummary() - - srcCount.olderFiles -= item.olderFiles - srcCount.changedFiles -= item.changedFiles - srcCount.matchedFiles -= item.matchedFiles - - destCount.olderFiles -= linkedItem.olderFiles - destCount.changedFiles -= linkedItem.changedFiles - destCount.matchedFiles -= linkedItem.matchedFiles - - comparator?.compare(item, linkedItem) - applyFilters(item, linkedItem) - removeFiltered(item, linkedItem) - - srcCount.olderFiles += item.olderFiles - srcCount.changedFiles += item.changedFiles - srcCount.matchedFiles += item.matchedFiles - - destCount.olderFiles += linkedItem.olderFiles - destCount.changedFiles += linkedItem.changedFiles - destCount.matchedFiles += linkedItem.matchedFiles - - parentSrcCount += srcCount - parentDestCount += destCount - } - - private func applyFilters( - _ lhs: CompareItem, - _ rhs: CompareItem - ) { - guard let fileFilters = operationManager.filterConfig.predicate else { - return - } - let isFiltered = lhs.evaluate(filter: fileFilters) || rhs.evaluate(filter: fileFilters) - lhs.isFiltered = isFiltered - rhs.isFiltered = isFiltered - } - - private func removeFiltered( - _ lhs: CompareItem, - _ rhs: CompareItem - ) { - guard !operationManager.filterConfig.showFilteredFiles, lhs.isFiltered else { - return - } - if let parentVI = lhs.parent?.visibleItem, - let vi = lhs.visibleItem { - parentVI.remove(vi) - } - if let parentVI = rhs.parent?.visibleItem, - let vi = rhs.visibleItem { - parentVI.remove(vi) - } - } - - @discardableResult - private func touchFolders( - _ srcRoot: CompareItem, - volumeType: String, - attrs: [FileAttributeKey: Any]?, - includeSubfolders: Bool, - comparator: ItemComparator?, - parentSrcCount: inout CompareSummary, - parentDestCount: inout CompareSummary - ) -> Bool { - guard includeSubfolders, srcRoot.isFolder else { - return true - } - var srcCount = CompareSummary() - var destCount = CompareSummary() - var retVal = true - - for item in srcRoot.children { - // swiftlint:disable:next for_where - if !doTouch( - item, - attrs: attrs, - includeSubfolders: includeSubfolders, - comparator: comparator, - parentSrcCount: &srcCount, - parentDestCount: &destCount, - volumeType: volumeType - ) { - retVal = false - break - } - } - srcRoot.addOlderFiles(srcCount.olderFiles) - srcRoot.addChangedFiles(srcCount.changedFiles) - srcRoot.addMatchedFiles(srcCount.matchedFiles) - parentSrcCount += srcCount - - if let linkedItem = srcRoot.linkedItem { - linkedItem.addOlderFiles(destCount.olderFiles) - linkedItem.addChangedFiles(destCount.changedFiles) - linkedItem.addMatchedFiles(destCount.matchedFiles) - parentDestCount += destCount - } - - return retVal - } -} - -// swiftlint:enable function_parameter_count diff --git a/Sources/Features/FoldersCompare/Navigator/CommonPrefs+DifferenceNavigator.swift b/Sources/Features/FoldersCompare/Navigator/CommonPrefs+DifferenceNavigator.swift deleted file mode 100644 index 3b7f087..0000000 --- a/Sources/Features/FoldersCompare/Navigator/CommonPrefs+DifferenceNavigator.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// CommonPrefs+DifferenceNavigator.swift -// VisualDiffer -// -// Created by davide ficano on 13/02/21. -// Copyright (c) 2021 visualdiffer.com -// - -extension CommonPrefs.Name { - enum Navigator { - static let wrap = CommonPrefs.Name(rawValue: "foldersDifferenceNavigatorWrap") - static let traverseFolders = CommonPrefs.Name(rawValue: "foldersDifferenceNavigatorTraverseFolders") - static let centerInWindow = CommonPrefs.Name(rawValue: "foldersDifferenceNavigatorCenterInWindow") - } -} - -extension CommonPrefs { - var folderDifferenceNavigatorWrap: DifferenceNavigator { - bool(forKey: .Navigator.wrap) ? .wrap : [] - } - - var folderDifferenceNavigatorTraverseFolders: DifferenceNavigator { - bool(forKey: .Navigator.traverseFolders) ? .traverseFolders : [] - } - - var folderDifferenceNavigatorCenterInWindow: DifferenceNavigator { - bool(forKey: .Navigator.centerInWindow) ? .centerInWindow : [] - } - - var folderDifferenceNavigatorOptions: DifferenceNavigator { - [ - folderDifferenceNavigatorWrap, - folderDifferenceNavigatorTraverseFolders, - folderDifferenceNavigatorCenterInWindow, - ] - } -} diff --git a/Sources/Features/FoldersCompare/Navigator/CompareItem+DifferenceNavigator.swift b/Sources/Features/FoldersCompare/Navigator/CompareItem+DifferenceNavigator.swift deleted file mode 100644 index 2156a28..0000000 --- a/Sources/Features/FoldersCompare/Navigator/CompareItem+DifferenceNavigator.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// CompareItem+DifferenceNavigator.swift -// VisualDiffer -// -// Created by davide ficano on 08/02/21. -// Copyright (c) 2021 visualdiffer.com -// - -@objc extension CompareItem { - var hasDifferences: Bool { - !isValidFile || - isOrphanFolder || - isOrphanFile || - orphanFiles > 0 || - changedFiles > 0 || - olderFiles > 0 - } -} diff --git a/Sources/Features/FoldersCompare/Navigator/FoldersOutlineView+DifferenceNavigator.swift b/Sources/Features/FoldersCompare/Navigator/FoldersOutlineView+DifferenceNavigator.swift deleted file mode 100644 index 5a5b811..0000000 --- a/Sources/Features/FoldersCompare/Navigator/FoldersOutlineView+DifferenceNavigator.swift +++ /dev/null @@ -1,249 +0,0 @@ -// -// FoldersOutlineView+DifferenceNavigator.swift -// VisualDiffer -// -// Created by davide ficano on 08/02/21. -// Copyright (c) 2021 visualdiffer.com -// - -struct DifferenceNavigator: OptionSet { - let rawValue: Int - - static let previous = DifferenceNavigator(rawValue: 1 << 0) - static let next = DifferenceNavigator(rawValue: 1 << 1) - static let wrap = DifferenceNavigator(rawValue: 1 << 2) - static let traverseFolders = DifferenceNavigator(rawValue: 1 << 3) - static let centerInWindow = DifferenceNavigator(rawValue: 1 << 4) -} - -extension FoldersOutlineView { - func moveToDifference( - options: DifferenceNavigator, - didWrap: inout Bool - ) -> VisibleItem? { - let foundItem: VisibleItem? = if options.contains(.next) { - findNextDifference(options: options, didWrap: &didWrap) - } else if options.contains(.previous) { - findPreviousDifference(options: options, didWrap: &didWrap) - } else { - nil - } - guard let foundItem else { - return nil - } - expandParents(of: foundItem) - select( - visibleItems: [foundItem], - scrollToFirst: true, - center: options.contains(.centerInWindow), - selectLinked: true - ) - return foundItem - } - - private func findNextDifference( - options: DifferenceNavigator, - didWrap: inout Bool - ) -> VisibleItem? { - var row = selectedRow - - if row < 0 { - row = 0 - } - - let wrapAround = options.contains(.wrap) - let traverseSubfolders = options.contains(.traverseFolders) - guard let from = item(atRow: row) as? VisibleItem else { - return nil - } - var count: Int - - if wrapAround { - count = numberOfRows - } else { - // determine if we can move from last row - if row == numberOfRows - 1 { - if from.item.isFile { - return nil - } - if !traverseSubfolders { - return nil - } - } - count = numberOfRows - row - } - - // If folders must **NOT* be traversed or the current item is a file then go to next row - // This prevents to find the current row - if !traverseSubfolders || from.item.isFile { - row += 1 - } - let foundItem = findDifference( - from, - startRow: row, - count: count, - traverseSubfolders: traverseSubfolders, - findNext: true, - didWrap: &didWrap - ) - return foundItem == from ? nil : foundItem - } - - private func findPreviousDifference( - options: DifferenceNavigator, - didWrap: inout Bool - ) -> VisibleItem? { - let wrapAround = options.contains(.wrap) - let traverseSubfolders = options.contains(.traverseFolders) - var row = selectedRow - var from: VisibleItem? - var count: Int - - if row < 0 { - row = 0 - } - - if wrapAround { - if row == 0 { - from = item(atRow: 0) as? VisibleItem - row = -1 - } else { - from = item(atRow: row) as? VisibleItem - row -= 1 - } - count = numberOfRows - } else { - if row == 0 { - return nil - } - from = item(atRow: row) as? VisibleItem - count = row - row -= 1 - } - - guard let from else { - return nil - } - - let foundItem = findDifference( - from, - startRow: row, - count: count, - traverseSubfolders: traverseSubfolders, - findNext: false, - didWrap: &didWrap - ) - return foundItem == from ? nil : foundItem - } - - // swiftlint:disable:next function_parameter_count - private func findDifference( - _ rootItem: VisibleItem, - startRow row: Int, - count: Int, - traverseSubfolders: Bool, - findNext: Bool, - didWrap: inout Bool - ) -> VisibleItem? { - let sign = findNext ? 1 : -1 - var index = row - var found: VisibleItem? - didWrap = false - var from = rootItem - - for _ in 0 ..< count { - if index < 0 { - index = numberOfRows - 1 - didWrap = true - } else if index >= numberOfRows { - index = 0 - didWrap = true - } - guard let item = item(atRow: index) as? VisibleItem else { - break - } - - found = findDifference( - for: item, - relativeTo: from, - traverseSubfolders: traverseSubfolders, - findNext: findNext - ) - if found != nil { - break - } - index += sign - from = item - } - return found - } - - func findDifference( - for rootItem: VisibleItem, - relativeTo relativeItem: VisibleItem, - traverseSubfolders: Bool, - findNext: Bool - ) -> VisibleItem? { - let item = rootItem.item - - if !item.hasDifferences { - return nil - } - - if item.isFile { - return rootItem - } - - if !item.isFolder { - return nil - } - - // if traverseSubfolders is false return the item only if it's a leaf - // no matters if children contain differences - if !traverseSubfolders, !isItemExpanded(rootItem) { - return rootItem - } - - // the relative item is child of 'self' so the parent has been already visited - if relativeItem.item.parent == item { - return nil - } - - let count = rootItem.children.count - - if count == 0 { - // to avoid 'stay on selected item forever' move to next - if findNext, relativeItem == rootItem { - return nil - } - return rootItem - } - - var sign: Int - var index: Int - - if findNext { - sign = 1 - index = 0 - } else { - sign = -1 - index = count - 1 - } - - for _ in 0 ..< count { - let child = rootItem.children[index] - let foundItem = findDifference( - for: child, - relativeTo: rootItem, - traverseSubfolders: traverseSubfolders, - findNext: findNext - ) - if foundItem != nil { - return foundItem - } - index += sign - } - - return nil - } -} diff --git a/Sources/Features/FoldersCompare/Services/FolderReader/FilterConfig.swift b/Sources/Features/FoldersCompare/Services/FolderReader/FilterConfig.swift deleted file mode 100644 index 7114684..0000000 --- a/Sources/Features/FoldersCompare/Services/FolderReader/FilterConfig.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// FilterConfig.swift -// VisualDiffer -// -// Created by davide ficano on 26/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -public struct FilterConfig { - public let showFilteredFiles: Bool - public let hideEmptyFolders: Bool - public let followSymLinks: Bool - public let skipPackages: Bool - public let traverseFilteredFolders: Bool - public let predicate: NSPredicate? - public let displayOptions: DisplayOptions - public let fileExtraOptions: FileExtraOptions - - public init( - showFilteredFiles: Bool, - hideEmptyFolders: Bool, - followSymLinks: Bool, - skipPackages: Bool, - traverseFilteredFolders: Bool, - predicate: NSPredicate?, - fileExtraOptions: FileExtraOptions, - displayOptions: DisplayOptions - ) { - self.showFilteredFiles = showFilteredFiles - self.hideEmptyFolders = hideEmptyFolders - self.followSymLinks = followSymLinks - self.skipPackages = skipPackages - self.traverseFilteredFolders = traverseFilteredFolders - self.predicate = predicate - self.displayOptions = displayOptions - self.fileExtraOptions = fileExtraOptions - } -} - -extension FilterConfig { - init( - from sessionDiff: SessionDiff, - showFilteredFiles: Bool, - hideEmptyFolders: Bool - ) { - self.init( - showFilteredFiles: showFilteredFiles, - hideEmptyFolders: hideEmptyFolders, - followSymLinks: sessionDiff.followSymLinks, - skipPackages: sessionDiff.skipPackages, - traverseFilteredFolders: sessionDiff.traverseFilteredFolders, - predicate: sessionDiff.exclusionFileFiltersPredicate, - fileExtraOptions: sessionDiff.fileExtraOptions, - displayOptions: sessionDiff.displayOptions - ) - } -} diff --git a/Sources/Features/FoldersCompare/Services/FolderReader/FolderReader+Log.swift b/Sources/Features/FoldersCompare/Services/FolderReader/FolderReader+Log.swift deleted file mode 100644 index da3447a..0000000 --- a/Sources/Features/FoldersCompare/Services/FolderReader/FolderReader+Log.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// FolderReader+Log.swift -// VisualDiffer -// -// Created by davide ficano on 23/02/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -#if DEBUG - extension FolderReader { - // periphery:ignore - func writelog(_ leftRoot: CompareItem) { - let home = URL(filePath: NSHomeDirectory()) - let fullPath = home.appendingPathComponent("vd.txt").osPath - Logger.debug.info("log output to \(fullPath)") - - try? FileManager.default.removeItem(atPath: fullPath) - var fileHandle = FileHandle(forWritingAtPath: fullPath) - - if fileHandle == nil { - FileManager.default.createFile(atPath: fullPath, contents: nil, attributes: nil) - fileHandle = FileHandle(forWritingAtPath: fullPath) - } - guard let fileHandle else { - return - } - defer { - fileHandle.closeFile() - } - - let des = String( - format: "flags %@\ntolerance %ld maxReadBytes %ld isLeftCase %d isRightCase %d", - comparator.options.debugDescription, - comparator.timestampToleranceSeconds, - comparator.bufferSize, - comparator.isLeftCaseSensitive, - comparator.isRightCaseSensitive - ) - - let config = String( - format: "showFilteredFiles = %d, hideEmptyFolders = %d, followSymLinks = %d, skipPackages = %d, traverseFilteredFolders = %d", - filterConfig.showFilteredFiles, - filterConfig.hideEmptyFolders, - filterConfig.followSymLinks, - filterConfig.skipPackages, - filterConfig.traverseFilteredFolders - ) - VisibleItem.writeLine(fileHandle, line: config) - - VisibleItem.writeLine(fileHandle, line: des) - leftRoot.visibleItem?.writelog(fileHandle, indent: 2) - } - } -#endif diff --git a/Sources/Features/FoldersCompare/Services/FolderReader/FolderReader.swift b/Sources/Features/FoldersCompare/Services/FolderReader/FolderReader.swift deleted file mode 100644 index 78824ae..0000000 --- a/Sources/Features/FoldersCompare/Services/FolderReader/FolderReader.swift +++ /dev/null @@ -1,389 +0,0 @@ -// -// FolderReader.swift -// VisualDiffer -// -// Created by davide ficano on 07/04/13. -// Copyright (c) 2013 visualdiffer.com -// - -public class FolderReader: @unchecked Sendable { - private let fileManager = FileManager.default - - var comparator: ItemComparator - - // delegate is strong - var delegate: FolderReaderDelegate - var refreshInfo: RefreshInfo - - private(set) var leftRoot: CompareItem? - private(set) var rightRoot: CompareItem? - - var filterConfig: FilterConfig - - private var isRunning: Bool { - delegate.isRunning(self) - } - - init( - with delegate: FolderReaderDelegate, - comparator: ItemComparator, - filterConfig: FilterConfig, - refreshInfo: RefreshInfo - ) { - self.delegate = delegate - self.comparator = comparator - self.filterConfig = filterConfig - self.refreshInfo = refreshInfo - } - - public func start( - withLeftRoot leftRoot: CompareItem?, - rightRoot: CompareItem?, - leftPath: URL, - rightPath: URL - ) { - self.leftRoot = leftRoot - self.rightRoot = rightRoot - - let startTime = Date() - delegate.progress(self, status: .will(startAt: startTime)) - readFolders( - leftItem: leftRoot, - rightItem: self.rightRoot, - leftPath: leftPath, - rightPath: rightPath - ) - let endTime = Date() - delegate.progress(self, status: .did(endAt: endTime, startedAt: startTime)) - } - - private func readFolders( - leftItem l: CompareItem?, - rightItem r: CompareItem?, - leftPath: URL?, - rightPath: URL? - ) { - if !isRunning { - return - } - - var leftItem = l - var rightItem = r - - if leftRoot != nil { - if refreshInfo.refreshFolders { - readFolder( - atPath: leftPath, - parent: leftItem, - recursive: false - ) - - readFolder( - atPath: rightPath, - parent: rightItem, - recursive: false - ) - } - } else { - leftRoot = readFolder( - atPath: leftPath, - parent: nil, - recursive: false - ) - - rightRoot = readFolder( - atPath: rightPath, - parent: nil, - recursive: false - ) - leftItem = leftRoot - rightItem = rightRoot - } - - guard let leftItem else { - return - } - guard let rightItem else { - return - } - - leftItem.linkedItem = rightItem - rightItem.linkedItem = leftItem - - if refreshInfo.realign { - let alignConfig = AlignConfig( - recursive: false, - followSymLinks: filterConfig.followSymLinks - ) - comparator.alignItem( - leftItem, - rightRoot: rightItem, - alignConfig: alignConfig - ) - } - - leftItem.applyComparison( - fileFilters: filterConfig.predicate, - comparator: refreshInfo.refreshComparison && isRunning ? comparator : nil, - recursive: false - ) - - leftItem.filterVisibleItems( - showFilteredFiles: filterConfig.showFilteredFiles, - hideEmptyFolders: false, - recursive: false - ) - - if leftItem.parent == nil { - var folders = 0 - for item in leftItem.children where item.isFolder { - folders += 1 - } - delegate.progress(self, status: .rootFoldersDidRead(folders)) - } - - var leftSummary = CompareSummary() - var rightSummary = CompareSummary() - - if leftItem.isFolder { - leftSummary.mismatchingFolderMetadata = leftItem.mismatchingFolderMetadata - rightSummary.mismatchingFolderMetadata = rightItem.mismatchingFolderMetadata - } - - let traversalOrder = folderTraversalOrder(leftItem) - - for item in traversalOrder { - if isRunning { - process(item: item) - } - guard let li = item.linkedItem else { - continue - } - - // do not exit from loop without updating correctly the counters - leftSummary += item.summary - rightSummary += li.summary - leftSummary.subfoldersSize += item.fileSize - rightSummary.subfoldersSize += li.fileSize - if item.isOrphanFolder { - item.addOrphanFolders(1) - } else if li.isOrphanFolder { - li.addOrphanFolders(1) - } - } - - leftItem.setSummary(leftSummary) - rightItem.setSummary(rightSummary) - - // store the current value to be used inside the block because leftItem can change - let capturedItem = leftItem - capturedItem.removeVisibleItems(filterConfig: filterConfig) - - if let parent = capturedItem.parent, parent.parent == nil { - delegate.progress(self, status: .didTraverse(capturedItem)) - } - } - - @discardableResult - func readFolder( - atPath parentPath: URL?, - parent: CompareItem?, - recursive: Bool - ) -> CompareItem? { - if let parent { - if parent.path == nil { - return parent - } - if !filterConfig.traverseFilteredFolders, parent.isFiltered { - return nil - } - // don't traverse symbolic links - if !filterConfig.followSymLinks, parent.isSymbolicLink { - return parent - } - - if filterConfig.skipPackages, parent.isPackage { - return parent - } - } - - guard let parentPath else { - return nil - } - do { - // symlink root must be traversed so allocate it after checking for symlink - let root = try createParentIfNil(path: parentPath, parent: parent) - let list = try contentsOfSandboxedDirectory(atPath: parentPath.path) - - for entry in list where isRunning { - addEntryFile(entry: entry, root: root, parentPath: parentPath, recursive: recursive) - } - root.sortChildren { - $0.compare(forList: $1, followSymLinks: self.filterConfig.followSymLinks) - } - return root - } catch { - delegate.folderReader(self, handleError: error, forPath: parentPath) - return nil - } - } - - /// Reads the contents of a directory, handling symbolic links outside the sandbox. - /// - /// If the path is a symbolic link to an inaccessible destination within the sandbox, - /// the function attempts to gain access via security-scoped bookmark. - /// - /// - Parameter path: The absolute path of the directory to read - /// - Returns: Array of file and folder names contained in the directory - /// - Throws: `NSFileReadNoPermissionError` if the symlink points outside the sandbox - /// without a valid bookmark in 'Trusted Paths' - /// - Throws: Other `FileManager` errors if reading fails for different reasons - private func contentsOfSandboxedDirectory(atPath path: String) throws -> [String] { - do { - return try fileManager.contentsOfDirectory(atPath: path) - } catch let error as NSError where error.domain == NSCocoaErrorDomain && - error.code == NSFileReadNoPermissionError { - guard let destination = try? fileManager.destinationOfSymbolicLink(atPath: path) else { - throw error - } - - let secureUrl = SecureBookmark.shared.secure( - fromBookmark: URL(filePath: destination), - startSecured: true - ) - - defer { - SecureBookmark.shared.stopAccessing(url: secureUrl) - } - - return try fileManager.contentsOfDirectory(atPath: path) - } - } - - private func addEntryFile(entry: String, root: CompareItem, parentPath: URL, recursive: Bool) { - do { - let fullPath = parentPath.appending(path: entry) - let attrs = try FileManager.default.attributesOfItem(atPath: fullPath.osPath) - - let newItem = CompareItem( - path: fullPath.osPath, - attrs: attrs, - fileExtraOptions: filterConfig.fileExtraOptions, - parent: root - ) - - if newItem.isFolder { - if isFolderFiltered(newItem) { - newItem.isFiltered = true - } else { - // protect against recursive loops due to symbolic links to folders - if try symbolicLinkToParent(newItem, attributes: attrs) != nil { - throw FileError.symlinkLoop(path: root.path ?? "Unknown") - } - } - - if recursive { - readFolder(atPath: fullPath, parent: newItem, recursive: recursive) - } - } - root.add(child: newItem) - } catch { - delegate.folderReader(self, handleError: error, forPath: parentPath) - } - } - - private func symbolicLinkToParent( - _ item: CompareItem, - attributes attrs: [FileAttributeKey: Any] - ) throws -> CompareItem? { - if !item.isSymbolicLink { - return nil - } - var parent = item.parent - - while let p = parent { - if let path = p.path { - let parentAttrs = try FileManager.default.attributesOfItem(atPath: path) - if - let parentSystemFileNumber = parentAttrs[.systemFileNumber] as? NSNumber, - let parentSystemNumber = parentAttrs[.systemNumber] as? NSNumber, - let currentSystemFileNumber = attrs[.systemFileNumber] as? NSNumber, - let currentSystemNumber = attrs[.systemNumber] as? NSNumber, - currentSystemFileNumber.isEqual(to: parentSystemFileNumber), - currentSystemNumber.isEqual(to: parentSystemNumber) { - return p - } - } else { - return nil - } - parent = p.parent - } - - return nil - } - - private func createParentIfNil(path: URL, parent: CompareItem?) throws -> CompareItem { - if let parent { - return parent - } - let osPath = path.osPath - let attrs = try FileManager.default.attributesOfItem(atPath: osPath) - return CompareItem( - path: osPath, - attrs: attrs, - fileExtraOptions: filterConfig.fileExtraOptions, - parent: nil - ) - } - - private func process(item: CompareItem) { - if item.isFolder { - delegate.progress(self, status: .willTraverse(item)) - if let li = item.linkedItem { - readFolders( - leftItem: item, - rightItem: li, - leftPath: item.toUrl(), - rightPath: li.toUrl() - ) - } - } - item.removeVisibleItems(filterConfig: filterConfig) - } - - private func folderTraversalOrder(_ leftItem: CompareItem) -> [CompareItem] { - // the list is traversed from top to bottom so the user can see the folders while expanding - // but the order can change from a different sort in the delegate - // so we get the current order and use it to traverse the folders - var traversalOrder = [CompareItem]() - var seenItems = Set() - if let visibleItem = leftItem.visibleItem { - for vi in visibleItem.children { - traversalOrder.append(vi.item) - seenItems.insert(ObjectIdentifier(vi.item)) - } - } - - // Only VisibleItems are ordered so filtered elements are not present inside the array - // We must iterate also the not filtered items to correctly update folders informations like the subfolders size - // We add the missing CompareItem to the end, this is correct because they are not visible - for item in leftItem.children where !seenItems.contains(ObjectIdentifier(item)) { - traversalOrder.append(item) - } - - return traversalOrder - } - - private func isFolderFiltered(_ item: CompareItem) -> Bool { - if !item.isFolder { - return false - } - if filterConfig.traverseFilteredFolders { - return false - } - if let predicate = filterConfig.predicate { - return item.evaluate(filter: predicate) - } - return false - } -} diff --git a/Sources/Features/FoldersCompare/Services/FolderReader/FolderReaderDelegate.swift b/Sources/Features/FoldersCompare/Services/FolderReader/FolderReaderDelegate.swift deleted file mode 100644 index 3647101..0000000 --- a/Sources/Features/FoldersCompare/Services/FolderReader/FolderReaderDelegate.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// FolderReaderDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 25/01/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -public enum FolderReaderStatus: Sendable { - case will(startAt: Date) - case did(endAt: Date, startedAt: Date) - case rootFoldersDidRead(Int) - case willTraverse(CompareItem) - case didTraverse(CompareItem) -} - -public protocol FolderReaderDelegate: AnyObject { - func isRunning(_ folderReader: FolderReader) -> Bool - - func progress(_ folderReader: FolderReader, status: FolderReaderStatus) - - @discardableResult - func folderReader(_ folderReader: FolderReader, handleError error: Error, forPath: URL) -> Bool -} diff --git a/Sources/Features/FoldersCompare/Services/FolderReader/RefreshInfo.swift b/Sources/Features/FoldersCompare/Services/FolderReader/RefreshInfo.swift deleted file mode 100644 index 30b3c4b..0000000 --- a/Sources/Features/FoldersCompare/Services/FolderReader/RefreshInfo.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// RefreshInfo.swift -// VisualDiffer -// -// Created by davide ficano on 23/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -public struct RefreshInfo { - let refreshFolders: Bool - let realign: Bool - let refreshComparison: Bool - let expandAllFolders: Bool - - init( - initState: Bool, - realign: Bool? = nil, - refreshComparison: Bool? = nil, - expandAllFolders: Bool? = nil - ) { - refreshFolders = initState - // refresh folders forces reAlign - self.realign = refreshFolders ? true : realign ?? initState - // reAlign forces refreshComparison - self.refreshComparison = self.realign ? true : refreshComparison ?? initState - self.expandAllFolders = expandAllFolders ?? initState - } -} diff --git a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+Align.swift b/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+Align.swift deleted file mode 100644 index 6906d88..0000000 --- a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+Align.swift +++ /dev/null @@ -1,393 +0,0 @@ -// -// ItemComparator+Align.swift -// VisualDiffer -// -// Created by davide ficano on 29/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -import os.log - -public struct AlignConfig { - let recursive: Bool - let followSymLinks: Bool -} - -// Both files are invalid but their path string contains a valid value -@inline(__always) func bothInvalidWithPath(_ lfs: CompareItem, _ rfs: CompareItem) -> Bool { - !lfs.isValidFile && !rfs.isValidFile && (lfs.path != nil && rfs.path != nil) -} - -public extension ComparatorOptions { - /** - * Return leftCaseSensitive and rightCaseSensitive determinated using the comparatorFlags value. - * If it's necessary to access to file system then use the passed paths - */ - func fileNameCase(leftPath: URL, rightPath: URL) -> (Bool, Bool) { - if contains(.alignFileSystemCase) { - ( - (try? leftPath.volumeSupportsCaseSensitive()) ?? false, - (try? rightPath.volumeSupportsCaseSensitive()) ?? false - ) - } else if contains(.alignMatchCase) { - (true, true) - } else if contains(.alignIgnoreCase) { - (false, false) - } else { - (true, true) - } - } -} - -// swiftlint:disable function_parameter_count -public extension ItemComparator { - func alignItem( - _ leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig - ) { - var lIndex = 0 - var rIndex = 0 - var leftChildrenCount = leftRoot.children.count - var rightChildrenCount = rightRoot.children.count - - while (lIndex < leftChildrenCount) || (rIndex < rightChildrenCount) { - var pos: ComparisonResult - - if lIndex >= leftChildrenCount { - pos = .orderedDescending - } else if rIndex >= rightChildrenCount { - pos = .orderedAscending - } else if (leftRoot.child(at: lIndex).isValidFile && rightRoot.child(at: rIndex).isValidFile) - || bothInvalidWithPath(leftRoot.child(at: lIndex), rightRoot.child(at: rIndex)) { - pos = align( - leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &lIndex, - rightIndex: &rIndex - ) - } else { - if !leftRoot.child(at: lIndex).isValidFile { - lIndex += 1 - } - if !rightRoot.child(at: rIndex).isValidFile { - rIndex += 1 - } - continue - } - if pos == .orderedSame { - if leftRoot.child(at: lIndex).isFile, rightRoot.child(at: rIndex).isFile { - // ignore this case - } else { - if alignConfig.recursive { - alignItem( - leftRoot.child(at: lIndex), - rightRoot: rightRoot.child(at: rIndex), - alignConfig: alignConfig - ) - } - } - - leftRoot.child(at: lIndex).linkedItem = rightRoot.child(at: rIndex) - rightRoot.child(at: rIndex).linkedItem = leftRoot.child(at: lIndex) - - lIndex += 1 - rIndex += 1 - } else if pos == .orderedAscending { - // insert left orphan - insert( - orphan: leftRoot.child(at: lIndex), - otherSide: rightRoot, - alignConfig: alignConfig, - leftIndex: &lIndex, - rightIndex: &rIndex - ) - } else if pos == .orderedDescending { - // insert right orphan - insert( - orphan: rightRoot.child(at: rIndex), - otherSide: leftRoot, - alignConfig: alignConfig, - leftIndex: &lIndex, - rightIndex: &rIndex - ) - } else { - Logger.general.error("Invalid pos value \(pos.rawValue)") - } - leftChildrenCount = leftRoot.children.count - rightChildrenCount = rightRoot.children.count - } - } - - func insert( - orphan: CompareItem, - otherSide: CompareItem, - alignConfig: AlignConfig, - leftIndex: inout Int, - rightIndex: inout Int - ) { - let newItem = CompareItem( - path: nil, - attrs: nil, - fileExtraOptions: [], - parent: otherSide - ) - - orphan.linkedItem = newItem - newItem.linkedItem = orphan - newItem.linkedItemIsFolder(orphan.isFolder) - otherSide.insert(child: newItem, at: leftIndex) - - if alignConfig.recursive { - alignItem( - orphan, - rightRoot: otherSide.child(at: leftIndex), - alignConfig: alignConfig - ) - } - leftIndex += 1 - rightIndex += 1 - } - - func align( - _ leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig, - leftIndex: inout Int, - rightIndex: inout Int - ) -> ComparisonResult { - if let fileNameAlignments, !fileNameAlignments.isEmpty { - return alignByRegularExpression( - leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - } - return alignByFileName( - leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - } - - // MARK: - Filenames alignment - - func alignByFileName( - _ leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig, - leftIndex: inout Int, - rightIndex: inout Int - ) -> ComparisonResult { - var pos: ComparisonResult = .orderedSame - var l = leftIndex - var r = rightIndex - let rightChildren = rightRoot.children - let followSymLinks = alignConfig.followSymLinks - - if isLeftCaseSensitive, isRightCaseSensitive { - pos = leftRoot.child(at: l).compare( - forAlign: rightRoot.child(at: r), - followSymLinks: followSymLinks, - insensitiveCompare: false - ) - } else if !isLeftCaseSensitive, !isRightCaseSensitive { - pos = leftRoot.child(at: l).compare( - forAlign: rightRoot.child(at: r), - followSymLinks: followSymLinks, - insensitiveCompare: true - ) - } else { - let leftChild = leftRoot.child(at: l) - let index = findInsertIndex( - left: leftChild, - right: rightRoot, - startIndex: r, - followSymLinks: followSymLinks - ) - // left name doesn't exist on right so determine the insertion point using a match case - if index == -1 { - pos = leftChild.compare( - forAlign: rightRoot.child(at: r), - followSymLinks: followSymLinks, - insensitiveCompare: false - ) - } else { - while r < index { - insert( - orphan: rightRoot.child(at: r), - otherSide: leftRoot, - alignConfig: alignConfig, - leftIndex: &l, - rightIndex: &r - ) - } - let leftFileName = leftRoot.child(at: l).fileName ?? "" - let (isExactlyMatch, rightIndex) = findExactlyMatchIndex( - leftFileName, - subfolders: rightChildren, - followSymLinks: followSymLinks, - startIndex: r - ) - - if isExactlyMatch { - pos = insertOrphans( - atExactIndex: rightIndex, - leftRoot: leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &l, - rightIndex: &r - ) - } else { - pos = insertOrphans( - atClosestIndex: rightIndex, - leftRoot: leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &l, - rightIndex: &r - ) - } - } - } - leftIndex = l - rightIndex = r - - return pos - } - - func insertOrphans( - atExactIndex exactMatchRightIndex: Int, - leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig, - leftIndex l: inout Int, - rightIndex r: inout Int - ) -> ComparisonResult { - while r < exactMatchRightIndex { - insert( - orphan: rightRoot.child(at: r), - otherSide: leftRoot, - alignConfig: alignConfig, - leftIndex: &l, - rightIndex: &r - ) - } - return .orderedSame - } - - func insertOrphans( - atClosestIndex closestRightIndex: Int, - leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig, - leftIndex l: inout Int, - rightIndex r: inout Int - ) -> ComparisonResult { - let leftChildren = leftRoot.children - let hasLeftMoreSameNames = (l + 1) < leftChildren.count - && leftChildren[l].compare( - forAlign: leftChildren[l + 1], - followSymLinks: alignConfig.followSymLinks, - insensitiveCompare: true - ) == .orderedSame - - if hasLeftMoreSameNames { - return .orderedAscending - } - while r < closestRightIndex { - insert( - orphan: rightRoot.child(at: r), - otherSide: leftRoot, - alignConfig: alignConfig, - leftIndex: &l, - rightIndex: &r - ) - } - return .orderedSame - } - - func findInsertIndex( - left: CompareItem, - right: CompareItem, - startIndex: Int, - followSymLinks: Bool - ) -> Int { - for i in startIndex ..< right.children.count { - let result = left.compare( - forAlign: right.child(at: i), - followSymLinks: followSymLinks, - insensitiveCompare: true - ) - if result == .orderedSame { - return i - } - if result == .orderedDescending { - return -1 - } - } - return -1 - } -} - -func findExactlyMatchIndex( - _ leftFileName: String, - subfolders: [CompareItem], - followSymLinks: Bool, - startIndex: Int -) -> (isExactlyMatch: Bool, index: Int) { - var isExactlyMatch = false - var index = startIndex - - enumerateWithSameFileName( - subfolders, - startIndex: startIndex, - followSymLinks: followSymLinks - ) { item, i, stop in - let fileName = item.fileName ?? "" - let comparisonResult = leftFileName.localizedCompare(fileName) - // update only if it isn't already found - if index == startIndex, comparisonResult == .orderedAscending { - index = i - } - if comparisonResult == .orderedSame { - index = i - isExactlyMatch = true - stop = true - } - } - return (isExactlyMatch, index) -} - -func enumerateWithSameFileName( - _ children: [CompareItem], - startIndex: Int, - followSymLinks: Bool, - block: (CompareItem, Int, inout Bool) -> Void -) { - var index = startIndex - var item: CompareItem - - repeat { - item = children[index] - var stop = false - block(item, index, &stop) - if stop { - break - } - index += 1 - } while index < children.count - && item.compare( - forAlign: children[index], - followSymLinks: followSymLinks, - insensitiveCompare: true - ) == .orderedSame -} - -// swiftlint:enable function_parameter_count diff --git a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+AlignRegularExpression.swift b/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+AlignRegularExpression.swift deleted file mode 100644 index 82345e8..0000000 --- a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+AlignRegularExpression.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// ItemComparator+AlignRegularExpression.swift -// VisualDiffer -// -// Created by davide ficano on 29/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension ItemComparator { - func alignByRegularExpression( - _ leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig, - leftIndex: inout Int, - rightIndex: inout Int - ) -> ComparisonResult { - let leftChild = leftRoot.child(at: leftIndex) - let rightChild = rightRoot.child(at: rightIndex) - - var lIndex = leftIndex - var rIndex = rightIndex - - let result = leftChild.compare( - rightChild, - followSymLinks: alignConfig.followSymLinks - ) { self.compareByRegularExpression( - lhs: $0, - rhs: $1, - leftRoot: leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &lIndex, - rightIndex: &rIndex - ) - } - - leftIndex = lIndex - rightIndex = rIndex - - return result - } - - // swiftlint:disable:next function_parameter_count - private func compareByRegularExpression( - lhs: CompareItem, - rhs: CompareItem, - leftRoot: CompareItem, - rightRoot: CompareItem, - alignConfig: AlignConfig, - leftIndex: inout Int, - rightIndex: inout Int - ) -> ComparisonResult { - guard let lhsName = lhs.fileName, - let rhsName = rhs.fileName else { - return .orderedSame - } - if let fileNameAlignments { - for rule in fileNameAlignments where rule.matches(name: lhsName, with: rhsName) { - return .orderedSame - } - } - return alignByFileName( - leftRoot, - rightRoot: rightRoot, - alignConfig: alignConfig, - leftIndex: &leftIndex, - rightIndex: &rightIndex - ) - } -} diff --git a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+Compare.swift b/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+Compare.swift deleted file mode 100644 index baf9556..0000000 --- a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator+Compare.swift +++ /dev/null @@ -1,262 +0,0 @@ -// -// ItemComparator+Compare.swift -// VisualDiffer -// -// Created by davide ficano on 29/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension ItemComparator { - /** - * Determine the comparison to apply (timestamp, size, ...) based on the comparatorOptions value - * This method must always be used to make comparisons - */ - @discardableResult - public func compare(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - var res: ComparisonResult = .orderedSame - let canCompareFolders = !options.isDisjoint(with: .supportFolderCompare) - - if !canCompareFolders, !lhs.isFile || !rhs.isFile { - return res - } - - if lhs.isValidFile, rhs.isValidFile { - // Run only the comparisons supported by folders - if lhs.isFolder { - // label and tags are mutually exclusive - if options.contains(.finderLabel) { - res = compareFinderLabel(lhs, rhs) - } else if options.contains(.finderTags) { - res = compareFinderTag(lhs, rhs) - } - return res - } - // label and tags are mutually exclusive - if options.contains(.finderLabel) { - res = compareFinderLabel(lhs, rhs) - } else if options.contains(.finderTags) { - res = compareFinderTag(lhs, rhs) - } - if res == .orderedSame, options.contains(.contentTimestamp) { - res = compareContentAndTimestamp(lhs, rhs) - } else { - if res == .orderedSame, options.contains(.filename) { - res = compareFilename(lhs, rhs) - } - if res == .orderedSame, options.contains(.timestamp) { - res = compareTimestamp(lhs, rhs) - } - if res == .orderedSame, options.contains(.size) { - res = compareSize(lhs, rhs) - } - if res == .orderedSame, options.contains(.content) { - res = compareContent(lhs, rhs, ignoreLineEndingDiff: false) - } - if res == .orderedSame, options.contains(.asText) { - res = compareContent(lhs, rhs, ignoreLineEndingDiff: true) - } - } - } else if lhs.isValidFile { - lhs.addOrphanFiles(1) - res = .orderedAscending - } else if rhs.isValidFile { - rhs.addOrphanFiles(1) - res = .orderedDescending - } - return res - } - - private func compareFinderLabel(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - guard let lhsPath = lhs.toUrl(), - let rhsPath = rhs.toUrl(), - let leftLabelNumber = lhsPath.labelNumber(), - let rightLabelNumber = rhsPath.labelNumber() else { - return .orderedSame - } - - if leftLabelNumber == rightLabelNumber { - return .orderedSame - } - - lhs.addMismatchingLabels(1) - rhs.addMismatchingLabels(1) - - if lhs.isFolder { - lhs.setMismatchingFolderMetadataLabels(true) - rhs.setMismatchingFolderMetadataLabels(true) - } - - return leftLabelNumber < rightLabelNumber ? .orderedAscending : .orderedDescending - } - - private func compareFinderTag(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - guard let lhsPath = lhs.toUrl(), - let rhsPath = rhs.toUrl(), - let leftTags = lhsPath.tagNames(sorted: true), - let rightTags = rhsPath.tagNames(sorted: true) else { - return .orderedSame - } - - var res: ComparisonResult = leftTags.count == rightTags.count - ? .orderedSame - : leftTags.count < rightTags.count ? .orderedAscending : .orderedDescending - if res == .orderedSame { - for (index, item) in leftTags.enumerated() where res == .orderedSame { - res = item.caseInsensitiveCompare(rightTags[index]) - } - } - - if res != .orderedSame { - if lhs.isFile { - lhs.addMismatchingTags(1) - rhs.addMismatchingTags(1) - } else { - lhs.setMismatchingFolderMetadataTags(true) - rhs.setMismatchingFolderMetadataTags(true) - } - } - return res - } - - private func compareContentAndTimestamp(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - var sign = compareSize(lhs, rhs) - - if sign == .orderedSame { - sign = compareContent(lhs, rhs, ignoreLineEndingDiff: false) - } - - if sign != .orderedSame { - // return the content sign - if compareTimestamp(lhs, rhs) == .orderedSame { - lhs.addChangedFiles(1) - rhs.addChangedFiles(1) - } - } - return sign - } - - private func compareSize(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - let sign = lhs.fileSize - rhs.fileSize - - if sign < 0 { - lhs.addChangedFiles(1) - rhs.addChangedFiles(1) - return .orderedAscending - } - if sign > 0 { - lhs.addChangedFiles(1) - rhs.addChangedFiles(1) - return .orderedDescending - } - lhs.addMatchedFiles(1) - rhs.addMatchedFiles(1) - - return .orderedSame - } - - public func compareContent( - _ lhs: CompareItem, - _ rhs: CompareItem, - ignoreLineEndingDiff: Bool - ) -> ComparisonResult { - if ignoreLineEndingDiff { - return compareAsText(lhs, rhs) - } else { - if lhs.fileSize != rhs.fileSize { - lhs.addChangedFiles(1) - rhs.addChangedFiles(1) - return lhs.fileSize < rhs.fileSize ? .orderedAscending : .orderedDescending - } - } - guard let lhsPath = lhs.toUrl(), - let rhsPath = rhs.toUrl() else { - return .orderedSame - } - - var ret: ComparisonResult = .orderedSame - - do { - ret = try compareBinaryFiles(lhsPath, rhsPath, bufferSize) { - delegate?.isRunning(self) ?? false - } - } catch {} - - // check resource data - if ret == .orderedSame, lhs.isResourceFork, rhs.isResourceFork { - if let leftData = try? lhsPath.readResFork(), - let rightData = try? rhsPath.readResFork(), - !leftData.elementsEqual(rightData) { - ret = .orderedAscending - } - } - - if ret == .orderedSame { - lhs.addMatchedFiles(1) - rhs.addMatchedFiles(1) - } else { - lhs.addChangedFiles(1) - rhs.addChangedFiles(1) - } - - return ret - } - - private func compareTimestamp(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - guard let lhsDate = lhs.fileModificationDate, - let rhsDate = rhs.fileModificationDate else { - return .orderedSame - } - - let result = compareDates(lhsDate, rhsDate, timestampToleranceSeconds) - - switch result { - case .orderedAscending: - lhs.addOlderFiles(1) - rhs.addChangedFiles(1) - case .orderedDescending: - lhs.addChangedFiles(1) - rhs.addOlderFiles(1) - default: - lhs.addMatchedFiles(1) - rhs.addMatchedFiles(1) - } - - return result - } - - private func compareAsText(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - guard let lhsPath = lhs.toUrl(), - let rhsPath = rhs.toUrl() else { - return .orderedSame - } - - do { - let ret = try compareTextFiles( - lhsPath, - rhsPath, - .utf8, - bufferSize - ) { - delegate?.isRunning(self) ?? false - } - if ret == .orderedSame { - lhs.addMatchedFiles(1) - rhs.addMatchedFiles(1) - } else { - lhs.addChangedFiles(1) - rhs.addChangedFiles(1) - } - return ret - } catch { - return .orderedSame - } - } - - private func compareFilename(_ lhs: CompareItem, _ rhs: CompareItem) -> ComparisonResult { - // no comparison is necessary so simply mark files as matched - lhs.addMatchedFiles(1) - rhs.addMatchedFiles(1) - - return .orderedSame - } -} diff --git a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator.swift b/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator.swift deleted file mode 100644 index 02832d0..0000000 --- a/Sources/Features/FoldersCompare/Services/ItemComparator/ItemComparator.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// ItemComparator.swift -// VisualDiffer -// -// Created by davide ficano on 29/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -public protocol ItemComparatorDelegate: AnyObject { - func isRunning(_ comparator: ItemComparator) -> Bool -} - -@objc public class ItemComparator: NSObject { - let options: ComparatorOptions - weak var delegate: ItemComparatorDelegate? - let timestampToleranceSeconds: Int - let bufferSize: Int - - let isLeftCaseSensitive: Bool - let isRightCaseSensitive: Bool - - let fileNameAlignments: [AlignRule]? - - init( - options: ComparatorOptions, - delegate: ItemComparatorDelegate, - bufferSize: Int, - timestampToleranceSeconds: Int = 0, - isLeftCaseSensitive: Bool = true, - isRightCaseSensitive: Bool = true, - fileNameAlignments: [AlignRule]? = nil - ) { - self.options = options - self.delegate = delegate - self.bufferSize = bufferSize - self.timestampToleranceSeconds = timestampToleranceSeconds - self.isLeftCaseSensitive = isLeftCaseSensitive - self.isRightCaseSensitive = isRightCaseSensitive - self.fileNameAlignments = fileNameAlignments - } -} diff --git a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+CompareActionValidator.swift b/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+CompareActionValidator.swift deleted file mode 100644 index 8ad744e..0000000 --- a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+CompareActionValidator.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// FolderSelectionInfo+CompareActionValidator.swift -// VisualDiffer -// -// Created by davide ficano on 05/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -@MainActor extension FolderSelectionInfo { - func validateCompareFiles() -> Bool { - guard let linkedSelInfo = view.linkedView?.selectionInfo else { - return false - } - if selType == .file, linkedSelInfo.selType.isEmpty { - return filesCount == 2 - } - if selType == .file, linkedSelInfo.selType == .file { - return filesCount == 1 && linkedSelInfo.filesCount == 1 - } - return false - } - - func validateCompareFolders() -> Bool { - guard let linkedSelInfo = view.linkedView?.selectionInfo else { - return false - } - if selType == .folder, linkedSelInfo.selType.isEmpty { - return foldersCount == 2 - } - if selType == .folder, linkedSelInfo.selType == .folder { - return foldersCount == 1 && linkedSelInfo.foldersCount == 1 - } - return false - } -} diff --git a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FileSystemActionValidator.swift b/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FileSystemActionValidator.swift deleted file mode 100644 index 1bad87d..0000000 --- a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FileSystemActionValidator.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// FolderSelectionInfo+FileSystemActionValidator.swift -// VisualDiffer -// -// Created by davide ficano on 05/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -@MainActor extension FolderSelectionInfo { - func validateCopyFiles(_ sessionDiff: SessionDiff) -> Bool { - let isValid = switch view.side { - case .left: - !sessionDiff.rightReadOnly - case .right: - !sessionDiff.leftReadOnly - } - return isValid && (foldersCount > 0 || filesCount > 0) - } - - func validateMoveFiles(_ sessionDiff: SessionDiff) -> Bool { - let isValid = switch view.side { - case .left: - !sessionDiff.leftReadOnly && !sessionDiff.rightReadOnly - case .right: - !sessionDiff.leftReadOnly && !sessionDiff.rightReadOnly - } - return isValid && (foldersCount > 0 || filesCount > 0) - } - - func validateSyncFiles(_ sessionDiff: SessionDiff) -> Bool { - let isValid = switch view.side { - case .left: - !sessionDiff.rightReadOnly - case .right: - !sessionDiff.leftReadOnly - } - return isValid && (foldersCount > 0 || filesCount > 0) - } - - func validateDeleteFiles(_ sessionDiff: SessionDiff) -> Bool { - let isValid = switch view.side { - case .left: - !sessionDiff.leftReadOnly - case .right: - !sessionDiff.rightReadOnly - } - return isValid && (foldersCount > 0 || filesCount > 0) - } - - func validateFileTouch(_ sessionDiff: SessionDiff) -> Bool { - let isValid = switch view.side { - case .left: - !sessionDiff.leftReadOnly - case .right: - !sessionDiff.rightReadOnly - } - return isValid && (foldersCount > 0 || filesCount > 0) - } - - func validateClipboardCopy() -> Bool { - !selType.isDisjoint(with: [.folder, .file]) || hasValidPaths - } -} diff --git a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FilterActionValidator.swift b/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FilterActionValidator.swift deleted file mode 100644 index 3b12f6b..0000000 --- a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FilterActionValidator.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// FolderSelectionInfo+FilterActionValidator.swift -// VisualDiffer -// -// Created by davide ficano on 05/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -@MainActor extension FolderSelectionInfo { - func validateExclude(byName outExcludedFileName: inout String?) -> Bool { - if selType.isEmpty || selType == .nullfile { - return false - } - if outExcludedFileName != nil { - // Only one element selected - if filesCount + foldersCount == 1 { - if let row = filesCount > 0 ? filesIndexes.first : foldersIndexes.first, - let vi = view.item(atRow: row) as? VisibleItem { - let item = vi.item - outExcludedFileName = foldersCount == 1 ? item.pathRelativeToRoot : item.fileName - } - } else { - outExcludedFileName = nil - } - } - - return true - } - - func validateExclude(byExt outExcludedExt: inout String?) -> Bool { - // Only valid for selection containing only files (null files are skipped) - if filesCount == 0 || foldersCount > 0 { - return false - } - let indexes = filesIndexes - - guard let row = indexes.first, - let vi = view.item(atRow: row) as? VisibleItem else { - return false - } - let item = vi.item - - guard let path = item.toUrl() else { - return false - } - - var allFilesWithSameExt = true - let fileExt = path.pathExtension - - for row in indexes.dropFirst() where allFilesWithSameExt { - if let viAtRow = view.item(atRow: row) as? VisibleItem { - let itemAtRow = viAtRow.item - if let path = itemAtRow.toUrl() { - let tempExt = path.pathExtension - allFilesWithSameExt = tempExt == fileExt - } - } - } - - if outExcludedExt != nil { - outExcludedExt = fileExt - } - return allFilesWithSameExt - } -} diff --git a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FolderActionValidator.swift b/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FolderActionValidator.swift deleted file mode 100644 index 8e71239..0000000 --- a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+FolderActionValidator.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// FolderSelectionInfo+FolderActionValidator.swift -// VisualDiffer -// -// Created by davide ficano on 05/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -@MainActor extension FolderSelectionInfo { - func validateSetAsBaseFolder() -> Bool { - foldersCount == 1 - } - - func validateSetAsBaseFolderOtherSide() -> Bool { - foldersCount == 1 - } - - func validateSetAsBaseFoldersBothSides() -> Bool { - guard let linkedSelInfo = view.linkedView?.selectionInfo else { - return false - } - - if selType == .folder, linkedSelInfo.selType.isEmpty { - return foldersCount == 2 - } - if selType == .folder, linkedSelInfo.selType == .folder { - return foldersCount == 1 && linkedSelInfo.foldersCount == 1 - } - return false - } - - func validateExpandSelectedSubfolders() -> Bool { - filesCount == 0 && foldersCount > 0 - } -} diff --git a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+ViewerActionValidator.swift b/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+ViewerActionValidator.swift deleted file mode 100644 index eed76b6..0000000 --- a/Sources/Features/FoldersCompare/Validators/FolderSelectionInfo+ViewerActionValidator.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// FolderSelectionInfo+ViewerActionValidator.swift -// VisualDiffer -// -// Created by davide ficano on 05/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -@MainActor extension FolderSelectionInfo { - func validateShowInFinder() -> Bool { - !selType.isDisjoint(with: [.folder, .file]) - } - - func validateOpen(withApp outSelectedPath: inout String?) -> Bool { - guard let itemRow = view.item(atRow: view.selectedRow) as? VisibleItem else { - return false - } - let item = itemRow.item - - if let path = item.path, outSelectedPath != nil { - outSelectedPath = path - } - - return item.isValidFile - } - - func validatePreviewPanel() -> Bool { - foldersCount > 0 || filesCount > 0 - } -} diff --git a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Find.swift b/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Find.swift deleted file mode 100644 index d6a5849..0000000 --- a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Find.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// VisibleItem+Find.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension VisibleItem { - func findFileName( - regex: NSRegularExpression, - searchFullPath usePath: Bool, - items: inout [VisibleItem] - ) { - let fileName = if usePath { - item.path ?? item.linkedItem?.path - } else { - item.fileName ?? item.linkedItem?.fileName - } - guard let fileName else { - return - } - if regex.firstMatch( - in: fileName, - options: [], - range: NSRange(location: 0, length: fileName.count) - ) != nil { - items.append(self) - } - - for vi in children { - vi.findFileName(regex: regex, searchFullPath: usePath, items: &items) - } - } -} diff --git a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Log.swift b/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Log.swift deleted file mode 100644 index a2e1fc7..0000000 --- a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Log.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// VisibleItem+Log.swift -// VisualDiffer -// -// Created by davide ficano on 23/02/25. -// Copyright (c) 2025 visualdiffer.com -// - -#if DEBUG - - import os.log - - extension VisibleItem { - // periphery:ignore - func log(indent: Int) { - let line = String(format: "%*c%@ %@", indent, " ", item.fileName ?? "", item.linkedItem?.fileName ?? "") - Logger.debug.info("\(line)") - - for vi in children { - vi.log(indent: indent + 2) - } - } - - func writelog( - _ fileHandle: FileHandle, - indent: Int - ) { - let spaces = String(repeating: " ", count: indent) - let line = String( - format: "%@ %@ %@ %@", - spaces, - item.fileName ?? "", - item.summary.description, - item.linkedItem?.fileName ?? "", - item.linkedItem?.summary.description ?? "" - ) - - Self.writeLine(fileHandle, line: line) - - for vi in children { - vi.writelog(fileHandle, indent: indent + 2) - } - } - - // periphery:ignore - static func writeLine(_ fileHandle: FileHandle, line: String) { - if let data = String(format: "%@\n", line).data(using: .ascii) { - fileHandle.write(data) - } - } - } -#endif diff --git a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+QLPreviewItem.swift b/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+QLPreviewItem.swift deleted file mode 100644 index 40193ad..0000000 --- a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+QLPreviewItem.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// VisibleItem+QLPreviewItem.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -import Quartz - -extension VisibleItem: QLPreviewItem { - // swiftlint:disable:next implicitly_unwrapped_optional - public var previewItemURL: URL! { - item.toUrl() - } -} diff --git a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Sort.swift b/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Sort.swift deleted file mode 100644 index ca62ca4..0000000 --- a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem+Sort.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// VisibleItem+Sort.swift -// VisualDiffer -// -// Created by davide ficano on 07/07/20. -// Copyright (c) 2020 visualdiffer.com -// - -func compare(_ lhs: T, _ rhs: T) -> ComparisonResult { - lhs == rhs ? .orderedSame : lhs < rhs ? .orderedAscending : .orderedDescending -} - -@objc extension VisibleItem { - func sort( - byFileName ascending: Bool, - ignoreCase: Bool, - followSymLinks: Bool - ) { - sortChildren(ascending, ignoreCase: ignoreCase) { fs1, fs2 in - fs1.compare(forList: fs2, followSymLinks: followSymLinks) - } - } - - func sort( - byDate ascending: Bool, - ignoreCase: Bool - ) { - sortChildren(ascending, ignoreCase: ignoreCase) { fs1, fs2 in - guard let date1 = fs1.fileModificationDate else { - return .orderedDescending - } - guard let date2 = fs2.fileModificationDate else { - return .orderedAscending - } - return date1.compare(date2) - } - } - - func sort( - byFileSize ascending: Bool, - ignoreCase: Bool - ) { - sortChildren(ascending, ignoreCase: ignoreCase) { fs1, fs2 in - if fs1.isFolder { - return compare(fs1.subfoldersSize, fs2.subfoldersSize) - } - return compare(fs1.fileSize, fs2.fileSize) - } - } -} diff --git a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem.swift b/Sources/Features/FoldersCompare/VisibleItem/VisibleItem.swift deleted file mode 100644 index 1e43c1b..0000000 --- a/Sources/Features/FoldersCompare/VisibleItem/VisibleItem.swift +++ /dev/null @@ -1,124 +0,0 @@ -// -// VisibleItem.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -public class VisibleItem: NSObject { - var item: CompareItem - var linkedItem: VisibleItem? - private(set) var children: [VisibleItem] = [] - - private init(_ item: CompareItem) { - self.item = item - - super.init() - - self.item.visibleItem = self - } - - static func createLinked(_ item: CompareItem) -> VisibleItem { - guard let linkedItem = item.linkedItem else { - fatalError("Linked item must be set") - } - - let vi = VisibleItem(item) - let linkedVI = VisibleItem(linkedItem) - - vi.linkedItem = linkedVI - linkedVI.linkedItem = vi - - return vi - } - - func add(_ vi: VisibleItem) { - children.append(vi) - } - - func remove(_ vi: VisibleItem) { - let index = children.firstIndex(of: vi) - if let index { - children.remove(at: index) - } - } - - func removeAll() { - children.removeAll() - } - - func swap() { - guard let linkedItem else { - return - } - let tempItem = item - item = linkedItem.item - linkedItem.item = tempItem - - for vi in children { - vi.swap() - } - } - - var childrenAllFiltered: Bool { - if item.isFolder { - for vi in children where !vi.item.isFiltered { - return false - } - return true - } - return false - } - - /** - If the file object is invalid the linkedItem is passed, - this ensure the comparator receives always two valid file object - */ - func sortChildren( - _ ascending: Bool, - ignoreCase: Bool, - comparator: (CompareItem, CompareItem) -> ComparisonResult - ) { - for vi in children where vi.item.isFolder { - vi.sortChildren(ascending, ignoreCase: ignoreCase, comparator: comparator) - } - children.sort { (lhs: VisibleItem, rhs: VisibleItem) -> Bool in - guard let fs1 = lhs.item.isValidFile ? lhs.item : lhs.item.linkedItem, - let fs2 = rhs.item.isValidFile ? rhs.item : rhs.item.linkedItem else { - return false - } - - let isFile1 = fs1.isFile - let isFile2 = fs2.isFile - let isFolder1 = fs1.isFolder - let isFolder2 = fs2.isFolder - // check if invalid files have path otherwise isn't necessary to compare - let bothInvalidWithPath = !fs1.isValidFile && !fs2.isValidFile && (fs1.path != nil || fs2.path != nil) - - if isFolder1 && isFolder2 || (isFile1 && isFile2) || bothInvalidWithPath { - var result = comparator(fs1, fs2) - - if result == .orderedSame { - // swiftlint:disable force_unwrapping - result = ignoreCase - ? fs1.fileName!.localizedCaseInsensitiveCompare(fs2.fileName!) - : fs1.fileName!.localizedCompare(fs2.fileName!) - // swiftlint:enable force_unwrapping - } - if !ascending, result != .orderedSame { - result = result == .orderedAscending ? .orderedDescending : .orderedAscending - } - return result == .orderedAscending - } - // Place directories before files - return isFolder1 - } - for (index, vi) in children.enumerated() { - guard let li = vi.linkedItem else { - fatalError("LinkedItem must be set for all visible items") - } - linkedItem?.children[index] = li - } - } -} diff --git a/Sources/Features/FoldersCompare/Windows/FolderCompareInfoWindow/DescriptionOutlineNode+FolderCompareInfo.swift b/Sources/Features/FoldersCompare/Windows/FolderCompareInfoWindow/DescriptionOutlineNode+FolderCompareInfo.swift deleted file mode 100644 index 69e72e1..0000000 --- a/Sources/Features/FoldersCompare/Windows/FolderCompareInfoWindow/DescriptionOutlineNode+FolderCompareInfo.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// DescriptionOutlineNode+FolderCompareInfo.swift -// VisualDiffer -// -// Created by davide ficano on 01/02/12. -// Copyright (c) 2012 visualdiffer.com -// - -extension DescriptionOutlineNode { - private func appendChild( - pattern: String, - rootPath: String, - items: [CompareItem], - side: DisplaySide - ) { - if items.isEmpty { - return - } - let path = String.localizedStringWithFormat(pattern, items.count, side.rawValue) - children.append(DescriptionOutlineNode( - relativePath: path, - items: items, - rootPath: rootPath - )) - } - - func addCompareGroup( - leftRoot: CompareItem, - comparatorOptions: ComparatorOptions - ) { - var leftOrphanFiles = [CompareItem]() - var rightOrphanFiles = [CompareItem]() - var matchesFiles = [CompareItem]() - var leftNewerFiles = [CompareItem]() - var rightNewerFiles = [CompareItem]() - - // if path is nil the associated counts will be 0 - // so it's safe to use the default to empty string because it will never be used - let leftPath = leftRoot.path ?? "" - let rightPath = leftRoot.linkedItem?.path ?? "" - - leftRoot.compareInfo( - leftOrphanFiles: &leftOrphanFiles, - rightOrphanFiles: &rightOrphanFiles, - matchesFiles: &matchesFiles, - newerLeftFiles: &leftNewerFiles, - newerRightFiles: &rightNewerFiles - ) - - appendChild( - pattern: NSLocalizedString("%ld %lu orphans", comment: "4 left/right orphans"), - rootPath: leftPath, - items: leftOrphanFiles, - side: .left - ) - appendChild( - pattern: NSLocalizedString("%ld %lu orphans", comment: "4 left/right orphans"), - rootPath: rightPath, - items: rightOrphanFiles, - side: .right - ) - - if comparatorOptions.contains(.timestamp) { - appendChild( - pattern: NSLocalizedString("%ld %lu newer", comment: "4 left/right newer"), - rootPath: leftPath, - items: leftNewerFiles, - side: .left - ) - appendChild( - pattern: NSLocalizedString("%ld %lu newer", comment: "4 left/right newer"), - rootPath: rightPath, - items: rightNewerFiles, - side: .right - ) - } else { - appendChild( - pattern: NSLocalizedString("%ld different", comment: ""), - rootPath: leftPath, - items: leftNewerFiles, - side: .left - ) - } - - appendChild( - pattern: NSLocalizedString("%ld same", comment: ""), - rootPath: leftPath, - items: matchesFiles, - side: .left - ) - } -} diff --git a/Sources/Features/FoldersCompare/Windows/FolderCompareInfoWindow/FolderCompareInfoWindow.swift b/Sources/Features/FoldersCompare/Windows/FolderCompareInfoWindow/FolderCompareInfoWindow.swift deleted file mode 100644 index e60c65f..0000000 --- a/Sources/Features/FoldersCompare/Windows/FolderCompareInfoWindow/FolderCompareInfoWindow.swift +++ /dev/null @@ -1,254 +0,0 @@ -// -// FolderCompareInfoWindow.swift -// VisualDiffer -// -// Created by davide ficano on 01/02/12. -// Copyright (c) 2012 visualdiffer.com -// - -class FolderCompareInfoWindow: NSWindow, NSOutlineViewDataSource, NSOutlineViewDelegate { - var leftRoot: CompareItem? - var comparatorOptions: ComparatorOptions = [] - var selectedItems: [CompareItem]? - - private var compareItems: DescriptionOutlineNode? - - private lazy var scrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = outlineView - - return view - }() - - private lazy var closeButton: NSButton = { - let view = NSButton(frame: .zero) - - view.title = NSLocalizedString("Close", comment: "") - view.setButtonType(.momentaryPushIn) - - view.isBordered = true - view.state = .off - view.bezelStyle = .flexiblePush - view.imagePosition = .noImage - view.alignment = .center - view.keyEquivalent = "\u{1B}" - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(closeSheet) - - return view - }() - - private lazy var outlineView: NSOutlineView = { - let view = NSOutlineView(frame: .zero) - - view.controlSize = .small - view.headerView = nil - view.dataSource = self - view.delegate = self - - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("")) - column.isEditable = false - column.width = 180 - column.minWidth = 100 - column.maxWidth = 1000 - column.resizingMask = [.autoresizingMask, .userResizingMask] - - view.addTableColumn(column) - - return view - }() - - static func createSheet() -> FolderCompareInfoWindow { - let styleMask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable] - - return FolderCompareInfoWindow( - contentRect: NSRect(x: 0, y: 0, width: 485, height: 286), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - } - - override init( - contentRect: NSRect, - styleMask style: NSWindow.StyleMask, - backing backingStoreType: NSWindow.BackingStoreType, - defer flag: Bool - ) { - super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag) - - setupViews() - } - - private func setupViews() { - contentMinSize = NSSize(width: 300, height: 200) - hasShadow = true - isReleasedWhenClosed = true - - let title = createTitle() - - if let contentView { - contentView.addSubview(title) - contentView.addSubview(scrollView) - contentView.addSubview(closeButton) - } - - setupConstraints(title) - } - - private func setupConstraints(_ title: NSView) { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - title.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 19), - title.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -19), - title.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), - - scrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - scrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -19), - scrollView.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 7), - scrollView.bottomAnchor.constraint(equalTo: closeButton.topAnchor, constant: -14), - - closeButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -19), - closeButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16), - ]) - } - - private func createTitle() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = NSLocalizedString("Folder Compare Info", comment: "") - view.isBordered = false - view.isBezeled = false - view.drawsBackground = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.font = NSFont.boldSystemFont(ofSize: 13) - view.isEditable = false - view.isSelectable = false - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func beginSheetModal(for window: NSWindow) { - let items = DescriptionOutlineNode(text: "", isContainer: true) - let allNode = DescriptionOutlineNode(text: NSLocalizedString("From Root", comment: ""), isContainer: true) - - items.children.append(allNode) - - if let leftRoot, - let leftPath = leftRoot.path, - let rightRoot = leftRoot.linkedItem, - let rightPath = rightRoot.path, - let selectedItems { - allNode.addCompareGroup(leftRoot: leftRoot, comparatorOptions: comparatorOptions) - for item in selectedItems { - if let path = item.path, item.isFolder, item.isValidFile { - let (isOnLeft, node) = relative(node: leftPath, rightPath: rightPath, for: path) - - if let node { - items.children.append(node) - if let leftRoot = isOnLeft ? item : item.linkedItem { - node.addCompareGroup(leftRoot: leftRoot, comparatorOptions: comparatorOptions) - } - } - } - } - - compareItems = items - - outlineView.reloadData() - for node in items.children { - outlineView.expandItem(node) - } - } - - window.beginSheet(self) - } - - private func relative(node leftPath: String, rightPath: String, for path: String) -> (Bool, DescriptionOutlineNode?) { - var isOnLeft = true - var relativePath = relative(path: leftPath, for: path) - - if relativePath == nil { - relativePath = relative(path: rightPath, for: path) - isOnLeft = false - } - if let relativePath { - return (isOnLeft, DescriptionOutlineNode(text: relativePath, isContainer: true)) - } - - return (isOnLeft, nil) - } - - private func relative(path root: String, for path: String) -> String? { - guard let range = path.range(of: root) else { - return nil - } - // skip path separator - let startIndex = path.index(after: range.upperBound) - return String(path[startIndex ..< path.endIndex]) - } - - @objc func closeSheet(_: AnyObject) { - sheetParent?.endSheet(self) - } - - // MARK: - NSOulineView delegates messages - - func outlineView(_: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - let node = item as? DescriptionOutlineNode ?? compareItems - - guard let children = node?.children else { - return 0 - } - return children.count - } - - func outlineView(_: NSOutlineView, isItemExpandable item: Any) -> Bool { - let node = item as? DescriptionOutlineNode ?? compareItems - - guard let node else { - return false - } - - return node.isContainer - } - - func outlineView(_: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { - let node = item as? DescriptionOutlineNode ?? compareItems - - guard let children = node?.children else { - fatalError("Children cannot be nil") - } - return children[index] - } - - func outlineView(_: NSOutlineView, objectValueFor _: NSTableColumn?, byItem item: Any?) -> Any? { - guard let item = item as? DescriptionOutlineNode else { - return nil - } - - return item.text - } - - func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool { - outlineView.parent(forItem: item) == nil - } -} diff --git a/Sources/Features/HistoryController/HistoryController.swift b/Sources/Features/HistoryController/HistoryController.swift deleted file mode 100644 index 5b003a6..0000000 --- a/Sources/Features/HistoryController/HistoryController.swift +++ /dev/null @@ -1,265 +0,0 @@ -// -// HistoryController.swift -// VisualDiffer -// -// Created by davide ficano on 25/07/20. -// Copyright (c) 2020 visualdiffer.com -// - -import os.log - -@MainActor protocol HistoryControllerDelegate: AnyObject { - func history(controller: HistoryController, selectedEntities entities: [HistoryEntity]) - func history(controller: HistoryController, doubleClickedEntity entity: HistoryEntity?) - - func history(controller: HistoryController, droppedPaths paths: [URL]) -> Bool -} - -@MainActor class HistoryController: NSObject, NSTableViewDelegate, NSTableViewDataSource, TableViewCommonDelegate { - lazy var scrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = tableView - - return view - }() - - var delegate: HistoryControllerDelegate? - - lazy var tableView: NSTableView = { - let view = HistoryTableView(frame: .zero) - - view.dataSource = self - view.delegate = self - view.target = self - view.doubleAction = #selector(handleDoubleClick) - view.menu = contextMenu() - - return view - }() - - private var results: NSFetchedResultsController - private lazy var resultsControllerDelegate = HistoryFetchedResultsControllerDelegate(tableView: tableView) - - override init() { - results = NSFetchedResultsController( - fetchRequest: HistoryEntity.requestUpdateTime(), - managedObjectContext: HistorySessionManager.shared.historyMOC, - sectionNameKeyPath: nil, - cacheName: nil - ) - super.init() - - setupResults() - } - - func contextMenu() -> NSMenu { - let menu = NSMenu(title: NSLocalizedString("Contextual Menu", comment: "")) - menu.autoenablesItems = false - - menu.addItem( - withTitle: NSLocalizedString("Remove", comment: ""), - action: #selector(removeHistory), - keyEquivalent: "" - ) - .target = self - menu.addItem( - withTitle: NSLocalizedString("Select Invalid Paths", comment: ""), - action: #selector(selectInvalidPaths), - keyEquivalent: "" - ) - .target = self - - return menu - } - - @MainActor func setupResults() { - results.delegate = resultsControllerDelegate - try? results.performFetch() - tableView.reloadData() - } - - @objc @MainActor func handleDoubleClick(_: AnyObject) { - let row = tableView.clickedRow - - // make sure double click was not in table header - if row != -1, - let delegate { - delegate.history(controller: self, doubleClickedEntity: results.fetchedObjects?[row]) - } - } - - // MARK: - NSTableView delegate and datasource methods - - func tableViewSelectionDidChange(_: Notification) { - guard let delegate, - let fetchedObjects = results.fetchedObjects else { - return - } - var entities = [HistoryEntity]() - - for idx in tableView.selectedRowIndexes { - entities.append(fetchedObjects[idx]) - } - - delegate.history(controller: self, selectedEntities: entities) - } - - func numberOfRows(in _: NSTableView) -> Int { - guard let fetchedObjects = results.fetchedObjects else { - return 0 - } - return fetchedObjects.count - } - - func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - guard let identifier = tableColumn?.identifier, - let entity = results.fetchedObjects?[row] else { - return nil - } - let cell = tableView.makeView(withIdentifier: identifier, owner: nil) as? HistoryEntityTableCellView - ?? HistoryEntityTableCellView(identifier: identifier) - - cell.pattern = resultsControllerDelegate.pattern - cell.setupCell(entity: entity) - - return cell - } - - func tableView(_: NSTableView, heightOfRow _: Int) -> CGFloat { - 80 - } - - // MARK: Drag&Drop - - func tableView(_ tableView: NSTableView, validateDrop info: any NSDraggingInfo, proposedRow _: Int, proposedDropOperation _: NSTableView.DropOperation) -> NSDragOperation { - var result: NSDragOperation = [] - - tableView.setDropRow(-1, dropOperation: .on) - let pasteboard = info.draggingPasteboard - - guard pasteboard.availableType(from: [NSPasteboard.PasteboardType.fileURL]) != nil, - let arr = pasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] else { - return result - } - - if arr.count < 2 { - let isOk = FileManager.default.fileExists(atPath: arr[0].osPath, isDirectory: nil) - if isOk { - result = .copy - } - } else { - if arr[0].matchesFileType(of: arr[1]) { - result = .copy - } - } - - return result - } - - func tableView(_: NSTableView, acceptDrop info: any NSDraggingInfo, row _: Int, dropOperation _: NSTableView.DropOperation) -> Bool { - let pasteboard = info.draggingPasteboard - - guard pasteboard.availableType(from: [NSPasteboard.PasteboardType.fileURL]) != nil, - let arr = pasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL], - let delegate else { - return false - } - - return delegate.history(controller: self, droppedPaths: arr) - } - - // To receive keydown declare the table class as TOPTableViewCommon inside the XIB definition - func tableViewCommonKeyDown(_ tableView: NSTableView, event: NSEvent) -> Bool { - if event.isDeleteShortcutKey(true) { - removeEntity(indexes: tableView.selectedRowIndexes) - return true - } - - if let delegate, - let entity = results.fetchedObjects?[tableView.selectedRow], - let str = event.charactersIgnoringModifiers, - !str.isEmpty, - let key = str[str.startIndex].asciiValue { - if key == NSCarriageReturnCharacter || key == NSEnterCharacter { - delegate.history(controller: self, doubleClickedEntity: entity) - return true - } - } - return false - } - - // MARK: - Private methods - - @MainActor private func removeEntity(indexes: IndexSet) { - if indexes.isEmpty { - return - } - guard let fetchedObjects = results.fetchedObjects else { - return - } - for row in indexes.reversed() { - HistorySessionManager.shared.historyMOC.delete(fetchedObjects[row]) - } - do { - try HistorySessionManager.shared.historyMOC.save() - } catch { - if let window = tableView.window { - NSAlert(error: error).beginSheetModal(for: window) - } - Logger.ui.error("Unable to delete history, reason \(error)") - } - } - - @objc @MainActor func removeHistory(_: AnyObject) { - removeEntity(indexes: tableView.selectedRowIndexes) - } - - @objc @MainActor func selectInvalidPaths(_: AnyObject) { - guard let fetchedObjects = results.fetchedObjects else { - return - } - let fm = FileManager.default - var indexSet = IndexSet() - - for (idx, entity) in fetchedObjects.enumerated() { - if let entityLeftPath = entity.leftPath, - let entityRightPath = entity.rightPath, - !fm.fileExists(atPath: entityLeftPath) || !fm.fileExists(atPath: entityRightPath) { - indexSet.insert(idx) - } - } - - tableView.selectRowIndexes(indexSet, byExtendingSelection: false) - tableView.window?.makeFirstResponder(tableView) - } - - @MainActor func filterFor(pattern: String?) { - if let pattern, !pattern.isEmpty { - resultsControllerDelegate.pattern = pattern - results.fetchRequest.predicate = NSPredicate(format: "(leftPath contains[cd] %@) OR (rightPath contains[cd] %@)", pattern, pattern) - } else { - resultsControllerDelegate.pattern = nil - results.fetchRequest.predicate = nil - } - - do { - try results.performFetch() - } catch { - Logger.ui.error("Unresolved error \(error)") - } - - tableView.reloadData() - } -} diff --git a/Sources/Features/HistoryController/HistoryEntityTableCellView.swift b/Sources/Features/HistoryController/HistoryEntityTableCellView.swift deleted file mode 100644 index bdc0a79..0000000 --- a/Sources/Features/HistoryController/HistoryEntityTableCellView.swift +++ /dev/null @@ -1,125 +0,0 @@ -// -// HistoryEntityTableCellView.swift -// VisualDiffer -// -// Created by davide ficano on 24/07/20. -// Copyright (c) 2020 visualdiffer.com -// - -class HistoryEntityTableCellView: NSTableCellView { - var pattern: String? { - didSet { - if let pattern { - leftPath.update(pattern: pattern) - rightPath.update(pattern: pattern) - } - } - } - - private lazy var leftPath: FilePathTableCellView = createFilePathTableCellView() - private lazy var rightPath: FilePathTableCellView = createFilePathTableCellView() - private lazy var timeDescription: NSTextField = createTimeDescription() - - convenience init(identifier: NSUserInterfaceItemIdentifier) { - self.init(frame: .zero) - self.identifier = identifier - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setup() - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("Method not implemented") - } - - func setup() { - addSubview(leftPath) - addSubview(rightPath) - addSubview(timeDescription) - - setupConstraints() - } - - private func createFilePathTableCellView() -> FilePathTableCellView { - let view = FilePathTableCellView(frame: .zero) - - view.translatesAutoresizingMaskIntoConstraints = false - view.textField?.font = NSFont.systemFont(ofSize: NSFont.systemFontSize) - - return view - } - - private func createTimeDescription() -> NSTextField { - let dateFormatter = DateFormatter() - - dateFormatter.dateFormat = DateFormatter.dateFormat( - fromTemplate: "ddMMyyyyHHmmss", - options: 0, - locale: Locale.current - ) - - let view = NSTextField(frame: .zero) - - view.isBordered = false - view.isBezeled = false - view.drawsBackground = false - view.isEditable = false - view.isSelectable = false - view.formatter = dateFormatter - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func setupConstraints() { - guard let leftText = leftPath.textField else { - fatalError("Can't happen") - } - NSLayoutConstraint.activate([ - leftPath.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4), - leftPath.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 4), - leftPath.topAnchor.constraint(equalTo: topAnchor, constant: 2), - leftPath.heightAnchor.constraint(equalToConstant: 24), - - rightPath.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4), - rightPath.trailingAnchor.constraint(equalTo: trailingAnchor), - rightPath.topAnchor.constraint(equalTo: leftPath.bottomAnchor), - rightPath.heightAnchor.constraint(equalToConstant: 24), - - timeDescription.leadingAnchor.constraint(equalTo: leftText.leadingAnchor), - timeDescription.trailingAnchor.constraint(equalTo: trailingAnchor), - timeDescription.topAnchor.constraint(equalTo: rightPath.bottomAnchor), - ]) - } - - func setupCell(entity: HistoryEntity?) { - guard let entity, - let entityLeftPath = entity.leftPath, - let entityRightPath = entity.rightPath else { - leftPath.textField?.stringValue = "" - rightPath.textField?.stringValue = "" - timeDescription.stringValue = "" - return - } - leftPath.update(path: entityLeftPath) - rightPath.update(path: entityRightPath) - - timeDescription.objectValue = entity.updateTime - } - - override var backgroundStyle: NSView.BackgroundStyle { - didSet { - // The timeDescription color (gray) is ugly when the row is selected - // so we change it - if backgroundStyle == .emphasized { - timeDescription.textColor = NSColor.controlTextColor - } else { - timeDescription.textColor = NSColor.secondaryLabelColor - } - } - } -} diff --git a/Sources/Features/HistoryController/HistoryFetchedResultsControllerDelegate.swift b/Sources/Features/HistoryController/HistoryFetchedResultsControllerDelegate.swift deleted file mode 100644 index 1a1dcae..0000000 --- a/Sources/Features/HistoryController/HistoryFetchedResultsControllerDelegate.swift +++ /dev/null @@ -1,164 +0,0 @@ -// -// HistoryFetchedResultsControllerDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 27/07/20. -// Copyright (c) 2020 visualdiffer.com -// - -import os.log - -typealias IndexPathPair = (IndexPath, IndexPath?) - -@objc class HistoryFetchedResultsControllerDelegate: NSObject, @preconcurrency NSFetchedResultsControllerDelegate { - private(set) var tableView: NSTableView - @objc var pattern: String? - - private var objectChanges = [NSFetchedResultsChangeType: [IndexPathPair]]() - - @objc init(tableView: NSTableView) { - self.tableView = tableView - - super.init() - } - - func controllerWillChangeContent(_: NSFetchedResultsController) { - objectChanges.removeAll() - } - - @MainActor func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - tableView.beginUpdates() - - onContentInserted(controller, array: objectChanges[.insert]) - onContentUpdated(controller, array: objectChanges[.update]) - onContentDeleted(controller, array: objectChanges[.delete]) - onContentMoved(controller, array: objectChanges[.move]) - - tableView.endUpdates() - - let indexes = IndexSet(integer: 0) - tableView.selectRowIndexes(indexes, byExtendingSelection: false) - tableView.scrollRowToVisible(0) - } - - func controller( - _: NSFetchedResultsController, - didChange _: Any, - at indexPath: IndexPath?, - for type: NSFetchedResultsChangeType, - newIndexPath: IndexPath? - ) { - // The indexPath order is not guaranteed to be sequential (1, 2, 3) so removing items from table - // the application could crash, to avoid this we collect all change types here - // and then update the tableView inside the controllerDidChangeContent method - // The idea comes from: https://github.com/visualdiffer/gityhub/blob/master/gityhub/gityhub/Controllers/CollectionViewDataFetcher.m#L116 - // To reproduce the crash try to delete last 3 rows from table - // The error is: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. NSTableView error inserting/removing/moving row X (numberOfRows: X). with userInfo (null) - var changeSet = objectChanges[type] ?? [] - - switch type { - case .insert: - if let newIndexPath { - let item: IndexPathPair = (newIndexPath, nil) - changeSet.append(item) - } - case .delete: - if let indexPath { - let item: IndexPathPair = (indexPath, nil) - changeSet.append(item) - } - case .update: - if let indexPath { - let item: IndexPathPair = (indexPath, nil) - changeSet.append(item) - } - case .move: - if let indexPath, - let newIndexPath { - let item: IndexPathPair = (indexPath, newIndexPath) - changeSet.append(item) - } - default: - Logger.general.error("Found invalid type: \(type.rawValue)") - } - objectChanges[type] = changeSet - } - - @MainActor func onContentInserted( - _: NSFetchedResultsController, - array: [IndexPathPair]? - ) { - guard let array, - !array.isEmpty else { - return - } - var indexes = IndexSet() - for (from, _) in array { - indexes.insert(from.item) - } - tableView.insertRows( - at: indexes, - withAnimation: .effectFade - ) - } - - @MainActor func onContentUpdated( - _ controller: NSFetchedResultsController, - array: [IndexPathPair]? - ) { - guard let array, - !array.isEmpty else { - return - } - for (from, _) in array { - let row = from.item - let cell = tableView.view(atColumn: 0, row: row, makeIfNecessary: true) - if let cell = cell as? HistoryEntityTableCellView { - cell.pattern = pattern - if let entity = controller.fetchedObjects?[row] as? HistoryEntity { - cell.setupCell(entity: entity) - } - } - } - } - - @MainActor func onContentDeleted( - _: NSFetchedResultsController, - array: [IndexPathPair]? - ) { - guard let array, - !array.isEmpty else { - return - } - var indexes = IndexSet() - for (from, _) in array { - indexes.insert(from.item) - } - tableView.removeRows( - at: indexes, - withAnimation: .effectFade - ) - } - - @MainActor func onContentMoved( - _: NSFetchedResultsController, - array: [IndexPathPair]? - ) { - guard let array, - !array.isEmpty else { - return - } - for (fromIndex, toIndex) in array { - if let toIndex { - tableView.removeRows( - at: IndexSet(integer: fromIndex.item), - withAnimation: .effectFade - ) - tableView.insertRows( - at: IndexSet(integer: toIndex.item), - withAnimation: .effectFade - ) - } - } - } -} diff --git a/Sources/Features/HistoryController/HistorySearchField.swift b/Sources/Features/HistoryController/HistorySearchField.swift deleted file mode 100644 index 41e008e..0000000 --- a/Sources/Features/HistoryController/HistorySearchField.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// HistorySearchField.swift -// VisualDiffer -// -// Created by davide ficano on 20/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -@objc class HistorySearchField: NSSearchField, NSSearchFieldDelegate { - @objc var historyController: HistoryController? - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("Method not implemented") - } - - func setupViews() { - placeholderString = NSLocalizedString("Search History <⌘F>", comment: "") - bezelStyle = .roundedBezel - target = self - action = #selector(search) - translatesAutoresizingMaskIntoConstraints = false - - // used to receive NSControlTextEditingDelegate notifications - delegate = self - } - - @objc func search(_: AnyObject) { - if let historyController { - let pattern = stringValue.trimmingCharacters(in: NSCharacterSet.whitespaces) - historyController.filterFor(pattern: pattern) - } - } - - func control(_: NSControl, textView _: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - guard let historyController else { - return false - } - let row = historyController.tableView.selectedRow - - if commandSelector == #selector(moveUp) { - historyController.tableView.selectRow(closestTo: row - 1, byExtendingSelection: false, ensureVisible: true) - return true - } else if commandSelector == #selector(moveDown) { - historyController.tableView.selectRow(closestTo: row + 1, byExtendingSelection: false, ensureVisible: true) - return true - } else if commandSelector == #selector(insertNewline) { - historyController.delegate?.history(controller: historyController, doubleClickedEntity: nil) - return true - } - return false - } -} diff --git a/Sources/Features/HistoryController/HistoryTableView.swift b/Sources/Features/HistoryController/HistoryTableView.swift deleted file mode 100644 index 3b6c411..0000000 --- a/Sources/Features/HistoryController/HistoryTableView.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// HistoryTableView.swift -// VisualDiffer -// -// Created by davide ficano on 20/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -class HistoryTableView: TableViewCommon { - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - setup() - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("Method not implemented") - } - - func setup() { - allowsEmptySelection = true - allowsColumnReordering = false - allowsColumnResizing = true - allowsMultipleSelection = true - allowsColumnSelection = true - allowsTypeSelect = true - usesAlternatingRowBackgroundColors = true - - focusRingType = .none - allowsExpansionToolTips = true - columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle - autosaveTableColumns = false - - // Needed by drop - registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL]) - setDraggingSourceOperationMask(.every, forLocal: true) - setDraggingSourceOperationMask(.every, forLocal: false) - - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "HistoryPath")) - column.title = NSLocalizedString("History", comment: "") - column.resizingMask = .autoresizingMask - addTableColumn(column) - sizeLastColumnToFit() - } -} diff --git a/Sources/Features/Preferences/Box/ComparisonStandardUserDataSource.swift b/Sources/Features/Preferences/Box/ComparisonStandardUserDataSource.swift deleted file mode 100644 index a428a86..0000000 --- a/Sources/Features/Preferences/Box/ComparisonStandardUserDataSource.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// ComparisonStandardUserDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ComparisonStandardUserDataSource: StandardUserPreferencesBoxDataSource { - override func preferenceBox( - _ preferencesBox: PreferencesBox, - boolForKey key: CommonPrefs.Name - ) -> Bool { - switch key { - case .virtualResourceFork: - CommonPrefs.shared.fileExtraOptions[.resourceFork] - case .virtualFinderLabel: - CommonPrefs.shared.finderLabel - case .virtualFinderTags: - CommonPrefs.shared.finderTags - default: - super.preferenceBox(preferencesBox, boolForKey: key) - } - } - - override func preferenceBox( - _ preferencesBox: PreferencesBox, - setBool value: Bool, - forKey key: CommonPrefs.Name - ) { - switch key { - case .virtualResourceFork: - CommonPrefs.shared.fileExtraOptions[.resourceFork] = value - case .virtualFinderLabel: - CommonPrefs.shared.finderLabel = value - case .virtualFinderTags: - CommonPrefs.shared.finderTags = value - default: - super.preferenceBox(preferencesBox, setBool: value, forKey: key) - } - } - - override func preferenceBox( - _ preferencesBox: PreferencesBox, - integerForKey key: CommonPrefs.Name - ) -> Int { - switch key { - case .virtualComparatorWithoutMethod: - CommonPrefs.shared.comparatorWithoutMethod.rawValue - case .virtualDisplayFiltersWithoutMethod: - CommonPrefs.shared.displayFiltersWithoutMethod.rawValue - default: - super.preferenceBox(preferencesBox, integerForKey: key) - } - } - - override func preferenceBox( - _ preferencesBox: PreferencesBox, - setInteger value: Int, - forKey key: CommonPrefs.Name - ) { - switch key { - case .virtualComparatorWithoutMethod: - CommonPrefs.shared.comparatorWithoutMethod = ComparatorOptions(rawValue: value) - case .virtualDisplayFiltersWithoutMethod: - CommonPrefs.shared.displayFiltersWithoutMethod = DisplayOptions(rawValue: value) - default: - super.preferenceBox(preferencesBox, setInteger: value, forKey: key) - } - } - - override func preferenceBox(_ preferencesBox: PreferencesBox, isEnabled key: CommonPrefs.Name) -> Bool { - switch key { - case .virtualFinderLabel: - preferenceBox(preferencesBox, boolForKey: .virtualFinderTags) == false - case .virtualFinderTags: - preferenceBox(preferencesBox, boolForKey: .virtualFinderLabel) == false - default: - true - } - } -} diff --git a/Sources/Features/Preferences/Box/PreferencesBox.swift b/Sources/Features/Preferences/Box/PreferencesBox.swift deleted file mode 100644 index a7705fb..0000000 --- a/Sources/Features/Preferences/Box/PreferencesBox.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// PreferencesBox.swift -// VisualDiffer -// -// Created by davide ficano on 18/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class PreferencesBox: NSBox { - private var defaultDelegate: StandardUserPreferencesBoxDataSource - private var checkboxes: [CommonPrefs.Name: PreferencesCheckbox] - - var delegate: PreferencesBoxDataSource? - - init(title: String) { - defaultDelegate = StandardUserPreferencesBoxDataSource() - delegate = defaultDelegate - checkboxes = [:] - - super.init(frame: .zero) - - self.title = title - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - titleFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - titlePosition = .atTop - boxType = .primary - translatesAutoresizingMaskIntoConstraints = false - } - - func createCheckBox( - title: String, - prefName: CommonPrefs.Name, - isNegated: Bool = false - ) -> PreferencesCheckbox { - let view = PreferencesCheckbox(title: title, prefName: prefName, isNegated: isNegated) - - setupCheckBox(view) - - return view - } - - func setupCheckBox(_ checkbox: PreferencesCheckbox) { - checkbox.target = self - checkbox.action = #selector(toggleCheckbox) - - checkboxes[checkbox.prefName] = checkbox - } - - @objc func toggleCheckbox(_ sender: PreferencesCheckbox) { - let isOn = sender.state == .on - - delegate?.preferenceBox(self, setBool: isOn, forKey: sender.prefName) - } - - @objc func reloadData() { - guard let delegate else { - return - } - for checkbox in checkboxes.values { - let value = delegate.preferenceBox(self, boolForKey: checkbox.prefName) - checkbox.state = value ? .on : .off - checkbox.isEnabled = delegate.preferenceBox(self, isEnabled: checkbox.prefName) - } - } -} diff --git a/Sources/Features/Preferences/Box/PreferencesBoxDataSource+Default.swift b/Sources/Features/Preferences/Box/PreferencesBoxDataSource+Default.swift deleted file mode 100644 index 2372f09..0000000 --- a/Sources/Features/Preferences/Box/PreferencesBoxDataSource+Default.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// PreferencesBoxDataSource+Default.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension PreferencesBoxDataSource { - func preferenceBox(_: PreferencesBox, boolForKey key: CommonPrefs.Name) -> Bool { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, setBool _: Bool, forKey key: CommonPrefs.Name) { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, integerForKey key: CommonPrefs.Name) -> Int { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, setInteger _: Int, forKey key: CommonPrefs.Name) { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, stringForKey key: CommonPrefs.Name) -> String? { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, setString _: String?, forKey key: CommonPrefs.Name) { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, objectForKey key: CommonPrefs.Name) -> Any? { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, setObject _: Any?, forKey key: CommonPrefs.Name) { - fatalError("key \(key) not handled") - } - - func preferenceBox(_: PreferencesBox, isEnabled key: CommonPrefs.Name) -> Bool { - fatalError("key \(key) not handled") - } -} diff --git a/Sources/Features/Preferences/Box/PreferencesBoxDataSource.swift b/Sources/Features/Preferences/Box/PreferencesBoxDataSource.swift deleted file mode 100644 index 1b3b855..0000000 --- a/Sources/Features/Preferences/Box/PreferencesBoxDataSource.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// PreferencesBoxDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -protocol PreferencesBoxDataSource { - func preferenceBox( - _ preferencesBox: PreferencesBox, - boolForKey key: CommonPrefs.Name - ) -> Bool - func preferenceBox( - _ preferencesBox: PreferencesBox, - setBool value: Bool, - forKey key: CommonPrefs.Name - ) - - func preferenceBox( - _ preferencesBox: PreferencesBox, - integerForKey key: CommonPrefs.Name - ) -> Int - func preferenceBox( - _ preferencesBox: PreferencesBox, - setInteger value: Int, - forKey key: CommonPrefs.Name - ) - - func preferenceBox( - _ preferencesBox: PreferencesBox, - stringForKey key: CommonPrefs.Name - ) -> String? - func preferenceBox( - _ preferencesBox: PreferencesBox, - setString value: String?, - forKey key: CommonPrefs.Name - ) - - func preferenceBox( - _ preferencesBox: PreferencesBox, - objectForKey key: CommonPrefs.Name - ) -> Any? - func preferenceBox( - _ preferencesBox: PreferencesBox, - setObject value: Any?, - forKey key: CommonPrefs.Name - ) - - func preferenceBox(_ preferencesBox: PreferencesBox, isEnabled key: CommonPrefs.Name) -> Bool -} diff --git a/Sources/Features/Preferences/Box/PreferencesCheckbox.swift b/Sources/Features/Preferences/Box/PreferencesCheckbox.swift deleted file mode 100644 index 1a3461d..0000000 --- a/Sources/Features/Preferences/Box/PreferencesCheckbox.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// PreferencesCheckbox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class PreferencesCheckbox: NSButton { - private(set) var prefName: CommonPrefs.Name - private(set) var isNegated = false - - init( - title: String, - prefName: CommonPrefs.Name, - isNegated: Bool = false - ) { - self.prefName = prefName - self.isNegated = isNegated - super.init(frame: .zero) - - self.title = title - setButtonType(.switch) - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override var state: NSControl.StateValue { - get { - var isChecked = super.state == .on - if isNegated { - isChecked.toggle() - } - return isChecked ? .on : .off - } - - set { - var isChecked = newValue == .on - if isNegated { - isChecked.toggle() - } - super.state = isChecked ? .on : .off - } - } -} diff --git a/Sources/Features/Preferences/Box/StandardUserPreferencesBoxDataSource.swift b/Sources/Features/Preferences/Box/StandardUserPreferencesBoxDataSource.swift deleted file mode 100644 index 46886f6..0000000 --- a/Sources/Features/Preferences/Box/StandardUserPreferencesBoxDataSource.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// StandardUserPreferencesBoxDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class StandardUserPreferencesBoxDataSource: NSObject, PreferencesBoxDataSource { - func preferenceBox( - _: PreferencesBox, - boolForKey key: CommonPrefs.Name - ) -> Bool { - CommonPrefs.shared.bool(forKey: key) - } - - func preferenceBox( - _: PreferencesBox, - setBool value: Bool, - forKey key: CommonPrefs.Name - ) { - CommonPrefs.shared.set(value, forKey: key) - } - - func preferenceBox( - _: PreferencesBox, - integerForKey key: CommonPrefs.Name - ) -> Int { - CommonPrefs.shared.integer(forKey: key) - } - - func preferenceBox( - _: PreferencesBox, - setInteger value: Int, - forKey key: CommonPrefs.Name - ) { - CommonPrefs.shared.set(value, forKey: key) - } - - func preferenceBox(_: PreferencesBox, stringForKey _: CommonPrefs.Name) -> String? { - fatalError("Not implemented") - } - - func preferenceBox(_: PreferencesBox, setString _: String?, forKey _: CommonPrefs.Name) { - fatalError("Not implemented") - } - - func preferenceBox(_: PreferencesBox, objectForKey _: CommonPrefs.Name) -> Any? { - fatalError("Not implemented") - } - - func preferenceBox(_: PreferencesBox, setObject _: Any?, forKey _: CommonPrefs.Name) { - fatalError("Not implemented") - } - - func preferenceBox(_: PreferencesBox, isEnabled _: CommonPrefs.Name) -> Bool { - true - } -} diff --git a/Sources/Features/Preferences/Main/Controllers/BasePreferences.swift b/Sources/Features/Preferences/Main/Controllers/BasePreferences.swift deleted file mode 100644 index d4e4404..0000000 --- a/Sources/Features/Preferences/Main/Controllers/BasePreferences.swift +++ /dev/null @@ -1,246 +0,0 @@ -// -// BasePreferences.swift -// VisualDiffer -// -// Created by davide ficano on 16/08/15. -// Copyright (c) 2015 visualdiffer.com -// - -class BasePreferences: NSWindowController, NSToolbarDelegate, NSTabViewDelegate, NSWindowDelegate { - static let prefsToolbarIdentifier = NSToolbar.Identifier("PreferencesToolbar") - static let lastVisiblePrefTab = "lastVisiblePrefTab" - - private lazy var tabView: NSTabView = createTabView() - private lazy var prefPanel: NSWindow = createPrefPanel() - - // Contains the toolbar identifiers sorted by show order - var toolbarIdentifiers: [NSToolbarItem.Identifier] { - [] - } - - init() { - super.init(window: nil) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - setupTabView() - prefPanel.contentView?.addSubview(tabView) - - setupConstraints() - } - - private func createPrefPanel() -> NSWindow { - let styleMask: NSWindow.StyleMask = [ - .titled, - .closable, - .miniaturizable, - .resizable, - ] - - let view = NSPanel( - contentRect: .zero, - styleMask: styleMask, - backing: .buffered, - defer: false - ) - - view.title = NSLocalizedString("Settings", comment: "") - view.hasShadow = true - view.isRestorable = true - view.titlebarSeparatorStyle = .automatic - view.setFrameAutosaveName("Settings") - view.toolbarStyle = .automatic - - view.isFloatingPanel = false - view.hidesOnDeactivate = false // Don't hide when app deactivates - view.becomesKeyOnlyIfNeeded = false // Allow it to become key - view.isReleasedWhenClosed = false - - if #available(macOS 11.0, *) { - view.toolbarStyle = .preference - } - - view.toolbar = createToolbar() - view.delegate = self - - return view - } - - private func createTabView() -> NSTabView { - let view = NSTabView(frame: .zero) - - view.tabViewType = .noTabsBezelBorder - view.allowsTruncatedLabels = false - view.drawsBackground = true - view.translatesAutoresizingMaskIntoConstraints = false - - view.delegate = self - - return view - } - - private func setupTabView() { - for identifier in toolbarIdentifiers { - tabView.addTabViewItem(NSTabViewItem(identifier: identifier)) - } - } - - func createToolbar() -> NSToolbar { - let toolbar = NSToolbar(identifier: Self.prefsToolbarIdentifier) - - toolbar.allowsUserCustomization = false - toolbar.autosavesConfiguration = true - toolbar.sizeMode = .regular - toolbar.displayMode = .iconAndLabel - toolbar.delegate = self - - return toolbar - } - - private func setupConstraints() { - guard let contentView = prefPanel.contentView else { - return - } - NSLayoutConstraint.activate([ - tabView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - tabView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - tabView.topAnchor.constraint(equalTo: contentView.topAnchor), - tabView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - ]) - } - - func selectLastUsedTab() { - var selectedItemItenIdentifier: NSToolbarItem.Identifier? - var selectedItemIndex = NSNotFound - - if let lastPrefTab = UserDefaults.standard.string(forKey: Self.lastVisiblePrefTab) { - let index = tabView.indexOfTabViewItem(withIdentifier: lastPrefTab) - if index == NSNotFound { - selectedItemItenIdentifier = toolbarIdentifiers.first - selectedItemIndex = 0 - } else { - selectedItemItenIdentifier = NSToolbarItem.Identifier(lastPrefTab) - selectedItemIndex = index - } - } - if let selectedItemItenIdentifier { - prefPanel.toolbar?.selectedItemIdentifier = selectedItemItenIdentifier - tabView.selectTabViewItem(at: selectedItemIndex) - } - } - - // Called before the panel is shown on screen, any UI initialization can be done here - // the default implementation calls selectLastUsedTab - func panelWillShow() { - selectLastUsedTab() - } - - // This method is called when the Preference panel is inside the Main.xib - @objc func show(_: Any?) { - panelWillShow() - - prefPanel.center() - prefPanel.makeKeyAndOrderFront(self) - } - - // This method is called when the Preference panel is implemented as NSWindowController - // otherwise isn't called - override func showWindow(_ sender: Any?) { - super.showWindow(sender) - - show(sender) - } - - // MARK: - Toolbar delegate - - func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - toolbarIdentifiers - } - - func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - toolbarIdentifiers - } - - func toolbarSelectableItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] { - toolbarIdentifiers - } - - @objc func selectPrefTab(_ sender: Any) { - if let toolbarItem = sender as? NSToolbarItem { - UserDefaults.standard.setValue(toolbarItem.itemIdentifier.rawValue, forKey: Self.lastVisiblePrefTab) - tabView.selectTabViewItem(withIdentifier: toolbarItem.itemIdentifier) - } - } - - // MARK: - Position and size - - func contentRect() -> NSRect { - var contentRect = NSRect.zero - - if let selectedTabView = tabView.selectedTabViewItem?.view { - for view in selectedTabView.subviews { - // the result of Swift's GCRect.union is different from NSUnionRect - // so we stay on it - // swiftlint:disable:next legacy_nsgeometry_functions - contentRect = NSUnionRect(contentRect, view.frame) - } - } - - return contentRect - } - - func toolbarHeight(_: NSWindow) -> CGFloat { - let windowFrame = NSWindow.contentRect(forFrameRect: prefPanel.frame, styleMask: prefPanel.styleMask) - return windowFrame.size.height - (prefPanel.contentView?.frame.size.height ?? 0) - } - - func minWindowHeight() -> CGFloat { - let contentRect = contentRect() - // Border top + toolbar - return contentRect.size.height + 40 + toolbarHeight(prefPanel) - } - - func resize() { - let windowFrame = NSWindow.contentRect(forFrameRect: prefPanel.frame, styleMask: prefPanel.styleMask) - let height = minWindowHeight() - let frameRect = NSRect( - x: windowFrame.origin.x, - y: windowFrame.origin.y + windowFrame.size.height - height, - width: windowFrame.size.width, - height: height - ) - prefPanel.setFrame( - NSWindow.frameRect(forContentRect: frameRect, styleMask: prefPanel.styleMask), - display: true, - animate: prefPanel.isVisible - ) - } - - func tabView(_: NSTabView, didSelect _: NSTabViewItem?) { - resize() - } - - func windowDidBecomeKey(_: Notification) { - resize() - } - - func windowWillResize(_: NSWindow, to frameSize: NSSize) -> NSSize { - // Only allow horizontal sizing - NSSize(width: frameSize.width, height: prefPanel.frame.size.height) - } - - /* - We do this to catch the case where the user enters a value into - one of the text fields but closes the window without hitting enter or tab. - */ - func windowShouldClose(_ sender: NSWindow) -> Bool { - sender.makeFirstResponder(nil) // validate editing - } -} diff --git a/Sources/Features/Preferences/Main/Controllers/Preferences.swift b/Sources/Features/Preferences/Main/Controllers/Preferences.swift deleted file mode 100644 index 72c3f48..0000000 --- a/Sources/Features/Preferences/Main/Controllers/Preferences.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// Preferences.swift -// VisualDiffer -// -// Created by davide ficano on 10/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -class Preferences: BasePreferences { - private var loadedTabs = Set() - - // Must have same values used for TabViewItem identifiers - override var toolbarIdentifiers: [NSToolbarItem.Identifier] { - [ - .generalPrefs, - .fontsPrefs, - .textPrefs, - .folderPrefs, - .confirmationsPrefs, - .keyboardPrefs, - .trustedPathsPrefs, - ] - } - - // MARK: - Toolbar creation - - func toolbar( - _: NSToolbar, - itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, - willBeInsertedIntoToolbar _: Bool - ) -> NSToolbarItem? { - var toolbarItem: NSToolbarItem? - - if itemIdentifier == .generalPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("General", comment: ""), - tooltip: NSLocalizedString("General Settings", comment: ""), - image: NSImage.imageSymbolCompat(NSImage.preferencesGeneralName), - target: self, - action: #selector(selectPrefTab) - ) - } else if itemIdentifier == .fontsPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Fonts", comment: ""), - tooltip: NSLocalizedString("Change Fonts", comment: ""), - image: NSImage.imageSymbolCompat(NSImage.fontPanelName), - target: self, - action: #selector(selectPrefTab) - ) - } else if itemIdentifier == .textPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Text", comment: ""), - tooltip: NSLocalizedString("Text Differences", comment: ""), - image: NSImage.imageSymbolCompat("prefs_text"), - target: self, - action: #selector(selectPrefTab) - ) - } else if itemIdentifier == .trustedPathsPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Trusted Paths", comment: ""), - tooltip: NSLocalizedString("Paths granted access to VisualDiffer", comment: ""), - image: NSImage.imageSymbolCompat("prefs_paths"), - target: self, - action: #selector(selectPrefTab) - ) - } else if itemIdentifier == .folderPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Folder", comment: ""), - tooltip: NSLocalizedString("Folder View", comment: ""), - image: NSImage.imageSymbolCompat("prefs_folder"), - target: self, - action: #selector(selectPrefTab) - ) - } else if itemIdentifier == .confirmationsPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Confirmations", comment: ""), - tooltip: NSLocalizedString("Confirmations and Warnings", comment: ""), - image: NSImage.imageSymbolCompat("prefs_confirmations"), - target: self, - action: #selector(selectPrefTab) - ) - } else if itemIdentifier == .keyboardPrefs { - toolbarItem = NSToolbarItem( - identifier: itemIdentifier, - label: NSLocalizedString("Keyboard", comment: ""), - tooltip: NSLocalizedString("Keyboard shortcuts", comment: ""), - image: NSImage.imageSymbolCompat("prefs_keyboard"), - target: self, - action: #selector(selectPrefTab) - ) - } - return toolbarItem - } - - func tabView(_: NSTabView, willSelect tabViewItem: NSTabViewItem?) { - guard let tabViewItem, - let identifier = tabViewItem.identifier as? NSToolbarItem.Identifier else { - return - } - - if loadedTabs.contains(identifier) { - return - } - loadedTabs.insert(identifier) - - if identifier == .generalPrefs { - tabViewItem.view = GeneralPreferencesPanel(frame: .zero) - } else if identifier == .fontsPrefs { - tabViewItem.view = FontPreferencesPanel(frame: .zero) - } else if identifier == .textPrefs { - tabViewItem.view = TextPreferencesPanel(frame: .zero) - } else if identifier == .trustedPathsPrefs { - tabViewItem.view = TrustedPathsPreferencesPanel(frame: .zero) - } else if identifier == .folderPrefs { - tabViewItem.view = FolderPreferencesPanel(frame: .zero) - } else if identifier == .confirmationsPrefs { - tabViewItem.view = ConfirmationsPreferencesPanel(frame: .zero) - } else if identifier == .keyboardPrefs { - tabViewItem.view = KeyboardPreferencesPanel(frame: .zero) - } - } - - override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) { - super.tabView(tabView, didSelect: tabViewItem) - - if let dataSource = tabViewItem?.view as? PreferencesPanelDataSource { - dataSource.reloadData() - } - } -} - -private extension NSToolbarItem.Identifier { - static let generalPrefs = Self("general") - static let fontsPrefs = Self("fonts") - static let textPrefs = Self("text") - static let trustedPathsPrefs = Self("trustedPaths") - static let folderPrefs = Self("folder") - static let confirmationsPrefs = Self("confirmations") - static let keyboardPrefs = Self("keyboard") -} diff --git a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsDocumentsBox.swift b/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsDocumentsBox.swift deleted file mode 100644 index 3e9ba0b..0000000 --- a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsDocumentsBox.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ConfirmationsDocumentsBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ConfirmationsDocumentsBox: PreferencesBox { - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let stackView = NSStackView.preferences(with: [ - createCheckBox( - title: NSLocalizedString("Don't ask to save the document with changes when closing", comment: ""), - prefName: .confirmDontAskToSaveSession - ), - NSTextField.hintWithTitle(NSLocalizedString("Be careful, any changes will be lost (filters, alignment rules, ...)", comment: "")), - ]) - - if let contentView { - contentView.addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - - heightAnchor.constraint(equalToConstant: 80), - ]) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsFilesBox.swift b/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsFilesBox.swift deleted file mode 100644 index 2faea53..0000000 --- a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsFilesBox.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ConfirmationsFilesBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ConfirmationsFilesBox: PreferencesBox { - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let stackView = NSStackView.preferences(with: [ - createCheckBox( - title: NSLocalizedString("Confirm reloading files changed on the file system by another application", comment: ""), - prefName: .confirmReloadFiles, - isNegated: true - ), - ]) - - if let contentView { - contentView.addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - - heightAnchor.constraint(equalToConstant: 60), - ]) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsFoldersBox.swift b/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsFoldersBox.swift deleted file mode 100644 index 7ee79d6..0000000 --- a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsFoldersBox.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// ConfirmationsFoldersBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ConfirmationsFoldersBox: PreferencesBox { - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let separator = NSBox(frame: .zero) - separator.boxType = .separator - - let stackView = NSStackView.preferences(with: [ - createCheckBox( - title: NSLocalizedString("Confirm opening a large number of windows in Finder", comment: ""), - prefName: .confirmShowInFinder - ), - createCheckBox( - title: NSLocalizedString("Confirm stopping the operation in progress", comment: ""), - prefName: .confirmStopLongOperation - ), - createCheckBox( - title: NSLocalizedString("Warn that opened files may not be visible in Finder", comment: ""), - prefName: .confirmShowInFinderNotVisibleFiles - ), - separator, - createCheckBox( - title: NSLocalizedString("Confirm Copy", comment: ""), - prefName: .confirmCopy - ), - createCheckBox( - title: NSLocalizedString("Confirm Delete", comment: ""), - prefName: .confirmDelete - ), - createCheckBox( - title: NSLocalizedString("Confirm Move", comment: ""), - prefName: .confirmMove - ), - createCheckBox( - title: NSLocalizedString("Include Filtered Items By Default", comment: ""), - prefName: .confirmIncludeFilteredItems - ), - NSTextField.hintWithTitle(NSLocalizedString("Hold down ⌥ key (ALT) to override confirmation flag and show dialog", comment: "")), - ]) - - if let contentView { - contentView.addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - - heightAnchor.constraint(equalToConstant: 230), - ]) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsPreferencesPanel.swift deleted file mode 100644 index 79b5455..0000000 --- a/Sources/Features/Preferences/Main/Panels/Confirmations/ConfirmationsPreferencesPanel.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ConfirmationsPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 19/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ConfirmationsPreferencesPanel: NSView, PreferencesPanelDataSource { - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - let views = [ - ConfirmationsFoldersBox(title: NSLocalizedString("Confirmation and Warning Messages for Folders", comment: "")), - ConfirmationsFilesBox(title: NSLocalizedString("Confirmation and Warning Messages for Files", comment: "")), - ConfirmationsDocumentsBox(title: NSLocalizedString("Confirmation for Documents", comment: "")), - ] - - let stackView = NSStackView.preferences(with: views) - - addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - stackView.topAnchor.constraint(equalTo: topAnchor), - ]) - - for view in views { - view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true - view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true - } - } - - func reloadData() { - guard let stackView = subviews[0] as? NSStackView else { - return - } - - for view in stackView.views { - if let box = view as? PreferencesBox { - box.reloadData() - } - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Folder/DifferenceNavigatorBox.swift b/Sources/Features/Preferences/Main/Panels/Folder/DifferenceNavigatorBox.swift deleted file mode 100644 index 966f36a..0000000 --- a/Sources/Features/Preferences/Main/Panels/Folder/DifferenceNavigatorBox.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// DifferenceNavigatorBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DifferenceNavigatorBox: PreferencesBox { - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let stackView = NSStackView.preferences(with: [ - createCheckBox( - title: NSLocalizedString("Auto search wrap around", comment: ""), - prefName: .Navigator.wrap - ), - createCheckBox( - title: NSLocalizedString("Traverse collapsed subfolders", comment: ""), - prefName: .Navigator.traverseFolders - ), - ]) - - if let contentView { - contentView.addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - - heightAnchor.constraint(equalToConstant: 80), - ]) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Folder/FolderPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/Folder/FolderPreferencesPanel.swift deleted file mode 100644 index a564d42..0000000 --- a/Sources/Features/Preferences/Main/Panels/Folder/FolderPreferencesPanel.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// FolderPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 18/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FolderPreferencesPanel: NSView, PreferencesPanelDataSource { - private var comparisonDelegate: ComparisonStandardUserDataSource - - override init(frame frameRect: NSRect) { - comparisonDelegate = ComparisonStandardUserDataSource() - - super.init(frame: frameRect) - - setupViews() - } - - private func setupViews() { - let views = [ - FoldersTraversalBox(title: NSLocalizedString("Folder Traversal", comment: "")), - FolderViewBox(title: NSLocalizedString("View", comment: "")), - DifferenceNavigatorBox(title: NSLocalizedString("Difference Navigator", comment: "")), - ] - - let stackView = NSStackView.preferences(with: views) - - addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - stackView.topAnchor.constraint(equalTo: topAnchor), - ]) - - for view in views { - view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true - view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true - view.delegate = comparisonDelegate - } - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func reloadData() { - guard let stackView = subviews[0] as? NSStackView else { - return - } - - for view in stackView.views { - if let box = view as? PreferencesBox { - box.reloadData() - } - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Folder/FolderViewBox.swift b/Sources/Features/Preferences/Main/Panels/Folder/FolderViewBox.swift deleted file mode 100644 index bc39855..0000000 --- a/Sources/Features/Preferences/Main/Panels/Folder/FolderViewBox.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// FolderViewBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FolderViewBox: PreferencesBox { - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let stackView = NSStackView.preferences(with: [ - createCheckBox( - title: NSLocalizedString("Expand All Folders After Session Load", comment: ""), - prefName: .expandAllFolders - ), - ]) - - if let contentView { - contentView.addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - // setting bottomAnchor isn't necessary to explicitly setting heightAnchor - stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - ]) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Folder/FoldersTraversalBox.swift b/Sources/Features/Preferences/Main/Panels/Folder/FoldersTraversalBox.swift deleted file mode 100644 index e3ac00b..0000000 --- a/Sources/Features/Preferences/Main/Panels/Folder/FoldersTraversalBox.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// FoldersTraversalBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FoldersTraversalBox: PreferencesBox { - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let stackView = NSStackView.preferences(with: [ - createCheckBox(title: NSLocalizedString("Follow Symbolic Links", comment: ""), prefName: .followSymLinks), - createCheckBox(title: NSLocalizedString("Skip Packages (Applications, Frameworks, ...)", comment: ""), prefName: .skipPackages), - createCheckBox(title: NSLocalizedString("Check Resource Forks", comment: ""), prefName: .virtualResourceFork), - createCheckBox(title: NSLocalizedString("Traverse the folders that match the 'File Filters'", comment: ""), prefName: .traverseFilteredFolders), - ]) - - if let contentView { - contentView.addSubview(stackView) - - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - - heightAnchor.constraint(equalToConstant: 120), - ]) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Font/FontBox.swift b/Sources/Features/Preferences/Main/Panels/Font/FontBox.swift deleted file mode 100644 index 4b77320..0000000 --- a/Sources/Features/Preferences/Main/Panels/Font/FontBox.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// FontBox.swift -// VisualDiffer -// -// Created by davide ficano on 15/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FontBox: PreferencesBox { - var previewLabel: String { - get { - preview.stringValue - } - - set { - preview.stringValue = newValue - } - } - - var previewFont: NSFont? { - get { - preview.font - } - - set { - if let font = newValue { - selectFont.isEnabled = true - preview.font = font - fontName.stringValue = String(format: "%@, %2.0fpt", font.displayName ?? "font", font.pointSize) - } else { - selectFont.isEnabled = false - } - } - } - - private lazy var selectFont: NSButton = createSelectFont() - private lazy var preview: NSTextField = createPreview() - private lazy var fontName: NSTextField = createFontName() - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(selectFont) - contentView.addSubview(preview) - contentView.addSubview(fontName) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - selectFont.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - selectFont.topAnchor.constraint(equalTo: contentView.topAnchor), - selectFont.widthAnchor.constraint(equalToConstant: 100), - - preview.leadingAnchor.constraint(equalTo: selectFont.trailingAnchor, constant: 20), - preview.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - preview.topAnchor.constraint(equalTo: contentView.topAnchor), - preview.heightAnchor.constraint(equalToConstant: 22), - - fontName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - fontName.topAnchor.constraint(equalTo: preview.bottomAnchor), - ]) - } - - private func createFontName() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = "" - view.alignment = .right - view.isBezeled = false - view.isBordered = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.isEditable = false - view.isSelectable = false - view.font = NSFont.menuFont(ofSize: 9) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createSelectFont() -> NSButton { - let view = NSButton(frame: .zero) - - view.title = NSLocalizedString("Select Font...", comment: "") - view.bezelStyle = .push - view.setButtonType(.momentaryPushIn) - view.isBordered = true - view.alignment = .center - view.font = NSFont.messageFont(ofSize: 11) - view.isEnabled = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(browseFont) - - return view - } - - private func createPreview() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = "" - view.isEditable = false - view.isSelectable = false - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - @objc func browseFont(_: AnyObject) { - if let font = preview.font { - NSFontManager.shared.setSelectedFont(font, isMultiple: false) - } - NSFontManager.shared.orderFrontFontPanel(self) - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Font/FontPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/Font/FontPreferencesPanel.swift deleted file mode 100644 index 353bc8d..0000000 --- a/Sources/Features/Preferences/Main/Panels/Font/FontPreferencesPanel.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// FontPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 15/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FontPreferencesPanel: NSView, NSFontChanging, PreferencesPanelDataSource { - private lazy var folderFontBox: FontBox = createFontBox( - title: NSLocalizedString("Folder Viewer Listing", comment: ""), - previewLabel: NSLocalizedString("filename.ext", comment: "") - ) - - private lazy var fileFontBox: FontBox = createFontBox( - title: NSLocalizedString("File Viewer Text", comment: ""), - previewLabel: NSLocalizedString("while (a < b) {}", comment: "") - ) - - private lazy var restoreDefaults: NSButton = createRestoreDefaults() - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - NSFontManager.shared.target = self - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(folderFontBox) - addSubview(fileFontBox) - addSubview(restoreDefaults) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - folderFontBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - folderFontBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - folderFontBox.topAnchor.constraint(equalTo: topAnchor, constant: 5), - folderFontBox.heightAnchor.constraint(equalToConstant: 80), - - fileFontBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - fileFontBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - fileFontBox.topAnchor.constraint(equalTo: folderFontBox.bottomAnchor, constant: 5), - fileFontBox.heightAnchor.constraint(equalToConstant: 80), - - restoreDefaults.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - restoreDefaults.topAnchor.constraint(equalTo: fileFontBox.bottomAnchor, constant: 5), - ]) - } - - private func createFontBox(title: String, previewLabel: String) -> FontBox { - let view = FontBox(title: title) - - view.previewLabel = previewLabel - - return view - } - - private func createRestoreDefaults() -> NSButton { - let view = NSButton(frame: .zero) - - view.title = NSLocalizedString("Defaults", comment: "") - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(restoreDefaultsAction) - - return view - } - - @objc func restoreDefaultsAction(_: AnyObject) { - CommonPrefs.shared.restoreDefaultFonts() - reloadData() - } - - @objc func changeFont(_ sender: NSFontManager?) { - guard let sender, - let selectedFont = sender.selectedFont else { - return - } - let newFont = sender.convert(selectedFont) - - if selectedFont == fileFontBox.previewFont { - CommonPrefs.shared.fileTextFont = newFont - fileFontBox.previewFont = newFont - } else if selectedFont == folderFontBox.previewFont { - CommonPrefs.shared.folderListingFont = newFont - folderFontBox.previewFont = newFont - } - } - - func reloadData() { - folderFontBox.previewFont = CommonPrefs.shared.folderListingFont - fileFontBox.previewFont = CommonPrefs.shared.fileTextFont - } -} diff --git a/Sources/Features/Preferences/Main/Panels/General/AppearanceBox.swift b/Sources/Features/Preferences/Main/Panels/General/AppearanceBox.swift deleted file mode 100644 index d847bba..0000000 --- a/Sources/Features/Preferences/Main/Panels/General/AppearanceBox.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// AppearanceBox.swift -// VisualDiffer -// -// Created by davide ficano on 16/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class AppearanceBox: PreferencesBox { - private lazy var appearancePopup: NSPopUpButton = createAppearancePopup() - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - contentView?.addSubview(appearancePopup) - - setupConstraints() - } - - private func createAppearancePopup() -> NSPopUpButton { - let menu = NSMenu() - - menu.addItem(withTitle: NSLocalizedString("System", comment: ""), action: nil, keyEquivalent: "") - menu.addItem(NSMenuItem.separator()) - menu.addItem(withTitle: NSLocalizedString("Light", comment: ""), action: nil, keyEquivalent: "").tag = 1 - menu.addItem(withTitle: NSLocalizedString("Dark", comment: ""), action: nil, keyEquivalent: "").tag = 2 - - let view = NSPopUpButton(frame: .zero) - view.menu = menu - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(updateAppearance) - - return view - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - appearancePopup.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5), - appearancePopup.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), - appearancePopup.widthAnchor.constraint(greaterThanOrEqualToConstant: 140), - ]) - } - - @objc func updateAppearance(_: AnyObject) { - if #available(macOS 10.14, *) { - if !alertForCustomColorScheme() { - // restore the previous selection - appearancePopup.selectItem(withTag: UserDefaults.standard.integer(forKey: "appAppearance")) - return - } - - guard let tag = appearancePopup.selectedItem?.tag else { - return - } - switch tag { - case 0: - if UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" { - NSApp.appearance = NSAppearance(named: NSAppearance.Name.darkAqua) - } else { - NSApp.appearance = nil - } - UserDefaults.standard.removeObject(forKey: "appAppearance") - case 1: - NSApp.appearance = NSAppearance(named: NSAppearance.Name.aqua) - UserDefaults.standard.setValue(tag, forKey: "appAppearance") - case 2: - NSApp.appearance = NSAppearance(named: NSAppearance.Name.darkAqua) - UserDefaults.standard.setValue(tag, forKey: "appAppearance") - default: - break - } - } - } - - private func alertForCustomColorScheme() -> Bool { - if CommonPrefs.shared.colorsConfigPath == nil { - return true - } - return NSAlert.showModalConfirm( - messageText: NSLocalizedString("The app is using a custom color scheme that will continue to be used when changing appearance. Colors may not be suitable for the new appearance. Are you sure?", comment: ""), - informativeText: "" - ) - } - - override func reloadData() { - if #available(macOS 10.14, *) { - appearancePopup.selectItem(withTag: UserDefaults.standard.integer(forKey: "appAppearance")) - } else { - appearancePopup.isEnabled = false - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/General/FolderComparisonBox.swift b/Sources/Features/Preferences/Main/Panels/General/FolderComparisonBox.swift deleted file mode 100644 index d112331..0000000 --- a/Sources/Features/Preferences/Main/Panels/General/FolderComparisonBox.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// FolderComparisonBox.swift -// VisualDiffer -// -// Created by davide ficano on 17/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FolderComparisonBox: PreferencesBox { - private lazy var comparisonPopup: NSPopUpButton = createComparisonPopup() - - private lazy var displayFiltersPopup: NSPopUpButton = createDisplayFiltersPopup() - - private lazy var finderLabelCheckButton: PreferencesCheckbox = { - let view = PreferencesCheckbox( - title: NSLocalizedString("Compare Finder Label", comment: ""), - prefName: .virtualFinderLabel - ) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var finderTagsCheckButton: PreferencesCheckbox = { - let view = PreferencesCheckbox( - title: NSLocalizedString("Compare Finder Tags", comment: ""), - prefName: .virtualFinderTags - ) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var timeToleranceView: TimeToleranceView = { - let view = TimeToleranceView(frame: .zero) - - view.onTextChanged = { (sender: TimeToleranceView) in - self.delegate?.preferenceBox( - self, - setInteger: sender.tolerance, - forKey: .timestampToleranceSeconds - ) - } - - return view - }() - - private lazy var stackView: NSStackView = .preferences(with: [ - comparisonPopup, - finderLabelCheckButton, - finderTagsCheckButton, - timeToleranceView, - displayFiltersPopup, - ]) - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - setupCheckBox(finderTagsCheckButton) - setupCheckBox(finderLabelCheckButton) - - contentView?.addSubview(stackView) - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - // setting bottomAnchor isn't necessary to explicitly setting heightAnchor - stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - ]) - - for view in stackView.views { - view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true - view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true - } - - timeToleranceView.heightAnchor.constraint(equalToConstant: 20).isActive = true - } - - private func createComparisonPopup() -> NSPopUpButton { - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.cell = ComparatorPopUpButtonCell(textCell: "") - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(updateComparatorFlags) - - return view - } - - private func createDisplayFiltersPopup() -> NSPopUpButton { - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.cell = DisplayFiltersPopUpButtonCell(textCell: "") - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(updateDisplayFilters) - - return view - } - - @objc func updateComparatorFlags(_: AnyObject) { - guard let comparatorFlags = comparisonPopup.selectedItem?.tag else { - return - } - delegate?.preferenceBox( - self, - setInteger: comparatorFlags, - forKey: .virtualComparatorWithoutMethod - ) - updateTolerance(comparatorFlags, makeFirstResponder: true) - } - - @objc func updateDisplayFilters(_: AnyObject) { - guard let tag = displayFiltersPopup.selectedItem?.tag else { - return - } - delegate?.preferenceBox( - self, - setInteger: tag, - forKey: .virtualDisplayFiltersWithoutMethod - ) - } - - @objc override func toggleCheckbox(_ sender: PreferencesCheckbox) { - super.toggleCheckbox(sender) - - if sender.prefName == .virtualFinderLabel { - // Disable tags - finderTagsCheckButton.isEnabled = sender.state == .off - } else if sender.prefName == .virtualFinderTags { - // Disable label - finderLabelCheckButton.isEnabled = sender.state == .off - } - } - - private func updateTolerance( - _ comparatorOptions: Int, - makeFirstResponder: Bool - ) { - let supportTolerance = ComparatorOptions(rawValue: comparatorOptions).contains(.timestamp) - timeToleranceView.isHidden = !supportTolerance - - if supportTolerance, makeFirstResponder { - timeToleranceView.becomeFirstResponder() - } - } - - override func reloadData() { - super.reloadData() - - guard let delegate else { - return - } - - let comparatorOptions = delegate.preferenceBox(self, integerForKey: .virtualComparatorWithoutMethod) - comparisonPopup.selectItem(withTag: comparatorOptions) - updateTolerance(comparatorOptions, makeFirstResponder: false) - - let tag = delegate.preferenceBox(self, integerForKey: .virtualDisplayFiltersWithoutMethod) - displayFiltersPopup.selectItem(withTag: tag) - - let tolerance = delegate.preferenceBox(self, integerForKey: .timestampToleranceSeconds) - timeToleranceView.tolerance = tolerance - } -} diff --git a/Sources/Features/Preferences/Main/Panels/General/GeneralPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/General/GeneralPreferencesPanel.swift deleted file mode 100644 index 9330a9b..0000000 --- a/Sources/Features/Preferences/Main/Panels/General/GeneralPreferencesPanel.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// GeneralPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 16/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class GeneralPreferencesPanel: NSView, PreferencesPanelDataSource { - private var appearanceBox: AppearanceBox - private var preferredEditorBox: PreferredEditorBox - private var comparisonBox: FolderComparisonBox - - private var comparisonDelegate: ComparisonStandardUserDataSource - - override init(frame frameRect: NSRect) { - comparisonDelegate = ComparisonStandardUserDataSource() - - appearanceBox = AppearanceBox(title: NSLocalizedString("Appearance", comment: "")) - - comparisonBox = FolderComparisonBox( - title: NSLocalizedString("Comparison and Display Defaults for New Folder Documents", comment: "") - ) - comparisonBox.delegate = comparisonDelegate - - preferredEditorBox = PreferredEditorBox(title: NSLocalizedString("Preferred Viewer/Editor", comment: "")) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(appearanceBox) - addSubview(comparisonBox) - addSubview(preferredEditorBox) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - appearanceBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - appearanceBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - appearanceBox.topAnchor.constraint(equalTo: topAnchor, constant: 5), - appearanceBox.heightAnchor.constraint(equalToConstant: 60), - - comparisonBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - comparisonBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - comparisonBox.topAnchor.constraint(equalTo: appearanceBox.bottomAnchor, constant: 5), - comparisonBox.heightAnchor.constraint(equalToConstant: 160), - - preferredEditorBox.topAnchor.constraint(equalTo: comparisonBox.bottomAnchor, constant: 5), - preferredEditorBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - preferredEditorBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - preferredEditorBox.heightAnchor.constraint(equalToConstant: 80), - ]) - } - - func reloadData() { - appearanceBox.reloadData() - comparisonBox.reloadData() - } -} diff --git a/Sources/Features/Preferences/Main/Panels/General/PreferredEditorBox.swift b/Sources/Features/Preferences/Main/Panels/General/PreferredEditorBox.swift deleted file mode 100644 index 4a8a9a2..0000000 --- a/Sources/Features/Preferences/Main/Panels/General/PreferredEditorBox.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// PreferredEditorBox.swift -// VisualDiffer -// -// Created by davide ficano on 16/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class PreferredEditorBox: PreferencesBox { - private lazy var editorPopup: NSPopUpButton = createEditorPopup() - private lazy var removeButton: NSButton = createRemoveButton() - private lazy var scriptLinkButton: NSButton = createScriptLinkButton() - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - contentView?.addSubview(editorPopup) - contentView?.addSubview(removeButton) - contentView?.addSubview(scriptLinkButton) - - setupConstraints() - } - - private func createEditorPopup() -> NSPopUpButton { - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.cell = PreferredEditorPopupCell(textCell: "") - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createRemoveButton() -> NSButton { - let view = NSButton(frame: .zero) - - view.title = NSLocalizedString("Remove", comment: "") - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = editorPopup.cell - view.action = #selector(PreferredEditorPopupCell.removePreferredEditor) - - return view - } - - private func createScriptLinkButton() -> NSButton { - let view = LinkButton( - title: NSLocalizedString("Open Script Folder", comment: ""), - target: self, - action: #selector(openScriptFolder) - ) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func setupConstraints() { - guard let contentView else { - return - } - - NSLayoutConstraint.activate([ - editorPopup.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5), - editorPopup.trailingAnchor.constraint(equalTo: removeButton.leadingAnchor, constant: -5), - editorPopup.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), - - removeButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - removeButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), - - scriptLinkButton.leadingAnchor.constraint(equalTo: editorPopup.leadingAnchor), - scriptLinkButton.topAnchor.constraint(equalTo: editorPopup.bottomAnchor, constant: 5), - ]) - } - - @objc func openScriptFolder(_: AnyObject) { - guard let folder = try? FileManager.default.url( - for: .applicationScriptsDirectory, - in: .userDomainMask, - appropriateFor: nil, - create: false - ) else { - return - } - - NSWorkspace.shared.show(inFinder: [folder.osPath]) - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Keyboard/KeyboardDocumentBox.swift b/Sources/Features/Preferences/Main/Panels/Keyboard/KeyboardDocumentBox.swift deleted file mode 100644 index f4f66ee..0000000 --- a/Sources/Features/Preferences/Main/Panels/Keyboard/KeyboardDocumentBox.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// KeyboardDocumentBox.swift -// VisualDiffer -// -// Created by davide ficano on 23/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class KeyboardDocumentBox: PreferencesBox { - private let stackView: NSStackView - - override init(title: String) { - stackView = NSStackView.preferencesStackView() - - super.init(title: title) - - setupViews() - } - - private func setupViews() { - stackView.addArrangedSubview( - createCheckBox( - title: NSLocalizedString("ESC key closes documents/application", comment: ""), - prefName: .escCloseWindow - ) - ) - contentView?.addSubview(stackView) - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - - heightAnchor.constraint(equalToConstant: 80), - ]) - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Keyboard/KeyboardPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/Keyboard/KeyboardPreferencesPanel.swift deleted file mode 100644 index 78c8633..0000000 --- a/Sources/Features/Preferences/Main/Panels/Keyboard/KeyboardPreferencesPanel.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// KeyboardPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 19/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class KeyboardPreferencesPanel: NSView, PreferencesPanelDataSource { - private var documentBox: KeyboardDocumentBox - - override init(frame frameRect: NSRect) { - documentBox = KeyboardDocumentBox(title: NSLocalizedString("Document", comment: "")) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(documentBox) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - documentBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - documentBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - documentBox.topAnchor.constraint(equalTo: topAnchor, constant: 5), - ]) - } - - func reloadData() { - documentBox.reloadData() - } -} diff --git a/Sources/Features/Preferences/Main/Panels/NSStackView+PreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/NSStackView+PreferencesPanel.swift deleted file mode 100644 index 0be1efd..0000000 --- a/Sources/Features/Preferences/Main/Panels/NSStackView+PreferencesPanel.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// NSStackView+PreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 18/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSStackView { - func setupPreferences() { - orientation = .vertical - alignment = .leading - spacing = 8 - edgeInsets = NSEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) - distribution = .fill - translatesAutoresizingMaskIntoConstraints = false - } - - static func preferencesStackView() -> NSStackView { - let view = NSStackView(frame: .zero) - - view.setupPreferences() - - return view - } - - static func preferences(with views: [NSView]) -> NSStackView { - let view = NSStackView(views: views) - - view.setupPreferences() - - return view - } - - func addArrangedSubviews(_ views: [NSView]) { - for view in views { - addArrangedSubview(view) - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Text/FileComparisonBox.swift b/Sources/Features/Preferences/Main/Panels/Text/FileComparisonBox.swift deleted file mode 100644 index 35618fd..0000000 --- a/Sources/Features/Preferences/Main/Panels/Text/FileComparisonBox.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// FileComparisonBox.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FileComparisonBox: PreferencesBox { - private let stackView: NSStackView - - override init(title: String) { - stackView = NSStackView.preferencesStackView() - - super.init(title: title) - - setupViews() - } - - private func setupViews() { - let checkboxes: [(String, CommonPrefs.Name)] = [ - (NSLocalizedString("Ignore Line Endings (DOS/Mac)", comment: ""), .ignoreLineEndings), - (NSLocalizedString("Ignore Leading Whitespace", comment: ""), .ignoreLeadingWhitespaces), - (NSLocalizedString("Ignore Trailing Whitespace", comment: ""), .ignoreTrailingWhitespaces), - (NSLocalizedString("Ignore Internal Whitespace", comment: ""), .ignoreInternalWhitespaces), - (NSLocalizedString("Ignore Character Case", comment: ""), .ignoreCharacterCase), - ] - - for (title, prefName) in checkboxes { - let view = PreferencesCheckbox(title: title, prefName: prefName) - view.translatesAutoresizingMaskIntoConstraints = false - - setupCheckBox(view) - - stackView.addArrangedSubview(view) - } - - contentView?.addSubview(stackView) - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - - NSLayoutConstraint.activate([ - stackView.topAnchor.constraint(equalTo: contentView.topAnchor), - stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - // setting bottomAnchor isn't necessary to explicitly setting heightAnchor - stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - ]) - - for view in stackView.views { - view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true - view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true - } - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Text/TextPreferencesPanel+PreferencesBoxDataSource.swift b/Sources/Features/Preferences/Main/Panels/Text/TextPreferencesPanel+PreferencesBoxDataSource.swift deleted file mode 100644 index be11e55..0000000 --- a/Sources/Features/Preferences/Main/Panels/Text/TextPreferencesPanel+PreferencesBoxDataSource.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// TextPreferencesPanel+PreferencesBoxDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension TextPreferencesPanel: @preconcurrency PreferencesBoxDataSource { - func preferenceBox(_: PreferencesBox, boolForKey key: CommonPrefs.Name) -> Bool { - switch key { - case .ignoreLineEndings, - .ignoreLeadingWhitespaces, - .ignoreTrailingWhitespaces, - .ignoreInternalWhitespaces, - .ignoreCharacterCase: - CommonPrefs.shared.bool(forKey: key) - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, setBool value: Bool, forKey key: CommonPrefs.Name) { - switch key { - case .ignoreLineEndings, - .ignoreLeadingWhitespaces, - .ignoreTrailingWhitespaces, - .ignoreInternalWhitespaces, - .ignoreCharacterCase: - CommonPrefs.shared.set(value, forKey: key) - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, isEnabled _: CommonPrefs.Name) -> Bool { - true - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Text/TextPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/Text/TextPreferencesPanel.swift deleted file mode 100644 index b7d16d8..0000000 --- a/Sources/Features/Preferences/Main/Panels/Text/TextPreferencesPanel.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// TextPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 17/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class TextPreferencesPanel: NSView, PreferencesPanelDataSource { - private var visualizationBox = VisualizationBox( - title: NSLocalizedString("Visualization", comment: "") - ) - private var fileComparisonBox = FileComparisonBox( - title: NSLocalizedString("Comparison for New File Documents", comment: "") - ) - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - fileComparisonBox.delegate = self - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(visualizationBox) - addSubview(fileComparisonBox) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - visualizationBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - visualizationBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - visualizationBox.topAnchor.constraint(equalTo: topAnchor, constant: 5), - visualizationBox.heightAnchor.constraint(equalToConstant: 80), - - fileComparisonBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - fileComparisonBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - fileComparisonBox.topAnchor.constraint(equalTo: visualizationBox.bottomAnchor, constant: 5), - fileComparisonBox.heightAnchor.constraint(equalToConstant: 160), - ]) - } - - func reloadData() { - visualizationBox.reloadData() - fileComparisonBox.reloadData() - } -} diff --git a/Sources/Features/Preferences/Main/Panels/Text/VisualizationBox.swift b/Sources/Features/Preferences/Main/Panels/Text/VisualizationBox.swift deleted file mode 100644 index 366a2a7..0000000 --- a/Sources/Features/Preferences/Main/Panels/Text/VisualizationBox.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// VisualizationBox.swift -// VisualDiffer -// -// Created by davide ficano on 26/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -class VisualizationBox: PreferencesBox, NSTextFieldDelegate { - private lazy var tabWidthTitle: NSTextField = createTabWidthTitle() - private lazy var tabWidthInput: NSTextField = createTabWidthInput() - private lazy var tabWidthStepper: NSStepper = createTabWidthStepper() - - var tabWidth = 0 { - didSet { - CommonPrefs.shared.tabWidth = tabWidth - tabWidthStepper.integerValue = tabWidth - tabWidthInput.integerValue = tabWidth - } - } - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - func setupViews() { - if let contentView { - contentView.addSubview(tabWidthTitle) - contentView.addSubview(tabWidthInput) - contentView.addSubview(tabWidthStepper) - } - - setupConstraints() - } - - private func createTabWidthTitle() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = NSLocalizedString("Tab Width", comment: "") - view.isEditable = false - view.isSelectable = false - view.drawsBackground = false - view.isBordered = false - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createTabWidthInput() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.alignment = .right - view.translatesAutoresizingMaskIntoConstraints = false - view.cell?.sendsActionOnEndEditing = true - view.formatter = IntegerFormatter() - view.delegate = self - - return view - } - - private func createTabWidthStepper() -> NSStepper { - let view = NSStepper(frame: .zero) - - view.autorepeat = true - view.minValue = 1 - view.maxValue = 100 - view.increment = 1 - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(tabWidthStepperChanged) - - return view - } - - func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - tabWidthTitle.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5), - tabWidthTitle.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), - - tabWidthInput.leadingAnchor.constraint(equalTo: tabWidthTitle.trailingAnchor, constant: 2), - tabWidthInput.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), - tabWidthInput.widthAnchor.constraint(equalToConstant: 40), - - tabWidthStepper.leadingAnchor.constraint(equalTo: tabWidthInput.trailingAnchor, constant: 2), - tabWidthStepper.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), - ]) - } - - @objc func tabWidthStepperChanged(_: AnyObject) { - tabWidth = tabWidthStepper.integerValue - } - - func controlTextDidChange(_: Notification) { - tabWidth = tabWidthInput.integerValue - } - - func control(_: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - // arrow up and down are used to increment and decrement current value - if commandSelector == #selector(moveUp) { - tabWidth += 1 - textView.selectAll(nil) - return true - } else if commandSelector == #selector(moveDown) { - if tabWidth > 0 { - tabWidth -= 1 - } - textView.selectAll(nil) - return true - } - return false - } - - override func reloadData() { - tabWidth = CommonPrefs.shared.tabWidth - } -} diff --git a/Sources/Features/Preferences/Main/Panels/TrustedPaths/TrustedPathsPreferencesPanel.swift b/Sources/Features/Preferences/Main/Panels/TrustedPaths/TrustedPathsPreferencesPanel.swift deleted file mode 100644 index a30b535..0000000 --- a/Sources/Features/Preferences/Main/Panels/TrustedPaths/TrustedPathsPreferencesPanel.swift +++ /dev/null @@ -1,341 +0,0 @@ -// -// TrustedPathsPreferencesPanel.swift -// VisualDiffer -// -// Created by davide ficano on 16/08/15. -// Copyright (c) 2015 visualdiffer.com -// - -class TrustedPathsPreferencesPanel: NSView, NSTableViewDataSource, NSTableViewDelegate, - TableViewCommonDelegate, PreferencesPanelDataSource { - private lazy var title: NSTextField = { - let view = NSTextField(frame: .zero) - - view.stringValue = NSLocalizedString( - "Access to paths without showing file selection dialog every time", - comment: "" - ) - view.font = NSFont.boldSystemFont(ofSize: NSFont.smallSystemFontSize) - view.isBordered = false - view.isBezeled = false - view.isEnabled = false - view.isSelectable = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var subTitle: NSTextField = { - let view = NSTextField(frame: .zero) - - view.stringValue = NSLocalizedString("Click + or drag a folder onto the list", comment: "") - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - view.isBordered = false - view.isBezeled = false - view.isEnabled = false - view.isSelectable = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var pathTableScrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = pathTableView - - return view - }() - - private lazy var pathTableView: NSTableView = { - let view = TableViewCommon(frame: .zero) - - view.allowsEmptySelection = true - view.allowsColumnReordering = false - view.allowsColumnResizing = true - view.allowsMultipleSelection = true - view.allowsColumnSelection = true - view.allowsTypeSelect = true - view.usesAlternatingRowBackgroundColors = true - - view.focusRingType = .none - view.allowsExpansionToolTips = true - view.columnAutoresizingStyle = .uniformColumnAutoresizingStyle - view.autosaveTableColumns = false - view.intercellSpacing = .zero - view.headerView = nil - - // Enable dragging on NSTableView - view.registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL]) - view.setDraggingSourceOperationMask(.every, forLocal: true) - view.setDraggingSourceOperationMask(.every, forLocal: false) - - // the column is full width and it resizes properly when the table changes dimensions - // see https://stackoverflow.com/a/15390614/195893 - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Paths")) - column.resizingMask = .autoresizingMask - view.addTableColumn(column) - view.sizeLastColumnToFit() - - view.delegate = self - view.dataSource = self - - return view - }() - - private lazy var actionBar: ActionBarView = { - let view = ActionBarView(frame: .zero) - - view.firstButton.toolTip = NSLocalizedString("Add a folder to trusted paths", comment: "") - - view.secondButton.toolTip = NSLocalizedString( - "Remove selected items from the list", - comment: "" - ) - - view.firstButton.target = self - view.firstButton.action = #selector(choosePaths) - - view.secondButton.target = self - view.secondButton.action = #selector(removePaths) - - if let menu = view.popup.menu { - menu.addItem( - withTitle: NSLocalizedString("Select invalid paths", comment: ""), - action: #selector(selectInvalidPaths), - keyEquivalent: "" - ).target = self - } - view.popup.target = self - - return view - }() - - var trustedPaths = [String]() - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - addSubview(title) - addSubview(subTitle) - addSubview(pathTableScrollView) - addSubview(actionBar) - - setupConstraints() - } - - func setupConstraints() { - NSLayoutConstraint.activate([ - title.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - title.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - title.topAnchor.constraint(equalTo: topAnchor, constant: 5), - - subTitle.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - subTitle.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - subTitle.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 2), - - pathTableScrollView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - pathTableScrollView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - pathTableScrollView.topAnchor.constraint(equalTo: subTitle.bottomAnchor, constant: 2), - pathTableScrollView.heightAnchor.constraint(equalToConstant: 200), - pathTableScrollView.bottomAnchor.constraint(equalTo: actionBar.topAnchor, constant: -2), - - actionBar.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - actionBar.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5), - ]) - } - - /** - * If the array doesn't contain @path, adds it at index position or at the end - * Return true if the passed array contains the path, false otherwise - */ - private func insert(path: URL, destArray arr: inout [String]) -> (Bool, Int) { - var found = false - let index = arr.firstIndex { - let result = $0.caseInsensitiveCompare(path.path) - if result == .orderedDescending { - return true - } else if result == .orderedSame { - found = true - return true - } - return false - } - - var insertIndex = 0 - if !found { - if let index { - arr.insert(path.path, at: index) - insertIndex = index - } else { - arr.append(path.path) - insertIndex = arr.count - 1 - } - } - return (found, insertIndex) - } - - private func insert(paths: [URL]) { - var indexSet = IndexSet() - - for path in paths { - let (containsItem, index) = insert(path: path, destArray: &trustedPaths) - indexSet.insert(index) - if !containsItem { - SecureBookmark.shared.add(path, searchClosestPath: false) - } - } - if let index = indexSet.first { - pathTableView.reloadData() - pathTableView.selectRowIndexes(indexSet, byExtendingSelection: false) - pathTableView.scrollRowToVisible(index) - } - } - - @objc func choosePaths(_: AnyObject) { - let openPanel = NSOpenPanel() - openPanel.title = NSLocalizedString("Select Trusted Paths", comment: "") - // since 10.11 the title is no longer shown so we use the message property - openPanel.message = NSLocalizedString("Select Trusted Paths", comment: "") - openPanel.canChooseDirectories = true - openPanel.canChooseFiles = true - openPanel.allowsMultipleSelection = true - - if openPanel.runModal() == .OK { - insert(paths: openPanel.urls) - } - - // if modal returns NSModalResponseCancel the window is no longer if front - pathTableView.window?.makeKeyAndOrderFront(nil) - pathTableView.window?.makeFirstResponder(pathTableView) - } - - @objc func removePaths(_: AnyObject) { - let selectedRows = pathTableView.selectedRowIndexes - var paths = [String]() - - for row in selectedRows.reversed() { - paths.append(trustedPaths[row]) - trustedPaths.remove(at: row) - } - SecureBookmark.shared.removePaths(paths) - - pathTableView.reloadData() - pathTableView.deselectAll(nil) - pathTableView.window?.makeFirstResponder(pathTableView) - } - - @objc func selectInvalidPaths(_: AnyObject) { - let fm = FileManager.default - var indexSet = IndexSet() - - for (idx, path) in trustedPaths.enumerated() where !fm.fileExists(atPath: path) { - indexSet.insert(idx) - } - pathTableView.selectRowIndexes(indexSet, byExtendingSelection: false) - } - - func reloadData() { - guard let arr = SecureBookmark.shared.securedPaths?.keys else { - return - } - trustedPaths = arr.map(\.self) - trustedPaths.sort { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending } - - pathTableView.reloadData() - } - - // MARK: - NSTableView data source messages - - func numberOfRows(in _: NSTableView) -> Int { - trustedPaths.count - } - - func tableView( - _ tableView: NSTableView, - viewFor tableColumn: NSTableColumn?, - row: Int - ) -> NSView? { - guard let identifier = tableColumn?.identifier else { - return nil - } - let cell = tableView.makeView( - withIdentifier: identifier, - owner: self - ) as? FilePathTableCellView ?? - FilePathTableCellView(identifier: identifier) - - cell.update(path: trustedPaths[row]) - - return cell - } - - func tableView( - _ tableView: NSTableView, - validateDrop _: any NSDraggingInfo, - proposedRow _: Int, - proposedDropOperation _: NSTableView.DropOperation - ) -> NSDragOperation { - tableView.setDropRow(-1, dropOperation: .on) - return .copy - } - - func tableView( - _: NSTableView, - acceptDrop info: any NSDraggingInfo, - row _: Int, - dropOperation _: NSTableView.DropOperation - ) -> Bool { - let pasteboard = info.draggingPasteboard - - guard pasteboard.availableType(from: [NSPasteboard.PasteboardType.fileURL]) != nil, - let arr = pasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] - else { - return false - } - - insert(paths: arr) - return true - } - - // To receive keydown declare the table class as TOPTableViewCommon inside the XIB definition - func tableViewCommonKeyDown(_ tableView: NSTableView, event: NSEvent) -> Bool { - if event.isDeleteShortcutKey(true) { - let rowIndexes = tableView.selectedRowIndexes - if let index = rowIndexes.first { - removePaths(tableView) - tableView.selectRow( - closestTo: index, - byExtendingSelection: false, - ensureVisible: true - ) - } - return true - } - return false - } -} diff --git a/Sources/Features/Preferences/PreferencesPanelDataSource.swift b/Sources/Features/Preferences/PreferencesPanelDataSource.swift deleted file mode 100644 index 8717597..0000000 --- a/Sources/Features/Preferences/PreferencesPanelDataSource.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// PreferencesPanelDataSource.swift -// VisualDiffer -// -// Created by davide ficano on 18/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -protocol PreferencesPanelDataSource { - @MainActor func reloadData() -} diff --git a/Sources/Features/Preferences/Session/Controllers/SessionPreferencesWindow+Data.swift b/Sources/Features/Preferences/Session/Controllers/SessionPreferencesWindow+Data.swift deleted file mode 100644 index 6836350..0000000 --- a/Sources/Features/Preferences/Session/Controllers/SessionPreferencesWindow+Data.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// SessionPreferencesWindow+Data.swift -// VisualDiffer -// -// Created by davide ficano on 27/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension SessionPreferencesWindow { - struct Data { - var comparatorOptions: ComparatorOptions - var displayOptions: DisplayOptions - var followSymLinks: Bool - var skipPackages: Bool - var traverseFilteredFolders: Bool - var timestampToleranceSeconds: Int - var expandAllFolders: Bool - var fileExtraOptions: FileExtraOptions - var fileFilters: String? - var alignRules: [AlignRule] - - init() { - comparatorOptions = [] - displayOptions = [] - followSymLinks = false - skipPackages = false - traverseFilteredFolders = false - timestampToleranceSeconds = 0 - expandAllFolders = false - fileExtraOptions = [] - fileFilters = nil - alignRules = [AlignRule]() - } - } -} - -extension SessionPreferencesWindow.Data { - func updateSessionDiff(_ sessionDiff: SessionDiff) { - sessionDiff.comparatorOptions = comparatorOptions - sessionDiff.displayOptions = displayOptions - sessionDiff.followSymLinks = followSymLinks - sessionDiff.timestampToleranceSeconds = Double(timestampToleranceSeconds) - sessionDiff.exclusionFileFilters = fileFilters - sessionDiff.skipPackages = skipPackages - sessionDiff.fileExtraOptions = fileExtraOptions - sessionDiff.traverseFilteredFolders = traverseFilteredFolders - sessionDiff.fileNameAlignments = alignRules - sessionDiff.expandAllFolders = expandAllFolders - } - - static func fromUserDefaults() -> SessionPreferencesWindow.Data { - var data = SessionPreferencesWindow.Data() - - data.comparatorOptions = CommonPrefs.shared.comparatorOptions - data.displayOptions = CommonPrefs.shared.displayOptions - data.followSymLinks = CommonPrefs.shared.followSymLinks - data.skipPackages = CommonPrefs.shared.bool(forKey: .skipPackages) - data.traverseFilteredFolders = CommonPrefs.shared.bool(forKey: .traverseFilteredFolders) - data.timestampToleranceSeconds = CommonPrefs.shared.integer(forKey: .timestampToleranceSeconds) - data.expandAllFolders = CommonPrefs.shared.bool(forKey: .expandAllFolders) - data.fileExtraOptions = CommonPrefs.shared.fileExtraOptions - data.fileFilters = SessionDiff.defaultFileFilters() - data.alignRules = [AlignRule]() - - return data - } - - static func fromSessionDiff(_ sessionDiff: SessionDiff) -> SessionPreferencesWindow.Data { - var data = SessionPreferencesWindow.Data() - - data.comparatorOptions = sessionDiff.comparatorOptions - data.displayOptions = sessionDiff.displayOptions - data.followSymLinks = sessionDiff.followSymLinks - data.skipPackages = sessionDiff.skipPackages - data.traverseFilteredFolders = sessionDiff.traverseFilteredFolders - data.timestampToleranceSeconds = Int(sessionDiff.timestampToleranceSeconds) - data.expandAllFolders = sessionDiff.expandAllFolders - data.fileExtraOptions = sessionDiff.fileExtraOptions - data.fileFilters = sessionDiff.exclusionFileFilters - data.alignRules = if let fileNameAlignments = sessionDiff.fileNameAlignments { - fileNameAlignments - } else { - [AlignRule]() - } - - return data - } -} diff --git a/Sources/Features/Preferences/Session/Controllers/SessionPreferencesWindow.swift b/Sources/Features/Preferences/Session/Controllers/SessionPreferencesWindow.swift deleted file mode 100644 index 3643aa2..0000000 --- a/Sources/Features/Preferences/Session/Controllers/SessionPreferencesWindow.swift +++ /dev/null @@ -1,377 +0,0 @@ -// -// SessionPreferencesWindow.swift -// VisualDiffer -// -// Created by davide ficano on 07/03/13. -// Copyright (c) 2013 visualdiffer.com -// - -class SessionPreferencesWindow: NSWindowController, NSTabViewDelegate, @preconcurrency PreferencesBoxDataSource { - private lazy var tabView: NSTabView = createTabView() - private lazy var standardButtons: StandardButtons = .init( - primaryTitle: NSLocalizedString("OK", comment: ""), - secondaryTitle: NSLocalizedString("Cancel", comment: ""), - target: self, - action: #selector(closeSessionSettingsSheet) - ) - - private var loadedTabs = Set() - private var currentPreferences = Data() - - // used to retrieve pending data not updated on every modification - private var sessionPreferencesFiltersPanel: SessionPreferencesFiltersPanel? - - // TabView Identifiers - private static let comparisonPanelIdentifier = NSUserInterfaceItemIdentifier("Comparison") - private static let fileFiltersPanelIdentifier = NSUserInterfaceItemIdentifier("FileFilters") - private static let alignmentPanelIdentifier = NSUserInterfaceItemIdentifier("Alignment") - - required init() { - super.init(window: Self.createPanel()) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - if let contentView = window?.contentView { - contentView.addSubview(tabView) - contentView.addSubview(standardButtons) - } - - setupConstraints() - } - - private static func createPanel() -> NSPanel { - let styleMask: NSWindow.StyleMask = [ - .titled, - .closable, - .miniaturizable, - .resizable, - ] - - let view = NSPanel( - contentRect: NSRect(x: 0, y: 0, width: 500, height: 400), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - view.hasShadow = true - view.isRestorable = true - view.setFrameAutosaveName("sessionPreferencesFrame") - view.minSize = NSSize(width: 500, height: 400) - - return view - } - - private func createTabView() -> NSTabView { - let view = NSTabView(frame: .zero) - - view.tabViewType = .topTabsBezelBorder - view.allowsTruncatedLabels = false - view.drawsBackground = true - view.translatesAutoresizingMaskIntoConstraints = false - - view.delegate = self - - let tabInfo = [ - (Self.comparisonPanelIdentifier, NSLocalizedString("Comparison", comment: "")), - (Self.fileFiltersPanelIdentifier, NSLocalizedString("File Filters", comment: "")), - (Self.alignmentPanelIdentifier, NSLocalizedString("Alignment", comment: "")), - ] - - for (identifier, label) in tabInfo { - let item = NSTabViewItem(identifier: identifier) - item.label = label - view.addTabViewItem(item) - } - - return view - } - - private func setupConstraints() { - guard let contentView = window?.contentView else { - return - } - NSLayoutConstraint.activate([ - tabView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - tabView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), - tabView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), - - // very bad approach because I'm unable to have the tabView fills the height and place the button below it. - // Problem: If I place the button below the tabView when I change tab the button resizes vertically instead of stay fixed - // Workaround: I move the tabView to parent bottom and reduce its size (i.e. -40) - tabView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -40), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - func beginSheet( - _ sheetWindow: NSWindow, - sessionDiff: SessionDiff?, - selectedTab: SessionPreferencesWindow.Tab, - completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil - ) { - guard let window else { - return - } - if let sessionDiff { - fillWithSessionDiff(sessionDiff) - } - - tabView.selectTabViewItem(at: selectedTab.rawValue) - - sheetWindow.beginSheet(window, completionHandler: handler) - } - - func updateSessionDiff(_ sessionDiff: SessionDiff) { - currentPreferences.updateSessionDiff(sessionDiff) - } - - func fillWithSessionDiff(_ sessionDiff: SessionDiff) { - currentPreferences = Data.fromSessionDiff(sessionDiff) - reload(tabItem: tabView.selectedTabViewItem) - } - - func fillWithUserDefaults() { - currentPreferences = Data.fromUserDefaults() - reload(tabItem: tabView.selectedTabViewItem) - } - - @objc func closeSessionSettingsSheet(_ sender: AnyObject) { - guard let window else { - return - } - let response = NSApplication.ModalResponse(sender.tag) - if response == .OK { - updatePendingData() - window.endEditing() - } - window.sheetParent?.endSheet(window, returnCode: response) - } - - func updatePendingData() { - sessionPreferencesFiltersPanel?.updatePendingData() - } - - // MARK: - TabView delegate - - func tabView(_: NSTabView, willSelect tabViewItem: NSTabViewItem?) { - guard let tabViewItem, - let identifier = tabViewItem.identifier as? NSUserInterfaceItemIdentifier else { - return - } - - if loadedTabs.contains(identifier) { - return - } - loadedTabs.insert(identifier) - - if identifier == Self.comparisonPanelIdentifier { - let view = SessionPreferencesComparisonPanel(frame: .zero) - view.delegate = self - tabViewItem.view = view - } else if identifier == Self.fileFiltersPanelIdentifier { - let panel = SessionPreferencesFiltersPanel(frame: .zero) - panel.delegate = self - sessionPreferencesFiltersPanel = panel - tabViewItem.view = sessionPreferencesFiltersPanel - } else if identifier == Self.alignmentPanelIdentifier { - let view = AlignmentPanel(frame: .zero) - view.delegate = self - tabViewItem.view = view - } - reload(tabItem: tabViewItem) - } - - func tabView(_: NSTabView, didSelect _: NSTabViewItem?) { - resize() - } - - func reload(tabItem: NSTabViewItem?) { - if let view = tabItem?.view as? PreferencesPanelDataSource { - view.reloadData() - } - } - - // MARK: - PreferenceBox delegate - - func preferenceBox(_: PreferencesBox, boolForKey key: CommonPrefs.Name) -> Bool { - switch key { - case .virtualResourceFork: - currentPreferences.fileExtraOptions[.resourceFork] - case .virtualFinderLabel: - currentPreferences.comparatorOptions.hasFinderLabel - case .virtualFinderTags: - currentPreferences.comparatorOptions.hasFinderTags - case .followSymLinks: - currentPreferences.followSymLinks - case .skipPackages: - currentPreferences.skipPackages - case .traverseFilteredFolders: - currentPreferences.traverseFilteredFolders - case .expandAllFolders: - currentPreferences.expandAllFolders - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, setBool value: Bool, forKey key: CommonPrefs.Name) { - switch key { - case .virtualResourceFork: - currentPreferences.fileExtraOptions[.resourceFork] = value - case .virtualFinderLabel: - currentPreferences.comparatorOptions = currentPreferences.comparatorOptions.changeFinderLabel(value) - case .virtualFinderTags: - currentPreferences.comparatorOptions = currentPreferences.comparatorOptions.changeFinderTags(value) - case .followSymLinks: - currentPreferences.followSymLinks = value - case .skipPackages: - currentPreferences.skipPackages = value - case .traverseFilteredFolders: - currentPreferences.traverseFilteredFolders = value - case .expandAllFolders: - currentPreferences.expandAllFolders = value - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, integerForKey key: CommonPrefs.Name) -> Int { - switch key { - case .virtualComparatorWithoutMethod: - currentPreferences.comparatorOptions.onlyMethodFlags.rawValue - case .virtualDisplayFiltersWithoutMethod: - currentPreferences.displayOptions.onlyMethodFlags.rawValue - case .virtualAlignFlags: - currentPreferences.comparatorOptions.onlyAlignFlags.rawValue - case .timestampToleranceSeconds: - currentPreferences.timestampToleranceSeconds - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, setInteger value: Int, forKey key: CommonPrefs.Name) { - switch key { - case .virtualComparatorWithoutMethod: - currentPreferences.comparatorOptions = currentPreferences.comparatorOptions.changeWithoutMethod(value) - case .virtualDisplayFiltersWithoutMethod: - currentPreferences.displayOptions = currentPreferences.displayOptions.changeWithoutMethod(value) - case .virtualAlignFlags: - currentPreferences.comparatorOptions = currentPreferences.comparatorOptions.changeAlign(value) - case .timestampToleranceSeconds: - currentPreferences.timestampToleranceSeconds = value - default: - fatalError("key \(key) not handled") - } - } - - func preferenceBox(_: PreferencesBox, stringForKey key: CommonPrefs.Name) -> String? { - switch key { - case .defaultFileFilters: - currentPreferences.fileFilters - default: - fatalError("The key \(key) is not supported") - } - } - - func preferenceBox(_: PreferencesBox, setString value: String?, forKey key: CommonPrefs.Name) { - switch key { - case .defaultFileFilters: - currentPreferences.fileFilters = value - default: - fatalError("The key \(key) is not supported") - } - } - - func preferenceBox(_: PreferencesBox, objectForKey key: CommonPrefs.Name) -> Any? { - switch key { - case .virtualAlignRules: - currentPreferences.alignRules - default: - fatalError("The key \(key) is not supported") - } - } - - func preferenceBox(_: PreferencesBox, setObject value: Any?, forKey key: CommonPrefs.Name) { - switch key { - case .virtualAlignRules: - if let value = value as? [AlignRule] { - currentPreferences.alignRules = value - } - default: - fatalError("The key \(key) is not supported") - } - } - - func preferenceBox(_ preferencesBox: PreferencesBox, isEnabled key: CommonPrefs.Name) -> Bool { - switch key { - case .virtualFinderLabel: - preferenceBox(preferencesBox, boolForKey: .virtualFinderTags) == false - case .virtualFinderTags: - preferenceBox(preferencesBox, boolForKey: .virtualFinderLabel) == false - default: - true - } - } - - // MARK: - Position and size - - func contentRect() -> NSRect { - var contentRect = NSRect.zero - - if let selectedTabView = tabView.selectedTabViewItem?.view { - for view in selectedTabView.subviews { - // the result of Swift's GCRect.union is different from NSUnionRect - // so we stay on it - // swiftlint:disable:next legacy_nsgeometry_functions - contentRect = NSUnionRect(contentRect, view.frame) - } - } - - return contentRect - } - - func toolbarHeight(_ window: NSWindow?) -> CGFloat { - guard let window else { - return 0 - } - let windowFrame = NSWindow.contentRect(forFrameRect: window.frame, styleMask: window.styleMask) - return windowFrame.size.height - (window.contentView?.frame.size.height ?? 0) - } - - func minWindowHeight() -> CGFloat { - let contentRect = contentRect() - // Border top + toolbar - return contentRect.size.height + 40 + toolbarHeight(window) - } - - func resize() { - guard let window, - window.isVisible else { - return - } - let newSize = NSSize( - width: window.contentView?.frame.size.width ?? 0, - height: minWindowHeight() - ) - - window.setContentSize(newSize) - } -} - -extension SessionPreferencesWindow { - enum Tab: Int { - case comparison - case filters - case alignment - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/AlignRuleWindow.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/AlignRuleWindow.swift deleted file mode 100644 index f7c611a..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/AlignRuleWindow.swift +++ /dev/null @@ -1,285 +0,0 @@ -// -// AlignRuleWindow.swift -// VisualDiffer -// -// Created by davide ficano on 15/08/13. -// Copyright (c) 2013 visualdiffer.com -// - -class AlignRuleWindow: NSWindow, NSTextFieldDelegate { - enum Mode { - case insert - case update - } - - var alignRules = [AlignRule]() - var mode = Mode.insert - - var editedRule: AlignRule? - - private var leftOptions: NSRegularExpression.Options = [] - private var rightOptions: AlignTemplateOptions = [] - - private lazy var leftExpressionBox: ExpressionBox = { - let view = ExpressionBox( - title: NSLocalizedString("Left file name matches regular expression", comment: "") - ) - view.delegate = self - view.popupMenu = ExpressionBox.defaultRegExpMenu - - return view - }() - - private lazy var rightExpressionBox: ExpressionBox = { - let view = ExpressionBox( - title: NSLocalizedString("Right file name matches pattern (this is not a regular expression)", comment: "") - ) - view.delegate = self - view.popupMenu = ExpressionBox.defaultRightExpressionMenu - - return view - }() - - private lazy var testBox: AlignTestResultBox = .init( - title: NSLocalizedString("Test Rule", comment: "") - ) - private lazy var standardButtons: StandardButtons = .init( - primaryTitle: NSLocalizedString("Add", comment: ""), - secondaryTitle: NSLocalizedString("Cancel", comment: ""), - target: self, - action: #selector(closeAlignWindow) - ) - - override init( - contentRect: NSRect, - styleMask style: NSWindow.StyleMask, - backing backingStoreType: NSWindow.BackingStoreType, - defer flag: Bool - ) { - super.init( - contentRect: contentRect, - styleMask: style, - backing: backingStoreType, - defer: flag - ) - - minSize = NSSize(width: 480, height: 300) - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(leftExpressionBox) - contentView.addSubview(rightExpressionBox) - contentView.addSubview(testBox) - contentView.addSubview(standardButtons) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - leftExpressionBox.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - leftExpressionBox.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - leftExpressionBox.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), - leftExpressionBox.heightAnchor.constraint(equalToConstant: 60), - - rightExpressionBox.leadingAnchor.constraint(equalTo: leftExpressionBox.leadingAnchor), - rightExpressionBox.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - rightExpressionBox.topAnchor.constraint(equalTo: leftExpressionBox.bottomAnchor, constant: 10), - rightExpressionBox.heightAnchor.constraint(equalToConstant: 60), - - testBox.leadingAnchor.constraint(equalTo: leftExpressionBox.leadingAnchor), - testBox.trailingAnchor.constraint(equalTo: leftExpressionBox.trailingAnchor), - testBox.topAnchor.constraint(equalTo: rightExpressionBox.bottomAnchor, constant: 10), - testBox.heightAnchor.constraint(equalToConstant: 100), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - func beginSheet( - _ sheetWindow: NSWindow, - alignRule: AlignRule, - mode: Mode, - completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil - ) { - editedRule = alignRule - self.mode = mode - - fillUI(alignRule) - - sheetWindow.beginSheet(self, completionHandler: handler) - // The sheet isn't destroyed so when it is reopen the first responder - // corresponds to the last set when sheet was closed - leftExpressionBox.becomeFirstResponder() - } - - // MARK: - NSTextFieldDelegate - - func controlTextDidChange(_: Notification) { - updateTestResult() - } - - // MARK: - Private methods - - private func fillUI(_ alignRule: AlignRule) { - standardButtons.primaryButton.title = mode == .insert ? NSLocalizedString("Add", comment: "") : NSLocalizedString("Update", comment: "") - - // make a local copy of all values, so we can compare them with current rule to find changes - leftExpressionBox.text = alignRule.regExp.pattern - rightExpressionBox.text = alignRule.template.pattern - leftOptions = alignRule.regExp.options - rightOptions = alignRule.template.options - - testBox.clear() - } - - private func updateTestResult() { - testBox.leftExpression = leftExpressionBox.text - testBox.rightExpression = rightExpressionBox.text - testBox.regularExpressionOptions = leftOptions - testBox.reloadData() - } - - private func validateRule() throws { - let strLeftExpression = leftExpressionBox.text - let strRightExpression = rightExpressionBox.text - - if strLeftExpression.trimmingCharacters(in: CharacterSet.whitespaces).isEmpty { - throw AlignRuleError.emptyExpression(isLeft: true) - } - - do { - _ = try NSRegularExpression( - pattern: strLeftExpression, - options: leftOptions - ) - } catch { - throw AlignRuleError.invalidRegularExpression(error: error) - } - - if strRightExpression.trimmingCharacters(in: CharacterSet.whitespaces).isEmpty { - throw AlignRuleError.emptyExpression(isLeft: false) - } - - // check if current rule already exists - if mode == .insert { - let item = alignRules.first { - $0.regExp.pattern == strLeftExpression && - $0.regExp.options == leftOptions && - $0.template.pattern == strRightExpression && - $0.template.options == rightOptions - } - - if item != nil { - throw AlignRuleError.ruleAlreadyExists - } - } - } - - // MARK: - Action methods - - @objc func closeAlignWindow(_ sender: AnyObject) { - guard let sender = sender as? NSButton else { - return - } - editedRule = nil - if sender === standardButtons.primaryButton { - do { - try validateRule() - editedRule = AlignRule( - regExp: AlignRegExp(pattern: leftExpressionBox.text, options: leftOptions), - template: AlignTemplate(pattern: rightExpressionBox.text, options: rightOptions) - ) - } catch { - let alert = NSAlert() - - alert.messageText = NSLocalizedString("Rule cannot be saved", comment: "") - alert.informativeText = error.localizedDescription - alert.alertStyle = .warning - alert.runModal() - - // do not close the window - return - } - } - - sheetParent?.endSheet(self, returnCode: .init(sender.tag)) - } - - @objc func toggleLeftExpressionCaseSensitive(_: AnyObject) { - if leftOptions.contains(.caseInsensitive) { - leftOptions.remove(.caseInsensitive) - } else { - leftOptions.insert(.caseInsensitive) - } - updateTestResult() - } - - @objc func toggleRightExpressionCaseSensitive(_: AnyObject) { - if rightOptions.contains(.caseInsensitive) { - rightOptions.remove(.caseInsensitive) - } else { - rightOptions.insert(.caseInsensitive) - } - updateTestResult() - } - - override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - let action = menuItem.action - - if action == #selector(toggleLeftExpressionCaseSensitive) { - menuItem.state = leftOptions.contains(.caseInsensitive) ? .on : .off - } else if action == #selector(toggleRightExpressionCaseSensitive) { - menuItem.state = rightOptions.contains(.caseInsensitive) ? .on : .off - } - return true - } -} - -extension AlignRuleWindow { - static func createSheet() -> AlignRuleWindow { - let styleMask: NSWindow.StyleMask = [ - .titled, - .closable, - .miniaturizable, - .resizable, - ] - - return AlignRuleWindow( - contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - } - - private enum AlignRuleError: LocalizedError { - case emptyExpression(isLeft: Bool) - case ruleAlreadyExists - case invalidRegularExpression(error: Error) - - var errorDescription: String? { - switch self { - case let .emptyExpression(isLeft): - isLeft - // swiftlint:disable:next void_function_in_ternary - ? NSLocalizedString("Left expression can't be empty", comment: "") - : NSLocalizedString("Right expression can't be empty", comment: "") - case .ruleAlreadyExists: - NSLocalizedString("An identical rule already exists", comment: "") - case let .invalidRegularExpression(error): - String( - format: NSLocalizedString("Regular expression doesn't compile: %@", comment: ""), - error.localizedDescription - ) - } - } - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/AlignTestResultBox.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/AlignTestResultBox.swift deleted file mode 100644 index ea01185..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/AlignTestResultBox.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// AlignTestResultBox.swift -// VisualDiffer -// -// Created by davide ficano on 28/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class AlignTestResultBox: NSBox, NSTextFieldDelegate { - private let fileNameTitle: NSTextField - private let fileName: NSTextField - private let outputTitle: NSTextField - private let output: NSTextField - private let errorMessage: NSTextField - - @objc var leftExpression: String - @objc var rightExpression: String - @objc var regularExpressionOptions: NSRegularExpression.Options - - @objc convenience init(title: String) { - self.init(frame: .zero) - - self.title = title - titleFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - titlePosition = .atTop - boxType = .primary - translatesAutoresizingMaskIntoConstraints = false - } - - override init(frame frameRect: NSRect) { - fileNameTitle = NSTextField.labelWithTitle(NSLocalizedString("File name", comment: "")) - fileName = NSTextField(frame: .zero) - outputTitle = NSTextField.labelWithTitle(NSLocalizedString("Result", comment: "")) - output = NSTextField(frame: .zero) - errorMessage = NSTextField.labelWithTitle("") - - leftExpression = "" - rightExpression = "" - regularExpressionOptions = [] - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - fileNameTitle.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - fileName.placeholderString = NSLocalizedString("Enter a file name to test the rule", comment: "") - fileName.translatesAutoresizingMaskIntoConstraints = false - fileName.delegate = self - - outputTitle.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - output.translatesAutoresizingMaskIntoConstraints = false - - errorMessage.placeholderString = NSLocalizedString("No errors.", comment: "") - errorMessage.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - errorMessage.translatesAutoresizingMaskIntoConstraints = false - - if let contentView { - contentView.addSubview(fileNameTitle) - contentView.addSubview(fileName) - - contentView.addSubview(outputTitle) - contentView.addSubview(output) - - contentView.addSubview(errorMessage) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - - NSLayoutConstraint.activate([ - fileNameTitle.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5), - fileNameTitle.topAnchor.constraint(equalTo: contentView.topAnchor), - fileNameTitle.widthAnchor.constraint(equalToConstant: 60), - - fileName.leadingAnchor.constraint(equalTo: fileNameTitle.trailingAnchor, constant: 5), - fileName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5), - fileName.topAnchor.constraint(equalTo: fileNameTitle.topAnchor), - - outputTitle.leadingAnchor.constraint(equalTo: fileNameTitle.leadingAnchor), - outputTitle.topAnchor.constraint(equalTo: fileNameTitle.bottomAnchor, constant: 10), - outputTitle.widthAnchor.constraint(equalToConstant: 60), - - output.leadingAnchor.constraint(equalTo: outputTitle.trailingAnchor, constant: 5), - output.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5), - output.topAnchor.constraint(equalTo: outputTitle.topAnchor), - - errorMessage.leadingAnchor.constraint(equalTo: fileNameTitle.leadingAnchor), - errorMessage.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - errorMessage.topAnchor.constraint(equalTo: output.bottomAnchor, constant: 5), - ]) - } - - func controlTextDidChange(_: Notification) { - reloadData() - } - - @objc func reloadData() { - output.stringValue = "" - errorMessage.stringValue = "" - - if leftExpression.isEmpty, rightExpression.isEmpty { - return - } - - do { - let re = try NSRegularExpression( - pattern: leftExpression, - options: regularExpressionOptions - ) - let result = re.firstMatch( - in: fileName.stringValue, - options: [], - range: NSRange(location: 0, length: fileName.stringValue.count) - ) - if let result { - let rightExpr = rightExpression - output.stringValue = fileName.stringValue.replace( - template: rightExpr, - result: result - ) - } - } catch { - errorMessage.stringValue = String(format: NSLocalizedString("Regexp error: %@", comment: ""), error.localizedDescription) - return - } - } - - @objc func clear() { - fileName.stringValue = "" - output.stringValue = "" - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/AlignmentPanel.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/AlignmentPanel.swift deleted file mode 100644 index 5368839..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/AlignmentPanel.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// AlignmentPanel.swift -// VisualDiffer -// -// Created by davide ficano on 24/08/13. -// Copyright (c) 2013 visualdiffer.com -// - -class AlignmentPanel: NSView, PreferencesPanelDataSource { - var delegate: PreferencesBoxDataSource? { - didSet { - fileNameCaseAlignmentBox.delegate = delegate - userDefinedAlignRulesBox.delegate = delegate - } - } - - private var fileNameCaseAlignmentBox: FileNameCaseBox - private var userDefinedAlignRulesBox: UserDefinedRulesBox - - override init(frame frameRect: NSRect) { - fileNameCaseAlignmentBox = FileNameCaseBox( - title: NSLocalizedString("File Name Case Alignment", comment: "") - ) - userDefinedAlignRulesBox = UserDefinedRulesBox( - title: NSLocalizedString("User Defined Alignment Rules", comment: "") - ) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(fileNameCaseAlignmentBox) - addSubview(userDefinedAlignRulesBox) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - fileNameCaseAlignmentBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4), - fileNameCaseAlignmentBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4), - fileNameCaseAlignmentBox.topAnchor.constraint(equalTo: topAnchor, constant: 4), - fileNameCaseAlignmentBox.heightAnchor.constraint(equalToConstant: 55), - - userDefinedAlignRulesBox.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4), - userDefinedAlignRulesBox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4), - userDefinedAlignRulesBox.topAnchor.constraint(equalTo: fileNameCaseAlignmentBox.bottomAnchor, constant: 4), - userDefinedAlignRulesBox.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4), - ]) - } - - // MARK: - Action Methods - - func reloadData() { - fileNameCaseAlignmentBox.reloadData() - userDefinedAlignRulesBox.reloadData() - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/ExpressionBox+Menu.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/ExpressionBox+Menu.swift deleted file mode 100644 index 5964170..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/ExpressionBox+Menu.swift +++ /dev/null @@ -1,212 +0,0 @@ -// -// ExpressionBox+Menu.swift -// VisualDiffer -// -// Created by davide ficano on 28/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension ExpressionBox { - @objc static var defaultRightExpressionMenu: NSMenu { - let menu = NSMenu() - - // the button title image - menu.addItem( - withTitle: "", - action: nil, - keyEquivalent: "" - ) - .image = NSImage(named: NSImage.actionTemplateName) - - menu.addItem( - withTitle: NSLocalizedString("Ignore Case", comment: ""), - action: #selector(AlignRuleWindow.toggleRightExpressionCaseSensitive), - keyEquivalent: "" - ) - menu.addItem(NSMenuItem.separator()) - - menu.addItem( - withTitle: NSLocalizedString("Matched Range $0", comment: ""), - action: #selector(appendGroupExpression), - keyEquivalent: "" - ) - .representedObject = "$0" - - for i in 1 ..< 10 { - menu.addItem( - withTitle: String(format: NSLocalizedString("Capture Group $%ld", comment: ""), i), - action: #selector(appendGroupExpression), - keyEquivalent: "" - ) - .representedObject = String(format: "$%ld", i) - } - - return menu - } - - @objc static var defaultRegExpMenu: NSMenu { - let menu = NSMenu() - - // the button title image - menu.addItem( - withTitle: "", - action: nil, - keyEquivalent: "" - ) - .image = NSImage(named: NSImage.actionTemplateName) - - menu.addItem( - withTitle: NSLocalizedString("Ignore Case", comment: ""), - action: #selector(AlignRuleWindow.toggleLeftExpressionCaseSensitive), - keyEquivalent: "" - ) - menu.addItem(NSMenuItem.separator()) - menu.addItem( - withTitle: NSLocalizedString("Regular Expression Special Characters", comment: ""), - action: nil, - keyEquivalent: "" - ) - - menu.addItem(NSMenuItem.separator()) - menu.addItem( - withTitle: NSLocalizedString(". : any character except a newline", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "." - menu.addItem( - withTitle: NSLocalizedString("\\d : any decimal digit", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\d" - menu.addItem( - withTitle: NSLocalizedString("\\D : any non-digit", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\D" - menu.addItem( - withTitle: NSLocalizedString("\\s : any whitespace character", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\s" - menu.addItem( - withTitle: NSLocalizedString("\\S : any non-whitespace character", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\S" - menu.addItem( - withTitle: NSLocalizedString("\\w : any alphanumeric character", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\w" - menu.addItem( - withTitle: NSLocalizedString("\\W : any non-alphanumeric character", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\W" - - menu.addItem(NSMenuItem.separator()) - menu.addItem( - withTitle: NSLocalizedString("* : zero or more of the preceding block", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "*" - menu.addItem( - withTitle: NSLocalizedString("*? : zero or more of the preceding block (non-greedy)", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "*?" - menu.addItem( - withTitle: NSLocalizedString("+ : one or more of the preceding block", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "+" - menu.addItem( - withTitle: NSLocalizedString("+? : one or more of the preceding block (non-greedy)", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "+?" - menu.addItem( - withTitle: NSLocalizedString("? : zero or one of the preceding block", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "?" - menu.addItem( - withTitle: NSLocalizedString("?? : zero or one of the preceding block (non-greedy)", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "??" - menu.addItem( - withTitle: NSLocalizedString("{m} : exactly 'm' copies of the preceding block", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "{m}" - menu.addItem( - withTitle: NSLocalizedString("{m,n} : 'm' to 'n' copies of the preceding block", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "{m,n}" - menu.addItem( - withTitle: NSLocalizedString("{m,n}? : 'm' to 'n' copies of the preceding block (non-greedy)", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "{m,n}?" - - menu.addItem(NSMenuItem.separator()) - menu.addItem( - withTitle: NSLocalizedString("^ : beginning of line", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "^" - menu.addItem( - withTitle: NSLocalizedString("$ : end of line", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "$" - menu.addItem( - withTitle: NSLocalizedString("\\b : the beginning or end of a word", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\b" - menu.addItem( - withTitle: NSLocalizedString("\\B : anything BUT the beginning or end of a word", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\B" - menu.addItem( - withTitle: NSLocalizedString("\\A : beginning of the string", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "\\A" - - menu.addItem(NSMenuItem.separator()) - menu.addItem( - withTitle: NSLocalizedString("(...) : group", comment: ""), - action: #selector(insertRegExp), - keyEquivalent: "" - ) - .representedObject = "(...)" - - return menu - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/ExpressionBox.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/ExpressionBox.swift deleted file mode 100644 index c04819f..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/ExpressionBox.swift +++ /dev/null @@ -1,149 +0,0 @@ -// -// ExpressionBox.swift -// VisualDiffer -// -// Created by davide ficano on 28/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ExpressionBox: NSBox { - var delegate: NSTextFieldDelegate? { - get { - expression.delegate - } - - set { - expression.delegate = newValue - } - } - - var popupMenu: NSMenu? { - get { - popup.menu - } - - set { - popup.menu = newValue - // must be done only *after* setting the menu otherwise doesn't work - expression.attachTo(popUpButton: popup) - } - } - - var text: String { - get { - expression.stringValue - } - - set { - expression.stringValue = newValue - } - } - - private lazy var expression: TextFieldSelectionHolder = { - let view = TextFieldSelectionHolder(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var popup: NSPopUpButton = createPopUpButton() - - convenience init(title: String) { - self.init(frame: .zero) - - self.title = title - titleFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - titlePosition = .atTop - boxType = .primary - translatesAutoresizingMaskIntoConstraints = false - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - contentView?.addSubview(expression) - contentView?.addSubview(popup) - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - expression.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - expression.trailingAnchor.constraint(equalTo: popup.leadingAnchor, constant: -4), - expression.topAnchor.constraint(equalTo: contentView.topAnchor), - - popup.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - popup.topAnchor.constraint(equalTo: expression.topAnchor), - ]) - } - - private func createPopUpButton() -> NSPopUpButton { - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - @objc func insertRegExp(_ sender: AnyObject) { - guard let shortcut = sender.representedObject as? String, - let editor = expression.currentEditor() else { - return - } - let ellipsis = "..." - let pattern = expression.stringValue - - if let ellipsisRange = shortcut.range(of: ellipsis) { - let selectedRange = editor.selectedRange - if selectedRange.length > 0 { - if let range = Range(selectedRange, in: pattern) { - let selectionText = pattern[range] - let shortcutReplaced = shortcut.replacingOccurrences(of: ellipsis, with: selectionText) - let modified = pattern.replacingCharacters(in: range, with: shortcutReplaced) - - // thanks Apple to make string manipulation so complicated!! - let location = shortcut.distance(from: shortcut.startIndex, to: ellipsisRange.lowerBound) - let newLowerBound = modified.index(range.lowerBound, offsetBy: location) - let selectionRange = NSRange(newLowerBound ... range.upperBound, in: modified) - - // now select the string - editor.string = modified - editor.selectedRange = selectionRange - } - } else { - editor.replaceCharacters(in: selectedRange, with: shortcut) - - let location = shortcut.distance(from: shortcut.startIndex, to: ellipsisRange.lowerBound) - editor.selectedRange = NSRange(location: selectedRange.location + location, length: ellipsis.count) - } - } else { - editor.insertText(shortcut) - } - } - - @objc func appendGroupExpression(_ sender: AnyObject) { - if let sender = sender as? NSMenuItem, - let text = sender.representedObject, - let editor = expression.currentEditor() { - editor.insertText(text) - } - } - - @discardableResult - override func becomeFirstResponder() -> Bool { - expression.becomeFirstResponder() - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/FileNameCaseBox.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/FileNameCaseBox.swift deleted file mode 100644 index 1ba7b71..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/FileNameCaseBox.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// FileNameCaseBox.swift -// VisualDiffer -// -// Created by davide ficano on 29/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FileNameCaseBox: PreferencesBox { - private lazy var alignPopup: NSPopUpButton = { - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.cell = AlignPopupButtonCell(textCell: "") - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.action = #selector(comparisonAlignChange) - - return view - }() - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - contentView?.addSubview(alignPopup) - - setupConstraints() - } - - func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - alignPopup.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 4), - alignPopup.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -4), - alignPopup.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4), - alignPopup.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 4), - ]) - } - - @objc func comparisonAlignChange(_: AnyObject) { - guard let item = alignPopup.selectedItem else { - return - } - let alignFlags = item.tag - delegate?.preferenceBox(self, setInteger: alignFlags, forKey: .virtualAlignFlags) - } - - override func reloadData() { - alignPopup.selectItem(withTag: delegate?.preferenceBox(self, integerForKey: .virtualAlignFlags) ?? 0) - super.reloadData() - } -} diff --git a/Sources/Features/Preferences/Session/Panels/AlignRule/UserDefinedRulesBox.swift b/Sources/Features/Preferences/Session/Panels/AlignRule/UserDefinedRulesBox.swift deleted file mode 100644 index 5579eaa..0000000 --- a/Sources/Features/Preferences/Session/Panels/AlignRule/UserDefinedRulesBox.swift +++ /dev/null @@ -1,347 +0,0 @@ -// -// UserDefinedRulesBox.swift -// VisualDiffer -// -// Created by davide ficano on 29/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class UserDefinedRulesBox: PreferencesBox, - @preconcurrency NSTableViewDataSource, - NSTableViewDelegate, - NSMenuItemValidation, - TableViewCommonDelegate { - enum Identifier { - static let leftExpression = NSUserInterfaceItemIdentifier(rawValue: "leftExpression") - static let rightExpression = NSUserInterfaceItemIdentifier(rawValue: "rightExpression") - } - - private lazy var rulesTableView: NSTableView = { - let view = TableViewCommon(frame: .zero) - - view.allowsEmptySelection = true - view.allowsColumnReordering = false - view.allowsColumnResizing = true - view.allowsMultipleSelection = true - view.allowsColumnSelection = true - view.allowsTypeSelect = true - view.usesAlternatingRowBackgroundColors = true - - view.focusRingType = .none - view.allowsExpansionToolTips = true - view.columnAutoresizingStyle = .uniformColumnAutoresizingStyle - view.autosaveTableColumns = false - view.intercellSpacing = .zero - - // Enable dragging on NSTableView - view.registerForDraggedTypes([NSPasteboard.PasteboardType.alignRule]) - view.setDraggingSourceOperationMask(.every, forLocal: true) - view.setDraggingSourceOperationMask(.every, forLocal: false) - - let columns = [ - (Identifier.leftExpression, NSLocalizedString("Left Regular Expression", comment: "")), - (Identifier.rightExpression, NSLocalizedString("Right Pattern Expression", comment: "")), - ] - for (identifier, title) in columns { - let column = NSTableColumn(identifier: identifier) - column.title = title - column.resizingMask = [.autoresizingMask, .userResizingMask] - - view.addTableColumn(column) - } - - view.delegate = self - view.dataSource = self - - view.target = self - view.doubleAction = #selector(handleDoubleClick) - - return view - }() - - private lazy var tableScrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = rulesTableView - - return view - }() - - private lazy var actionBarView: ActionBarView = { - let view = ActionBarView(frame: .zero) - - view.firstButton.target = self - view.firstButton.action = #selector(addAlignRule) - - view.secondButton.target = self - view.secondButton.action = #selector(deleteAlignRules) - // initially is disabled - view.secondButton.isEnabled = false - - if let menu = view.popup.menu { - menu.addItem( - withTitle: NSLocalizedString("Edit Rule...", comment: ""), - action: #selector(updateAlignRule), - keyEquivalent: "" - ) - .target = self - let item = menu.addItem( - withTitle: NSLocalizedString("Duplicate Rule...", comment: ""), - action: #selector(duplicateAlignRule), - keyEquivalent: "d" - ) - item.target = self - item.keyEquivalentModifierMask = .command - } - - view.popup.target = self - - return view - }() - - private lazy var label: NSTextField = { - let view = NSTextField.labelWithTitle( - NSLocalizedString("Drag items to change evaluation order (topmost first)", comment: "") - ) - view.font = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) - - return view - }() - - lazy var alignRuleWindow: AlignRuleWindow = .createSheet() - - var alignRules: [AlignRule] = [] - private var editedIndex = -1 - - @objc override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(tableScrollView) - contentView.addSubview(actionBarView) - contentView.addSubview(label) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - tableScrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - tableScrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - tableScrollView.topAnchor.constraint(equalTo: contentView.topAnchor), - tableScrollView.bottomAnchor.constraint(equalTo: actionBarView.topAnchor), - tableScrollView.heightAnchor.constraint(greaterThanOrEqualToConstant: 200), - - actionBarView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - actionBarView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - - label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - ]) - } - - @objc func addAlignRule(_: AnyObject) { - openAlignRuleWindow( - AlignRule(regExp: AlignRegExp(), template: AlignTemplate()), - index: -1, - mode: .insert - ) - } - - @objc func updateAlignRule(_: AnyObject) { - if !alignRules.isEmpty { - openAlignRuleWindow( - alignRules[rulesTableView.selectedRow], - index: rulesTableView.selectedRow, - mode: .update - ) - } - } - - @objc func deleteAlignRules(_: AnyObject?) { - for row in rulesTableView.selectedRowIndexes.reversed() { - alignRules.remove(at: row) - } - delegate?.preferenceBox(self, setObject: alignRules, forKey: .virtualAlignRules) - rulesTableView.reloadData() - } - - @objc func duplicateAlignRule(_: AnyObject) { - let row = rulesTableView.selectedRow - - if row != -1 { - openAlignRuleWindow( - alignRules[row], - index: -1, - mode: .insert - ) - } - } - - func moveRule(from rowIndexes: IndexSet, to row: Int) -> IndexSet { - let movedIndexes = alignRules.move(from: rowIndexes as IndexSet, to: row) - - delegate?.preferenceBox(self, setObject: alignRules, forKey: .virtualAlignRules) - - return movedIndexes - } - - private func openAlignRuleWindow( - _ rule: AlignRule, - index: Int, - mode: AlignRuleWindow.Mode - ) { - guard let window else { - return - } - editedIndex = index - alignRuleWindow.alignRules = alignRules - alignRuleWindow.beginSheet( - window, - alignRule: rule, - mode: mode - ) { - self.alignRule($0) - } - } - - func alignRule(_ returnCode: NSApplication.ModalResponse) { - if returnCode == .OK { - if let editedRule = alignRuleWindow.editedRule { - // on update alignRule is already inside array so it isn't necessary to add it - if alignRuleWindow.mode == .insert { - alignRules.append(editedRule) - } else { - alignRules[editedIndex] = editedRule - } - rulesTableView.reloadData() - delegate?.preferenceBox(self, setObject: alignRules, forKey: .virtualAlignRules) - } - } - } - - // MARK: - TableView data source methods - - func numberOfRows(in _: NSTableView) -> Int { - alignRules.count - } - - func tableView(_: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { - if let identifier = tableColumn?.identifier { - if identifier == Identifier.leftExpression { - return alignRules[row].regExp.pattern - } - if identifier == Identifier.rightExpression { - return alignRules[row].template.pattern - } - } - return nil - } - - func tableViewSelectionDidChange(_: Notification) { - actionBarView.secondButton.isEnabled = rulesTableView.selectedRow != -1 - } - - func tableViewCommonKeyDown(_: NSTableView, event: NSEvent) -> Bool { - if event.isDeleteShortcutKey(true) { - if actionBarView.secondButton.isEnabled { - deleteAlignRules(nil) - } - return true - } - return false - } - - // MARK: Drag and Drop - - func tableView(_: NSTableView, validateDrop _: any NSDraggingInfo, proposedRow _: Int, proposedDropOperation _: NSTableView.DropOperation) -> NSDragOperation { - .every - } - - func tableView(_ tableView: NSTableView, acceptDrop info: any NSDraggingInfo, row: Int, dropOperation _: NSTableView.DropOperation) -> Bool { - let pasteboard = info.draggingPasteboard - - guard pasteboard.availableType(from: [.alignRule]) != nil else { - return false - } - - if let rowData = info.draggingPasteboard.data(forType: .alignRule), - let rowIndexes = try? NSKeyedUnarchiver.unarchivedObject( - ofClass: NSIndexSet.self, - from: rowData - ) { - let movedIndexes = moveRule(from: rowIndexes as IndexSet, to: row) - tableView.selectRowIndexes(movedIndexes, byExtendingSelection: false) - tableView.reloadData() - - return true - } - - return false - } - - func tableView(_: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { - guard let data = try? NSKeyedArchiver.archivedData( - withRootObject: rowIndexes, - requiringSecureCoding: false - ) else { - return false - } - - pboard.declareTypes([.alignRule], owner: self) - pboard.setData(data, forType: .alignRule) - return true - } - - @objc func handleDoubleClick(_ sender: AnyObject) { - if rulesTableView.clickedRow != -1 { // make sure double click was not in table header - updateAlignRule(sender) - } - } - - func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - var enabled = true - let action = menuItem.action - - if action == #selector(updateAlignRule) { - enabled = rulesTableView.selectedRow != -1 - } else if action == #selector(duplicateAlignRule) { - enabled = rulesTableView.selectedRow != -1 - } - - return enabled - } - - override func reloadData() { - alignRules = if let rules = delegate?.preferenceBox(self, objectForKey: .virtualAlignRules) as? [AlignRule] { - rules - } else { - [] - } - - rulesTableView.reloadData() - } -} - -extension NSPasteboard.PasteboardType { - static let alignRule = NSPasteboard.PasteboardType("AlignRulePboardType") -} diff --git a/Sources/Features/Preferences/Session/Panels/Comparison/SessionPreferencesComparisonPanel.swift b/Sources/Features/Preferences/Session/Panels/Comparison/SessionPreferencesComparisonPanel.swift deleted file mode 100644 index 5651a89..0000000 --- a/Sources/Features/Preferences/Session/Panels/Comparison/SessionPreferencesComparisonPanel.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// SessionPreferencesComparisonPanel.swift -// VisualDiffer -// -// Created by davide ficano on 24/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class SessionPreferencesComparisonPanel: NSView, PreferencesPanelDataSource { - var delegate: PreferencesBoxDataSource? { - didSet { - for view in stackView.views { - if let delegate, - let box = view as? PreferencesBox { - box.delegate = delegate - } - } - } - } - - let stackView: NSStackView - - override init(frame frameRect: NSRect) { - stackView = NSStackView.preferences(with: [ - FolderComparisonBox(title: NSLocalizedString("Comparison", comment: "")), - FoldersTraversalBox(title: "Folder Traversal"), - FolderViewBox(title: NSLocalizedString("View", comment: "")), - ]) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - translatesAutoresizingMaskIntoConstraints = false - addSubview(stackView) - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - stackView.topAnchor.constraint(equalTo: topAnchor), - heightAnchor.constraint(equalToConstant: 360), - ]) - - for view in stackView.views { - view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true - view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true - } - } - - func reloadData() { - for view in stackView.views { - if let box = view as? PreferencesBox { - box.reloadData() - } - } - } -} diff --git a/Sources/Features/Preferences/Session/Panels/Filters/FiltersPredicateEditor.swift b/Sources/Features/Preferences/Session/Panels/Filters/FiltersPredicateEditor.swift deleted file mode 100644 index 0bcd3c8..0000000 --- a/Sources/Features/Preferences/Session/Panels/Filters/FiltersPredicateEditor.swift +++ /dev/null @@ -1,176 +0,0 @@ -// -// FiltersPredicateEditor.swift -// VisualDiffer -// -// Created by davide ficano on 25/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -// swiftlint:disable multiline_arguments -class FiltersPredicateEditor: NSPredicateEditor { - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - rowHeight = 25.0 - autoresizingMask = [.width, .height] - canRemoveAllRows = false - nestingMode = .compound - - rowTemplates = [ - NSPredicateEditorRowTemplate(compoundTypes: nsNumberArray([ - NSCompoundPredicate.LogicalType.and.rawValue, - NSCompoundPredicate.LogicalType.or.rawValue, - ])), - - // File Name Ignore Case - TitlePredicateEditorRowTemplate( - keyPathDisplayNames: ["fileName": NSLocalizedString("Name (Ignore Case)", comment: "")], - leftKeyPath: "fileName", - rightExpressionAttributeType: .stringAttributeType, - caseInsensitive: true, - operators: comparisonPredicateMap([ - .contains, - .beginsWith, - .endsWith, - .equalTo, - .notEqualTo, - .like, - ], attributeType: .stringAttributeType) - ), - - // File Name Case Sensitive - TitlePredicateEditorRowTemplate( - keyPathDisplayNames: ["fileName": NSLocalizedString("Name", comment: "")], - leftKeyPath: "fileName", - rightExpressionAttributeType: .stringAttributeType, - caseInsensitive: false, - operators: comparisonPredicateMap([ - .contains, - .beginsWith, - .endsWith, - .equalTo, - .notEqualTo, - .like, - ], attributeType: .stringAttributeType) - ), - - // Path Ignore Case - TitlePredicateEditorRowTemplate( - keyPathDisplayNames: ["pathRelativeToRoot": NSLocalizedString("Path (Ignore Case)", comment: "")], - leftKeyPath: "pathRelativeToRoot", - rightExpressionAttributeType: .stringAttributeType, - caseInsensitive: true, - operators: comparisonPredicateMap([ - .contains, - .equalTo, - .endsWith, - ], attributeType: .stringAttributeType) - ), - // Path Case Sensitive - TitlePredicateEditorRowTemplate( - keyPathDisplayNames: ["pathRelativeToRoot": NSLocalizedString("Path", comment: "")], - leftKeyPath: "pathRelativeToRoot", - rightExpressionAttributeType: .stringAttributeType, - caseInsensitive: false, - operators: comparisonPredicateMap([ - .contains, - .equalTo, - .endsWith, - ], attributeType: .stringAttributeType) - ), - - // Size - TOPFileSizePredicateEditorRowTemplate( - keyPathDisplayNames: ["fileSize": NSLocalizedString("File Size", comment: "")], - leftKeyPath: "fileSize", - rightExpressionAttributeType: .floatAttributeType, - caseInsensitive: false, - operators: comparisonPredicateMap([ - .equalTo, - .notEqualTo, - .lessThan, - .greaterThan, - ], attributeType: .floatAttributeType) - ), - - // Date - TOPTimestampPredicateEditorRowTemplate( - keyPathDisplayNames: ["fileObjectModificationDate": NSLocalizedString("File Modification Date", comment: "")], - leftKeyPath: "fileObjectModificationDate", - rightExpressionAttributeType: .dateAttributeType, - caseInsensitive: false, - operators: comparisonPredicateMap([ - .equalTo, - .notEqualTo, - .lessThan, - .greaterThan, - ], attributeType: .dateAttributeType) - ), - ] - resizeRows(200) - } - - private func nsNumberArray(_ array: [UInt]) -> [NSNumber] { - array.map { NSNumber(value: $0) } - } - - private func comparisonPredicateMap( - _ array: [NSComparisonPredicate.Operator], - attributeType: NSAttributeType - ) -> [[NSNumber: String]] { - array.map { [NSNumber(value: $0.rawValue): operatorString($0, attributeType: attributeType)] } - } - - private func operatorString( - _ comparisonOperator: NSComparisonPredicate.Operator, - attributeType: NSAttributeType - ) -> String { - if attributeType == .floatAttributeType { - return switch comparisonOperator { - case .equalTo: NSLocalizedString("equals", comment: "") - case .notEqualTo: NSLocalizedString("is not", comment: "") - case .lessThan: NSLocalizedString("is less than", comment: "") - case .greaterThan: NSLocalizedString("is greater than", comment: "") - default: fatalError("Unsupported operator \(comparisonOperator)") - } - } - - if attributeType == .dateAttributeType { - return switch comparisonOperator { - case .equalTo: NSLocalizedString("exactly", comment: "") - case .notEqualTo: NSLocalizedString("is not", comment: "") - case .lessThan: NSLocalizedString("before", comment: "") - case .greaterThan: NSLocalizedString("after", comment: "") - default: fatalError("Unsupported operator \(comparisonOperator)") - } - } - - if attributeType == .stringAttributeType { - return switch comparisonOperator { - case .beginsWith: NSLocalizedString("begins with", comment: "") - case .contains: NSLocalizedString("contains", comment: "") - case .endsWith: NSLocalizedString("ends with", comment: "") - case .equalTo: NSLocalizedString("is", comment: "") - case .greaterThan: NSLocalizedString("is greater than", comment: "") - case .lessThan: NSLocalizedString("is less than", comment: "") - case .like: NSLocalizedString("is like", comment: "") - case .notEqualTo: NSLocalizedString("is not", comment: "") - default: fatalError("Unsupported operator \(comparisonOperator)") - } - } - fatalError("Unsupoorted attribute type \(attributeType)") - } -} - -// swiftlint:enable multiline_arguments diff --git a/Sources/Features/Preferences/Session/Panels/Filters/SessionPreferencesFiltersBox.swift b/Sources/Features/Preferences/Session/Panels/Filters/SessionPreferencesFiltersBox.swift deleted file mode 100644 index bd49c68..0000000 --- a/Sources/Features/Preferences/Session/Panels/Filters/SessionPreferencesFiltersBox.swift +++ /dev/null @@ -1,232 +0,0 @@ -// -// SessionPreferencesFiltersBox.swift -// VisualDiffer -// -// Created by davide ficano on 26/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class SessionPreferencesFiltersBox: PreferencesBox, NSMenuItemValidation { - private lazy var actionMenu: NSPopUpButton = { - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.bezelStyle = .shadowlessSquare - view.setButtonType(.momentaryPushIn) - view.isBordered = true - view.alignment = .left - - view.translatesAutoresizingMaskIntoConstraints = false - - view.target = self - view.menu = createActionPopupMenu() - - return view - }() - - private lazy var predicateEditorScrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = predicateEditor - - predicateEditor.translatesAutoresizingMaskIntoConstraints = false - predicateEditor.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true - - return view - }() - - private lazy var predicateEditor: FiltersPredicateEditor = { - let view = FiltersPredicateEditor(frame: .zero) - - if let defaultFilters = SessionDiff.defaultFileFilters() { - view.objectValue = NSPredicate(format: defaultFilters) - } - return view - }() - - private var currentFilters: String? { - (predicateEditor.objectValue as? NSPredicate)?.description - } - - override init(title: String) { - super.init(title: title) - - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(predicateEditorScrollView) - contentView.addSubview(actionMenu) - } - - setupConstraints() - } - - private func createActionPopupMenu() -> NSMenu { - let popupMenu = NSMenu() - - // the button title image - popupMenu.addItem( - withTitle: "", - action: nil, - keyEquivalent: "" - ) - .image = NSImage(named: NSImage.actionTemplateName) - popupMenu.addItem( - withTitle: NSLocalizedString("Fill with Defaults", comment: ""), - action: #selector(fillWithDefaults), - keyEquivalent: "" - ) - popupMenu.addItem( - withTitle: NSLocalizedString("Set Current as Defaults", comment: ""), - action: #selector(saveDefaults), - keyEquivalent: "" - ) - popupMenu.addItem( - withTitle: NSLocalizedString("Restore Factory Defaults", comment: ""), - action: #selector(restoreDefaults), - keyEquivalent: "" - ) - popupMenu.addItem(NSMenuItem.separator()) - popupMenu.addItem( - withTitle: NSLocalizedString("Copy to Clipboard", comment: ""), - action: #selector(copyToClipboard), - keyEquivalent: "" - ) - popupMenu.addItem( - withTitle: NSLocalizedString("Paste from Clipboard", comment: ""), - action: #selector(pasteFromClipboard), - keyEquivalent: "" - ) - - for item in popupMenu.items { - item.target = self - } - - return popupMenu - } - - private func setupConstraints() { - guard let contentView else { - return - } - NSLayoutConstraint.activate([ - actionMenu.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - actionMenu.topAnchor.constraint(equalTo: contentView.topAnchor), - - predicateEditorScrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - predicateEditorScrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - predicateEditorScrollView.topAnchor.constraint(equalTo: actionMenu.bottomAnchor, constant: 10), - predicateEditorScrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), - ]) - } - - // MARK: - Action Methods - - @objc func copyToClipboard(_: AnyObject) { - if let filters = currentFilters as? NSString { - let pasteboard = NSPasteboard.general - pasteboard.clearContents() - pasteboard.writeObjects([filters]) - } - } - - @objc func pasteFromClipboard(_: AnyObject) { - let pasteboard = NSPasteboard.general - let supportedTypes = [NSPasteboard.PasteboardType.string] - - if let bestType = pasteboard.availableType(from: supportedTypes), - let filters = pasteboard.string(forType: bestType) { - do { - predicateEditor.objectValue = try NSPredicate.createSafe(withFormat: filters) - } catch { - let alert = NSAlert() - - alert.messageText = NSLocalizedString("The file filter expression contains errors", comment: "") - alert.alertStyle = .critical - alert.informativeText = error.localizedDescription - - alert.runModal() - } - } - } - - @objc func saveDefaults(_: AnyObject) { - guard let filters = currentFilters else { - return - } - - var overwrite = true - if CommonPrefs.shared.defaultFileFilters != nil { - overwrite = NSAlert.showModalConfirm( - messageText: NSLocalizedString("Replace Custom Defaults", comment: ""), - informativeText: NSLocalizedString("Do you want to replace the current custom defaults?", comment: "") - ) - } - if overwrite { - CommonPrefs.shared.defaultFileFilters = filters - } - } - - @objc func restoreDefaults(_: AnyObject) { - let result = NSAlert.showModalConfirm( - messageText: NSLocalizedString("Restore Defaults", comment: ""), - informativeText: NSLocalizedString("The custom-defined defaults will be replaced with the application defaults. Are you sure?", comment: "") - ) - if result { - CommonPrefs.shared.defaultFileFilters = nil - } - } - - @objc func fillWithDefaults(_: AnyObject) { - if let defaultFilters = SessionDiff.defaultFileFilters() { - predicateEditor.objectValue = NSPredicate(format: defaultFilters) - } - } - - // MARK: - NSMenuItemValidation - - func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - var enabled = true - let action = menuItem.action - - if action == #selector(restoreDefaults) { - let defaultFilters = CommonPrefs.shared.defaultFileFilters - menuItem.isHidden = defaultFilters == nil - } else if action == #selector(saveDefaults) { - enabled = if let defaultFilters = CommonPrefs.shared.defaultFileFilters, - let filters = currentFilters { - defaultFilters != filters - } else { - false - } - } - - return enabled - } - - override func reloadData() { - if let str = delegate?.preferenceBox(self, stringForKey: .defaultFileFilters) { - predicateEditor.objectValue = NSPredicate(format: str) - } else { - predicateEditor.objectValue = nil - } - } - - func updatePendingData() { - if let filters = currentFilters { - delegate?.preferenceBox(self, setString: filters, forKey: .defaultFileFilters) - } - } -} diff --git a/Sources/Features/Preferences/Session/Panels/Filters/SessionPreferencesFiltersPanel.swift b/Sources/Features/Preferences/Session/Panels/Filters/SessionPreferencesFiltersPanel.swift deleted file mode 100644 index a85fa0b..0000000 --- a/Sources/Features/Preferences/Session/Panels/Filters/SessionPreferencesFiltersPanel.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// SessionPreferencesFiltersPanel.swift -// VisualDiffer -// -// Created by davide ficano on 25/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class SessionPreferencesFiltersPanel: NSView, PreferencesPanelDataSource { - var delegate: PreferencesBoxDataSource? { - didSet { - filterBox.delegate = delegate - } - } - - private let filterBox: SessionPreferencesFiltersBox - - override init(frame frameRect: NSRect) { - filterBox = SessionPreferencesFiltersBox( - title: NSLocalizedString("Do not show files matching the criteria below", comment: "") - ) - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - super.addSubview(filterBox) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - filterBox.leadingAnchor.constraint(equalTo: leadingAnchor), - filterBox.trailingAnchor.constraint(equalTo: trailingAnchor), - filterBox.topAnchor.constraint(equalTo: topAnchor, constant: 5), - filterBox.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5), - ]) - } - - func reloadData() { - filterBox.reloadData() - } - - @objc func updatePendingData() { - filterBox.updatePendingData() - } -} diff --git a/Sources/Legacy/DiffEngine/UDiffScriptBuilder.h b/Sources/Legacy/DiffEngine/UDiffScriptBuilder.h deleted file mode 100644 index 64e8bc5..0000000 --- a/Sources/Legacy/DiffEngine/UDiffScriptBuilder.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// ScriptBuilder.h -// VisualDiffer -// -// Created by davide ficano on 23/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** The result of comparison is an "edit script": a chain of change objects. - Each change represents one place where some lines are deleted - and some are inserted. - - LINE0 and LINE1 are the first affected lines in the two files (origin 0). - DELETED is the number of lines deleted here from file 0. - INSERTED is the number of lines inserted here in file 1. - - If DELETED is 0 then LINE0 is the number of the line before - which the insertion was done; vice versa for INSERTED and LINE1. */ - -@interface UDiffChange : NSObject -/** Previous or next edit command. */ -@property (strong, nullable) UDiffChange* link; -/** Line number of 1st deleted line. */ -@property (assign) int line0; -/** Line number of 1st inserted line. */ -@property (assign) int line1; -/** # lines of file 1 changed here. */ -@property (assign) int inserted; -/** # lines of file 0 changed here. */ -@property (assign) int deleted; - -+ (instancetype)changeWithRange:(int)line0 line1:(int)line1 deleted:(int)deleted inserted:(int)inserted old:(nullable UDiffChange*)old; -- (instancetype)initWithRange:(int)line0 line1:(int)line1 deleted:(int)deleted inserted:(int)inserted old:(nullable UDiffChange*)old; -@end - - -@interface UDiffScriptBuilder : NSObject -/** Scan the tables of which lines are inserted and deleted, - producing an edit script. - @param changed0 true for lines in first file which do not match 2nd - @param len0 number of lines in first file - @param changed1 true for lines in 2nd file which do not match 1st - @param len1 number of lines in 2nd file - @return a linked list of changes - or null - */ -- (nullable UDiffChange*)build_script:(BOOL*)changed0 len0:(NSUInteger)len0 changed1:(BOOL*)changed1 len1:(NSUInteger)len1; -@end - -@interface UDiffReverseScript : UDiffScriptBuilder -@end; - -@interface UDiffForwardScript : UDiffScriptBuilder -@end; - -NS_ASSUME_NONNULL_END diff --git a/Sources/Legacy/DiffEngine/UDiffScriptBuilder.m b/Sources/Legacy/DiffEngine/UDiffScriptBuilder.m deleted file mode 100644 index fd77b3d..0000000 --- a/Sources/Legacy/DiffEngine/UDiffScriptBuilder.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// ScriptBuilder.m -// VisualDiffer -// -// Created by davide ficano on 23/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import "UDiffScriptBuilder.h" - -@implementation UDiffChange - -+ (instancetype)changeWithRange:(int)pline0 line1:(int)pline1 deleted:(int)pdeleted inserted:(int)pinserted old:(UDiffChange*)pold { - return [[self alloc] initWithRange:pline0 line1:pline1 deleted:pdeleted inserted:pinserted old:pold]; -} - -/** Cons an additional entry onto the front of an edit script OLD. - LINE0 and LINE1 are the first affected lines in the two files (origin 0). - DELETED is the number of lines deleted here from file 0. - INSERTED is the number of lines inserted here in file 1. - - If DELETED is 0 then LINE0 is the number of the line before - which the insertion was done; vice versa for INSERTED and LINE1. */ -- (instancetype)initWithRange:(int)pline0 line1:(int)pline1 deleted:(int)pdeleted inserted:(int)pinserted old:(UDiffChange*)pold { - self = [super init]; - - if (self) { - _line0 = pline0; - _line1 = pline1; - _inserted = pinserted; - _deleted = pdeleted; - _link = pold; - } - - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"%d, %d inserted %d, deleted %d", self.line0 + 1, self.line1 + 1, self.inserted, self.deleted]; -} - -@end - -@implementation UDiffScriptBuilder - -- (UDiffChange*)build_script:(BOOL*)changed0 len0:(NSUInteger)len0 changed1:(BOOL*)changed1 len1:(NSUInteger)len1 { - return nil; -} - -@end - -/** Scan the tables of which lines are inserted and deleted, - producing an edit script in reverse order. */ - -@implementation UDiffReverseScript -- (UDiffChange*)build_script:(BOOL*)changed0 len0:(NSUInteger)len0 changed1:(BOOL*)changed1 len1:(NSUInteger)len1 { - UDiffChange* script = nil; - int i0 = 0, i1 = 0; - while (i0 < (NSInteger)len0 || i1 < (NSInteger)len1) { - if (changed0[1+i0] || changed1[1+i1]) { - int line0 = i0, line1 = i1; - - /* Find # lines changed here in each file. */ - while (changed0[1+i0]) ++i0; - while (changed1[1+i1]) ++i1; - - /* Record this change. */ - script = [UDiffChange changeWithRange:line0 line1:line1 deleted:i0 - line0 inserted:i1 - line1 old:script]; - } - - /* We have reached lines in the two files that match each other. */ - i0++; i1++; - } - - return script; -} -@end; - -@implementation UDiffForwardScript -- (UDiffChange*)build_script:(BOOL*)changed0 len0:(NSUInteger)len0 changed1:(BOOL*)changed1 len1:(NSUInteger)len1 { - UDiffChange* script = nil; - int i0 = (int)len0, i1 = (int)len1; - - while (i0 >= 0 || i1 >= 0) - { - if (changed0[i0] || changed1[i1]) - { - int line0 = i0, line1 = i1; - - /* Find # lines changed here in each file. */ - while (changed0[i0]) --i0; - while (changed1[i1]) --i1; - - /* Record this change. */ - script = [UDiffChange changeWithRange:i0 line1:i1 deleted:line0 - i0 inserted:line1 - i1 old:script]; - } - - /* We have reached lines in the two files that match each other. */ - i0--; i1--; - } - - return script; -} -@end; diff --git a/Sources/Legacy/DiffEngine/UnifiedDiff.h b/Sources/Legacy/DiffEngine/UnifiedDiff.h deleted file mode 100644 index dbebf01..0000000 --- a/Sources/Legacy/DiffEngine/UnifiedDiff.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// UnifiedDiff.h -// VisualDiffer -// -// Created by davide ficano on 23/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import - -@class UDiffScriptBuilder; -@class UDiffChange; - -NS_ASSUME_NONNULL_BEGIN - -@interface UDiffFileData : NSObject { - /** Vector, indexed by line number, containing an equivalence code for - each line. It is this vector that is actually compared with that - of another file to generate differences. */ - int* equivs; - - int* pequiv_max; - BOOL* pno_discards; -} - -/** Number of elements (lines) in this file. */ -@property (assign) NSUInteger buffered_lines; -/** Array, indexed by real origin-1 line number, containing true for a line that is an insertion or a deletion. */ -@property (assign, nullable) BOOL* changed_flag; -/** Vector, like the previous one except that the elements for discarded lines have been squeezed out. */ -@property (assign) int* undiscarded; -/** Vector mapping virtual line numbers (not counting discarded lines) to real ones (counting those lines). Both are origin-0. */ -@property (assign) int* realindexes; -/** Total number of nondiscarded lines. */ -@property (assign) int nondiscarded_lines; - -+ (instancetype)fileData:(NSArray*)data h:(NSMutableDictionary*)h equivMax:(int*)ppequiv_max noDiscards:(BOOL*)ppnoDiscards - stringifier:(NSString* (^)(id obj))block; -- (instancetype)initWithData:(NSArray*)data h:(NSMutableDictionary*)h equivMax:(int*)ppequiv_max noDiscards:(BOOL*)ppnoDiscards - stringifier:(NSString* (^)(id obj))block; - -@end - -@interface UnifiedDiff : NSObject { -@private - /** 1 more than the maximum equivalence value used for this or its - sibling file. */ - int equiv_max; - - /** When set to true, the comparison uses a heuristic to speed it up. - With this heuristic, for files with a constant small density - of changes, the algorithm is linear in the file size. */ - BOOL heuristic; - - /** When set to true, the algorithm returns a guarranteed minimal - set of changes. This makes things slower, sometimes much slower. */ - BOOL no_discards; - - int* xvec, *yvec; /* Vectors being compared. */ - int* fdiag; /* Vector, indexed by diagonal, containing - the X coordinate of the point furthest - along the given diagonal in the forward - search of the edit matrix. */ - int* bdiag; /* Vector, indexed by diagonal, containing - the X coordinate of the point furthest - along the given diagonal in the backward - search of the edit matrix. */ - int fdiagoff, bdiagoff; - int cost; - - BOOL inhibit; -} - -- (instancetype)initWithOriginalLines:(NSArray*)a revisedLines:(NSArray*)b stringifier:(NSString* (^)(id obj))block; -- (nullable UDiffChange*)diff_2:(BOOL)reverse; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/Legacy/DiffEngine/UnifiedDiff.m b/Sources/Legacy/DiffEngine/UnifiedDiff.m deleted file mode 100644 index 38ba620..0000000 --- a/Sources/Legacy/DiffEngine/UnifiedDiff.m +++ /dev/null @@ -1,739 +0,0 @@ -// -// UnifiedDiff.m -// VisualDiffer -// -// Created by davide ficano on 23/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import "UnifiedDiff.h" -#import "UDiffScriptBuilder.h" - -/** Snakes bigger than this are considered "big". */ -static const NSInteger SNAKE_LIMIT = 20; - -static UDiffScriptBuilder* forwardScript; -static UDiffScriptBuilder* reverseScript; - -@interface UDiffFileData() -- (char*)discardable:(int*)counts NS_RETURNS_INNER_POINTER; -- (void)filterDiscards:(char*)discards; -- (void)discard:(char*)discards; -@end - -@interface UnifiedDiff() -- (UDiffChange*)diff:(UDiffScriptBuilder*)bld; -@end - -@implementation UDiffFileData - -+ (instancetype)fileData:(NSArray*)data h:(NSMutableDictionary*)h equivMax:(int*)ppequiv_max noDiscards:(BOOL*)ppno_discards - stringifier:(NSString* (^)(id obj))block { - return [[self alloc] initWithData:data h:h equivMax:ppequiv_max noDiscards:ppno_discards stringifier:block]; -} - -- (instancetype)initWithData:(NSArray*)data h:(NSMutableDictionary*)h equivMax:(int*)ppequiv_max noDiscards:(BOOL*)ppno_discards - stringifier:(NSString* (^)(id obj))block { - self = [super init]; - - if (self) { - pequiv_max = ppequiv_max; - pno_discards = ppno_discards; - _buffered_lines = data.count; - - equivs = calloc(_buffered_lines, sizeof(int)); - _undiscarded = calloc(_buffered_lines, sizeof(int)); - _realindexes = calloc(_buffered_lines, sizeof(int)); - _changed_flag = NULL; - - int i = 0; - for (id o in data) { - NSString* str = block(o); - NSNumber* ir = h[str]; - if (ir == nil) { - h[str] = @(equivs[i] = *pequiv_max); - (*pequiv_max)++; - } else { - equivs[i] = [ir intValue]; - } - ++i; - } - } - - return self; -} - -- (void)dealloc { - free(equivs); - free(_undiscarded); - free(_realindexes); - free(_changed_flag); - -} - -/** Allocate changed array for the results of comparison. */ -- (void)clear { - /* Allocate a flag for each line of each file, saying whether that line - is an insertion or deletion. - Allocate an extra element, always zero, at each end of each vector. - */ - _changed_flag = calloc(_buffered_lines + 2, sizeof(BOOL)); -} - -/** Return equiv_count[I] as the number of lines in this file - that fall in equivalence class I. - @return the array of equivalence class counts. - */ -- (int*)equivCount { - int equiv_max = *pequiv_max; - int* equiv_count = calloc(equiv_max, sizeof(int)); - for (int i = 0; i < (NSInteger)_buffered_lines; ++i) - ++equiv_count[equivs[i]]; - return equiv_count; -} - -/** Discard lines that have no matches in another file. - - A line which is discarded will not be considered by the actual - comparison algorithm; it will be as if that line were not in the file. - The file's `realindexes' table maps virtual line numbers - (which don't count the discarded lines) into real line numbers; - this is how the actual comparison algorithm produces results - that are comprehensible when the discarded lines are counted. -

- When we discard a line, we also mark it as a deletion or insertion - so that it will be printed in the output. - @param f the other file - */ -- (void)discard_confusing_lines:(UDiffFileData*)f { - [self clear]; - /* Set up table of which lines are going to be discarded. */ - int* tempEquivCount = [f equivCount]; - char* discarded = [self discardable:tempEquivCount]; - - /* Don't really discard the provisional lines except when they occur - in a run of discardables, with nonprovisionals at the beginning - and end. */ - [self filterDiscards:discarded]; - - /* Actually discard the lines. */ - [self discard:discarded]; - - free(discarded); - free(tempEquivCount); -} - -/** Mark to be discarded each line that matches no line of another file. - If a line matches many lines, mark it as provisionally discardable. - @see equivCount() - @param counts The count of each equivalence number for the other file. - @return 0=nondiscardable, 1=discardable or 2=provisionally discardable - for each line - */ - -- (char*)discardable:(int*)counts { - NSInteger end = (NSInteger)_buffered_lines; - char* discards = calloc(end, sizeof(char)); - int many = 5; - NSUInteger tem = end / 64; - - /* Multiply MANY by approximate square root of number of lines. - That is the threshold for provisionally discardable lines. */ - while ((tem = tem >> 2) > 0) - many *= 2; - - for (int i = 0; i < end; i++) - { - int nmatch; - if (equivs[i] == 0) - continue; - nmatch = counts[equivs[i]]; - if (nmatch == 0) - discards[i] = 1; - else if (nmatch > many) - discards[i] = 2; - } - return discards; -} - -/** Don't really discard the provisional lines except when they occur - in a run of discardables, with nonprovisionals at the beginning - and end. */ - -- (void)filterDiscards:(char*)discards { - NSInteger end = (NSInteger)_buffered_lines; - - for (int i = 0; i < end; i++) - { - /* Cancel provisional discards not in middle of run of discards. */ - if (discards[i] == 2) - discards[i] = 0; - else if (discards[i] != 0) - { - /* We have found a nonprovisional discard. */ - int j; - int length; - int provisional = 0; - - /* Find end of this run of discardable lines. - Count how many are provisionally discardable. */ - for (j = i; j < end; j++) - { - if (discards[j] == 0) - break; - if (discards[j] == 2) - ++provisional; - } - - /* Cancel provisional discards at end, and shrink the run. */ - while (j > i && discards[j - 1] == 2) { - discards[--j] = 0; --provisional; - } - - /* Now we have the length of a run of discardable lines - whose first and last are not provisional. */ - length = j - i; - - /* If 1/4 of the lines in the run are provisional, - cancel discarding of all provisional lines in the run. */ - if (provisional * 4 > length) - { - while (j > i) - if (discards[--j] == 2) - discards[j] = 0; - } - else - { - int consec; - int minimum = 1; - int tem = length / 4; - - /* MINIMUM is approximate square root of LENGTH/4. - A subrun of two or more provisionals can stand - when LENGTH is at least 16. - A subrun of 4 or more can stand when LENGTH >= 64. */ - while ((tem = tem >> 2) > 0) - minimum *= 2; - minimum++; - - /* Cancel any subrun of MINIMUM or more provisionals - within the larger run. */ - for (j = 0, consec = 0; j < length; j++) - if (discards[i + j] != 2) - consec = 0; - else if (minimum == ++consec) - /* Back up to start of subrun, to cancel it all. */ - j -= consec; - else if (minimum < consec) - discards[i + j] = 0; - - /* Scan from beginning of run - until we find 3 or more nonprovisionals in a row - or until the first nonprovisional at least 8 lines in. - Until that point, cancel any provisionals. */ - for (j = 0, consec = 0; j < length; j++) - { - if (j >= 8 && discards[i + j] == 1) - break; - if (discards[i + j] == 2) { - consec = 0; discards[i + j] = 0; - } - else if (discards[i + j] == 0) - consec = 0; - else - consec++; - if (consec == 3) - break; - } - - /* I advances to the last line of the run. */ - i += length - 1; - - /* Same thing, from end. */ - for (j = 0, consec = 0; j < length; j++) - { - if (j >= 8 && discards[i - j] == 1) - break; - if (discards[i - j] == 2) { - consec = 0; discards[i - j] = 0; - } - else if (discards[i - j] == 0) - consec = 0; - else - consec++; - if (consec == 3) - break; - } - } - } - } -} - -/** Actually discard the lines. - @param discards flags lines to be discarded - */ -- (void)discard:(char*)discards { - NSInteger end = (NSInteger)_buffered_lines; - int j = 0; - for (int i = 0; i < end; ++i) - if (*pno_discards || discards[i] == 0) - { - _undiscarded[j] = equivs[i]; - _realindexes[j++] = i; - } - else - _changed_flag[1+i] = true; - _nondiscarded_lines = j; -} - -/** Adjust inserts/deletes of blank lines to join changes - as much as possible. - - We do something when a run of changed lines include a blank - line at one end and have an excluded blank line at the other. - We are free to choose which blank line is included. - `compareseq' always chooses the one at the beginning, - but usually it is cleaner to consider the following blank line - to be the "change". The only exception is if the preceding blank line - would join this change to other changes. - @param f the file being compared against - */ - -- (void)shift_boundaries:(UDiffFileData*)f { - BOOL* changed = _changed_flag; - BOOL* other_changed = f.changed_flag; - int i = 0; - int j = 0; - NSInteger i_end = (NSInteger)_buffered_lines; - int preceding = -1; - int other_preceding = -1; - - for (;;) - { - int start, end, other_start; - - /* Scan forwards to find beginning of another run of changes. - Also keep track of the corresponding point in the other file. */ - - while (i < i_end && !changed[1+i]) - { - while (other_changed[1+j++]) - /* Non-corresponding lines in the other file - will count as the preceding batch of changes. */ - other_preceding = j; - i++; - } - - if (i == i_end) - break; - - start = i; - other_start = j; - - for (;;) - { - /* Now find the end of this run of changes. */ - - while (i < i_end && changed[1+i]) i++; - end = i; - - /* If the first changed line matches the following unchanged one, - and this run does not follow right after a previous run, - and there are no lines deleted from the other file here, - then classify the first changed line as unchanged - and the following line as changed in its place. */ - - /* You might ask, how could this run follow right after another? - Only because the previous run was shifted here. */ - - if (end != i_end - && equivs[start] == equivs[end] - && !other_changed[1+j] - && end != i_end - && !((preceding >= 0 && start == preceding) - || (other_preceding >= 0 - && other_start == other_preceding))) - { - changed[1+end++] = true; - changed[1+start++] = false; - ++i; - /* Since one line-that-matches is now before this run - instead of after, we must advance in the other file - to keep in synch. */ - ++j; - } - else - break; - } - - preceding = i; - other_preceding = j; - } -} -@end - -#pragma mark - -#pragma mark UnifiedDiff methods - -@interface UnifiedDiff() -@property (strong) UDiffFileData* filevec0; -@property (strong) UDiffFileData* filevec1; -@end - -@implementation UnifiedDiff - -/** Prepare to find differences between two arrays. Each element of - the arrays is translated to an "equivalence number" based on - the result of equals. The original Object arrays - are no longer needed for computing the differences. They will - be needed again later to print the results of the comparison as - an edit script, if desired. - */ -- (instancetype)initWithOriginalLines:(NSArray*)a revisedLines:(NSArray*)b stringifier:(NSString* (^)(id obj))block { - static dispatch_once_t pred; - dispatch_once(&pred, ^{ - forwardScript = [[UDiffForwardScript alloc] init]; - reverseScript = [[UDiffReverseScript alloc] init]; - }); - self = [super init]; - - if (self) { - equiv_max = 1; - heuristic = NO; - no_discards = NO; - inhibit = NO; - NSMutableDictionary* h = [NSMutableDictionary dictionaryWithCapacity:a.count + b.count]; - _filevec0 = [UDiffFileData fileData:a h:h equivMax:&equiv_max noDiscards:&no_discards stringifier:block]; - _filevec1 = [UDiffFileData fileData:b h:h equivMax:&equiv_max noDiscards:&no_discards stringifier:block]; - } - - return self; -} - -/** Find the midpoint of the shortest edit script for a specified - portion of the two files. - - We scan from the beginnings of the files, and simultaneously from the ends, - doing a breadth-first search through the space of edit-sequence. - When the two searches meet, we have found the midpoint of the shortest - edit sequence. - - The value returned is the number of the diagonal on which the midpoint lies. - The diagonal number equals the number of inserted lines minus the number - of deleted lines (counting only lines before the midpoint). - The edit cost is stored into COST; this is the total number of - lines inserted or deleted (counting only lines before the midpoint). - - This function assumes that the first lines of the specified portions - of the two files do not match, and likewise that the last lines do not - match. The caller must trim matching lines from the beginning and end - of the portions it is going to specify. - - Note that if we return the "wrong" diagonal value, or if - the value of bdiag at that diagonal is "wrong", - the worst this can do is cause suboptimal diff output. - It cannot cause incorrect diff output. */ - -- (int)diag:(int)xoff xlim:(int)xlim yoff:(int)yoff ylim:(int)ylim { - int* fd = fdiag; // Give the compiler a chance. - int* bd = bdiag; // Additional help for the compiler. - int* xv = xvec; // Still more help for the compiler. - int* yv = yvec; // And more and more . . . - int dmin = xoff - ylim; // Minimum valid diagonal. - int dmax = xlim - yoff; // Maximum valid diagonal. - int fmid = xoff - yoff; // Center diagonal of top-down search. - int bmid = xlim - ylim; // Center diagonal of bottom-up search. - int fmin = fmid, fmax = fmid; // Limits of top-down search. - int bmin = bmid, bmax = bmid; // Limits of bottom-up search. - /* True if southeast corner is on an odd - diagonal with respect to the northwest. */ - BOOL odd = (fmid - bmid & 1) != 0; - - fd[fdiagoff + fmid] = xoff; - bd[bdiagoff + bmid] = xlim; - - for (int c = 1;; ++c) - { - int d; /* Active diagonal. */ - BOOL big_snake = NO; - - /* Extend the top-down search by an edit step in each diagonal. */ - if (fmin > dmin) - fd[fdiagoff + --fmin - 1] = -1; - else - ++fmin; - if (fmax < dmax) - fd[fdiagoff + ++fmax + 1] = -1; - else - --fmax; - for (d = fmax; d >= fmin; d -= 2) - { - int x, y, oldx, tlo = fd[fdiagoff + d - 1], thi = fd[fdiagoff + d + 1]; - - if (tlo >= thi) - x = tlo + 1; - else - x = thi; - oldx = x; - y = x - d; - while (x < xlim && y < ylim && xv[x] == yv[y]) { - ++x; ++y; - } - if (x - oldx > SNAKE_LIMIT) - big_snake = true; - fd[fdiagoff + d] = x; - if (odd && bmin <= d && d <= bmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) - { - cost = 2 * c - 1; - return d; - } - } - - /* Similar extend the bottom-up search. */ - if (bmin > dmin) - bd[bdiagoff + --bmin - 1] = INT_MAX; - else - ++bmin; - if (bmax < dmax) - bd[bdiagoff + ++bmax + 1] = INT_MAX; - else - --bmax; - for (d = bmax; d >= bmin; d -= 2) - { - int x, y, oldx, tlo = bd[bdiagoff + d - 1], thi = bd[bdiagoff + d + 1]; - - if (tlo < thi) - x = tlo; - else - x = thi - 1; - oldx = x; - y = x - d; - while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) { - --x; --y; - } - if (oldx - x > SNAKE_LIMIT) - big_snake = true; - bd[bdiagoff + d] = x; - if (!odd && fmin <= d && d <= fmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) - { - cost = 2 * c; - return d; - } - } - - /* Heuristic: check occasionally for a diagonal that has made - lots of progress compared with the edit distance. - If we have any such, find the one that has made the most - progress and return it as if it had succeeded. - - With this heuristic, for files with a constant small density - of changes, the algorithm is linear in the file size. */ - - if (c > 200 && big_snake && heuristic) - { - int best = 0; - int bestpos = -1; - - for (d = fmax; d >= fmin; d -= 2) - { - int dd = d - fmid; - int x = fd[fdiagoff + d]; - int y = x - d; - int v = (x - xoff) * 2 - dd; - if (v > 12 * (c + (dd < 0 ? -dd : dd))) - { - if (v > best - && xoff + SNAKE_LIMIT <= x && x < xlim - && yoff + SNAKE_LIMIT <= y && y < ylim) - { - /* We have a good enough best diagonal; - now insist that it end with a significant snake. */ - int k; - - for (k = 1; xvec[x - k] == yvec[y - k]; k++) - if (k == SNAKE_LIMIT) - { - best = v; - bestpos = d; - break; - } - } - } - } - if (best > 0) - { - cost = 2 * c - 1; - return bestpos; - } - - best = 0; - for (d = bmax; d >= bmin; d -= 2) - { - int dd = d - bmid; - int x = bd[bdiagoff + d]; - int y = x - d; - int v = (xlim - x) * 2 + dd; - if (v > 12 * (c + (dd < 0 ? -dd : dd))) - { - if (v > best - && xoff < x && x <= xlim - SNAKE_LIMIT - && yoff < y && y <= ylim - SNAKE_LIMIT) - { - /* We have a good enough best diagonal; - now insist that it end with a significant snake. */ - int k; - - for (k = 0; xvec[x + k] == yvec[y + k]; k++) - if (k == SNAKE_LIMIT) - { - best = v; - bestpos = d; - break; - } - } - } - } - if (best > 0) - { - cost = 2 * c - 1; - return bestpos; - } - } - } -} - -/** Compare in detail contiguous subsequences of the two files - which are known, as a whole, to match each other. - - The results are recorded in the vectors filevec[N].changed_flag, by - storing a 1 in the element for each line that is an insertion or deletion. - - The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. - - Note that XLIM, YLIM are exclusive bounds. - All line numbers are origin-0 and discarded lines are not counted. */ - -- (void)compareseq:(int)xoff xlim:(int)xlim yoff:(int)yoff ylim:(int)ylim { - /* Slide down the bottom initial diagonal. */ - while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff]) { - ++xoff; ++yoff; - } - /* Slide up the top initial diagonal. */ - while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1]) { - --xlim; --ylim; - } - - /* Handle simple cases. */ - if (xoff == xlim) - while (yoff < ylim) - self.filevec1.changed_flag[1 + self.filevec1.realindexes[yoff++]] = YES; - else if (yoff == ylim) - while (xoff < xlim) - self.filevec0.changed_flag[1 + self.filevec0.realindexes[xoff++]] = YES; - else - { - /* Find a point of correspondence in the middle of the files. */ - - int d = [self diag:xoff xlim:xlim yoff:yoff ylim:ylim]; - int c = cost; -// int f = fdiag[fdiagoff + d]; - int b = bdiag[bdiagoff + d]; - - if (c == 1) - { - /* This should be impossible, because it implies that - one of the two subsequences is empty, - and that case was handled above without calling `diag'. - Let's verify that this is true. */ -// /throw new IllegalArgumentException("Empty subsequence"); - } - else - { - /* Use that point to split this problem into two subproblems. */ - [self compareseq:xoff xlim:b yoff:yoff ylim:b - d]; - /* This used to use f instead of b, - but that is incorrect! - It is not necessarily the case that diagonal d - has a snake from b to f. */ - [self compareseq:b xlim:xlim yoff:b - d ylim:ylim]; - } - } -} - -/** Discard lines from one file that have no matches in the other file. - */ - -- (void)discard_confusing_lines { - [self.filevec0 discard_confusing_lines:self.filevec1]; - [self.filevec1 discard_confusing_lines:self.filevec0]; -} - -/** Adjust inserts/deletes of blank lines to join changes - as much as possible. - */ - -- (void)shift_boundaries { - if (inhibit) - return; - [self.filevec0 shift_boundaries:self.filevec1]; - [self.filevec1 shift_boundaries:self.filevec0]; -} - -/* Report the differences of two files. */ -- (UDiffChange*)diff_2:(BOOL)reverse { - return [self diff:reverse ? reverseScript : forwardScript]; -} - -/** Get the results of comparison as an edit script. The script - is described by a list of changes. The standard ScriptBuilder - implementations provide for forward and reverse edit scripts. - Alternate implementations could, for instance, list common elements - instead of differences. - @param bld an object to build the script from change flags - @return the head of a list of changes - */ -- (UDiffChange*)diff:(UDiffScriptBuilder*)bld { - - /* Some lines are obviously insertions or deletions - because they don't match anything. Detect them now, - and avoid even thinking about them in the main comparison algorithm. */ - - [self discard_confusing_lines]; - - /* Now do the main comparison algorithm, considering just the - undiscarded lines. */ - - xvec = self.filevec0.undiscarded; - yvec = self.filevec1.undiscarded; - - int diags = self.filevec0.nondiscarded_lines + self.filevec1.nondiscarded_lines + 3; - fdiag = calloc(diags, sizeof(int)); - fdiagoff = self.filevec1.nondiscarded_lines + 1; - bdiag = calloc(diags, sizeof(int)); - bdiagoff = self.filevec1.nondiscarded_lines + 1; - - [self compareseq:0 - xlim:self.filevec0.nondiscarded_lines - yoff:0 - ylim:self.filevec1.nondiscarded_lines]; - fdiag = nil; - bdiag = nil; - - /* Modify the results slightly to make them prettier - in cases where that can validly be done. */ - - [self shift_boundaries]; - - /* Get the results of comparison in the form of a chain - of struct change's -- an edit script. */ - return [bld build_script:self.filevec0.changed_flag - len0:self.filevec0.buffered_lines - changed1:self.filevec1.changed_flag - len1:self.filevec1.buffered_lines]; -} - -/** Data on one input file being compared. - */ - - -@end diff --git a/Sources/Legacy/FileManager/BigFileFileOperationManager.h b/Sources/Legacy/FileManager/BigFileFileOperationManager.h deleted file mode 100644 index 8b0fc31..0000000 --- a/Sources/Legacy/FileManager/BigFileFileOperationManager.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// BigFileFileOperationManager.h -// VisualDiffer -// -// Created by davide ficano on 10/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -#import - -NS_SWIFT_NAME(defaultBigFileSizeThreshold) -static const uint64 BIG_FILE_SIZE_THRESHOLD = (uint64)(10 * 1024 * 1024); - -NS_ASSUME_NONNULL_BEGIN - -@protocol FileOperationManagerDelegate; -@class FileOperationManager; -@class CompareItem; - -@interface BigFileFileOperationManager: NSObject - -@property (strong) FileOperationManager* operationManager; -@property (nullable, strong) id delegate; - -- (instancetype)init:(FileOperationManager*)operationManager - delegate:(nullable id) delegate; - -- (BOOL)copy:(CompareItem*)srcRoot -destFullPath:(NSString*)destFullPath - error:(out NSError ** _Nullable)outError; - -- (BOOL)move:(CompareItem*)srcRoot -destFullPath:(NSString*)destFullPath - error:(out NSError ** _Nullable)outError; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/Legacy/FileManager/BigFileFileOperationManager.m b/Sources/Legacy/FileManager/BigFileFileOperationManager.m deleted file mode 100644 index 56edb7c..0000000 --- a/Sources/Legacy/FileManager/BigFileFileOperationManager.m +++ /dev/null @@ -1,190 +0,0 @@ -// -// BigFileFileOperationManager.m -// VisualDiffer -// -// Created by davide ficano on 10/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -#import "BigFileFileOperationManager.h" -#import "VisualDiffer-Swift.h" - -typedef OSStatus (*FSOperationObjectAsync)(FSFileOperationRef fileOp, const FSRef *source, const FSRef *destDir, CFStringRef _Nullable destName, OptionBits flags, FSFileOperationStatusProcPtr callback, CFTimeInterval statusChangeInterval, FSFileOperationClientContext *clientContext); - -// disabled warning on deprecated api usage (FSMoveObjectAsync) -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -void copyStatusCallback(FSFileOperationRef fileOp, - const FSRef *currentItem, - FSFileOperationStage stage, - OSStatus error, - CFDictionaryRef statusDictionary, - void *info); - -@implementation BigFileFileOperationManager - -- (instancetype)init:(FileOperationManager*)operationManager - delegate:(id) delegate { - self = [super init]; - - if (self) { - self.operationManager = operationManager; - self.delegate = delegate; - } - - return self; -} - - -- (BOOL)copy:(CompareItem*)srcRoot -destFullPath:(NSString*)destFullPath - error:(NSError**)outError { - [self.delegate fileManager:self.operationManager startBigFileOperationForItem:srcRoot]; - - NSError* error; - - [self.class copyObject:srcRoot.path - destPath:destFullPath - fileManager:self.operationManager - asyncOperation:FSCopyObjectAsync - error:&error]; - if ([self.delegate isBigFileOperationCancelled:self.operationManager]) { - // error isn't stored if operation is cancelled - [self.delegate fileManager:self.operationManager updateForItem:srcRoot]; - return NO; - } - - if (outError) { - *outError = error; - } - return error == nil; -} - -- (BOOL)move:(CompareItem*)srcRoot -destFullPath:(NSString*)destFullPath - error:(NSError**)outError { - [self.delegate fileManager:self.operationManager startBigFileOperationForItem:srcRoot]; - - NSError* error; - - [self.class copyObject:srcRoot.path - destPath:destFullPath - fileManager:self.operationManager - asyncOperation:FSMoveObjectAsync - error:&error]; - - if ([self.delegate isBigFileOperationCancelled:self.operationManager]) { - // error isn't stored if operation is cancelled - [self.delegate fileManager:self.operationManager updateForItem:srcRoot]; - return NO; - } - - if (outError) { - *outError = error; - } - return error == nil; -} - -+ (BOOL)copyObject:(NSString*)srcPath - destPath:(NSString*)destPath - fileManager:(FileOperationManager*)fileManager - asyncOperation:(FSOperationObjectAsync)operation - error:(NSError**)error { - CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault); - OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, runLoop, kCFRunLoopDefaultMode); - - if (status == noErr) { - FSRef source; - FSRef destination; - - FSPathMakeRef( (const UInt8 *)[srcPath fileSystemRepresentation], &source, NULL); - - Boolean isDir = true; - FSPathMakeRef( (const UInt8 *)[[destPath stringByDeletingLastPathComponent] fileSystemRepresentation], &destination, &isDir); - - FSFileOperationClientContext clientContext = {}; - clientContext.info = (__bridge void *)(fileManager); - - status = operation(fileOp, - &source, - &destination, // Full path to destination dir - NULL, // Use the same filename as source - kFSFileOperationDefaultOptions, - copyStatusCallback, - 1.0, - &clientContext); - if (status == noErr) { - while (![fileManager.delegate isBigFileOperationCompleted:fileManager] && CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2.0, true)) { - ; //nop - } - - FSFileOperationCopyStatus(fileOp, NULL, NULL, &status, NULL, NULL); - } - } - - CFRelease(fileOp); - BOOL retVal = NO; - if (status) { - retVal = [self setError:error withStatus:status]; - } - return retVal; -} - -+ (BOOL)setError:(NSError **)error withStatus:(OSStatus)err { - if (error) { - *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; - } - return error != nil; -} - -@end - -#pragma mark - -#pragma mark Static callbacks - -void copyStatusCallback(FSFileOperationRef fileOp, - const FSRef *currentItem, - FSFileOperationStage stage, - OSStatus error, - CFDictionaryRef statusDictionary, - void *info) { - FileOperationManager* fileManager = (__bridge FileOperationManager*)info; - id delegate = fileManager.delegate; - - [delegate waitPauseFor:fileManager]; - - if (stage == kFSOperationStageComplete) { - [delegate fileManager:fileManager setCompleted:YES]; - } else { - if ([delegate isBigFileOperationCancelled:fileManager]) { - FSFileOperationCancel(fileOp); - return; - } - } - - if (statusDictionary && stage == kFSOperationStageRunning) { - CFNumberRef bytesCompleted = (CFNumberRef)CFDictionaryGetValue(statusDictionary, kFSOperationBytesCompleteKey); - CGFloat floatBytesCompleted = 0.0; - if (bytesCompleted) { - CFNumberGetValue(bytesCompleted, kCFNumberMaxType, &floatBytesCompleted); - } - - CFNumberRef throughput = (CFNumberRef)CFDictionaryGetValue(statusDictionary, kFSOperationThroughputKey); - CGFloat floatThroughput = 0.0; - if (throughput) { - CFNumberGetValue(throughput, kCFNumberMaxType, &floatThroughput); - } - - CFNumberRef totalBytes = (CFNumberRef)CFDictionaryGetValue(statusDictionary, kFSOperationTotalBytesKey); - CGFloat floatTotalBytes = 0.0; - if (totalBytes) { - CFNumberGetValue(totalBytes, kCFNumberMaxType, &floatTotalBytes); - } - - [delegate fileManager:fileManager - updateBytesCompleted:floatBytesCompleted - totalBytes:floatTotalBytes - throughput:floatThroughput]; - } -} diff --git a/Sources/Legacy/MGScopeBar/MGRecessedPopUpButtonCell.h b/Sources/Legacy/MGScopeBar/MGRecessedPopUpButtonCell.h deleted file mode 100644 index 9955ccf..0000000 --- a/Sources/Legacy/MGScopeBar/MGRecessedPopUpButtonCell.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// MGRecessedPopUpButtonCell.h -// MGScopeBar -// -// Created by Matt Gemmell on 20/03/2008. -// Copyright 2008 Instinctive Code. -// - -#import - -/* - This cell class is used only for NSPopUpButtons which do NOT automatically - get their titles from their selected menu-items, since such popup-buttons - are weirdly broken when using the recessed bezel-style. -*/ - -@interface MGRecessedPopUpButtonCell : NSPopUpButtonCell { - NSButton *recessedButton; // we use a separate NSButton to do the bezel-drawing. -} - -@end diff --git a/Sources/Legacy/MGScopeBar/MGRecessedPopUpButtonCell.m b/Sources/Legacy/MGScopeBar/MGRecessedPopUpButtonCell.m deleted file mode 100644 index f695d21..0000000 --- a/Sources/Legacy/MGScopeBar/MGRecessedPopUpButtonCell.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// MGRecessedPopUpButtonCell.m -// MGScopeBar -// -// Created by Matt Gemmell on 20/03/2008. -// Copyright 2008 Instinctive Code. -// - -#import "MGRecessedPopUpButtonCell.h" - - -@implementation MGRecessedPopUpButtonCell - - -- (instancetype)initTextCell:(NSString *)title pullsDown:(BOOL)pullsDown -{ - if ((self = [super initTextCell:title pullsDown:pullsDown])) { - recessedButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 30, 20)]; // arbitrary frame. - [recessedButton setTitle:@""]; - [recessedButton setBezelStyle:NSBezelStyleAccessoryBar]; - [recessedButton setButtonType:NSButtonTypePushOnPushOff]; - [[recessedButton cell] setHighlightsBy:(NSCellStyleMask)(NSCellIsBordered | NSCellIsInsetButton)]; - [recessedButton setShowsBorderOnlyWhileMouseInside:NO]; - [recessedButton setState:NSControlStateValueOn]; // ensures it looks pushed-in. - } - return self; -} - - - - -- (void)drawTitleWithFrame:(NSRect)cellFrame inView:(NSView *)controlView -{ - // Inset title rect since its position is broken when NSPopUpButton - // isn't using its selected item as its title. - NSRect titleFrame = cellFrame; - titleFrame.origin.y += 1.0; - [super drawTitleWithFrame:titleFrame inView:controlView]; -} - - -- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView -{ - [recessedButton setFrame:frame]; - [recessedButton drawRect:frame]; -} - - -@end diff --git a/Sources/Legacy/MGScopeBar/MGScopeBar.h b/Sources/Legacy/MGScopeBar/MGScopeBar.h deleted file mode 100644 index dee9f7a..0000000 --- a/Sources/Legacy/MGScopeBar/MGScopeBar.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// MGScopeBar.h -// MGScopeBar -// -// Created by Matt Gemmell on 15/03/2008. -// Copyright 2008 Instinctive Code. -// - -#import -#import "MGScopeBarDelegateProtocol.h" - -@interface MGScopeBar : NSView { -@private - IBOutlet id __unsafe_unretained delegate; // weak ref. - NSMutableArray *_separatorPositions; // x-coords of separators, indexed by their group-number. - NSMutableArray *_groups; // groups of items. - NSView *_accessoryView; // weak ref since it's a subview. - NSMutableDictionary *_identifiers; // map of identifiers to items. - NSMutableArray *_selectedItems; // all selected items in all groups; see note below. - CGFloat _lastWidth; // previous width of view from when we last resized. - NSInteger _firstCollapsedGroup; // index of first group collapsed into a popup. - CGFloat _totalGroupsWidthForPopups; // total width needed to show all groups expanded (excluding padding and accessory). - CGFloat _totalGroupsWidth; // total width needed to show all groups as native-width popups (excluding padding and accessory). - BOOL _smartResizeEnabled; // whether to do our clever collapsing/expanding of buttons when resizing (Smart Resizing). - BOOL _notifyDefaultSelections; // Whether to notify the delegate of default selections - CGFloat _fontSize; // Font size used by labels and buttons -} - -@property(unsafe_unretained, nonatomic) id delegate; // should implement the MGScopeBarDelegate protocol. - -- (void)reloadData; // causes the scope-bar to reload all groups/items from its delegate. -- (void)sizeToFit; // only resizes vertically to optimum height; does not affect width. -- (void)adjustSubviews; // performs Smart Resizing if enabled. You should only need to call this yourself if you change the width of the accessoryView. - -// Smart Resize is the intelligent conversion of button-groups into NSPopUpButtons and vice-versa, based on available space. -// This functionality is enabled (YES) by default. Changing this setting will automatically call -reloadData. -@property (NS_NONATOMIC_IOSONLY) BOOL smartResizeEnabled; - -// The following method must be used to manage selections in the scope-bar; do not attempt to manipulate buttons etc directly. -- (void)setSelected:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber; -- (void)setSelected:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber informDelegate:(BOOL)informDelegate; -@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSArray *selectedItems; -- (void)setEnabledAllGroups:(BOOL)enable; -- (void)setEnabledGroup:(int)groupNumber enable:(BOOL)enable; - -@property (NS_NONATOMIC_IOSONLY) BOOL notifyDefaultSelections; - -@property (NS_NONATOMIC_IOSONLY) CGFloat fontSize; - -/* - Note: The -selectedItems method returns an array of arrays. - Each index in the returned array represents the group of items at that index. - The contents of each sub-array are the identifiers of each selected item in that group. - Sub-arrays may be empty, but will always be present (i.e. you will always find an NSArray). - Depending on the group's selection-mode, sub-arrays may contain zero, one or many identifiers. - The identifiers in each sub-array are not in any particular order. - */ - -@end diff --git a/Sources/Legacy/MGScopeBar/MGScopeBar.m b/Sources/Legacy/MGScopeBar/MGScopeBar.m deleted file mode 100644 index 01f9327..0000000 --- a/Sources/Legacy/MGScopeBar/MGScopeBar.m +++ /dev/null @@ -1,1046 +0,0 @@ -// -// MGScopeBar.m -// MGScopeBar -// -// Created by Matt Gemmell on 15/03/2008. -// Copyright 2008 Instinctive Code. -// - -#import "MGScopeBar.h" -#import "MGRecessedPopUpButtonCell.h" - - -#define SCOPE_BAR_H_INSET 8.0 // inset on left and right -#define SCOPE_BAR_HEIGHT 25.0 // used in -sizeToFit -#define SCOPE_BAR_BORDER_COLOR [NSColor colorWithCalibratedWhite:0.69 alpha:1.0] // bottom line's color -#define SCOPE_BAR_BORDER_WIDTH 1.0 // bottom line's width - -#define SCOPE_BAR_SEPARATOR_COLOR [NSColor colorWithCalibratedWhite:0.52 alpha:1.0] // color of vertical-line separators between groups -#define SCOPE_BAR_SEPARATOR_WIDTH 1.0 // width of vertical-line separators between groups -#define SCOPE_BAR_SEPARATOR_HEIGHT 16.0 // separators are vertically centered in the bar - -#define SCOPE_BAR_LABEL_COLOR [NSColor labelColor] // color of groups' labels -#define SCOPE_BAR_FONTSIZE 12.0 // font-size of labels and buttons -#define SCOPE_BAR_ITEM_SPACING 3.0 // spacing between buttons/separators/labels -#define SCOPE_BAR_BUTTON_IMAGE_SIZE 16.0 // size of buttons' images (width and height) - -#define SCOPE_BAR_HIDE_POPUP_BG YES // whether the bezel background of an NSPopUpButton is hidden when none of its menu-items are selected. - -// Appearance metrics. These were chosen to mimic the Finder's "Find" (Spotlight / Smart Group / etc) window's scope-bar. -#define MENU_PADDING 25.0 // how much wider a popup-button is than a regular button with the same title. -#define MENU_MIN_WIDTH 60.0 // minimum width a popup-button can be narrowed to. - -// NSPopUpButton titles used for groups which allow multiple selection. -#define POPUP_TITLE_EMPTY_SELECTION NSLocalizedString(@"(None)", nil) // title used when no items in the popup are selected. -#define POPUP_TITLE_MULTIPLE_SELECTION NSLocalizedString(@"(Multiple)", nil) // title used when multiple items in the popup are selected. - - -// ---- end of configurable settings ---- // - - -// Keys for internal use. -#define GROUP_IDENTIFIERS @"Identifiers" // NSMutableArray of identifier strings. -#define GROUP_BUTTONS @"Buttons" // NSMutableArray of either NSButtons or NSMenuItems, one per item. -#define GROUP_SELECTION_MODE @"SelectionMode" // MGScopeBarGroupSelectionMode (int) as NSNumber. -#define GROUP_MENU_MODE @"MenuMode" // BOOL, YES if group is collected in a popup-menu, else NO. -#define GROUP_POPUP_BUTTON @"PopupButton" // NSPopUpButton (only present if group is in menu-mode). -#define GROUP_HAS_SEPARATOR @"HasSeparator" // BOOL, YES if group has a separator before it. -#define GROUP_HAS_LABEL @"HasLabel" // BOOL, YES if group has a label. -#define GROUP_LABEL_FIELD @"LabelField" // NSTextField for the label (optional; only if group has a label) -#define GROUP_TOTAL_BUTTONS_WIDTH @"TotalButtonsWidth" // Width of all buttons in a group plus spacings between them (doesn't include label etc) -#define GROUP_WIDEST_BUTTON_WIDTH @"WidestButtonWidth" // Width of widest button, used when making popup-menus. -#define GROUP_CUMULATIVE_WIDTH @"CumulativeWidth" // Width from left of leftmost group to right of this group (all groups fully expanded). - - -@interface MGScopeBar (MGPrivateMethods) - -- (IBAction)scopeButtonClicked:(id)sender; -- (NSButton *)getButtonForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber; // returns relevant button/menu-item -- (void)updateSelectedState:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber informDelegate:(BOOL)inform; -- (NSButton *)buttonForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber - withTitle:(NSString *)title image:(NSImage *)image; // creates a new NSButton -- (NSMenuItem *)menuItemForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber - withTitle:(NSString *)title image:(NSImage *)image; // creates a new NSMenuitem -- (NSPopUpButton *)popupButtonForGroup:(NSDictionary *)group; -- (void)setControl:(NSObject *)control forIdentifier:(NSString *)identifier inGroup:(NSInteger)groupNumber; -- (void)updateMenuTitleForGroupAtIndex:(NSInteger)groupNumber; - -@end - - -@implementation MGScopeBar - - -#pragma mark Setup and teardown - - -- (instancetype)initWithFrame:(NSRect)frame -{ - self = [super initWithFrame:frame]; - if (self) { - _smartResizeEnabled = YES; - _notifyDefaultSelections = YES; - _fontSize = SCOPE_BAR_FONTSIZE; - // Everything else is reset in -reloadData. - } - return self; -} - - -- (void)dealloc -{ - delegate = nil; - if (_accessoryView) { - [_accessoryView removeFromSuperview]; - _accessoryView = nil; // weak ref - } - -} - - -#pragma mark Data management - - -- (void)reloadData -{ - // Resize if necessary. - [self sizeToFit]; - - // Remove any old objects. - if (_accessoryView) { - [_accessoryView removeFromSuperview]; - _accessoryView = nil; // weak ref - } - - NSArray *subviews = [[self subviews] copy]; // so we don't mutate the collection we're iterating over. - [subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; - // because copies are retained. - - _separatorPositions = nil; - _groups = nil; - _identifiers = nil; - _selectedItems = nil; - _firstCollapsedGroup = NSNotFound; - _lastWidth = NSNotFound; - _totalGroupsWidth = 0; - _totalGroupsWidthForPopups = 0; - - // Configure contents via delegate. - if (self.delegate && [delegate conformsToProtocol:@protocol(MGScopeBarDelegate)]) { - NSInteger numGroups = [delegate numberOfGroupsInScopeBar:self]; - - if (numGroups > 0) { - _separatorPositions = [[NSMutableArray alloc] initWithCapacity:numGroups]; - _groups = [[NSMutableArray alloc] initWithCapacity:numGroups]; - _identifiers = [[NSMutableDictionary alloc] initWithCapacity:0]; - _selectedItems = [[NSMutableArray alloc] initWithCapacity:numGroups]; - - int xCoord = SCOPE_BAR_H_INSET; - NSRect ctrlRect = NSZeroRect; - BOOL providesImages = [delegate respondsToSelector:@selector(scopeBar:imageForItem:inGroup:)]; - - for (int groupNum = 0; groupNum < numGroups; groupNum++) { - // Add separator if appropriate. - BOOL addSeparator = (groupNum > 0); // default behavior. - if ([delegate respondsToSelector:@selector(scopeBar:showSeparatorBeforeGroup:)]) { - addSeparator = [delegate scopeBar:self showSeparatorBeforeGroup:groupNum]; - } - if (addSeparator) { - [_separatorPositions addObject:@(xCoord)]; - xCoord += (int)(SCOPE_BAR_SEPARATOR_WIDTH + SCOPE_BAR_ITEM_SPACING); - - _totalGroupsWidth += SCOPE_BAR_SEPARATOR_WIDTH + SCOPE_BAR_ITEM_SPACING; - _totalGroupsWidthForPopups += SCOPE_BAR_SEPARATOR_WIDTH + SCOPE_BAR_ITEM_SPACING; - } else { - [_separatorPositions addObject:[NSNull null]]; - } - - // Add label if appropriate. - NSString *groupLabel = [delegate scopeBar:self labelForGroup:groupNum]; - NSTextField *labelField = nil; - BOOL hasLabel = NO; - if (groupLabel && [groupLabel length] > 0) { - hasLabel = YES; - ctrlRect = NSMakeRect(xCoord, 6, 15, 50); - labelField = [[NSTextField alloc] initWithFrame:ctrlRect]; - [labelField setStringValue:groupLabel]; - [labelField setEditable:NO]; - [labelField setBordered:NO]; - [labelField setDrawsBackground:NO]; - [labelField setTextColor:SCOPE_BAR_LABEL_COLOR]; - [labelField setFont:[NSFont boldSystemFontOfSize:_fontSize]]; - [labelField sizeToFit]; - ctrlRect.size = [labelField frame].size; - [labelField setFrame:ctrlRect]; - [self addSubview:labelField]; - - xCoord += (int)(ctrlRect.size.width + SCOPE_BAR_ITEM_SPACING); - - _totalGroupsWidth += ctrlRect.size.width + SCOPE_BAR_ITEM_SPACING; - _totalGroupsWidthForPopups += ctrlRect.size.width + SCOPE_BAR_ITEM_SPACING; - } - - // Create group information for use during interaction. - NSArray *identifiers = [delegate scopeBar:self itemIdentifiersForGroup:groupNum]; - NSMutableArray *usedIdentifiers = [NSMutableArray arrayWithCapacity:[identifiers count]]; - NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:[identifiers count]]; - MGScopeBarGroupSelectionMode selMode = [delegate scopeBar:self selectionModeForGroup:groupNum]; - if (selMode != MGScopeBarGroupSelectionModeRadio && selMode != MGScopeBarGroupSelectionModeMultiple) { - // Sanity check, since this is just an int. - selMode = MGScopeBarGroupSelectionModeRadio; - } - NSMutableDictionary *groupInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: - usedIdentifiers, GROUP_IDENTIFIERS, - buttons, GROUP_BUTTONS, - @((int)selMode), GROUP_SELECTION_MODE, - @NO, GROUP_MENU_MODE, - @(hasLabel), GROUP_HAS_LABEL, - @(addSeparator), GROUP_HAS_SEPARATOR, - nil]; - if (hasLabel) { - groupInfo[GROUP_LABEL_FIELD] = labelField; - } - [_groups addObject:groupInfo]; - [_selectedItems addObject:[NSMutableArray arrayWithCapacity:0]]; - - // Add buttons for this group. - CGFloat widestButtonWidth = 0; - CGFloat totalButtonsWidth = 0; - for (NSString *itemID in identifiers) { - if (![usedIdentifiers containsObject:itemID]) { - [usedIdentifiers addObject:itemID]; - } else { - // Identifier already used for this group; skip it. - continue; - } - - NSString *title = [delegate scopeBar:self titleOfItem:itemID inGroup:groupNum]; - NSImage *image = nil; - if (providesImages) { - image = [delegate scopeBar:self imageForItem:itemID inGroup:groupNum]; - } - NSButton *button = [self buttonForItem:itemID inGroup:groupNum withTitle:title image:image]; - - ctrlRect = [button frame]; - ctrlRect.origin.x = xCoord; - [button setFrame:ctrlRect]; - [self addSubview:button]; - [buttons addObject:button]; - - // Adjust x-coordinate for next item in the bar. - xCoord += (int)(ctrlRect.size.width + SCOPE_BAR_ITEM_SPACING); - - // Update total and widest button widths. - if (totalButtonsWidth > 0) { - // Add spacing before this item, since it's not the first in the group. - totalButtonsWidth += SCOPE_BAR_ITEM_SPACING; - } - totalButtonsWidth += ctrlRect.size.width; - if (ctrlRect.size.width > widestButtonWidth) { - widestButtonWidth = ctrlRect.size.width; - } - } - - // Add the accumulated buttons' width and the widest button's width to groupInfo. - groupInfo[GROUP_TOTAL_BUTTONS_WIDTH] = @(totalButtonsWidth); - groupInfo[GROUP_WIDEST_BUTTON_WIDTH] = @(widestButtonWidth); - - _totalGroupsWidth += totalButtonsWidth; - _totalGroupsWidthForPopups += widestButtonWidth + MENU_PADDING; - - CGFloat cumulativeWidth = _totalGroupsWidth + (groupNum * SCOPE_BAR_ITEM_SPACING); - groupInfo[GROUP_CUMULATIVE_WIDTH] = @(cumulativeWidth); - - // If this is a radio-mode group, select the first item automatically. - if (selMode == MGScopeBarGroupSelectionModeRadio) { - [self updateSelectedState:YES forItem:identifiers[0] inGroup:groupNum informDelegate:_notifyDefaultSelections]; - } - } - - _totalGroupsWidth += ((numGroups - 1) * SCOPE_BAR_ITEM_SPACING); - _totalGroupsWidthForPopups += ((numGroups - 1) * SCOPE_BAR_ITEM_SPACING); - } - - // Add accessoryView, if provided. - if ([delegate respondsToSelector:@selector(accessoryViewForScopeBar:)]) { - _accessoryView = [delegate accessoryViewForScopeBar:self]; - if (_accessoryView) { - // Remove NSViewMaxXMargin flag from resizing mask, if present. - NSUInteger mask = [_accessoryView autoresizingMask]; - if (mask & NSViewMaxXMargin) { - mask &= ~NSViewMaxXMargin; - } - - // Add NSViewMinXMargin flag to resizing mask, if not present. - if (!(mask & NSViewMinXMargin)) { - mask = (mask | NSViewMinXMargin); - } - - // Update view sizing mask. - [_accessoryView setAutoresizingMask:mask]; - - // Adjust frame appropriately. - NSRect frame = [_accessoryView frame]; - frame.origin.x = round(NSMaxX([self bounds]) - (frame.size.width + SCOPE_BAR_H_INSET)); - frame.origin.y = round(((SCOPE_BAR_HEIGHT - frame.size.height) / 2.0)); - [_accessoryView setFrame:frame]; - - // Add as subview. - [self addSubview:_accessoryView]; - } - } - - // Layout subviews appropriately. - [self adjustSubviews]; - } - - [self setNeedsDisplay:YES]; -} - - -#pragma mark Utility methods - - -- (void)sizeToFit -{ - NSRect frame = [self frame]; - if (frame.size.height != SCOPE_BAR_HEIGHT) { - CGFloat delta = SCOPE_BAR_HEIGHT - frame.size.height; - frame.size.height += delta; - frame.origin.y -= delta; - [self setFrame:frame]; - } -} - - -- (void)adjustSubviews -{ - if (!_smartResizeEnabled) { - return; - } - - /* - We need to work out which groups we can show fully expanded, and which must be collapsed into popup-buttons. - Any kind of frame-change may have happened, so we need to take care to create or remove buttons or popup-buttons as needed. - */ - - // Bail out if we have nothing to do. - if (!_groups || [_groups count] == 0) { - return; - } - - // Obtain current width of view. - CGFloat viewWidth = [self bounds].size.width; - - // Abort if there hasn't been any genuine change in width. - if ((viewWidth == _lastWidth) && (_lastWidth != NSNotFound)) { - return; - } - - // Determine whether we got narrower or wider. - BOOL narrower = ((_lastWidth == NSNotFound) || (viewWidth < _lastWidth)); - - // Find width available for showing groups. - CGFloat availableWidth = viewWidth - (SCOPE_BAR_H_INSET * 2.0); - if (_accessoryView) { - // Account for _accessoryView, leaving a normal amount of spacing to the left of it. - availableWidth -= ([_accessoryView frame].size.width + SCOPE_BAR_ITEM_SPACING); - } - - BOOL shouldAdjustPopups = (availableWidth < _totalGroupsWidthForPopups); - NSInteger oldFirstCollapsedGroup = _firstCollapsedGroup; - - // Work out which groups we should now check for collapsibility/expandability. - NSEnumerator *groupsEnumerator = nil; - NSRange enumRange; - BOOL proceed = YES; - - if (narrower) { - // Got narrower, so work backwards from oldFirstCollapsedGroup (excluding that group, since it's already collapsed), - // checking to see if we need to collapse any more groups to the left. - enumRange = NSMakeRange(0, oldFirstCollapsedGroup); - // If no groups were previously collapsed, work backwards from the last group (including that group). - if (oldFirstCollapsedGroup == NSNotFound) { - enumRange.length = [_groups count]; - } - groupsEnumerator = [[_groups subarrayWithRange:enumRange] reverseObjectEnumerator]; - - } else { - // Got wider, so work forwards from oldFirstCollapsedGroup (including that group) checking to see if we can - // expand any groups into full buttons. - enumRange = NSMakeRange(oldFirstCollapsedGroup, [_groups count] - oldFirstCollapsedGroup); - // If no groups were previously collapsed, we have nothing to do here. - if (oldFirstCollapsedGroup == NSNotFound) { - proceed = NO; - } - if (proceed) { - groupsEnumerator = [[_groups subarrayWithRange:enumRange] objectEnumerator]; - } - } - - // Get the current occupied width within this view. - CGFloat currentOccupiedWidth = 0; - NSDictionary *group = _groups[0]; - BOOL menuMode = [group[GROUP_MENU_MODE] boolValue]; - NSButton *firstButton = nil; - if (menuMode) { - firstButton = group[GROUP_POPUP_BUTTON]; - } else { - firstButton = group[GROUP_BUTTONS][0]; - } - CGFloat leftLimit = NSMinX([firstButton frame]); - // Account for label in first group, if present. - if ([group[GROUP_HAS_LABEL] boolValue]) { - NSTextField *label = (NSTextField *)group[GROUP_LABEL_FIELD]; - leftLimit -= (SCOPE_BAR_ITEM_SPACING + [label frame].size.width); - } - - group = [_groups lastObject]; - menuMode = [group[GROUP_MENU_MODE] boolValue]; - NSButton *lastButton = nil; - if (menuMode) { - lastButton = group[GROUP_POPUP_BUTTON]; - } else { - lastButton = [group[GROUP_BUTTONS] lastObject]; - } - CGFloat rightLimit = NSMaxX([lastButton frame]); - currentOccupiedWidth = rightLimit - leftLimit; - - // Work out whether we need to try collapsing groups at all, if we're narrowing. - // We have already handled the case of not requiring to expand groups if we're widening, above. - if (proceed && narrower) { - if (availableWidth >= currentOccupiedWidth) { - // We still have enough room for what we're showing; no change needed. - proceed = NO; - } - } - - if (proceed) { - // See how many further groups we can expand or contract. - CGFloat theoreticalOccupiedWidth = currentOccupiedWidth; - for (NSDictionary *groupInfo in groupsEnumerator) { - BOOL complete = NO; - float expandedWidth = [groupInfo[GROUP_TOTAL_BUTTONS_WIDTH] floatValue]; - CGFloat contractedWidth = [groupInfo[GROUP_WIDEST_BUTTON_WIDTH] floatValue] + MENU_PADDING; - - if (narrower) { - // We're narrowing. See if collapsing this group brings us within availableWidth. - if (((theoreticalOccupiedWidth - expandedWidth) + contractedWidth) <= availableWidth) { - // We're now within width constraints, so we're done iterating. - complete = YES; - } // else, continue trying to to collapse groups. - theoreticalOccupiedWidth = ((theoreticalOccupiedWidth - expandedWidth) + contractedWidth); - - } else { - // We're widening. See if we can expand this group and still be within availableWidth. - if (((theoreticalOccupiedWidth - contractedWidth) + expandedWidth) > availableWidth) { - // We'd be too wide if we expanded this group. Terminate iteration without updating _firstCollapsedGroup. - //NSLog(@"We'd be too wide if we expanded right now"); - break; - } // else, continue trying to expand groups. - theoreticalOccupiedWidth = ((theoreticalOccupiedWidth - contractedWidth) + expandedWidth); - //NSLog(@"We can continue expanding"); - } - - // Update _firstCollapsedGroup appropriately. - if (_firstCollapsedGroup == NSNotFound) { - _firstCollapsedGroup = ((narrower) ? [_groups count] : -1); - oldFirstCollapsedGroup = _firstCollapsedGroup; - } - _firstCollapsedGroup += ((narrower) ? -1 : 1); - - // Terminate if we now fit the available space as best we can. - if (complete) { - break; - } - } - - // Work out how many groups we need to actually change. - NSRange changedRange = NSMakeRange(0, [_groups count]); - BOOL adjusting = YES; - //NSLog(@"Old firstCollapsedGroup: %d, new: %d", oldFirstCollapsedGroup, _firstCollapsedGroup); - if (_firstCollapsedGroup != oldFirstCollapsedGroup) { - if (narrower) { - // Narrower. _firstCollapsedGroup will be less (earlier) than oldFirstCollapsedGroup. - changedRange.location = _firstCollapsedGroup; - changedRange.length = (oldFirstCollapsedGroup - _firstCollapsedGroup); - } else { - // Wider. _firstCollapsedGroup will be greater (later) than oldFirstCollapsedGroup. - changedRange.location = oldFirstCollapsedGroup; - changedRange.length = (_firstCollapsedGroup - oldFirstCollapsedGroup); - } - } else { - // _firstCollapsedGroup and oldFirstCollapsedGroup are the same; nothing needs changed. - adjusting = NO; - } - - // If a change is required, ensure that each group is expanded or contracted as appropriate. - if (adjusting || shouldAdjustPopups) { - //NSLog(@"Got %@ - modifying groups %@", ((narrower) ? @"narrower" : @"wider"), NSStringFromRange(changedRange)); - NSInteger nextXCoord = NSNotFound; - if (adjusting) { - for (NSUInteger i = changedRange.location; i < NSMaxRange(changedRange); i++) { - NSMutableDictionary *groupInfo = _groups[i]; - - if (nextXCoord == NSNotFound) { - BOOL groupMenuMode = [groupInfo[GROUP_MENU_MODE] boolValue]; - NSButton *firstGroupButton = nil; - if (!groupMenuMode) { - firstGroupButton = groupInfo[GROUP_BUTTONS][0]; - } else { - firstGroupButton = groupInfo[GROUP_POPUP_BUTTON]; - } - nextXCoord = (NSInteger)[firstGroupButton frame].origin.x; - } else { - // Add group-spacing, separator and label as appropriate. - nextXCoord += (NSInteger)SCOPE_BAR_ITEM_SPACING; - if ([groupInfo[GROUP_HAS_SEPARATOR] boolValue]) { - nextXCoord += (NSInteger)(SCOPE_BAR_SEPARATOR_WIDTH + SCOPE_BAR_ITEM_SPACING); - } - if ([groupInfo[GROUP_HAS_LABEL] boolValue]) { - NSTextField *labelField = (NSTextField *)groupInfo[GROUP_LABEL_FIELD]; - CGFloat labelWidth = [labelField frame].size.width; - nextXCoord += (NSInteger)(labelWidth + SCOPE_BAR_ITEM_SPACING); - } - } - - NSPopUpButton *popup = nil; - if (narrower) { - // Remove buttons. - NSArray *buttons = groupInfo[GROUP_BUTTONS]; - [buttons makeObjectsPerformSelector:@selector(removeFromSuperview)]; - - // Create popup and add it to this view. - popup = [self popupButtonForGroup:groupInfo]; - NSRect popupFrame = [popup frame]; - popupFrame.origin.x = nextXCoord; - [popup setFrame:popupFrame]; - groupInfo[GROUP_POPUP_BUTTON] = popup; - [self addSubview:popup positioned:NSWindowBelow relativeTo:_accessoryView]; - nextXCoord += (NSInteger)(popupFrame.size.width); - - // Ensure popup has appropriate title. - [self updateMenuTitleForGroupAtIndex:i]; - - } else { - // Remove and release popup. - popup = groupInfo[GROUP_POPUP_BUTTON]; - [popup removeFromSuperview]; - [groupInfo removeObjectForKey:GROUP_POPUP_BUTTON]; - - // Replace menuItems with buttons. - CGFloat buttonX = nextXCoord; - NSMutableArray *menuItems = groupInfo[GROUP_BUTTONS]; - NSArray *selectedItems = _selectedItems[i]; - for (NSUInteger ii = 0; ii < [menuItems count]; ii++) { - NSMenuItem *menuItem = menuItems[ii]; - NSString *itemIdentifier = [menuItem representedObject]; - NSButton *button = [self buttonForItem:itemIdentifier - inGroup:[menuItem tag] - withTitle:[menuItem title] - image:[menuItem image]]; - NSRect buttonFrame = [button frame]; - buttonFrame.origin.x = buttonX; - [button setFrame:buttonFrame]; - if ([selectedItems containsObject:itemIdentifier]) { - [button setState:NSControlStateValueOn]; - } - [self addSubview:button positioned:NSWindowBelow relativeTo:_accessoryView]; - menuItems[ii] = button; - buttonX += [button frame].size.width + SCOPE_BAR_ITEM_SPACING; - } - nextXCoord = (NSInteger)(buttonX - SCOPE_BAR_ITEM_SPACING); - } - - // Update GROUP_MENU_MODE for this group. - groupInfo[GROUP_MENU_MODE] = @(narrower); - } - } - - // Modify positions/sizes of groups and separators as required. - float startIndex = MIN(changedRange.location, (NSUInteger)_firstCollapsedGroup); - CGFloat xCoord = 0; - CGFloat perGroupDelta = 0; - if (shouldAdjustPopups) { - perGroupDelta = ((_totalGroupsWidthForPopups - availableWidth) / [_groups count]); - } - for (NSUInteger i = (NSInteger)startIndex; i < [_groups count]; i++) { - NSDictionary *groupInfo = _groups[i]; - BOOL groupMenuMode = [groupInfo[GROUP_MENU_MODE] boolValue]; - - // Further contract or expand popups if appropriate. - if (shouldAdjustPopups) { - CGFloat fullPopupWidth = [groupInfo[GROUP_WIDEST_BUTTON_WIDTH] floatValue] + MENU_PADDING; - CGFloat popupWidth = fullPopupWidth - perGroupDelta; - popupWidth = MAX(popupWidth, MENU_MIN_WIDTH); - popupWidth = MIN(popupWidth, fullPopupWidth); - - NSPopUpButton *button = groupInfo[GROUP_POPUP_BUTTON]; - NSRect buttonRect = [button frame]; - buttonRect.size.width = popupWidth; - [button setFrame:buttonRect]; - } - - // Reposition groups appropriately. - if (i > startIndex) { - // Reposition separator if present. - if ([groupInfo[GROUP_HAS_SEPARATOR] boolValue]) { - _separatorPositions[i] = @((int)xCoord); - xCoord += (SCOPE_BAR_SEPARATOR_WIDTH + SCOPE_BAR_ITEM_SPACING); - } - - // Reposition label if present. - if ([groupInfo[GROUP_HAS_LABEL] boolValue]) { - NSTextField *label = groupInfo[GROUP_LABEL_FIELD]; - NSRect labelFrame = [label frame]; - labelFrame.origin.x = xCoord; - [label setFrame:labelFrame]; - xCoord = NSMaxX(labelFrame) + SCOPE_BAR_ITEM_SPACING; - } - - // Reposition buttons or popup. - if (groupMenuMode) { - NSPopUpButton *button = groupInfo[GROUP_POPUP_BUTTON]; - NSRect buttonRect = [button frame]; - buttonRect.origin.x = xCoord; - [button setFrame:buttonRect]; - xCoord = NSMaxX(buttonRect) + SCOPE_BAR_ITEM_SPACING; - - } else { - NSArray *buttons = groupInfo[GROUP_BUTTONS]; - for (NSButton *button in buttons) { - NSRect buttonRect = [button frame]; - buttonRect.origin.x = xCoord; - [button setFrame:buttonRect]; - xCoord = NSMaxX(buttonRect) + SCOPE_BAR_ITEM_SPACING; - } - } - - } else { - // Set up initial value of xCoord. - NSButton *button = nil; - if (groupMenuMode) { - button = groupInfo[GROUP_POPUP_BUTTON]; - } else { - button = [groupInfo[GROUP_BUTTONS] lastObject]; - } - xCoord = NSMaxX([button frame]) + SCOPE_BAR_ITEM_SPACING; - } - } - - // Reset _firstCollapsedGroup to NSNotFound if necessary. - if (!narrower) { - if (_firstCollapsedGroup >= (NSInteger)[_groups count]) { - _firstCollapsedGroup = NSNotFound; - } - } - } - } - - // Take note of our width for comparison next time. - _lastWidth = viewWidth; -} - - -- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize -{ - [super resizeSubviewsWithOldSize:oldBoundsSize]; - [self adjustSubviews]; -} - -- (NSButton *)getButtonForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber -{ - NSButton *button = nil; - NSArray *group = _identifiers[identifier]; - if (group && (NSInteger)[group count] > groupNumber) { - NSObject *element = group[groupNumber]; - if (element != [NSNull null]) { - button = (NSButton *)element; - } - } - - return button; -} - - -- (NSButton *)buttonForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber - withTitle:(NSString *)title image:(NSImage *)image -{ - NSRect ctrlRect = NSMakeRect(0, 0, 50, 20); // arbitrary size; will be resized later. - NSButton *button = [[NSButton alloc] initWithFrame:ctrlRect]; - [button setTitle:title]; - [[button cell] setRepresentedObject:identifier]; - [button setTag:groupNumber]; - [button setFont:[NSFont boldSystemFontOfSize:_fontSize]]; - [button setTarget:self]; - [button setAction:@selector(scopeButtonClicked:)]; - [button setBezelStyle:NSBezelStyleAccessoryBar]; - [button setButtonType:NSButtonTypePushOnPushOff]; - [[button cell] setHighlightsBy:(NSCellStyleMask)(NSCellIsBordered | NSCellIsInsetButton)]; - [button setShowsBorderOnlyWhileMouseInside:YES]; - if (image) { - [image setSize:NSMakeSize(SCOPE_BAR_BUTTON_IMAGE_SIZE, SCOPE_BAR_BUTTON_IMAGE_SIZE)]; - [button setImagePosition:NSImageLeft]; - [button setImage:image]; - } - [button sizeToFit]; - ctrlRect = [button frame]; - ctrlRect.origin.y = floor(([self frame].size.height - ctrlRect.size.height) / 2.0); - [button setFrame:ctrlRect]; - - [self setControl:button forIdentifier:identifier inGroup:groupNumber]; - - return button; -} - - -- (NSMenuItem *)menuItemForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber - withTitle:(NSString *)title image:(NSImage *)image -{ - NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(scopeButtonClicked:) keyEquivalent:@""]; - [menuItem setTarget:self]; - [menuItem setImage:image]; - [menuItem setRepresentedObject:identifier]; - [menuItem setTag:groupNumber]; - - [self setControl:menuItem forIdentifier:identifier inGroup:groupNumber]; - - return menuItem; -} - - -- (NSPopUpButton *)popupButtonForGroup:(NSDictionary *)group -{ - CGFloat popWidth = floor([group[GROUP_WIDEST_BUTTON_WIDTH] floatValue] + MENU_PADDING); - NSRect popFrame = NSMakeRect(0, 0, popWidth, 20); // arbitrary height. - NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:popFrame pullsDown:NO]; - - // Since we're not using the selected item's title, we need to specify a NSMenuItem for the title. - BOOL multiSelect = ([group[GROUP_SELECTION_MODE] intValue] == MGScopeBarGroupSelectionModeMultiple); - if (multiSelect) { - MGRecessedPopUpButtonCell *cell = [[MGRecessedPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]; - [popup setCell:cell]; - - [[popup cell] setUsesItemFromMenu:NO]; - NSMenuItem *titleItem = [[NSMenuItem alloc] init]; - [[popup cell] setMenuItem:titleItem]; - } - - // Configure appearance and behaviour. - [popup setFont:[NSFont boldSystemFontOfSize:_fontSize]]; - [popup setBezelStyle:NSBezelStyleAccessoryBar]; - [popup setButtonType:NSButtonTypePushOnPushOff]; - [[popup cell] setHighlightsBy:(NSCellStyleMask)(NSCellIsBordered | NSCellIsInsetButton)]; - [popup setShowsBorderOnlyWhileMouseInside:NO]; - [[popup cell] setAltersStateOfSelectedItem:NO]; - [[popup cell] setArrowPosition:NSPopUpArrowAtBottom]; - [popup setPreferredEdge:NSMaxXEdge]; - - // Add appropriate items. - [popup removeAllItems]; - NSMutableArray *buttons = group[GROUP_BUTTONS]; - for (NSUInteger i = 0; i < [buttons count]; i++) { - NSButton *button = (NSButton *)buttons[i]; - NSMenuItem *menuItem = [self menuItemForItem:[[button cell] representedObject] - inGroup:[button tag] - withTitle:[button title] - image:[button image]]; - [menuItem setState:[button state]]; - buttons[i] = menuItem; - [[popup menu] addItem:menuItem]; - } - - // Vertically center the popup within our frame. - if (!multiSelect) { - [popup sizeToFit]; - } - popFrame = [popup frame]; - popFrame.origin.y = ceil(([self frame].size.height - popFrame.size.height) / 2.0); - [popup setFrame:popFrame]; - - return popup; -} - - -- (void)setControl:(NSObject *)control forIdentifier:(NSString *)identifier inGroup:(NSInteger)groupNumber -{ - if (!_identifiers) { - _identifiers = [[NSMutableDictionary alloc] initWithCapacity:0]; - } - - NSMutableArray *identArray = _identifiers[identifier]; - if (!identArray) { - identArray = [[NSMutableArray alloc] initWithCapacity:groupNumber + 1]; - _identifiers[identifier] = identArray; - } - - NSInteger count = [identArray count]; - if (groupNumber >= count) { - // Pad identArray with nulls if appropriate, so this control lies at index groupNumber. - for (NSInteger i = count; i < groupNumber; i++) { - [identArray addObject:[NSNull null]]; - } - [identArray addObject:control]; - } else { - identArray[groupNumber] = control; - } -} - - -- (void)updateMenuTitleForGroupAtIndex:(NSInteger)groupNumber -{ - // Ensure that this group's popup (if present) has the correct title, - // accounting for the group's selection-mode and selected item(s). - - if (groupNumber < 0 || groupNumber >= (NSInteger)[_groups count]) { - return; - } - - NSDictionary *group = _groups[groupNumber]; - if (group) { - NSPopUpButton *popup = group[GROUP_POPUP_BUTTON]; - if (popup) { - NSArray *groupSelection = _selectedItems[groupNumber]; - NSInteger numSelected = [groupSelection count]; - if (numSelected == 0) { - // No items selected. - [popup setTitle:POPUP_TITLE_EMPTY_SELECTION]; - [[[popup cell] menuItem] setImage:nil]; - - } else if (numSelected > 1) { - // Multiple items selected. - [popup setTitle:POPUP_TITLE_MULTIPLE_SELECTION]; - [[[popup cell] menuItem] setImage:nil]; - - } else { - // One item selected. - NSString *identifier = groupSelection[0]; - NSArray *items = group[GROUP_BUTTONS]; - NSMenuItem *item = nil; - for (NSMenuItem *thisItem in items) { - if ([[thisItem representedObject] isEqualToString:identifier]) { - item = thisItem; - break; - } - } - if (item) { - [popup setTitle:[item title]]; - [[[popup cell] menuItem] setImage:[item image]]; - } - } - - if (SCOPE_BAR_HIDE_POPUP_BG) { - BOOL hasBackground = [[popup cell] isBordered]; - if (numSelected == 0 && hasBackground) { - [[popup cell] setBordered:NO]; - } else if (!hasBackground) { - [[popup cell] setBordered:YES]; - } - } - } - } -} - - -#pragma mark Drawing - - -- (void)drawRect:(NSRect)rect -{ - [super drawRect:rect]; - - // Draw border. - NSRect lineRect = [self bounds]; - lineRect.size.height = SCOPE_BAR_BORDER_WIDTH; - [SCOPE_BAR_BORDER_COLOR set]; - NSRectFill(lineRect); - - // Draw separators. - if ([_separatorPositions count] > 0) { - [SCOPE_BAR_SEPARATOR_COLOR set]; - NSRect sepRect = NSMakeRect(0, 0, SCOPE_BAR_SEPARATOR_WIDTH, SCOPE_BAR_SEPARATOR_HEIGHT); - sepRect.origin.y = (([self bounds].size.height - sepRect.size.height) / 2.0); - for (NSObject *sepPosn in _separatorPositions) { - if (sepPosn != [NSNull null]) { - sepRect.origin.x = [(NSNumber *)sepPosn intValue]; - NSRectFill(sepRect); - } - } - } -} - - -#pragma mark Interaction - - -- (IBAction)scopeButtonClicked:(id)sender -{ - NSButton *button = (NSButton *)sender; - BOOL menuMode = [sender isKindOfClass:[NSMenuItem class]]; - NSString *identifier = [((menuMode) ? sender : [sender cell]) representedObject]; - NSInteger groupNumber = [sender tag]; - BOOL nowSelected = YES; - if (menuMode) { - // MenuItem. Ensure item has appropriate state. - nowSelected = ![_selectedItems[groupNumber] containsObject:identifier]; - [sender setState:((nowSelected) ? NSControlStateValueOn : NSControlStateValueOff)]; - } else { - // Button. Item will already have appropriate state. - nowSelected = ([button state] != NSControlStateValueOff); - } - [self setSelected:nowSelected forItem:identifier inGroup:groupNumber]; -} - - -#pragma mark Accessors and properties - -- (void)setSelected:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber informDelegate:(BOOL)informDelegate { - // Change state of other items in group appropriately, informing delegate if possible. - // First we find the appropriate group-info for the item's identifier. - if (identifier && groupNumber >= 0 && groupNumber < (NSInteger)[_groups count]) { - NSDictionary *group = _groups[groupNumber]; - BOOL nowSelected = selected; - - if (group) { - // We found the group which this item belongs to. Obtain selection-mode and identifiers. - MGScopeBarGroupSelectionMode selMode = [group[GROUP_SELECTION_MODE] intValue]; - BOOL radioMode = (selMode == MGScopeBarGroupSelectionModeRadio); - - if (radioMode) { - // This is a radio-mode group. Ensure this item isn't already selected. - NSArray *groupSelections = [_selectedItems[groupNumber] copy]; - - if (nowSelected) { - // Before selecting this item, we first need to deselect any other selected items in this group. - for (NSString *selectedIdentifier in groupSelections) { - // Reselect the just-deselected item without informing the delegate, since nothing really changed. - [self updateSelectedState:NO forItem:selectedIdentifier inGroup:groupNumber informDelegate:NO]; - } - } else { - // Prevent deselection if this item is already selected. - if ([groupSelections containsObject:identifier]) { - nowSelected = YES; - informDelegate = NO; - } - } - } - - // Change selected state of this item. - [self updateSelectedState:nowSelected forItem:identifier inGroup:groupNumber informDelegate:informDelegate]; - - // Update popup-menu's title if appropriate. - if ([group[GROUP_MENU_MODE] boolValue]) { - [self updateMenuTitleForGroupAtIndex:groupNumber]; - } - } - } -} - -- (void)setSelected:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber { - [self setSelected:selected forItem:identifier inGroup:groupNumber informDelegate:YES]; -} - -- (void)updateSelectedState:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber informDelegate:(BOOL)inform -{ - if (identifier == nil) { - return; - } - // This method simply updates the selected state of the item's control, maintains selectedItems, and informs the delegate. - // All management of dependencies (such as deselecting other selected items in a radio-selection-mode group) is performed - // in the setSelected:forItem:inGroup: method. - - // Determine whether we can inform the delegate about this change. - SEL stateChangedSel = @selector(scopeBar:selectedStateChanged:forItem:inGroup:); - BOOL responds = (delegate && [delegate respondsToSelector:stateChangedSel]); - - // Ensure selected status of item's control reflects desired value. - NSButton *button = [self getButtonForItem:identifier inGroup:groupNumber]; - if (selected && [button state] == NSControlStateValueOff) { - [button setState:NSControlStateValueOn]; - } else if (!selected && [button state] != NSControlStateValueOff) { - [button setState:NSControlStateValueOff]; - } - - // Maintain _selectedItems appropriately. - if (_selectedItems && (NSInteger)[_selectedItems count] > groupNumber) { - NSMutableArray *groupSelections = _selectedItems[groupNumber]; - BOOL alreadySelected = [groupSelections containsObject:identifier]; - if (selected && !alreadySelected) { - [groupSelections addObject:identifier]; - } else if (!selected && alreadySelected) { - [groupSelections removeObject:identifier]; - } - } - - // Inform delegate about this change if possible. - if (inform && responds) { - [delegate scopeBar:self selectedStateChanged:selected forItem:identifier inGroup:groupNumber]; - } -} - - -- (NSArray *)selectedItems -{ - return [_selectedItems copy]; -} - - -- (void)setDelegate:(id)newDelegate -{ - if (delegate != newDelegate) { - delegate = newDelegate; - [self reloadData]; - } -} - - -- (BOOL)smartResizeEnabled -{ - return _smartResizeEnabled; -} - - -- (void)setSmartResizeEnabled:(BOOL)enabled -{ - if (enabled != _smartResizeEnabled) { - _smartResizeEnabled = enabled; - [self reloadData]; - } -} - -- (void)setEnabledAllGroups:(BOOL)enable { - for (int i = 0; i < (NSInteger)_groups.count; i++) { - [self setEnabledGroup:i enable:enable]; - } -} - -- (void)setEnabledGroup:(int)groupNumber enable:(BOOL)enable { - NSDictionary *group = _groups[groupNumber]; - if (group) { - NSArray* buttons = group[GROUP_BUTTONS]; - for (NSButton* button in buttons) { - [button setEnabled:enable]; - } - } -} - -- (BOOL)notifyDefaultSelections { - return _notifyDefaultSelections; -} - -- (void)setNotifyDefaultSelections:(BOOL)v { - _notifyDefaultSelections = v; -} - -- (CGFloat)fontSize { - return _fontSize; -} - -- (void)setFontSize:(CGFloat)fontSize { - _fontSize = fontSize; -} - -@synthesize delegate; - - -@end diff --git a/Sources/Legacy/MGScopeBar/MGScopeBarDelegateProtocol.h b/Sources/Legacy/MGScopeBar/MGScopeBarDelegateProtocol.h deleted file mode 100644 index 178eef8..0000000 --- a/Sources/Legacy/MGScopeBar/MGScopeBarDelegateProtocol.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// MGScopeBarDelegateProtocol.h -// MGScopeBar -// -// Created by Matt Gemmell on 15/03/2008. -// Copyright 2008 Instinctive Code. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -// Selection modes for the buttons within a group. -typedef NS_ENUM(NSInteger, MGScopeBarGroupSelectionMode) { - MGScopeBarGroupSelectionModeRadio = 0, // Exactly one item in the group will be selected at a time (no more, and no less). - MGScopeBarGroupSelectionModeMultiple = 1 // Any number of items in the group (including none) may be selected at a time. -}; - - -@class MGScopeBar; -@protocol MGScopeBarDelegate - - -// Methods used to configure the scope bar. -// Note: all groupNumber parameters are zero-based. - -@required -- (NSInteger)numberOfGroupsInScopeBar:(MGScopeBar *)theScopeBar; -- (NSArray *)scopeBar:(MGScopeBar *)theScopeBar itemIdentifiersForGroup:(NSInteger)groupNumber; -- (nullable NSString *)scopeBar:(MGScopeBar *)theScopeBar labelForGroup:(NSInteger)groupNumber; // return nil or an empty string for no label. -- (MGScopeBarGroupSelectionMode)scopeBar:(MGScopeBar *)theScopeBar selectionModeForGroup:(NSInteger)groupNumber; -- (nullable NSString *)scopeBar:(MGScopeBar *)theScopeBar titleOfItem:(NSString *)identifier inGroup:(NSInteger)groupNumber; - -@optional -// If the following method is not implemented, all groups except the first will have a separator before them. -- (BOOL)scopeBar:(MGScopeBar *)theScopeBar showSeparatorBeforeGroup:(NSInteger)groupNumber; -- (nullable NSImage *)scopeBar:(MGScopeBar *)theScopeBar imageForItem:(NSString *)identifier inGroup:(NSInteger)groupNumber; // default is no image. Will be shown at 16x16. -- (nullable NSView *)accessoryViewForScopeBar:(MGScopeBar *)theScopeBar; // default is no accessory view. - - -// Notification methods. - -@optional -- (void)scopeBar:(MGScopeBar *)theScopeBar selectedStateChanged:(BOOL)selected forItem:(NSString *)identifier inGroup:(NSInteger)groupNumber; - - -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/Legacy/MGScopeBar/ScopeBarItem.swift b/Sources/Legacy/MGScopeBar/ScopeBarItem.swift deleted file mode 100644 index 1eb8d6d..0000000 --- a/Sources/Legacy/MGScopeBar/ScopeBarItem.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ScopeBarItem.swift -// VisualDiffer -// -// Created by davide ficano on 12/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -enum ScopeBarGroupKey: String { - case label = "Label" // string - case separator = "HasSeparator" // Bool as NSNumber - case selectionMode = "SelectionMode" // MGScopeBarGroupSelectionMode (int) as NSNumber - case items = "Items" // array of dictionaries, each containing the following keys -} - -enum ScopeBarItem: String { - case identifier = "Identifier" // string - case name = "Name" // string -} diff --git a/Sources/Legacy/Predicate/NSPredicate+Objc.h b/Sources/Legacy/Predicate/NSPredicate+Objc.h deleted file mode 100644 index 7dd638d..0000000 --- a/Sources/Legacy/Predicate/NSPredicate+Objc.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSPredicate+Objc.h -// VisualDiffer -// -// Created by davide ficano on 02/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NSPredicate (Helper) - -/** - * Create NSPredicate handling Objective-C NSException - */ -+ (nullable instancetype)createSafeWithFormat:(NSString*)format error:(NSError *_Nullable * _Nullable)outError; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/Legacy/Predicate/NSPredicate+Objc.m b/Sources/Legacy/Predicate/NSPredicate+Objc.m deleted file mode 100644 index 12dcc34..0000000 --- a/Sources/Legacy/Predicate/NSPredicate+Objc.m +++ /dev/null @@ -1,28 +0,0 @@ -// -// NSPredicate+Objc.m -// VisualDiffer -// -// Created by davide ficano on 02/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -#import "NSPredicate+Objc.h" - -@implementation NSPredicate (Helper) - -+ (instancetype)createSafeWithFormat:(NSString*)format error: (NSError**)outError { - @try { - return [NSPredicate predicateWithFormat: format]; - } - @catch (NSException* exception) { - if (outError) { - *outError = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSFormattingError - userInfo:@{NSLocalizedDescriptionKey: exception.description}]; - - } - } - return nil; -} - -@end diff --git a/Sources/Legacy/Predicate/TOPFileSizePredicateEditorRowTemplate.h b/Sources/Legacy/Predicate/TOPFileSizePredicateEditorRowTemplate.h deleted file mode 100644 index d215e62..0000000 --- a/Sources/Legacy/Predicate/TOPFileSizePredicateEditorRowTemplate.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// FileSizePredicateEditorRowTemplate.h -// VisualDiffer -// -// Created by davide ficano on 07/03/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import -#import "TitlePredicateEditorRowTemplate.h" - -@interface TOPFileSizePredicateEditorRowTemplate : TitlePredicateEditorRowTemplate - -@property (strong) NSPopUpButton* sizePopupButton; -@end diff --git a/Sources/Legacy/Predicate/TOPFileSizePredicateEditorRowTemplate.m b/Sources/Legacy/Predicate/TOPFileSizePredicateEditorRowTemplate.m deleted file mode 100644 index 02558d8..0000000 --- a/Sources/Legacy/Predicate/TOPFileSizePredicateEditorRowTemplate.m +++ /dev/null @@ -1,89 +0,0 @@ -// -// FileSizePredicateEditorRowTemplate.m -// VisualDiffer -// -// Created by davide ficano on 07/03/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import "TOPFileSizePredicateEditorRowTemplate.h" - -static const NSInteger VD_SIZE_KB = (1024L); -static const NSInteger VD_SIZE_MB = (VD_SIZE_KB * 1024); -static const NSInteger VD_SIZE_GB = (VD_SIZE_MB * 1024); - -@implementation TOPFileSizePredicateEditorRowTemplate - -- (NSArray *)templateViews { - if (self.sizePopupButton == nil) { - self.sizePopupButton = [[NSPopUpButton alloc] init]; - [self.sizePopupButton addItemsWithTitles:@[@"Bytes", @"KB", @"MB", @"GB"]]; - } - NSArray* myviews = super.templateViews; - return @[myviews[0], - myviews[1], - myviews[2], - self.sizePopupButton]; -} - -- (void)setPredicate:(NSPredicate *)predicate { - NSComparisonPredicate* comparisonPredicate = (NSComparisonPredicate *)predicate; - long long num = [comparisonPredicate.rightExpression.constantValue longLongValue]; - long long size; - - // show in the view the original value entered by user - if ((num % VD_SIZE_GB) == 0) { - size = num / VD_SIZE_GB; - [self.sizePopupButton selectItemAtIndex:3]; - } else if ((num % VD_SIZE_MB) == 0) { - size = num / VD_SIZE_MB; - [self.sizePopupButton selectItemAtIndex:2]; - } else if ((num % VD_SIZE_KB) == 0) { - size = num / VD_SIZE_KB; - [self.sizePopupButton selectItemAtIndex:1]; - } else { - size = num; - [self.sizePopupButton selectItemAtIndex:0]; - } - - NSExpression* bytesExpression = [NSExpression expressionForConstantValue:@(size)]; - predicate = [NSComparisonPredicate predicateWithLeftExpression:comparisonPredicate.leftExpression - rightExpression:bytesExpression - modifier:comparisonPredicate.comparisonPredicateModifier - type:comparisonPredicate.predicateOperatorType - options:comparisonPredicate.options]; - [super setPredicate:predicate]; -} - -- (NSPredicate *)predicateWithSubpredicates:(NSArray *)subpredicates { - long unitySize = 1; - - NSPopUpButton* button = (NSPopUpButton*)self.templateViews[3]; - switch (button.indexOfSelectedItem) { - case 0: - unitySize = 1; - break; - case 1: - unitySize = VD_SIZE_KB; - break; - case 2: - unitySize = VD_SIZE_MB; - break; - case 3: - unitySize = VD_SIZE_GB; - break; - } - - NSComparisonPredicate *predicate = (NSComparisonPredicate *)[super predicateWithSubpredicates:subpredicates]; - long long num = [predicate.rightExpression.constantValue longLongValue]; - - // create the NSPredicate multiplying the value by unity size - NSExpression* bytesExpression = [NSExpression expressionForConstantValue:@(num * unitySize)]; - return [NSComparisonPredicate predicateWithLeftExpression:predicate.leftExpression - rightExpression:bytesExpression - modifier:predicate.comparisonPredicateModifier - type:predicate.predicateOperatorType - options:predicate.options]; -} - -@end diff --git a/Sources/Legacy/Predicate/TOPTimestampPredicateEditorRowTemplate.h b/Sources/Legacy/Predicate/TOPTimestampPredicateEditorRowTemplate.h deleted file mode 100644 index 7a577b3..0000000 --- a/Sources/Legacy/Predicate/TOPTimestampPredicateEditorRowTemplate.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// VDTimestampPredicateEditorRowTemplate.h -// VisualDiffer -// -// Created by davide ficano on 07/03/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import -#import "TitlePredicateEditorRowTemplate.h" - -@interface TOPTimestampPredicateEditorRowTemplate : TitlePredicateEditorRowTemplate - -@end diff --git a/Sources/Legacy/Predicate/TOPTimestampPredicateEditorRowTemplate.m b/Sources/Legacy/Predicate/TOPTimestampPredicateEditorRowTemplate.m deleted file mode 100644 index 372a28b..0000000 --- a/Sources/Legacy/Predicate/TOPTimestampPredicateEditorRowTemplate.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// VDTimestampPredicateEditorRowTemplate.m -// VisualDiffer -// -// Created by davide ficano on 07/03/12. -// Copyright (c) 2012 visualdiffer.com -// - -#import "TOPTimestampPredicateEditorRowTemplate.h" - -const NSInteger VDTimestampElementFlag = NSDatePickerElementFlagYearMonthDay | NSDatePickerElementFlagHourMinuteSecond; - -static NSDateFormatter* TOPTimestampPredicateEditorDateFormatter; - -@interface TOPTimestampPredicateEditorRowTemplate() { - BOOL isDefaultValuesSet; -} -@end; - -@implementation TOPTimestampPredicateEditorRowTemplate - -- (instancetype)init -{ - self = [super init]; - if (self) { - static dispatch_once_t pred; - - dispatch_once(&pred, ^{ - TOPTimestampPredicateEditorDateFormatter = [[NSDateFormatter alloc] init]; - TOPTimestampPredicateEditorDateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss z"; - TOPTimestampPredicateEditorDateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; - TOPTimestampPredicateEditorDateFormatter.timeZone = [NSTimeZone defaultTimeZone]; - TOPTimestampPredicateEditorDateFormatter.formatterBehavior = NSDateFormatterBehaviorDefault; - }); - } - return self; -} - -- (NSArray *)templateViews { - if (!isDefaultValuesSet) { - NSDatePicker* picker = (NSDatePicker*)super.templateViews[2]; - picker.datePickerElements = VDTimestampElementFlag; - picker.dateValue = [NSDate date]; - isDefaultValuesSet = YES; - } - - return super.templateViews; -} - -- (NSPredicate *)predicateWithSubpredicates:(NSArray *)subpredicates { - NSComparisonPredicate *predicate = (NSComparisonPredicate *)[super predicateWithSubpredicates:subpredicates]; - // set to zero from the decimal part present in the NSTimeInterval - // so exact compares (eg a == b) can be done correctly - NSDatePicker* picker = (NSDatePicker*)super.templateViews[2]; - NSDate* d1 = picker.dateValue; - NSDate* d2 = [TOPTimestampPredicateEditorDateFormatter dateFromString:d1.description]; - - NSExpression* dateExpression = [NSExpression expressionForConstantValue:d2]; - return [NSComparisonPredicate predicateWithLeftExpression:predicate.leftExpression - rightExpression:dateExpression - modifier:predicate.comparisonPredicateModifier - type:predicate.predicateOperatorType - options:predicate.options]; -} - -@end diff --git a/Sources/Legacy/Predicate/TitlePredicateEditorRowTemplate.h b/Sources/Legacy/Predicate/TitlePredicateEditorRowTemplate.h deleted file mode 100644 index e7c5cb8..0000000 --- a/Sources/Legacy/Predicate/TitlePredicateEditorRowTemplate.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// TitlePredicateEditorRowTemplate.h -// VisualDiffer -// -// Created by davide ficano on 25/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface TitlePredicateEditorRowTemplate : NSPredicateEditorRowTemplate - -- (instancetype)initWithKeyPathDisplayNames:(NSDictionary*)displayNames - leftKeyPath:(NSString *)leftKeyPath - rightExpressionAttributeType:(NSAttributeType)attributeType - caseInsensitive:(BOOL)caseInsensitive - operators:(NSArray*>*)operators; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/Legacy/Predicate/TitlePredicateEditorRowTemplate.m b/Sources/Legacy/Predicate/TitlePredicateEditorRowTemplate.m deleted file mode 100644 index c821f54..0000000 --- a/Sources/Legacy/Predicate/TitlePredicateEditorRowTemplate.m +++ /dev/null @@ -1,88 +0,0 @@ -// -// TitlePredicateEditorRowTemplate.m -// VisualDiffer -// -// Created by davide ficano on 25/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -#import "TitlePredicateEditorRowTemplate.h" - -@interface TitlePredicateEditorRowTemplate() -@property (nonatomic, copy) NSDictionary* keyPathDisplayNames; -@property (nonatomic, copy) NSDictionary* operatorDisplayNames; -@end - -@implementation TitlePredicateEditorRowTemplate - -- (instancetype)initWithKeyPathDisplayNames:(NSDictionary*)displayNames - leftKeyPath:(NSString *)leftKeyPath - rightExpressionAttributeType:(NSAttributeType)attributeType - caseInsensitive:(BOOL)caseInsensitive - operators:(NSArray*>*)operators { - NSExpression *leftExpr = [NSExpression expressionForKeyPath:leftKeyPath]; - NSMutableArray* ops = [NSMutableArray array]; - - // used to maintain the order inside popup menu - for (NSDictionary* dict in operators) { - [ops addObjectsFromArray:dict.allKeys]; - } - - self = [super initWithLeftExpressions:@[leftExpr] - rightExpressionAttributeType:attributeType - modifier:NSDirectPredicateModifier - operators:ops - options:caseInsensitive ? NSCaseInsensitivePredicateOption : 0]; - - if (self) { - NSMutableDictionary* map = [NSMutableDictionary dictionary]; - - for (NSDictionary* dict in operators) { - [dict enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { - map[key] = obj; - }]; - } - self.operatorDisplayNames = map; - self.keyPathDisplayNames = displayNames; - } - return self; -}; - -- (NSArray *)templateViews { - NSArray *views = [super templateViews]; - - for (NSView *view in views) { - if ([view isKindOfClass:[NSPopUpButton class]]) { - NSPopUpButton *popup = (NSPopUpButton *)view; - - for (NSMenuItem *item in popup.itemArray) { - id represented = item.representedObject; - - // Customize key path titles - if ([represented isKindOfClass:[NSExpression class]]) { - NSExpression *expr = (NSExpression *)represented; - - if (expr.expressionType == NSKeyPathExpressionType) { - NSString *keyPath = expr.keyPath; - NSString *display = self.keyPathDisplayNames[keyPath]; - if (display) { - item.title = display; - } - } - } - - // Customize operator titles - if ([represented isKindOfClass:[NSNumber class]]) { - NSNumber *opType = (NSNumber *)represented; - NSString *customOp = self.operatorDisplayNames[opType]; - if (customOp) { - item.title = customOp; - } - } - } - } - } - - return views; -} -@end diff --git a/Sources/Services/AppleScript/OpenDiffCommand.swift b/Sources/Services/AppleScript/OpenDiffCommand.swift deleted file mode 100644 index 9309bd5..0000000 --- a/Sources/Services/AppleScript/OpenDiffCommand.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// OpenDiffCommand.swift -// VisualDiffer -// -// Created by davide ficano on 04/11/20. -// Copyright (c) 2020 visualdiffer.com -// - -enum CommandError: Int { - case invalidPath = 4000 - case invalidDocument -} - -// the attribute @objc is necessary to work correctly in Swift -@objc(OpenDiffCommand) class OpenDiffCommand: NSScriptCommand { - override func performDefaultImplementation() -> Any? { - guard let evaluatedArguments, - let leftPath = evaluatedArguments["LeftPath"] as? String, - let rightPath = evaluatedArguments["RightPath"] as? String else { - return nil - } - - let leftUrl = URL(filePath: leftPath) - let rightUrl = URL(filePath: rightPath) - - var isDir = false - var leftExists = false - var rightExists = false - - let matches = leftUrl.matchesFileType( - of: rightUrl, - isDir: &isDir, - leftExists: &leftExists, - rightExists: &rightExists - ) - - if matches { - return openDocument( - leftUrl: leftUrl, - rightUrl: rightUrl - ) - } else { - let message = invalidPathMessage( - isDir: isDir, - leftExists: leftExists, - rightExists: rightExists - ) - setScriptError(error: .invalidPath, message: message) - } - return nil - } - - func openDocument( - leftUrl: URL, - rightUrl: URL - ) -> String? { - do { - return try MainActor.assumeIsolated { - try VDDocumentController.shared.openDifferDocument( - leftUrl: leftUrl, - rightUrl: rightUrl - )?.uuid - } - } catch { - setScriptError(error: .invalidDocument, message: error.localizedDescription) - - return nil - } - } - - func invalidPathMessage(isDir: Bool, leftExists: Bool, rightExists: Bool) -> String { - if leftExists, rightExists { - if isDir { - return NSLocalizedString("Left path is a folder but the right is a file; both must be folders or files", comment: "") - } else { - return NSLocalizedString("Left path is a file but the right is a folder; both must be folders or files", comment: "") - } - } - if !leftExists { - return NSLocalizedString("Left path doesn't exist", comment: "") - } - return NSLocalizedString("Right path doesn't exist", comment: "") - } - - func setScriptError(error: CommandError, message: String) { - scriptErrorNumber = error.rawValue - scriptErrorString = message - } -} diff --git a/Sources/SharedKit/Extensions/Appearance/NSAppearance+DarkMode.swift b/Sources/SharedKit/Extensions/Appearance/NSAppearance+DarkMode.swift deleted file mode 100644 index eb9417f..0000000 --- a/Sources/SharedKit/Extensions/Appearance/NSAppearance+DarkMode.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// NSAppearance+DarkMode.swift -// VisualDiffer -// -// Created by davide ficano on 26/10/20. -// Copyright (c) 2020 visualdiffer.com -// - -extension NSAppearance { - static var isDarkMode: Bool { - if #available(macOS 10.14, *) { - return MainActor.assumeIsolated { - let basicAppearance = NSApp.effectiveAppearance.bestMatch(from: [ - NSAppearance.Name.aqua, - NSAppearance.Name.darkAqua, - ]) - return basicAppearance == NSAppearance.Name.darkAqua - } - } - // check 'AppleInterfaceStyle' - return UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" - } - - @objc @MainActor static func change() { - if #available(macOS 10.14, *) { - switch UserDefaults.standard.integer(forKey: "appAppearance") { - case 1: - NSApp.appearance = NSAppearance(named: .aqua) - case 2: - NSApp.appearance = NSAppearance(named: .darkAqua) - default: - break - } - } - } -} diff --git a/Sources/SharedKit/Extensions/Collections/Array+MoveIndexes.swift b/Sources/SharedKit/Extensions/Collections/Array+MoveIndexes.swift deleted file mode 100644 index dab4c7f..0000000 --- a/Sources/SharedKit/Extensions/Collections/Array+MoveIndexes.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Array+MoveIndexes.swift -// VisualDiffer -// -// Created by davide ficano on 10/05/13. -// Copyright (c) 2013 visualdiffer.com -// - -extension Array { - mutating func move(from indexes: IndexSet, to toIndex: Int) -> IndexSet { - var toIndexBlock = toIndex - var delta = 0 - - for fromIdx in indexes { - if fromIdx < toIndexBlock { - let obj = self[fromIdx + delta] - insert(obj, at: toIndexBlock) - remove(at: fromIdx + delta) - delta -= 1 - } else if fromIdx > toIndexBlock { - let obj = self[fromIdx] - remove(at: fromIdx) - insert(obj, at: toIndexBlock) - toIndexBlock += 1 - } - } - var movedIndexes = IndexSet() - for i in 0 ..< indexes.count { - movedIndexes.insert(toIndex + delta + i) - } - return movedIndexes - } -} diff --git a/Sources/SharedKit/Extensions/Color/NSColor+Hex.swift b/Sources/SharedKit/Extensions/Color/NSColor+Hex.swift deleted file mode 100644 index 205ff35..0000000 --- a/Sources/SharedKit/Extensions/Color/NSColor+Hex.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// NSColor+Hex.swift -// VisualDiffer -// -// Created by davide ficano on 01/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Cocoa - -extension NSColor { - @objc static func colorRGBA(_ red: UInt, green: UInt, blue: UInt, alpha: UInt) -> NSColor { - NSColor( - calibratedRed: CGFloat(red) / 255.0, - green: CGFloat(green) / 255.0, - blue: CGFloat(blue) / 255.0, - alpha: CGFloat(alpha) / 255.0 - ) - } - - @objc static func colorFromHexRGBA(_ inColorString: String) -> NSColor? { - var red: UInt = 0 - var green: UInt = 0 - var blue: UInt = 0 - var alpha: UInt = 0 - - if hexRGBA(inColorString, red: &red, green: &green, blue: &blue, alpha: &alpha) { - return colorRGBA(red, green: green, blue: blue, alpha: alpha) - } - return nil - } - - static func hexRGBA( - _ inColorString: String, - red: inout UInt, - green: inout UInt, - blue: inout UInt, - alpha: inout UInt - ) -> Bool { - let scanner = Scanner(string: inColorString) - - // skip the prefix character if present - _ = scanner.scanString("#") - - let hexDigitsCount = scanner.string.distance( - from: scanner.currentIndex, - to: scanner.string.endIndex - ) - var hex: UInt64 = 0 - - guard scanner.scanHexInt64(&hex) else { - return false - } - switch hexDigitsCount { - case 3: - red = UInt((hex >> 8) & 15) - red += red << 4 - green = UInt((hex >> 4) & 15) - green += green << 4 - blue = UInt(hex & 15) - blue += blue << 4 - alpha = 255 - case 6: - red = UInt((hex >> 16) & 255) - green = UInt((hex >> 8) & 255) - blue = UInt(hex & 255) - alpha = 255 - default: - red = UInt((hex >> 24) & 255) - green = UInt((hex >> 16) & 255) - blue = UInt((hex >> 8) & 255) - alpha = UInt(hex & 255) - } - return true - } -} diff --git a/Sources/SharedKit/Extensions/CoreData/NSManagedObjectContext+Helpers.swift b/Sources/SharedKit/Extensions/CoreData/NSManagedObjectContext+Helpers.swift deleted file mode 100644 index d685397..0000000 --- a/Sources/SharedKit/Extensions/CoreData/NSManagedObjectContext+Helpers.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// NSManagedObjectContext+Helpers.swift -// VisualDiffer -// -// Created by davide ficano on 10/10/15. -// Copyright (c) 2015 visualdiffer.com -// - -extension NSManagedObjectContext { - /// - /// Wrap the block around disable/enable undo registration pair - /// - /// Parameters: - /// - block: the code to execute with registration disabled - @MainActor func updateWithoutRecordingModifications(_ block: () -> Void) { - undoManager?.disableUndoRegistration() - - block() - - processPendingChanges() - undoManager?.enableUndoRegistration() - } -} diff --git a/Sources/SharedKit/Extensions/Date/TimeInterval+Helper.swift b/Sources/SharedKit/Extensions/Date/TimeInterval+Helper.swift deleted file mode 100644 index a983ccb..0000000 --- a/Sources/SharedKit/Extensions/Date/TimeInterval+Helper.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// TimeInterval+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 01/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -extension TimeInterval { - func format() -> String { - if self < 60 { - return String(format: NSLocalizedString("%.2f seconds", comment: ""), self) - } - let longInterval = Int64(self) - let hours = longInterval / 3600 - let minutes = (longInterval % 3600) / 60 - let seconds = longInterval % 60 - - var result = [String]() - if hours > 0 { - result.append(String.localizedStringWithFormat(NSLocalizedString("%ld hours", comment: ""), hours)) - } - if minutes > 0 { - result.append(String.localizedStringWithFormat(NSLocalizedString("%ld minutes", comment: ""), minutes)) - } - if seconds > 0 { - result.append(String.localizedStringWithFormat(NSLocalizedString("%ld seconds", comment: ""), seconds)) - } - return result.joined(separator: ", ") - } -} diff --git a/Sources/SharedKit/Extensions/Encoding/CFStringEncoding+StringEncoding.swift b/Sources/SharedKit/Extensions/Encoding/CFStringEncoding+StringEncoding.swift deleted file mode 100644 index e03c9b1..0000000 --- a/Sources/SharedKit/Extensions/Encoding/CFStringEncoding+StringEncoding.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// CFStringEncoding+StringEncoding.swift -// VisualDiffer -// -// Created by davide ficano on 16/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -let noStringEncoding = String.Encoding(rawValue: UInt.max - 1) - -extension CFStringEncoding { - @inline(__always) var stringEncoding: String.Encoding? { - let nsEncoding = CFStringConvertEncodingToNSStringEncoding(self) - if nsEncoding == kCFStringEncodingInvalidId { - return nil - } - return String.Encoding(rawValue: nsEncoding) - } -} - -extension CFStringEncodings { - @inline(__always) var stringEncoding: String.Encoding? { - CFStringEncoding(rawValue).stringEncoding - } -} diff --git a/Sources/SharedKit/Extensions/Error/NSError+Format.swift b/Sources/SharedKit/Extensions/Error/NSError+Format.swift deleted file mode 100644 index c60248f..0000000 --- a/Sources/SharedKit/Extensions/Error/NSError+Format.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// NSError+Format.swift -// VisualDiffer -// -// Created by davide ficano on 31/08/11. -// Copyright (c) 2011 visualdiffer.com -// - -extension NSError { - func format(withPath path: String) -> String { - switch domain { - case NSOSStatusErrorDomain: - String(format: "%@: %@", path, self) - case NSCocoaErrorDomain: - if code == NSFileReadNoPermissionError, - let attrs = try? FileManager.default.attributesOfItem(atPath: path).fileAttributeType, - attrs == .typeSymbolicLink, - let destination = try? FileManager.default.destinationOfSymbolicLink(atPath: path) { - String.localizedStringWithFormat( - NSLocalizedString("Permission denied: symbolic link at %@ points to destination %@. Add the destination to 'Trusted Paths' to allow access.", comment: ""), - path, - destination - ) - } else { - String(format: "%@: %@", path, localizedDescription) - } - default: - String(format: "%@: %@", path, localizedDescription) - } - } -} diff --git a/Sources/SharedKit/Extensions/FileManager/FileManager+Attributes.swift b/Sources/SharedKit/Extensions/FileManager/FileManager+Attributes.swift deleted file mode 100644 index 6f5858c..0000000 --- a/Sources/SharedKit/Extensions/FileManager/FileManager+Attributes.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// FileManager+Attributes.swift -// VisualDiffer -// -// Created by davide ficano on 13/01/14. -// Copyright (c) 2014 visualdiffer.com -// - -import Foundation - -public extension FileManager { - /** - * 'Modification date' change isn't reliable on smb volumes - * sometimes it's ignored without reporting error so this method - * uses an hack to bypass this bug. - * The hack consists to call twice the setAttribute for modification date on smb - */ - func setFileAttributes( - _ attrs: [FileAttributeKey: Any], - ofItemAtPath path: URL, - volumeType type: String - ) throws { - try setFileAttributes( - attrs, - ofItemAtPath: path.osPath, - volumeType: type - ) - } - - @objc func setFileAttributes( - _ attrs: [FileAttributeKey: Any], - ofItemAtPath path: String, - volumeType type: String - ) throws { - if type == "smbfs" { - let creation = attrs[.creationDate] as? Date - let modification = attrs[.modificationDate] as? Date - - if creation != nil || modification != nil { - var patchedAttrs = attrs - var correctDates = [FileAttributeKey: Any]() - - if let creation { - patchedAttrs[.creationDate] = Date() - correctDates[.creationDate] = creation - } - if let modification { - patchedAttrs[.modificationDate] = Date() - correctDates[.modificationDate] = modification - } - try setAttributes( - patchedAttrs, - ofItemAtPath: path - ) - try setAttributes( - correctDates, - ofItemAtPath: path - ) - } - } - // and set attributes the second time... - try setAttributes( - attrs, - ofItemAtPath: path - ) - } -} - -extension [FileAttributeKey: Any] { - var fileAttributeType: FileAttributeType? { - if let type = self[.type] as? String { - FileAttributeType(rawValue: type) - } else { - nil - } - } -} diff --git a/Sources/SharedKit/Extensions/Formatters/DateFormatter+Helper.swift b/Sources/SharedKit/Extensions/Formatters/DateFormatter+Helper.swift deleted file mode 100644 index 7527dcd..0000000 --- a/Sources/SharedKit/Extensions/Formatters/DateFormatter+Helper.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// DateFormatter+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 28/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -@objc extension DateFormatter { - func widthOfLongestDateStringWithLevel(attrs: [NSAttributedString.Key: Any]) -> CGFloat { - // This code is rather tied to the gregorian date. - // We pick an arbitrary date, and iterate through each of the days of the week - // and each of the months to find the longest string for the given detail level. - // Because a person can customize (via the intl prefs) the string, - // we need to iterate through each level for each item. - let weekDayCount = weekdaySymbols.isEmpty ? 7 : weekdaySymbols.count - - var dateComponents = DateComponents() - dateComponents.year = 2015 - dateComponents.month = 5 - dateComponents.day = 16 - dateComponents.hour = 10 - dateComponents.minute = 40 - dateComponents.second = 30 - dateComponents.timeZone = TimeZone.current - let gregorian = Calendar(identifier: .gregorian) - - // Find the longest week day - var longestWeekDay = 1 - var result: CGFloat = 0 - - for dayOfWeek in 1 ... weekDayCount { - dateComponents.day = dayOfWeek - if let date = gregorian.date(from: dateComponents) { - let str = string(from: date) - let length = (str as NSString).size(withAttributes: attrs).width - if length > result { - result = length - longestWeekDay = dayOfWeek - } - } - } - - let monthCount = monthSymbols.isEmpty ? 12 : monthSymbols.count - - for month in 1 ... monthCount { - dateComponents.day = longestWeekDay - dateComponents.month = month - if let date = gregorian.date(from: dateComponents) { - let str = string(from: date) - let length = (str as NSString).size(withAttributes: attrs).width - result = max(result, length) - } - } - - return result - } -} diff --git a/Sources/SharedKit/Extensions/Image/NSImage+Tint.swift b/Sources/SharedKit/Extensions/Image/NSImage+Tint.swift deleted file mode 100644 index 3a94498..0000000 --- a/Sources/SharedKit/Extensions/Image/NSImage+Tint.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// NSImage+Tint.swift -// VisualDiffer -// -// Created by davide ficano on 04/07/15. -// Converted to Swift by davide ficano on 03/05/25. -// Copyright (c) 2010 visualdiffer.com - -import Cocoa - -public extension NSImage { - // copied from https://stackoverflow.com/a/1415200/195893 - // On Catalina kCIContextUseSoftwareRenderer raises error - // ** OpenCL Error Notification: [CL_DEVICE_NOT_AVAILABLE] : OpenCL Error : Error: build program driver returned (-1) ** - // ** OpenCL Error Notification: OpenCL Warning : clBuildProgram failed: could not build program for 0xffffffff (Intel(R) Core(TM) i5-3210M CPU 2.50GHz) (err:-1) ** - // https://developer.apple.com/devcenter/download.action?path=/videos/wwdc_2011__hd/session_422__using_core_image_on_ios_and_mac_os_x.m4v - // https://docs.huihoo.com/apple/wwdc/2011/session_422__using_core_image_on_ios_and_mac_os_x.pdf - /** - * Apply the tint color to image using monochrome filter - */ - func monochromaticTint( - _ tint: NSColor?, - useSoftwareRenderer: Bool - ) -> NSImage { - if let tint, - let color = CIColor(color: tint), - let compositingFilter = composingFilter(colorFilter(color: color), monochromeFilter()), - let outputImage = compositingFilter.value(forKey: kCIOutputImageKey) as? CIImage { - let extend = outputImage.extent - let tintedImage = NSImage(size: size) - - tintedImage.lockFocus() - if let contextRef = NSGraphicsContext.current?.cgContext { - let ciContext = CIContext( - cgContext: contextRef, - options: [CIContextOption.useSoftwareRenderer: useSoftwareRenderer] - ) - let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) - ciContext.draw(outputImage, in: rect, from: extend) - } - tintedImage.unlockFocus() - - return tintedImage - } - // swiftlint:disable:next force_cast - return copy() as! NSImage - } - - /** - * Create a new image drawing the overImage in front of current image (the background image). - * Use NSImageRep to improve image quality results - * @param overImage the image to draw over the background - */ - func overImage(_ overImage: NSImage) -> NSImage { - let size = overImage.size - let imageBounds = NSRect(x: 0, y: 0, width: size.width, height: size.height) - let rep = NoodleCustomImageRep { _ in - self.draw(at: NSPoint.zero, from: imageBounds, operation: .copy, fraction: 1) - overImage.draw(at: NSPoint.zero, from: imageBounds, operation: .sourceOver, fraction: 1) - } - rep.size = size - let image = NSImage(size: size) - image.addRepresentation(rep) - return image - } - - private func colorFilter(color: CIColor) -> CIFilter? { - guard let colorGenerator = CIFilter(name: "CIConstantColorGenerator"), - let colorFilter = CIFilter(name: "CIColorControls") else { - return nil - } - colorGenerator.setValue(color, forKey: kCIInputColorKey) - - colorFilter.setValue(colorGenerator.value(forKey: kCIOutputImageKey), forKey: kCIInputImageKey) - colorFilter.setValue(NSNumber(value: 3.0), forKey: kCIInputSaturationKey) - colorFilter.setValue(NSNumber(value: 0.35), forKey: kCIInputBrightnessKey) - colorFilter.setValue(NSNumber(value: 1.0), forKey: kCIInputContrastKey) - - return colorFilter - } - - private func monochromeFilter() -> CIFilter? { - guard let monochromeFilter = CIFilter(name: "CIColorMonochrome"), - let data = tiffRepresentation, - let baseImage = CIImage(data: data) else { - return nil - } - - monochromeFilter.setValue(baseImage, forKey: kCIInputImageKey) - monochromeFilter.setValue(CIColor(red: 0.75, green: 0.75, blue: 0.75), forKey: kCIInputColorKey) - monochromeFilter.setValue(NSNumber(value: 1.0), forKey: kCIInputIntensityKey) - - return monochromeFilter - } - - private func composingFilter(_ colorFilter: CIFilter?, _ monochromeFilter: CIFilter?) -> CIFilter? { - guard let colorFilter, - let monochromeFilter, - let compositingFilter = CIFilter(name: "CIMultiplyCompositing") else { - return nil - } - - compositingFilter.setValue( - colorFilter.value(forKey: kCIOutputImageKey), - forKey: kCIInputImageKey - ) - compositingFilter.setValue( - monochromeFilter.value(forKey: kCIOutputImageKey), - forKey: kCIInputBackgroundImageKey - ) - - return compositingFilter - } - - /** - * Apply the tint color to image - */ - func tinted(with color: NSColor) -> NSImage { - guard let image = copy() as? NSImage else { - return self - } - image.lockFocus() - color.set() - let imageRect = NSRect(origin: .zero, size: image.size) - imageRect.fill(using: .sourceAtop) - image.unlockFocus() - return image - } -} diff --git a/Sources/SharedKit/Extensions/Menu/NSMenu+File.swift b/Sources/SharedKit/Extensions/Menu/NSMenu+File.swift deleted file mode 100644 index e8198ae..0000000 --- a/Sources/SharedKit/Extensions/Menu/NSMenu+File.swift +++ /dev/null @@ -1,250 +0,0 @@ -// -// NSMenu+File.swift -// VisualDiffer -// -// Created by davide ficano on 20/12/12. -// Copyright (c) 2012 visualdiffer.com -// - -enum AppNameAttributeKey: String { - case preferred = "appNamePreferred" - case system = "appNameSystem" -} - -extension NSMenu { - static let preferredEditorPrefName = "preferredEditor" - - @objc static func appsMenuForFile( - _ path: URL?, - openAppAction: Selector, - openOtherAppAction: Selector - ) -> NSMenu { - let appsMenu = NSMenu() - - appsMenu.addMenuItemsForFile( - path, - openAppAction: openAppAction, - openOtherAppAction: openOtherAppAction - ) - - return appsMenu - } - - @objc func addMenuItemsForFile( - _ path: URL?, - openAppAction: Selector, - openOtherAppAction: Selector - ) { - guard let path else { - addItem( - withTitle: NSLocalizedString("", comment: ""), - action: nil, - keyEquivalent: "" - ) - return - } - - let appNames = addTopMenusForFile( - path, - descriptionColor: NSColor.gray, - openAppAction: openAppAction - ) - - // get all apps able to open path - let url = path as CFURL - guard let arr = LSCopyApplicationURLsForURL(url, .all)?.takeRetainedValue() as? [URL] else { - return - } - - // if default app exists it is surely into array - if arr.isEmpty { - if appNames[.preferred] == nil { - addItem( - withTitle: NSLocalizedString("", comment: ""), - action: nil, - keyEquivalent: "" - ) - } - } else { - if !appNames.isEmpty { - addItem(NSMenuItem.separator()) - } - - addAppMenuItems( - mapAppNameToPath(arr, excludeAppNames: Array(appNames.values)), - openAppAction: openAppAction - ) - } - - // this happens when the preferred editor is equal to the default system - // application and it is the only application - if let item = item(at: numberOfItems - 1), - !item.isSeparatorItem { - addItem(NSMenuItem.separator()) - } - addItem( - withTitle: NSLocalizedString("Other...", comment: ""), - action: openOtherAppAction, - keyEquivalent: "" - ) - } - - func addAppMenuItems( - _ dictAppNames: [String: URL], - openAppAction: Selector - ) { - let arrNames = dictAppNames.keys.sorted { - $0.localizedCaseInsensitiveCompare($1) == .orderedAscending - } - for name in arrNames { - if let path = dictAppNames[name] { - addAppItem( - title: name, - openAppAction: openAppAction, - appPath: path - ) - } - } - } - - func addTopMenusForFile( - _ path: URL, - descriptionColor: NSColor, - openAppAction: Selector - ) -> [AppNameAttributeKey: String] { - var appNames = [AppNameAttributeKey: String]() - - let (defaultAppUrl, defaultAppName) = getSystemDefaultAppForFile(path) - let (preferredAppUrl, preferredAppName) = getPreferredAppForFile(path) - - if let defaultAppUrl, let preferredAppUrl, defaultAppUrl == preferredAppUrl { - addItem( - defaultAppName ?? "", - description: NSLocalizedString(" (System Default)", comment: ""), - descriptionColor: descriptionColor, - appPath: defaultAppUrl, - openAppAction: openAppAction - ) - appNames[.preferred] = preferredAppName - appNames[.system] = defaultAppName - } else { - if let preferredAppName, let preferredAppUrl { - addItem( - preferredAppName, - description: NSLocalizedString(" (App Default)", comment: ""), - descriptionColor: descriptionColor, - appPath: preferredAppUrl, - openAppAction: openAppAction - ) - appNames[.preferred] = preferredAppName - } - if let defaultAppName, let defaultAppUrl { - addItem( - defaultAppName, - description: NSLocalizedString(" (System Default)", comment: ""), - descriptionColor: descriptionColor, - appPath: defaultAppUrl, - openAppAction: openAppAction - ) - appNames[.system] = defaultAppName - } - } - - return appNames - } - - @discardableResult - func addItem( - _ title: String, - description: String, - descriptionColor: NSColor, - appPath: URL, - openAppAction: Selector - ) -> NSMenuItem { - let item = addAppItem( - title: "", - openAppAction: openAppAction, - appPath: appPath - ) - if let font { - let attributes = AttributedMenuItem.createAttributes( - title: title, - description: description, - descriptionColor: descriptionColor, - font: font - ) - item.attributedTitle = AttributedMenuItem.createTitle(attributes) - } - - return item - } - - @discardableResult - func addAppItem( - title: String, - openAppAction: Selector, - appPath: URL - ) -> NSMenuItem { - let image = NSWorkspace.shared.icon(forFile: appPath.osPath) - image.size = NSSize(width: 16.0, height: 16.0) - - let item = addItem( - withTitle: title, - action: openAppAction, - keyEquivalent: "" - ) - item.representedObject = appPath.osPath - item.image = image - - return item - } - - func getSystemDefaultAppForFile(_ path: URL) -> (appUrl: URL?, appName: String?) { - guard let appUrl = NSWorkspace.shared.urlForApplication(toOpen: path) else { - return (nil, nil) - } - guard let values = try? appUrl.resourceValues(forKeys: [URLResourceKey.localizedNameKey]), - let appName = values.localizedName else { - return (appUrl, nil) - } - - return (appUrl, appName) - } - - func getPreferredAppForFile(_: URL) -> (appUrl: URL?, appName: String?) { - guard let appPath = UserDefaults.standard.string(forKey: Self.preferredEditorPrefName) else { - return (nil, nil) - } - - let appUrl = URL(filePath: appPath) - - guard let values = try? appUrl.resourceValues(forKeys: [URLResourceKey.localizedNameKey]), - let appName = values.localizedName else { - return (appUrl, nil) - } - - return (appUrl, appName) - } - - func mapAppNameToPath( - _ appUrls: [URL], - excludeAppNames: [String] - ) -> [String: URL] { - var dictAppNames = [String: URL]() - - for url in appUrls { - if let values = try? url.resourceValues(forKeys: [.localizedNameKey]), - let displayName = values.localizedName { - dictAppNames[displayName] = url - } else { - dictAppNames[url.lastPathComponent] = url - } - } - - for appName in excludeAppNames { - dictAppNames.removeValue(forKey: appName) - } - - return dictAppNames - } -} diff --git a/Sources/SharedKit/Extensions/OptionSet/FlagSet.swift b/Sources/SharedKit/Extensions/OptionSet/FlagSet.swift deleted file mode 100644 index 62d86a3..0000000 --- a/Sources/SharedKit/Extensions/OptionSet/FlagSet.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// FlagSet.swift -// VisualDiffer -// -// Created by davide ficano on 31/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -public protocol FlagSet: OptionSet {} - -public extension FlagSet { - @inlinable - subscript(option: Self.Element) -> Bool { - get { - contains(option) - } - set { - if newValue { - insert(option) - } else { - remove(option) - } - } - } -} diff --git a/Sources/SharedKit/Extensions/OptionSet/OptionSet+Toggle.swift b/Sources/SharedKit/Extensions/OptionSet/OptionSet+Toggle.swift deleted file mode 100644 index d0b9b28..0000000 --- a/Sources/SharedKit/Extensions/OptionSet/OptionSet+Toggle.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// OptionSet+Toggle.swift -// VisualDiffer -// -// Created by davide ficano on 23/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension OptionSet { - func toggled(_ member: Self) -> Self { - symmetricDifference(member) - } - - // periphery:ignore - mutating func toggle(_ member: Self) { - formSymmetricDifference(member) - } - - mutating func setValue(_ newValue: Bool, element: Self.Element) { - if newValue { - insert(element) - } else { - remove(element) - } - } -} diff --git a/Sources/SharedKit/Extensions/Panel/NSAlert+Helper.swift b/Sources/SharedKit/Extensions/Panel/NSAlert+Helper.swift deleted file mode 100644 index b038a57..0000000 --- a/Sources/SharedKit/Extensions/Panel/NSAlert+Helper.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// NSAlert+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 11/01/13. -// Copyright (c) 2013 visualdiffer.com -// - -@objc extension NSAlert { - static func showModalConfirm( - messageText: String, - informativeText: String, - suppressPropertyName: String? = nil, - yesText: String? = nil, - noText: String? = nil - ) -> Bool { - let defaults = UserDefaults.standard - - if let suppressPropertyName { - if defaults.bool(forKey: suppressPropertyName) { - return true - } - } - let alert = Self() - - alert.messageText = messageText - alert.informativeText = informativeText - alert.showsSuppressionButton = suppressPropertyName != nil - alert.addButton(withTitle: yesText ?? NSLocalizedString("Yes", comment: "")) - alert.addButton(withTitle: noText ?? NSLocalizedString("No", comment: "")) - alert.buttons[1].keyEquivalent = KeyEquivalent.escape - - let result = alert.runModal() == .alertFirstButtonReturn - - if result, - let suppressPropertyName, - let button = alert.suppressionButton, - button.state == .on { - defaults.setValue(true, forKey: suppressPropertyName) - } - - return result - } - - static func showModalInfo( - messageText: String, - informativeText: String, - suppressPropertyName: String? - ) { - let defaults = UserDefaults.standard - - if let suppressPropertyName { - if defaults.bool(forKey: suppressPropertyName) { - return - } - } - let alert = Self() - - alert.messageText = messageText - alert.alertStyle = .informational - alert.informativeText = informativeText - alert.showsSuppressionButton = suppressPropertyName != nil - - alert.runModal() - - if let suppressPropertyName, - let button = alert.suppressionButton, - button.state == .on { - defaults.setValue(true, forKey: suppressPropertyName) - } - } -} diff --git a/Sources/SharedKit/Extensions/Panel/NSOpenPanel+Application.swift b/Sources/SharedKit/Extensions/Panel/NSOpenPanel+Application.swift deleted file mode 100644 index 2c9f0cc..0000000 --- a/Sources/SharedKit/Extensions/Panel/NSOpenPanel+Application.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// NSOpenPanel+Application.swift -// VisualDiffer -// -// Created by davide ficano on 31/08/11. -// Copyright (c) 2011 visualdiffer.com -// - -extension NSOpenPanel { - @objc func openApplication(title: String) -> NSOpenPanel { - self.title = title - // since 10.11 the title is no longer shown so we use the message property - message = title - allowsMultipleSelection = false - allowedContentTypes = [ - .application, - .applicationBundle, - .executable, - ] - let paths = NSSearchPathForDirectoriesInDomains( - .applicationDirectory, - .localDomainMask, - true - ) - if !paths.isEmpty { - directoryURL = URL(filePath: paths[0]) - } - return self - } -} diff --git a/Sources/SharedKit/Extensions/Panel/QLPreviewPanel.swift b/Sources/SharedKit/Extensions/Panel/QLPreviewPanel.swift deleted file mode 100644 index 85f64fc..0000000 --- a/Sources/SharedKit/Extensions/Panel/QLPreviewPanel.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// QLPreviewPanel.swift -// VisualDiffer -// -// Created by davide ficano on 30/07/11. -// Copyright (c) 2011 visualdiffer.com -// - -import Quartz - -extension QLPreviewPanel { - @objc static func toggle() { - if sharedPreviewPanelExists(), shared().isVisible { - shared().orderOut(nil) - } else { - shared().makeKeyAndOrderFront(nil) - } - } -} diff --git a/Sources/SharedKit/Extensions/Pasteboard/NSPasteboard+Helper.swift b/Sources/SharedKit/Extensions/Pasteboard/NSPasteboard+Helper.swift deleted file mode 100644 index 21d1199..0000000 --- a/Sources/SharedKit/Extensions/Pasteboard/NSPasteboard+Helper.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// NSPasteboard+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 12/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSPasteboard { - @discardableResult - @objc func copy(lines: [String]) -> Bool { - clearContents() - return writeObjects([lines.joined(separator: "\n")] as [NSString]) - } - - @discardableResult - @objc func copy(urls: [URL]) -> Bool { - if urls.isEmpty { - return false - } - clearContents() - return writeObjects(urls as [NSURL]) - } -} diff --git a/Sources/SharedKit/Extensions/Predicate/NSPredicateEditor+Fix.swift b/Sources/SharedKit/Extensions/Predicate/NSPredicateEditor+Fix.swift deleted file mode 100644 index a9b15e3..0000000 --- a/Sources/SharedKit/Extensions/Predicate/NSPredicateEditor+Fix.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// NSPredicateEditor+Fix.swift -// VisualDiffer -// -// Created by davide ficano on 28/10/20. -// Copyright (c) 2020 visualdiffer.com -// - -extension NSPredicateEditor { - /** - * Resize editor input text - */ - func resizeRows(_ width: CGFloat) { - let rowTemplates = rowTemplates - for row in rowTemplates { - let views = row.templateViews - for view in views { - if let view = view as? NSTextField { - var r = view.frame - r.size.width = width - view.frame = r - - break - } - } - } - } -} diff --git a/Sources/SharedKit/Extensions/String/String+Helper.swift b/Sources/SharedKit/Extensions/String/String+Helper.swift deleted file mode 100644 index 06fdbc3..0000000 --- a/Sources/SharedKit/Extensions/String/String+Helper.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// String+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 12/08/13. -// Copyright (c) 2013 visualdiffer.com -// - -import Foundation - -public extension String { - func hasPrefix(_ prefix: String, ignoreCase: Bool) -> Bool { - if ignoreCase { - return range(of: prefix, options: [.caseInsensitive, .anchored]) != nil - } - return hasPrefix(prefix) - } - - func hasSuffix(_ suffix: String, ignoreCase: Bool) -> Bool { - if ignoreCase { - return range(of: suffix, options: [.caseInsensitive, .anchored, .backwards]) != nil - } - return hasSuffix(suffix) - } - - func trimmingSuffix(_ prefix: Character) -> String { - if let index = lastIndex(where: { $0 != prefix }) { - return String(self[..], - normalStyle: [NSAttributedString.Key: Any], - highlightStyle: [NSAttributedString.Key: Any] - ) -> NSAttributedString { - if ranges.isEmpty { - return NSAttributedString(string: self, attributes: normalStyle) - } - let attrString = NSMutableAttributedString(string: self) - - attrString.beginEditing() - var start = startIndex - - for range in ranges { - let normalRange = NSRange(start ..< range.lowerBound, in: self) - let highlightRange = NSRange(range, in: self) - attrString.addAttributes(highlightStyle, range: highlightRange) - attrString.addAttributes(normalStyle, range: normalRange) - - start = range.upperBound - } - let remainingRange = start ..< endIndex - attrString.addAttributes(normalStyle, range: NSRange(remainingRange, in: self)) - attrString.endEditing() - - return attrString - } - - func highlights( - _ ranges: [Range], - normalColor: NSColor, - highlightColor: NSColor, - font: NSFont - ) -> NSAttributedString { - let normalStyle = [ - NSAttributedString.Key.font: font, - NSAttributedString.Key.foregroundColor: normalColor, - ] - - let boldFont = NSFontManager.shared.convert(font, toHaveTrait: [.boldFontMask, .unitalicFontMask]) - let hightlightStyle = [ - NSAttributedString.Key.font: boldFont, - NSAttributedString.Key.foregroundColor: highlightColor, - ] - - return highlights( - ranges, - normalStyle: normalStyle, - highlightStyle: hightlightStyle - ) - } -} diff --git a/Sources/SharedKit/Extensions/String/String+Occcurrences.swift b/Sources/SharedKit/Extensions/String/String+Occcurrences.swift deleted file mode 100644 index 56e1d38..0000000 --- a/Sources/SharedKit/Extensions/String/String+Occcurrences.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// String+Occcurrences.swift -// VisualDiffer -// -// Created by davide ficano on 05/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -extension String { - // periphery:ignore - func countOccurrences( - _ pattern: String, - options: NSString.CompareOptions = [] - ) -> Int { - var count = 0 - var remainingRange = startIndex ..< endIndex - - while let foundRange = range( - of: pattern, - options: options, - range: remainingRange - ) { - count += 1 - remainingRange = foundRange.upperBound ..< endIndex - } - - return count - } - - func rangesOfString( - _ pattern: String, - options: NSString.CompareOptions - ) -> [Range] { - var ranges = [Range]() - var remainingRange = startIndex ..< endIndex - - while let foundRange = range( - of: pattern, - options: options, - range: remainingRange - ) { - ranges.append(foundRange) - remainingRange = foundRange.upperBound ..< endIndex - } - - return ranges - } -} diff --git a/Sources/SharedKit/Extensions/String/String+Path.swift b/Sources/SharedKit/Extensions/String/String+Path.swift deleted file mode 100644 index 1b55387..0000000 --- a/Sources/SharedKit/Extensions/String/String+Path.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// String+Path.swift -// VisualDiffer -// -// Created by davide ficano on 16/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -extension String { - func isAbsolutePath() -> Bool { - hasPrefix("/") - } - - /** - * No native valid alteratives to the NSString version - */ - var standardizingPath: String { - (self as NSString).standardizingPath - } -} diff --git a/Sources/SharedKit/Extensions/String/String+RegularExpression.swift b/Sources/SharedKit/Extensions/String/String+RegularExpression.swift deleted file mode 100644 index 2d2f19b..0000000 --- a/Sources/SharedKit/Extensions/String/String+RegularExpression.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// String+RegularExpression.swift -// VisualDiffer -// -// Created by davide ficano on 05/05/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Foundation - -private struct GlobConverter { - private var regex = [NSRegularExpression]() - private var templates = [String]() - - init() { - let globs = [ - ("([.^$+(){}\\[\\]\\\\|])", "\\\\$1"), - ("\\?", "(.|[\r\n])"), - ("\\*", "(.|[\r\n])*"), - ] - - for (regexString, templateString) in globs { - // swiftlint:disable:next force_try - let re = try! NSRegularExpression( - pattern: regexString, - options: .caseInsensitive - ) - regex.append(re) - templates.append(templateString) - } - } - - private func replace(index: Int, in str: String) -> String { - let re = regex[index] - let template = templates[index] - - return re.stringByReplacingMatches( - in: str, - options: [], - range: NSRange(location: 0, length: str.count), - withTemplate: template - ) - } - - func convert(_ str: String) -> String { - var re = str - - for i in 0 ..< regex.count { - re = replace(index: i, in: re) - } - return re - } -} - -public extension String { - /** - * Replace tagged expressions ($1, $2, ...) present in template - * with substrings ranges present in result - * Example: self = 001.jpg template = $1.raw returns 001.raw - */ - func replace( - template chars: String, - result: NSTextCheckingResult - ) -> String { - let size = chars.count - var foundTagged = false - var foundEscape = false - var i = 0 - - var inString = "" - - while i < size { - if foundTagged { - foundTagged = false - var value = 0 - var position = chars.index(chars.startIndex, offsetBy: i) - - if chars[position] == "$" { - inString.append(chars[position]) - i += 1 - } else { - while i < size, let num = chars[position].wholeNumberValue { - value = value * 10 + num - i += 1 - position = chars.index(chars.startIndex, offsetBy: i) - } - if value < result.numberOfRanges, let range = Range(result.range(at: value), in: self) { - inString.append(String(self[range])) - } - } - } else if foundEscape { - foundEscape = false - let position = chars.index(chars.startIndex, offsetBy: i) - i += 1 - switch chars[position] { - case "t": - inString.append("\t") - case "\\": - inString.append("\\") - default: - break - } - } - // should exceed array range - if i < size { - let position = chars.index(chars.startIndex, offsetBy: i) - i += 1 - let ch = chars[position] - - if ch == "$" { - foundTagged = true - } else if ch == "\\" { - foundEscape = true - } else { - inString.append(ch) - } - } - } - return inString - } - - /** - * Convert a glob string to a valid regular expression string - * Strings like "*m" are converted to ".*m" - * Brackets and "?" are correctly escaped - */ - private func _convertGlobMetaCharsToRegexpMetaChars() -> String { - enum Static { - static let converter: GlobConverter = .init() - } - - return Static.converter.convert(self) - } - - func convertGlobMetaCharsToRegexpMetaChars() -> String { - _convertGlobMetaCharsToRegexpMetaChars() - } -} diff --git a/Sources/SharedKit/Extensions/TableView/NSTableView+Font.swift b/Sources/SharedKit/Extensions/TableView/NSTableView+Font.swift deleted file mode 100644 index df854c9..0000000 --- a/Sources/SharedKit/Extensions/TableView/NSTableView+Font.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// NSTableView+Font.swift -// VisualDiffer -// -// Created by davide ficano on 07/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -@objc extension NSTableView { - func updateFont( - _ font: NSFont, - reloadData reload: Bool - ) { - let layoutManager = NSLayoutManager() - rowHeight = layoutManager.defaultLineHeight(for: font) + 4 - - if reload { - reloadData() - } - } -} diff --git a/Sources/SharedKit/Extensions/TableView/NSTableView+Row.swift b/Sources/SharedKit/Extensions/TableView/NSTableView+Row.swift deleted file mode 100644 index cdc32c1..0000000 --- a/Sources/SharedKit/Extensions/TableView/NSTableView+Row.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// NSTableView+Row.swift -// VisualDiffer -// -// Created by davide ficano on 17/08/15. -// Copyright (c) 2015 visualdiffer.com -// - -import Foundation - -extension NSTableView { - // Select the row or the closest to it, return the closest row or the original value - @discardableResult - @objc func selectRow( - closestTo row: Int, - byExtendingSelection: Bool, - ensureVisible: Bool - ) -> Int { - let closestRow = findValid(row: row) - - selectRowIndexes(IndexSet(integer: closestRow), byExtendingSelection: byExtendingSelection) - - if ensureVisible { - scrollRowToVisible(row) - } - - return closestRow - } - - private func findValid(row: Int) -> Int { - if row < 0 || dataSource == nil { - return 0 - } - if let dataSource, - let rows = dataSource.numberOfRows?(in: self) { - let lastRow = rows - 1 - if lastRow < row { - return lastRow - } - } - return row - } - - @objc var firstVisibleRow: Int { - guard let superview else { - return -1 - } - let bounds = superview.bounds - - return row(at: bounds.origin) - } - - @objc var lastVisibleRow: Int { - guard let superview else { - return -1 - } - var bounds = superview.bounds - bounds.origin.y += bounds.size.height - 1 - - return row(at: bounds.origin) - } - - /** - * If no row is visible then scroll to suggestedRow, if suggested row is -1 scroll to row 0 - */ - @discardableResult - @objc func ensureRowVisibility(suggestedRow: Int) -> Int { - var visibleRow = firstVisibleRow - - if visibleRow < 0 { - if suggestedRow < 0 { - visibleRow = 0 - } else { - visibleRow = suggestedRow - } - } - scrollRowToVisible(visibleRow) - return visibleRow - } - - @objc func scrollTo(row: Int, center: Bool) { - if row < 0 { - return - } - - // copied from https://stackoverflow.com/a/49192512/195893 - let rowRect = frameOfCell(atColumn: 0, row: row) - - let scrollView = enclosingScrollView - let headerHeight = headerView?.frame.size.height ?? 0 - - let point = if center, - let scrollView { - NSPoint( - x: 0, - y: rowRect.origin.y - headerHeight + (rowRect.size.height / 2) - (scrollView.frame.size.height / 2) - ) - } else { - NSPoint( - x: 0, - y: rowRect.origin.y - headerHeight - ) - } - scroll(point) - } -} diff --git a/Sources/SharedKit/Extensions/Toolbar/NSToolbar+Create.swift b/Sources/SharedKit/Extensions/Toolbar/NSToolbar+Create.swift deleted file mode 100644 index c32cab2..0000000 --- a/Sources/SharedKit/Extensions/Toolbar/NSToolbar+Create.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSToolbar+Create.swift -// VisualDiffer -// -// Created by davide ficano on 07/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -@objc extension NSToolbar { - convenience init(identifier: String, delegate dele: NSToolbarDelegate) { - self.init(identifier: identifier) - - displayMode = .iconAndLabel - allowsUserCustomization = true - autosavesConfiguration = true - delegate = dele - } -} diff --git a/Sources/SharedKit/Extensions/Toolbar/NSToolbarItem+Create.swift b/Sources/SharedKit/Extensions/Toolbar/NSToolbarItem+Create.swift deleted file mode 100644 index 01491bd..0000000 --- a/Sources/SharedKit/Extensions/Toolbar/NSToolbarItem+Create.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// NSToolbarItem+Create.swift -// VisualDiffer -// -// Created by davide ficano on 31/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -@objc extension NSToolbarItem { - convenience init( - identifier: NSToolbarItem.Identifier, - label: String, - tooltip: String?, - image: NSImage?, - target: AnyObject?, - action: Selector? - ) { - self.init(itemIdentifier: identifier) - _ = with( - label: label, - tooltip: tooltip, - image: image, - target: target, - action: action - ) - } - - func with( - label: String, - tooltip: String?, - image: NSImage?, - target: AnyObject?, - action: Selector? - ) -> Self { - self.label = label - paletteLabel = label - - toolTip = tooltip - self.image = image - - self.target = target - self.action = action - - return self - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+FileManager.swift b/Sources/SharedKit/Extensions/URL/URL+FileManager.swift deleted file mode 100644 index 21e15fc..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+FileManager.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// URL+FileManager.swift -// VisualDiffer -// -// Created by davide ficano on 10/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -public extension URL { - func isFileDirectory() throws -> Bool { - let resources = try resourceValues(forKeys: [.isDirectoryKey]) - - if let isDirectory = resources.isDirectory { - return isDirectory - } - - return false - } - - /** - * URL appends "/" to the end but this causes problems with the symlink, so it is removed to get the correct result - */ - func destinationOfSymbolicLink(directoryHint: DirectoryHint = .inferFromPath) throws -> URL { - let realPath = try FileManager.default.destinationOfSymbolicLink(atPath: osPath) - - return URL(filePath: realPath, directoryHint: directoryHint) - } - - func createSymbolicLink(withDestination destination: URL) throws { - try FileManager.default.createSymbolicLink(atPath: osPath, withDestinationPath: destination.osPath) - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+Finder.swift b/Sources/SharedKit/Extensions/URL/URL+Finder.swift deleted file mode 100644 index 5ff2025..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+Finder.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// URL+Finder.swift -// VisualDiffer -// -// Created by davide ficano on 29/10/10. -// Copyright (c) 2010 visualdiffer.com -// - -extension URL { - func labelNumber() -> Int? { - guard let resources = try? resourceValues(forKeys: [.labelNumberKey]) else { - return nil - } - return resources.labelNumber - } - - func tagNames(sorted: Bool) -> [String]? { - guard let resources = try? resourceValues(forKeys: [.tagNamesKey]) else { - return nil - } - guard let tagNames = resources.tagNames else { - return [] - } - - return if sorted { - tagNames.sorted { $0.caseInsensitiveCompare($1) == .orderedAscending } - } else { - tagNames - } - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+Metadata.swift b/Sources/SharedKit/Extensions/URL/URL+Metadata.swift deleted file mode 100644 index 40e4502..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+Metadata.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// URL+Metadata.swift -// VisualDiffer -// -// Created by davide ficano on 03/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -public extension URL { - func copyTags(to toUrl: inout URL) throws { - let resources = try resourceValues(forKeys: [.tagNamesKey]) - try toUrl.setResourceValues(resources) - } - - func copyLabel(to toUrl: inout URL) throws { - let resources = try resourceValues(forKeys: [.labelNumberKey]) - try toUrl.setResourceValues(resources) - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+Path.swift b/Sources/SharedKit/Extensions/URL/URL+Path.swift deleted file mode 100644 index fa9e426..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+Path.swift +++ /dev/null @@ -1,258 +0,0 @@ -// -// URL+Path.swift -// VisualDiffer -// -// Created by davide ficano on 19/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -public extension URL { - /** - * Directory url contains "/" at the end and this cause many problems (eg symlinks) - * so we return the path without it - */ - var osPath: String { - path(percentEncoded: false).trimmingSuffix("/") - } - - /** - * Standardize and then trim trailing separators - */ - var standardizingPath: String { - path(percentEncoded: false).standardizingPath - } - - func volumeType() -> String? { - if let values = try? resourceValues(forKeys: [.volumeTypeNameKey]), - let type = values.volumeTypeName { - return type - } - return nil - } - - static func buildDestinationPath( - _ srcURL: URL, - _ destURL: URL?, - _ srcBaseURL: URL, - _ destBaseURL: URL - ) -> URL { - let srcBaseDir = srcBaseURL.osPath - let srcPath = srcURL.osPath - - if let destURL { - // use destination file name if it is present so - // file names using a not match-case alignment works fine - let lastPathIndex = srcPath.lastIndex(of: "/") ?? srcPath.startIndex - let startIndex = srcBaseDir.endIndex - let relativePath = srcPath[startIndex ..< lastPathIndex].trimmingPrefix("/") - - return destBaseURL - .appending(path: String(relativePath)) - .appending( - path: destURL.lastPathComponent, - directoryHint: destURL.hasDirectoryPath ? .isDirectory : .notDirectory - ) - } - let trailingPath = String(srcPath[srcBaseDir.endIndex...]).trimmingPrefix("/") - return destBaseURL - .appending(path: trailingPath, directoryHint: srcURL.hasDirectoryPath ? .isDirectory : .notDirectory) - } -} - -// MARK: - Path check and selection - -public extension URL { - /** - * Check that self is the same type as the passed item (both are files or both are folders) - */ - func matchesFileType( - of rightUrl: URL, - isDir: inout Bool, - leftExists: inout Bool, - rightExists: inout Bool - ) -> Bool { - let fileManager = FileManager.default - var isLeftDir = ObjCBool(false) - var isRightDir = ObjCBool(false) - - let leftPath = osPath - let rightPath = rightUrl.osPath - - leftExists = fileManager.fileExists(atPath: leftPath, isDirectory: &isLeftDir) - rightExists = fileManager.fileExists(atPath: rightPath, isDirectory: &isRightDir) - - isDir = isLeftDir.boolValue - // must be both folders or both files - return leftExists && rightExists && (isLeftDir.boolValue == isRightDir.boolValue) - } - - func matchesFileType(of rightUrl: URL) -> Bool { - var isDir = false - var leftExists = false - var rightExists = false - - return matchesFileType( - of: rightUrl, - isDir: &isDir, - leftExists: &leftExists, - rightExists: &rightExists - ) - } - - @MainActor func selectPath( - panelTitle: String, - chooseFiles: Bool, - chooseDirectories: Bool - ) -> URL? { - let url = promptUrl( - at: findNearestExistingDirectory(), - title: panelTitle, - chooseDirectories: chooseDirectories, - chooseFiles: chooseFiles - ) - - if let url { - SecureBookmark.shared.add(url) - } - - return url - } - - /** - * Check if path is already sandboxed and doesn't need to be selected by user (e.g. using OpenPanel) - * @param allowFile the url can be a file - * @param allowDirectory the url can be a directory - * @return true if file is sandboxed and its type matches the allowed types, false otherwise - */ - func isSandboxed(allowFile: Bool, allowDirectory: Bool) -> Bool { - let fileManager = FileManager.default - var isDir = ObjCBool(false) - let startDir = osPath - - let securedURL = SecureBookmark.shared.secure(fromBookmark: self, startSecured: true) - defer { - SecureBookmark.shared.stopAccessing(url: securedURL) - } - let result = fileManager.fileExists(atPath: startDir, isDirectory: &isDir) - && fileManager.isReadableFile(atPath: startDir) - - let isFileTypeAllowed = if allowFile, allowDirectory { - true - } else if allowFile { - isDir.boolValue == false - } else if allowDirectory { - isDir.boolValue == true - } else { - false - } - return result && isFileTypeAllowed - } - - func findNearestExistingDirectory() -> URL { - let fileManager = FileManager.default - var isDir = ObjCBool(false) - var currDir = self - - while !fileManager.fileExists(atPath: currDir.osPath, isDirectory: &isDir) || !isDir.boolValue { - currDir.deleteLastPathComponent() - } - - return currDir - } - - @MainActor func promptUrl( - at startUrl: URL, - title: String, - chooseDirectories: Bool, - chooseFiles: Bool - ) -> URL? { - let openPanel = NSOpenPanel() - - openPanel.title = title - openPanel.canChooseDirectories = chooseDirectories - openPanel.canChooseFiles = chooseFiles - // since 10.11 the title is no longer shown so we use the message property - openPanel.message = title - openPanel.directoryURL = startUrl - - if openPanel.runModal() == .OK { - return openPanel.urls[0] - } - return nil - } - - static func compare(fileName lhs: String, with rhs: String) -> ComparisonResult { - lhs.localizedStandardCompare(rhs) - } - - static func compare( - path path1: URL?, - with path2: URL? - ) -> ComparisonResult { - guard let path1 else { - return .orderedAscending - } - guard let path2 else { - return .orderedDescending - } - let components1 = path1.pathComponents - let components2 = path2.pathComponents - let count1 = components1.count - let count2 = components2.count - - // do not compare last component because we compare only directory components - let minComponents = min(count1, count2) - 1 - - for i in 0 ..< minComponents { - let comp1 = components1[i] - let comp2 = components2[i] - let compareResult = compare(fileName: comp1, with: comp2) - - if compareResult != .orderedSame { - return compareResult - } - } - - let delta = count1 - count2 - var comp1: String - var comp2: String - - var isDir1 = path1.hasDirectoryPath - var isDir2 = path2.hasDirectoryPath - - if delta > 0 { - comp1 = components1[minComponents] - comp2 = components2[count2 - 1] - isDir1 = true - // comp1 is a comp2's subdirectory so we can compare them - if isDir2 { - let r = compare(fileName: comp1, with: comp2) - if r == .orderedSame { - return .orderedDescending - } - return r - } - } else if delta < 0 { - comp1 = components1[count1 - 1] - comp2 = components2[minComponents] - isDir2 = true - // comp2 is a subdirectory so we can compare them - if isDir1 { - let r = compare(fileName: comp1, with: comp2) - if r == .orderedSame { - return .orderedAscending - } - return r - } - } else { - comp1 = components1[count1 - 1] - comp2 = components2[count2 - 1] - } - - if isDir1 == isDir2 { - return comp1.compare(comp2, options: .caseInsensitive) - } - - return isDir1 ? .orderedAscending : .orderedDescending - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+ResourceFork.swift b/Sources/SharedKit/Extensions/URL/URL+ResourceFork.swift deleted file mode 100644 index 743dc9c..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+ResourceFork.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// URL+ResourceFork.swift -// VisualDiffer -// -// Created by davide ficano on 24/11/24. -// Copyright (c) 2024 visualdiffer.com -// - -import Foundation - -public extension URL { - /** - * Return resource fork sizes - * @param rsrcPhysicalSize - the physical size of the resource fork - * @param dataPhysicalSize - the physical size of the data fork - * @param rsrcLogicalSize - the logical size of the resource fork - * @param dataLogicalSize - the logical size of the data fork - */ - func resourceForkForPath( - _ rsrcPhysicalSize: inout Int, - _ dataPhysicalSize: inout Int, - _ rsrcLogicalSize: inout Int, - _ dataLogicalSize: inout Int - ) throws { - let keys: Set = [ - .fileSizeKey, - .fileAllocatedSizeKey, - .totalFileAllocatedSizeKey, - .totalFileSizeKey, - ] - - let values = try resourceValues(forKeys: keys) - let fileAllocatedSize = values.fileAllocatedSize ?? 0 - let totalFileAllocatedSize = values.totalFileAllocatedSize ?? 0 - let fileSize = values.fileSize ?? 0 - let totalFileSize = values.totalFileSize ?? 0 - - // FSGetCatalogInfo is deprecated so we us NSURL and compute the values manually - rsrcPhysicalSize = totalFileAllocatedSize - fileAllocatedSize - dataPhysicalSize = fileAllocatedSize - rsrcLogicalSize = totalFileSize - fileSize - dataLogicalSize = fileSize - } - - /** - * Return the physical file size - */ - func resourceForkSize() throws -> Int { - var rsrcPhysicalSize = 0 - var dataPhysicalSize = 0 - var rsrcLogicalSize = 0 - var dataLogicalSize = 0 - - try resourceForkForPath( - &rsrcPhysicalSize, - &dataPhysicalSize, - &rsrcLogicalSize, - &dataLogicalSize - ) - return dataLogicalSize + rsrcLogicalSize - } - - func volumeSupportsCaseSensitive() throws -> Bool { - let values = try resourceValues(forKeys: [.volumeSupportsCaseSensitiveNamesKey]) - return values.volumeSupportsCaseSensitiveNames ?? false - } - - func readResFork() throws -> Data { - let resForkPath = appending(path: "/..namedfork/rsrc") - let fileHandle = try FileHandle(forReadingFrom: resForkPath) - defer { - fileHandle.closeFile() - } - return fileHandle.readDataToEndOfFile() - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+StructuredContent.swift b/Sources/SharedKit/Extensions/URL/URL+StructuredContent.swift deleted file mode 100644 index b22390e..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+StructuredContent.swift +++ /dev/null @@ -1,157 +0,0 @@ -// -// URL+StructuredContent.swift -// VisualDiffer -// -// Created by davide ficano on 14/06/25. -// Copyright (c) 2025 visualdiffer.com -// - -import UniformTypeIdentifiers -import PDFKit - -struct StructuredContentValues { - let originalType: UTType? - let contentType: UTType? - let plainText: String? - let encoding: String.Encoding? -} - -extension URL { - /** - * Return the plain text content from current url. If the content is a rich format like - * Word .doc files the text content is extracted and returned. - * If the content is a plain text format then use the encoding specified into options (if any) - * then the NSWindowsCP1252StringEncoding and at least the auto detect offered by NSAttributedString - * @param options passed to [NSAttributedString initWithURL:url] - * @param docAttrs passed to [NSAttributedString initWithURL:url] - * @return the plain text url content on success, nil otherwise - */ - func readPlainText( - options: [NSAttributedString.DocumentReadingOptionKey: Any], - documentAttributes docAttrs: inout [NSAttributedString.DocumentAttributeKey: Any] - ) throws -> String? { - if let text = try readHandlingInapplicableStringEncoding(options: options, documentAttributes: &docAttrs) { - return text.string - } - // try fallback encoding NSWindowsCP1252StringEncoding - let optionsWithBestEncoding = options.merging( - [.characterEncoding: NSWindowsCP1252StringEncoding] - ) { _, new in new } - - if let text = try readHandlingInapplicableStringEncoding( - options: optionsWithBestEncoding, - documentAttributes: &docAttrs - ) { - return text.string - } - // try removing encoding - var optionsWithoutEncoding = options - optionsWithoutEncoding.removeValue(forKey: .characterEncoding) - if let text = try readHandlingInapplicableStringEncoding( - options: optionsWithoutEncoding, - documentAttributes: &docAttrs - ) { - return text.string - } - - return nil - } - - private func readHandlingInapplicableStringEncoding( - options: [NSAttributedString.DocumentReadingOptionKey: Any], - documentAttributes docAttrs: inout [NSAttributedString.DocumentAttributeKey: Any] - ) throws -> NSAttributedString? { - var nsDocAttrs: NSDictionary? = docAttrs as NSDictionary? - do { - let text = try NSAttributedString( - url: self, - options: options, - documentAttributes: &nsDocAttrs - ) - if let nsDocAttrs = nsDocAttrs as? [NSAttributedString.DocumentAttributeKey: Any] { - docAttrs.merge(nsDocAttrs) { _, new in new } - } - return text - } catch let error as NSError { - if error.domain == NSCocoaErrorDomain, - error.code == NSFileReadInapplicableStringEncodingError { - return nil - } - throw error - } - } - - func readStructuredContent(encoding: String.Encoding) throws -> StructuredContentValues { - enum Static { - static let documentTypeToUTType: [NSAttributedString.DocumentType: UTType] = { - var mapping: [NSAttributedString.DocumentType: UTType] = [ - .plain: .plainText, - .rtf: .rtf, - .rtfd: .rtfd, - .html: .html, - ] - - // add only if are not nil - mapping[.officeOpenXML] = UTType("org.openxmlformats.wordprocessingml.document") - mapping[.docFormat] = UTType("com.microsoft.word.doc") - - return mapping - }() - - static let supportedFormats = Set(documentTypeToUTType.values) - } - let values = try resourceValues(forKeys: [.contentTypeKey]) - let fileType = values.contentType - - if let fileType, - fileType == .pdf { - let pdfDoc = PDFDocument(url: self) - - return StructuredContentValues( - originalType: fileType, - contentType: fileType, - plainText: pdfDoc?.string, - encoding: nil - ) - } - - let usePlainText = if let fileType { - !Static.supportedFormats.contains(fileType) - } else { - true - } - - var options = [NSAttributedString.DocumentReadingOptionKey: Any]() - - // NSAttributedString parses HTML files but we want to show the source - // code so we force plain type for other supported formats - if usePlainText { - options[.documentType] = NSAttributedString.DocumentType.plain - } - options[.characterEncoding] = NSNumber(value: encoding.rawValue) - - var docAttrs = [NSAttributedString.DocumentAttributeKey: Any]() - let content = try readPlainText( - options: options, - documentAttributes: &docAttrs - ) - - let docEnconding: String.Encoding? = if let enc = docAttrs[.characterEncoding] as? NSNumber { - String.Encoding(rawValue: enc.uintValue) - } else { - nil - } - let contentType: UTType? = if let contentType = docAttrs[.documentType] as? NSAttributedString.DocumentType { - Static.documentTypeToUTType[contentType] - } else { - nil - } - - return StructuredContentValues( - originalType: fileType, - contentType: contentType, - plainText: content, - encoding: docEnconding - ) - } -} diff --git a/Sources/SharedKit/Extensions/URL/URL+SymLink.swift b/Sources/SharedKit/Extensions/URL/URL+SymLink.swift deleted file mode 100644 index 7976559..0000000 --- a/Sources/SharedKit/Extensions/URL/URL+SymLink.swift +++ /dev/null @@ -1,109 +0,0 @@ -// -// URL+SymLink.swift -// VisualDiffer -// -// Created by davide ficano on 31/08/11. -// Copyright (c) 2011 visualdiffer.com -// - -import os.log - -extension URL { - // MARK: - SymLinks - - /// - /// This differs from `[NSString stringByResolvingSymlinksInPath]` in the fact that - /// `/private` is resolved, too - /// - Returns: The resolved `URL` - /// - Throws: `POSIXError` if resolution fails - /// - func resolveSymlinks() throws -> URL { - guard let cPath = osPath.cString(using: .utf8) else { - throw EncodingError.conversionFailed(.utf8) - } - - guard let result = realpath(cPath, nil) else { - throw POSIXError(.init(rawValue: errno) ?? .ENOENT) - } - - let resolved = String(cString: result) - - free(result) - - return URL(filePath: resolved) - } - - /// - /// Resolve any symlink and alias from `self` then check if path is already secured. - /// If isn't secured shows the open panel to select path and then store it as secured - /// - Parameters: - /// - chooseFiles: Can choose files - /// - chooseDirectories: Can choose directories - /// - panelTitle: The text to show as title if the open panel must be shown - /// - alwaysResolveSymlinks: Try to resolve symlinks before check for aliases - /// - Returns: A tuple containing: - /// - `resolvedPath`: The selected path with aliases and symlinks resolved - /// - `userSelectOtherPath`: `true` if the selected path differs from the passed one - /// - Returns `nil` if the user doesn't select any path - /// - @MainActor func resolveSymLinksAndAliases( - chooseFiles: Bool, - chooseDirectories: Bool, - panelTitle: String, - alwaysResolveSymlinks: Bool - ) -> (resolvedPath: URL, userSelectOtherPath: Bool)? { - // temporary files saved on /private require to be opened using resolved symlinks - // otherwise the paths contained inside secure bookmarks don't match - var path = self - if alwaysResolveSymlinks { - do { - path = try path.resolveSymlinks() - } catch { - Logger.general.error("unable to resolve symlink for \(path) (error \(error))") - } - } - do { - if try path.isAliasFile() { - path = try path.realPathByAlias() - } - } catch { - Logger.general.error("unable to resolve alias for \(path) (error \(error))") - } - - let selectedPath = if path.isSandboxed(allowFile: chooseFiles, allowDirectory: chooseDirectories) { - path - } else { - path.selectPath( - panelTitle: panelTitle, - chooseFiles: chooseFiles, - chooseDirectories: chooseDirectories - ) - } - - guard let selectedPath else { - return nil - } - return (selectedPath, selectedPath != path) - } - - // MARK: - Aliases - - func isAliasFile() throws -> Bool { - guard let contentType = try resourceValues(forKeys: [.contentTypeKey]).contentType else { - return false - } - return contentType == .aliasFile - } - - func realPathByAlias() throws -> URL { - let alias = try URL.bookmarkData(withContentsOf: self) - - var isStale = false - return try URL( - resolvingBookmarkData: alias, - options: .withoutUI, - relativeTo: nil, - bookmarkDataIsStale: &isStale - ) - } -} diff --git a/Sources/SharedKit/Extensions/View/Box/NSBox+Helper.swift b/Sources/SharedKit/Extensions/View/Box/NSBox+Helper.swift deleted file mode 100644 index 8f48516..0000000 --- a/Sources/SharedKit/Extensions/View/Box/NSBox+Helper.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSBox+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 17/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -@objc extension NSBox { - static func separator() -> NSBox { - let view = NSBox() - - view.boxType = .separator - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } -} diff --git a/Sources/SharedKit/Extensions/View/Button/NSButton+Helper.swift b/Sources/SharedKit/Extensions/View/Button/NSButton+Helper.swift deleted file mode 100644 index a892d13..0000000 --- a/Sources/SharedKit/Extensions/View/Button/NSButton+Helper.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// NSButton+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 13/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Cocoa -import Foundation - -extension NSButton { - static func button(title: String, keyEquivalent: String) -> NSButton { - let view = NSButton(frame: .zero) - view.title = title - view.setButtonType(.momentaryPushIn) - - view.isBordered = true - view.state = .off - view.bezelStyle = .flexiblePush - view.imagePosition = .noImage - view.alignment = .center - view.keyEquivalent = keyEquivalent - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - static func cancelButton(title: String, target: Any? = nil, action: Selector? = nil) -> NSButton { - let button = NSButton( - title: title, - target: target, - action: action - ) - button.keyEquivalent = KeyEquivalent.escape - button.tag = NSApplication.ModalResponse.cancel.rawValue - button.translatesAutoresizingMaskIntoConstraints = false - - return button - } - - static func okButton(title: String, target: Any? = nil, action: Selector? = nil) -> NSButton { - let button = NSButton( - title: title, - target: target, - action: action - ) - button.keyEquivalent = KeyEquivalent.enter - button.tag = NSApplication.ModalResponse.OK.rawValue - button.translatesAutoresizingMaskIntoConstraints = false - - return button - } -} diff --git a/Sources/SharedKit/Extensions/View/Button/NSPopUpButton+Helper.swift b/Sources/SharedKit/Extensions/View/Button/NSPopUpButton+Helper.swift deleted file mode 100644 index 54d3ce1..0000000 --- a/Sources/SharedKit/Extensions/View/Button/NSPopUpButton+Helper.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// NSPopUpButton+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 17/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -@objc extension NSPopUpButton { - convenience init( - identifier: NSUserInterfaceItemIdentifier, - menuTitle: String, - menuImage: NSImage? - ) { - self.init(frame: .zero, pullsDown: true) - - bezelStyle = .texturedRounded - setButtonType(.momentaryPushIn) - imagePosition = .imageOnly - alignment = .left - lineBreakMode = .byTruncatingTail - state = .on - isBordered = true - imageScaling = .scaleProportionallyDown - - let menuItem = NSMenuItem() - menuItem.state = .on - menuItem.image = menuImage - menuItem.isHidden = true - - let popupMenu = NSMenu(title: menuTitle) - popupMenu.identifier = identifier - popupMenu.addItem(menuItem) - - menu = popupMenu - } -} diff --git a/Sources/SharedKit/Extensions/View/ProgressIndicator/NSProgressIndicator+Helper.swift b/Sources/SharedKit/Extensions/View/ProgressIndicator/NSProgressIndicator+Helper.swift deleted file mode 100644 index c26f7a4..0000000 --- a/Sources/SharedKit/Extensions/View/ProgressIndicator/NSProgressIndicator+Helper.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// NSProgressIndicator+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 18/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -extension NSProgressIndicator { - static func bar() -> NSProgressIndicator { - let view = NSProgressIndicator() - - view.isIndeterminate = false - view.minValue = 0 - view.maxValue = 0 - view.doubleValue = 0 - view.controlSize = .regular - view.style = .bar - - return view - } -} diff --git a/Sources/SharedKit/Extensions/View/TextField/NSTextField+Helper.swift b/Sources/SharedKit/Extensions/View/TextField/NSTextField+Helper.swift deleted file mode 100644 index d54c995..0000000 --- a/Sources/SharedKit/Extensions/View/TextField/NSTextField+Helper.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// NSTextField+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 28/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -@objc extension NSTextField { - static func labelWithTitle(_ title: String) -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = title - view.isEditable = false - view.isSelectable = false - view.drawsBackground = false - view.isBordered = false - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - static func hintWithTitle(_ title: String) -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = title - view.isEditable = false - view.isSelectable = false - view.drawsBackground = false - view.isBordered = false - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } -} diff --git a/Sources/SharedKit/Extensions/View/TextView/NSTextView+Style.swift b/Sources/SharedKit/Extensions/View/TextView/NSTextView+Style.swift deleted file mode 100644 index 0400533..0000000 --- a/Sources/SharedKit/Extensions/View/TextView/NSTextView+Style.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// NSTextView+Style.swift -// VisualDiffer -// -// Created by davide ficano on 14/11/11. -// Copyright (c) 2011 visualdiffer.com -// - -@objc extension NSTextView { - func setTabStop(_ tabSpaces: Int) { - guard let font else { - return - } - guard let paragraphStyle = defaultParagraphStyle?.mutableCopy() as? NSMutableParagraphStyle - ?? NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle else { - return - } - let charWidth = (" " as NSString).size(withAttributes: [.font: font]).width - - paragraphStyle.defaultTabInterval = charWidth * Double(tabSpaces) - paragraphStyle.tabStops = [] - defaultParagraphStyle = paragraphStyle - - var typingAttributes = typingAttributes - typingAttributes[NSAttributedString.Key.paragraphStyle] = paragraphStyle - typingAttributes[NSAttributedString.Key.font] = font - self.typingAttributes = typingAttributes - - let rangeOfChange = NSRange(location: 0, length: string.count) - shouldChangeText(in: rangeOfChange, replacementString: nil) - textStorage?.setAttributes( - typingAttributes, - range: rangeOfChange - ) - - didChangeText() - } - - func disableWordWrap() { - guard let textContainer else { - return - } - let largeNumberForText = 1.0e7 - - textContainer.containerSize = NSSize(width: largeNumberForText, height: largeNumberForText) - textContainer.widthTracksTextView = false - maxSize = NSSize(width: largeNumberForText, height: maxSize.height) - isHorizontallyResizable = true - } - - func setTextColor(_ textColor: NSColor?, backgroundColor: NSColor?) { - if textColor == nil, backgroundColor == nil { - return - } - guard let textStorage else { - return - } - let string = textStorage.string - let length = string.count - - // remove the old colors - let area = NSRange(location: 0, length: length) - textStorage.removeAttribute(NSAttributedString.Key.foregroundColor, range: area) - textStorage.removeAttribute(NSAttributedString.Key.backgroundColor, range: area) - - if let textColor { - textStorage.addAttribute( - NSAttributedString.Key.foregroundColor, - value: textColor, - range: area - ) - } - if let backgroundColor { - textStorage.addAttribute( - NSAttributedString.Key.backgroundColor, - value: backgroundColor, - range: area - ) - } - } - - func append(text: String, attributes: [NSAttributedString.Key: Any]? = nil) { - let attrString = NSAttributedString(string: text, attributes: attributes) - if let textStorage { - textStorage.append(attrString) - } - scrollRangeToVisible(NSRange(location: string.count, length: 0)) - } -} diff --git a/Sources/SharedKit/Extensions/Window/NSWindow+Editing.swift b/Sources/SharedKit/Extensions/Window/NSWindow+Editing.swift deleted file mode 100644 index 6efb09f..0000000 --- a/Sources/SharedKit/Extensions/Window/NSWindow+Editing.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// NSWindow+Editing.swift -// VisualDiffer -// -// Created by davide ficano on 07/12/15. -// Copyright (c) 2015 visualdiffer.com -// - -// Copied from https://red-sweater.com/blog/229/stay-responsive -// see http://stackoverflow.com/questions/14049913/update-property-bound-from-text-field-without-needing-to-press-enter -// This category allows to update text field's bindings without pressing enter or changing first responder - -extension NSWindow { - func endEditing() { - // Save the current first responder, respecting the fact - // that it might conceptually be the delegate of the - // field editor that is "first responder." - var oldFirstResponder: NSResponder? - - if let textView = firstResponder as? NSTextView, - textView.isFieldEditor { - // A field editor's delegate is the view we're editing - if let responder = textView.delegate as? NSResponder { - oldFirstResponder = responder - } - } - // Gracefully end all editing in our window (from Erik Buck). - // This will cause the user's changes to be committed. - if makeFirstResponder(self) { - // All editing is now ended and delegate messages sent etc. - } else { - // For some reason the text object being edited will - // not resign first responder status so force an - /// end to editing anyway - endEditing(for: nil) - } - - // If we had a first responder before, restore it - if let oldFirstResponder { - makeFirstResponder(oldFirstResponder) - } - } -} diff --git a/Sources/SharedKit/Extensions/Workspace/NSWorkspace+Finder.swift b/Sources/SharedKit/Extensions/Workspace/NSWorkspace+Finder.swift deleted file mode 100644 index 32fb156..0000000 --- a/Sources/SharedKit/Extensions/Workspace/NSWorkspace+Finder.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// NSWorkspace+Finder.swift -// VisualDiffer -// -// Created by davide ficano on 15/11/14. -// Copyright (c) 2014 visualdiffer.com -// - -let suppressShowInFinder = "suppressShowInFinder" -let maxFoldersToShowWithoutAlert = 8 - -extension NSWorkspace { - /** - * @paths array containing absolute file path strings - * Show the paths on Finder - */ - @MainActor @objc func show(inFinder paths: [String]) { - if paths.count > maxFoldersToShowWithoutAlert { - let confirmOpen = NSAlert.showModalConfirm( - messageText: String(format: NSLocalizedString("Are you sure you want to open %lu Finder windows?", comment: ""), paths.count), - informativeText: NSLocalizedString("You have chosen to open a large number of windows. This can take a long time", comment: ""), - suppressPropertyName: suppressShowInFinder, - yesText: nil, - noText: nil - ) - if !confirmOpen { - return - } - } - - if !paths.isEmpty { - var urls = [URL]() - // Get secure urls to avoid the warning - // __CFPasteboardIssueSandboxExtensionForPath: error for [/path/to/folder] - - var secureURLs = [URL]() - - for p in paths { - let pUrl = URL(filePath: p) - urls.append(pUrl) - if let secureURL = SecureBookmark.shared.secure(fromBookmark: pUrl, startSecured: true) { - secureURLs.append(secureURL) - } - } - NSWorkspace.shared.activateFileViewerSelecting(urls) - - for url in secureURLs { - SecureBookmark.shared.stopAccessing(url: url) - } - } - } -} diff --git a/Sources/SharedKit/Utilities/Document/SecureBookmark.swift b/Sources/SharedKit/Utilities/Document/SecureBookmark.swift deleted file mode 100644 index 40095ee..0000000 --- a/Sources/SharedKit/Utilities/Document/SecureBookmark.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// SecureBookmark.swift -// VisualDiffer -// -// Created by davide ficano on 02/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation -import os.log - -private let sandboxedPaths = "sandboxedPaths" - -class SecureBookmark: @unchecked Sendable { - static let shared = SecureBookmark() - - private init() {} - - /** - * Add a new url to the secure bookmarks - * @return true if the url is bookmarked with success, false otherwise - */ - @discardableResult - func add(_ path: URL, searchClosestPath: Bool = true) -> Bool { - var bookmark: Data? - - if searchClosestPath, - let securedPaths { - if let closestPath = findClosestPath(to: path, searchPaths: Array(securedPaths.keys)) { - bookmark = securedPaths[closestPath] - } - } else { - bookmark = securedPaths?[path.osPath] - } - - if bookmark == nil { - do { - bookmark = try path.bookmarkData( - options: .withSecurityScope, - includingResourceValuesForKeys: nil, - relativeTo: nil - ) - var dict = securedPaths - - if dict == nil { - dict = [String: Data]() - } - // swiftlint:disable:next force_unwrapping - dict![path.osPath] = bookmark - UserDefaults.standard.set(dict, forKey: sandboxedPaths) - } catch { - Logger.general.error("Secure bookmark failed \(error)") - return false - } - } - return true - } - - func secure(fromBookmark path: URL?, startSecured: Bool) -> URL? { - guard let path else { - return nil - } - guard let dict = securedPaths, - let bookmarkPath = findClosestPath(to: path, searchPaths: Array(dict.keys)), - let data = dict[bookmarkPath] else { - return nil - } - var isStale = false - do { - let url = try URL( - resolvingBookmarkData: data, - options: .withSecurityScope, - relativeTo: nil, - bookmarkDataIsStale: &isStale - ) - if startSecured { - _ = url.startAccessingSecurityScopedResource() - } - return url - } catch { - Logger.general.error("Secure bookmark error while resolving bookmark \(error)") - } - return nil - } - - func stopAccessing(url: URL?) { - url?.stopAccessingSecurityScopedResource() - } - - func removePaths(_ paths: [String]) { - guard var dict = securedPaths else { - return - } - for path in paths { - dict.removeValue(forKey: path) - } - UserDefaults.standard.set(dict, forKey: sandboxedPaths) - } - - var securedPaths: [String: Data]? { - UserDefaults.standard.dictionary(forKey: sandboxedPaths) as? [String: Data] - } - - func findClosestPath(to path: URL, searchPaths: [String]) -> String? { - // don't matter if path is a file or a directory, add the separator in any case - // so hasPrefix works fine with last path component - // eg "/Users/app 2 3" has prefix "/Users/app 2" but - // "/Users/app 2 3/" hasn't prefix "/Users/app 2/" and this is the correct result - let pathWithSep = path.osPath + "/" - let sorted = searchPaths.sorted { - $0.caseInsensitiveCompare($1) == .orderedDescending - } - for key in sorted where pathWithSep.hasPrefix(key + "/") { - return key - } - return nil - } -} diff --git a/Sources/SharedKit/Utilities/Encoding/EncodingError.swift b/Sources/SharedKit/Utilities/Encoding/EncodingError.swift deleted file mode 100644 index 1bad6a6..0000000 --- a/Sources/SharedKit/Utilities/Encoding/EncodingError.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// EncodingError.swift -// VisualDiffer -// -// Created by davide ficano on 08/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -enum EncodingError: Error { - case conversionFailed(String.Encoding) -} - -extension EncodingError: LocalizedError { - var errorDescription: String? { - switch self { - case let .conversionFailed(encoding): - // do not use localizedStringWithFormat to avoid formatting the number - String(format: NSLocalizedString("Can't convert to encoding: %ld", comment: ""), encoding.rawValue) - } - } -} diff --git a/Sources/SharedKit/Utilities/Event/NSEvent+VirtualKeys.swift b/Sources/SharedKit/Utilities/Event/NSEvent+VirtualKeys.swift deleted file mode 100644 index c0c1f12..0000000 --- a/Sources/SharedKit/Utilities/Event/NSEvent+VirtualKeys.swift +++ /dev/null @@ -1,277 +0,0 @@ -// -// NSEvent+VirtualKeys.swift -// VisualDiffer -// -// Created by davide ficano on 06/04/14. -// Copyright (c) 2014 visualdiffer.com -// - -// swiftlint:disable identifier_name -@objc enum KeyCode: UInt16 { - case ansi_A = 0x00 - case ansi_S = 0x01 - case ansi_D = 0x02 - case ansi_F = 0x03 - case ansi_H = 0x04 - case ansi_G = 0x05 - case ansi_Z = 0x06 - case ansi_X = 0x07 - case ansi_C = 0x08 - case ansi_V = 0x09 - case ansi_B = 0x0B - case ansi_Q = 0x0C - case ansi_W = 0x0D - case ansi_E = 0x0E - case ansi_R = 0x0F - case ansi_Y = 0x10 - case ansi_T = 0x11 - case ansi_1 = 0x12 - case ansi_2 = 0x13 - case ansi_3 = 0x14 - case ansi_4 = 0x15 - case ansi_6 = 0x16 - case ansi_5 = 0x17 - case ansi_Equal = 0x18 - case ansi_9 = 0x19 - case ansi_7 = 0x1A - case ansi_Minus = 0x1B - case ansi_8 = 0x1C - case ansi_0 = 0x1D - case ansi_RightBracket = 0x1E - case ansi_O = 0x1F - case ansi_U = 0x20 - case ansi_LeftBracket = 0x21 - case ansi_I = 0x22 - case ansi_P = 0x23 - case ansi_L = 0x25 - case ansi_J = 0x26 - case ansi_Quote = 0x27 - case ansi_K = 0x28 - case ansi_Semicolon = 0x29 - case ansi_Backslash = 0x2A - case ansi_Comma = 0x2B - case ansi_Slash = 0x2C - case ansi_N = 0x2D - case ansi_M = 0x2E - case ansi_Period = 0x2F - case ansi_Grave = 0x32 - case ansi_KeypadDecimal = 0x41 - case ansi_KeypadMultiply = 0x43 - case ansi_KeypadPlus = 0x45 - case ansi_KeypadClear = 0x47 - case ansi_KeypadDivide = 0x4B - case ansi_KeypadEnter = 0x4C - case ansi_KeypadMinus = 0x4E - case ansi_KeypadEquals = 0x51 - case ansi_Keypad0 = 0x52 - case ansi_Keypad1 = 0x53 - case ansi_Keypad2 = 0x54 - case ansi_Keypad3 = 0x55 - case ansi_Keypad4 = 0x56 - case ansi_Keypad5 = 0x57 - case ansi_Keypad6 = 0x58 - case ansi_Keypad7 = 0x59 - case ansi_Keypad8 = 0x5B - case ansi_Keypad9 = 0x5C - - /* keycodes for keys that are independent of keyboard layout */ - case returnKey = 0x24 - case tab = 0x30 - case space = 0x31 - case delete = 0x33 - case escape = 0x35 - case command = 0x37 - case shift = 0x38 - case capsLock = 0x39 - case option = 0x3A - case control = 0x3B - case rightShift = 0x3C - case rightOption = 0x3D - case rightControl = 0x3E - case function = 0x3F - case f17 = 0x40 - case volumeUp = 0x48 - case volumeDown = 0x49 - case mute = 0x4A - case f18 = 0x4F - case f19 = 0x50 - case f20 = 0x5A - case f5 = 0x60 - case f6 = 0x61 - case f7 = 0x62 - case f3 = 0x63 - case f8 = 0x64 - case f9 = 0x65 - case f11 = 0x67 - case f13 = 0x69 - case f16 = 0x6A - case f14 = 0x6B - case f10 = 0x6D - case f12 = 0x6F - case f15 = 0x71 - case help = 0x72 - case home = 0x73 - case pageUp = 0x74 - case forwardDelete = 0x75 - case f4 = 0x76 - case end = 0x77 - case f2 = 0x78 - case pageDown = 0x79 - case f1 = 0x7A - case leftArrow = 0x7B - case rightArrow = 0x7C - case downArrow = 0x7D - case upArrow = 0x7E - - /* ISO keyboards only */ - case iso_Section = 0x0A - - /* JIS keyboards only */ - case jis_Yen = 0x5D - case jis_Underscore = 0x5E - case jis_KeypadComma = 0x5F - case jis_Eisu = 0x66 - case jis_Kana = 0x68 -} - -extension KeyCode { - static var ansi_ACharacter: Int { Int(KeyCode.ansi_A.rawValue) } - static var ansi_SCharacter: Int { Int(KeyCode.ansi_S.rawValue) } - static var ansi_DCharacter: Int { Int(KeyCode.ansi_D.rawValue) } - static var ansi_FCharacter: Int { Int(KeyCode.ansi_F.rawValue) } - static var ansi_HCharacter: Int { Int(KeyCode.ansi_H.rawValue) } - static var ansi_GCharacter: Int { Int(KeyCode.ansi_G.rawValue) } - static var ansi_ZCharacter: Int { Int(KeyCode.ansi_Z.rawValue) } - static var ansi_XCharacter: Int { Int(KeyCode.ansi_X.rawValue) } - static var ansi_CCharacter: Int { Int(KeyCode.ansi_C.rawValue) } - static var ansi_VCharacter: Int { Int(KeyCode.ansi_V.rawValue) } - static var ansi_BCharacter: Int { Int(KeyCode.ansi_B.rawValue) } - static var ansi_QCharacter: Int { Int(KeyCode.ansi_Q.rawValue) } - static var ansi_WCharacter: Int { Int(KeyCode.ansi_W.rawValue) } - static var ansi_ECharacter: Int { Int(KeyCode.ansi_E.rawValue) } - static var ansi_RCharacter: Int { Int(KeyCode.ansi_R.rawValue) } - static var ansi_YCharacter: Int { Int(KeyCode.ansi_Y.rawValue) } - static var ansi_TCharacter: Int { Int(KeyCode.ansi_T.rawValue) } - static var ansi_1Character: Int { Int(KeyCode.ansi_1.rawValue) } - static var ansi_2Character: Int { Int(KeyCode.ansi_2.rawValue) } - static var ansi_3Character: Int { Int(KeyCode.ansi_3.rawValue) } - static var ansi_4Character: Int { Int(KeyCode.ansi_4.rawValue) } - static var ansi_6Character: Int { Int(KeyCode.ansi_6.rawValue) } - static var ansi_5Character: Int { Int(KeyCode.ansi_5.rawValue) } - static var ansi_EqualCharacter: Int { Int(KeyCode.ansi_Equal.rawValue) } - static var ansi_9Character: Int { Int(KeyCode.ansi_9.rawValue) } - static var ansi_7Character: Int { Int(KeyCode.ansi_7.rawValue) } - static var ansi_MinusCharacter: Int { Int(KeyCode.ansi_Minus.rawValue) } - static var ansi_8Character: Int { Int(KeyCode.ansi_8.rawValue) } - static var ansi_0Character: Int { Int(KeyCode.ansi_0.rawValue) } - static var ansi_RightBracketCharacter: Int { Int(KeyCode.ansi_RightBracket.rawValue) } - static var ansi_OCharacter: Int { Int(KeyCode.ansi_O.rawValue) } - static var ansi_UCharacter: Int { Int(KeyCode.ansi_U.rawValue) } - static var ansi_LeftBracketCharacter: Int { Int(KeyCode.ansi_LeftBracket.rawValue) } - static var ansi_ICharacter: Int { Int(KeyCode.ansi_I.rawValue) } - static var ansi_PCharacter: Int { Int(KeyCode.ansi_P.rawValue) } - static var ansi_LCharacter: Int { Int(KeyCode.ansi_L.rawValue) } - static var ansi_JCharacter: Int { Int(KeyCode.ansi_J.rawValue) } - static var ansi_QuoteCharacter: Int { Int(KeyCode.ansi_Quote.rawValue) } - static var ansi_KCharacter: Int { Int(KeyCode.ansi_K.rawValue) } - static var ansi_SemicolonCharacter: Int { Int(KeyCode.ansi_Semicolon.rawValue) } - static var ansi_BackslashCharacter: Int { Int(KeyCode.ansi_Backslash.rawValue) } - static var ansi_CommaCharacter: Int { Int(KeyCode.ansi_Comma.rawValue) } - static var ansi_SlashCharacter: Int { Int(KeyCode.ansi_Slash.rawValue) } - static var ansi_NCharacter: Int { Int(KeyCode.ansi_N.rawValue) } - static var ansi_MCharacter: Int { Int(KeyCode.ansi_M.rawValue) } - static var ansi_PeriodCharacter: Int { Int(KeyCode.ansi_Period.rawValue) } - static var ansi_GraveCharacter: Int { Int(KeyCode.ansi_Grave.rawValue) } - static var ansi_KeypadDecimalCharacter: Int { Int(KeyCode.ansi_KeypadDecimal.rawValue) } - static var ansi_KeypadMultiplyCharacter: Int { Int(KeyCode.ansi_KeypadMultiply.rawValue) } - static var ansi_KeypadPlusCharacter: Int { Int(KeyCode.ansi_KeypadPlus.rawValue) } - static var ansi_KeypadClearCharacter: Int { Int(KeyCode.ansi_KeypadClear.rawValue) } - static var ansi_KeypadDivideCharacter: Int { Int(KeyCode.ansi_KeypadDivide.rawValue) } - static var ansi_KeypadEnterCharacter: Int { Int(KeyCode.ansi_KeypadEnter.rawValue) } - static var ansi_KeypadMinusCharacter: Int { Int(KeyCode.ansi_KeypadMinus.rawValue) } - static var ansi_KeypadEqualsCharacter: Int { Int(KeyCode.ansi_KeypadEquals.rawValue) } - static var ansi_Keypad0Character: Int { Int(KeyCode.ansi_Keypad0.rawValue) } - static var ansi_Keypad1Character: Int { Int(KeyCode.ansi_Keypad1.rawValue) } - static var ansi_Keypad2Character: Int { Int(KeyCode.ansi_Keypad2.rawValue) } - static var ansi_Keypad3Character: Int { Int(KeyCode.ansi_Keypad3.rawValue) } - static var ansi_Keypad4Character: Int { Int(KeyCode.ansi_Keypad4.rawValue) } - static var ansi_Keypad5Character: Int { Int(KeyCode.ansi_Keypad5.rawValue) } - static var ansi_Keypad6Character: Int { Int(KeyCode.ansi_Keypad6.rawValue) } - static var ansi_Keypad7Character: Int { Int(KeyCode.ansi_Keypad7.rawValue) } - static var ansi_Keypad8Character: Int { Int(KeyCode.ansi_Keypad8.rawValue) } - static var ansi_Keypad9Character: Int { Int(KeyCode.ansi_Keypad9.rawValue) } - - /* keycodes for keys that are independent of keyboard layout */ - static var returnKeyCharacter: Int { Int(KeyCode.returnKey.rawValue) } - static var tabCharacter: Int { Int(KeyCode.tab.rawValue) } - static var spaceCharacter: Int { Int(KeyCode.space.rawValue) } - static var deleteCharacter: Int { Int(KeyCode.delete.rawValue) } - static var escapeCharacter: Int { Int(KeyCode.escape.rawValue) } - static var commandCharacter: Int { Int(KeyCode.command.rawValue) } - static var shiftCharacter: Int { Int(KeyCode.shift.rawValue) } - static var capsLockCharacter: Int { Int(KeyCode.capsLock.rawValue) } - static var optionCharacter: Int { Int(KeyCode.option.rawValue) } - static var controlCharacter: Int { Int(KeyCode.control.rawValue) } - static var rightShiftCharacter: Int { Int(KeyCode.rightShift.rawValue) } - static var rightOptionCharacter: Int { Int(KeyCode.rightOption.rawValue) } - static var rightControlCharacter: Int { Int(KeyCode.rightControl.rawValue) } - static var functionCharacter: Int { Int(KeyCode.function.rawValue) } - static var f17Character: Int { Int(KeyCode.f17.rawValue) } - static var volumeUpCharacter: Int { Int(KeyCode.volumeUp.rawValue) } - static var volumeDownCharacter: Int { Int(KeyCode.volumeDown.rawValue) } - static var muteCharacter: Int { Int(KeyCode.mute.rawValue) } - static var f18Character: Int { Int(KeyCode.f18.rawValue) } - static var f19Character: Int { Int(KeyCode.f19.rawValue) } - static var f20Character: Int { Int(KeyCode.f20.rawValue) } - static var f5Character: Int { Int(KeyCode.f5.rawValue) } - static var f6Character: Int { Int(KeyCode.f6.rawValue) } - static var f7Character: Int { Int(KeyCode.f7.rawValue) } - static var f3Character: Int { Int(KeyCode.f3.rawValue) } - static var f8Character: Int { Int(KeyCode.f8.rawValue) } - static var f9Character: Int { Int(KeyCode.f9.rawValue) } - static var f11Character: Int { Int(KeyCode.f11.rawValue) } - static var f13Character: Int { Int(KeyCode.f13.rawValue) } - static var f16Character: Int { Int(KeyCode.f16.rawValue) } - static var f14Character: Int { Int(KeyCode.f14.rawValue) } - static var f10Character: Int { Int(KeyCode.f10.rawValue) } - static var f12Character: Int { Int(KeyCode.f12.rawValue) } - static var f15Character: Int { Int(KeyCode.f15.rawValue) } - static var helpCharacter: Int { Int(KeyCode.help.rawValue) } - static var homeCharacter: Int { Int(KeyCode.home.rawValue) } - static var pageUpCharacter: Int { Int(KeyCode.pageUp.rawValue) } - static var forwardDeleteCharacter: Int { Int(KeyCode.forwardDelete.rawValue) } - static var f4Character: Int { Int(KeyCode.f4.rawValue) } - static var endCharacter: Int { Int(KeyCode.end.rawValue) } - static var f2Character: Int { Int(KeyCode.f2.rawValue) } - static var pageDownCharacter: Int { Int(KeyCode.pageDown.rawValue) } - static var f1Character: Int { Int(KeyCode.f1.rawValue) } - static var leftArrowCharacter: Int { Int(KeyCode.leftArrow.rawValue) } - static var rightArrowCharacter: Int { Int(KeyCode.rightArrow.rawValue) } - static var downArrowCharacter: Int { Int(KeyCode.downArrow.rawValue) } - static var upArrowCharacter: Int { Int(KeyCode.upArrow.rawValue) } - - /* ISO keyboards only */ - static var iso_SectionCharacter: Int { Int(KeyCode.iso_Section.rawValue) } - - /* JIS keyboards only */ - static var jis_YenCharacter: Int { Int(KeyCode.jis_Yen.rawValue) } - static var jis_UnderscoreCharacter: Int { Int(KeyCode.jis_Underscore.rawValue) } - static var jis_KeypadCommaCharacter: Int { Int(KeyCode.jis_KeypadComma.rawValue) } - static var jis_EisuCharacter: Int { Int(KeyCode.jis_Eisu.rawValue) } - static var jis_KanaCharacter: Int { Int(KeyCode.jis_Kana.rawValue) } - - var intValue: Int { - Int(rawValue) - } -} - -// swiftlint:enable identifier_name - -extension NSEvent { - @objc func isDeleteShortcutKey(_ checkCommandDeleteKey: Bool) -> Bool { - if checkCommandDeleteKey, modifierFlags.contains(.command), keyCode == KeyCode.deleteCharacter { - return true - } - return keyCode == KeyCode.forwardDeleteCharacter - } -} diff --git a/Sources/SharedKit/Utilities/Formatters/FileSizeFormatter.swift b/Sources/SharedKit/Utilities/Formatters/FileSizeFormatter.swift deleted file mode 100644 index e6b1f7a..0000000 --- a/Sources/SharedKit/Utilities/Formatters/FileSizeFormatter.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// FileSizeFormatter.swift -// VisualDiffer -// -// Created by davide ficano on 12/08/13. -// Copyright (c) 2013 visualdiffer.com -// - -@objc class FileSizeFormatter: NumberFormatter, @unchecked Sendable { - private(set) var showInBytes = false - private(set) var showUnitForBytes = true - - @objc static let `default` = FileSizeFormatter() - - @objc override init() { - super.init() - - numberStyle = .decimal - maximumFractionDigits = 2 - } - - @objc convenience init( - showInBytes: Bool, - showUnitForBytes: Bool - ) { - self.init() - - self.showInBytes = showInBytes - self.showUnitForBytes = showUnitForBytes - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func string( - from number: NSNumber, - showInBytes: Bool, - showUnitForBytes: Bool - ) -> String? { - var floatSize = number.doubleValue - - if floatSize < 1023 || showInBytes { - if let value = super.string(from: NSNumber(value: floatSize)) { - return String(format: "%@%@", value, showUnitForBytes ? " bytes" : "") - } - return "\(floatSize)" - } - - floatSize /= 1024 - if floatSize < 1023 { - if let value = super.string(from: NSNumber(value: floatSize)) { - return String(format: "%@ KB", value) - } - return "\(floatSize)" - } - - floatSize /= 1024 - if floatSize < 1023 { - if let value = super.string(from: NSNumber(value: floatSize)) { - return String(format: "%@ MB", value) - } - return "\(floatSize)" - } - - floatSize /= 1024 - if let value = super.string(from: NSNumber(value: floatSize)) { - return String(format: "%@ GB", value) - } - return "\(floatSize)" - } - - override func string(from number: NSNumber) -> String? { - string(from: number, showInBytes: showInBytes, showUnitForBytes: showUnitForBytes) - } -} diff --git a/Sources/SharedKit/Utilities/Formatters/IntegerFormatter.swift b/Sources/SharedKit/Utilities/Formatters/IntegerFormatter.swift deleted file mode 100644 index 9c979e6..0000000 --- a/Sources/SharedKit/Utilities/Formatters/IntegerFormatter.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// IntegerFormatter.swift -// VisualDiffer -// -// Created by davide ficano on 07/03/13. -// Copyright (c) 2013 visualdiffer.com -// - -class IntegerFormatter: NumberFormatter, @unchecked Sendable { - override open func isPartialStringValid( - _ partialString: String, - newEditingString _: AutoreleasingUnsafeMutablePointer?, - errorDescription _: AutoreleasingUnsafeMutablePointer? - ) -> Bool { - if partialString.isEmpty { - return true - } - - // it is valid if contains only decimal digits - return partialString.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil - } -} diff --git a/Sources/SharedKit/Utilities/IO/BufferedInputStream.swift b/Sources/SharedKit/Utilities/IO/BufferedInputStream.swift deleted file mode 100644 index 24b0631..0000000 --- a/Sources/SharedKit/Utilities/IO/BufferedInputStream.swift +++ /dev/null @@ -1,269 +0,0 @@ -// -// BufferedInputStream.swift -// VisualDiffer -// -// Created by davide ficano on 15/07/14. -// Copyright (c) 2014 visualdiffer.com -// - -import Foundation -import AppKit - -/// A line-by-line `InputStream` supporting `\n`, `\r`, and `\r\n` terminators. -// swiftlint:disable force_unwrapping -public class BufferedInputStream: InputStream { - public enum Constants { - public static let invalidated = -2 - public static let unmarked = -1 - public static let defaultCharBufferSize = 8192 - public static let defaultExpectedLineLength = 80 - } - - // The decorated stream - private var stream: InputStream - // The size of each chunk read from the stream into memory - private var bufferSize: Int = 0 - // The encoding of the text represented by the underlying stream - private var encoding: String.Encoding - - // Buffer containing the bytes of the current chunk read from the stream - private var dataBuffer: [UInt8] = [] - private var dataBufferLen: Int = 0 - - // Whether the stream was opened by this decorator and should therefore be closed with it - private var shouldCloseStream = false - - private var skipLF = false - private var nextChar = 0 - private var nChars = 0 - private var markedChar = Constants.unmarked - private var readAheadLimit = 0 - - /** - * if using the object encoding doesn't create a valid string - * then try to determine the best one - */ - private let detectBestEncoding: Bool - - public init( - stream: InputStream, - encoding: String.Encoding, - bufferSize: Int, - detectBestEncoding: Bool = false - ) { - self.stream = stream - self.encoding = encoding - self.bufferSize = bufferSize - self.detectBestEncoding = detectBestEncoding - - super.init() - } - - public convenience init( - url: URL, - encoding: String.Encoding, - bufferSize: Int = Constants.defaultCharBufferSize, - detectBestEncoding: Bool = false - ) throws { - if let stream = InputStream(url: url) { - self.init( - stream: stream, - encoding: encoding, - bufferSize: bufferSize, - detectBestEncoding: detectBestEncoding - ) - shouldCloseStream = true - } else { - throw FileError.openFile(path: url.osPath) - } - } - - // MARK: read methods - - func readLine(ignoreLF: Bool = false) -> String? { - var line: String? - if let data = readLineAsData(ignoreLF: ignoreLF) { - // we can't use the native Swift String(data:encoding) because the results are different - // For example - // let data: Data = Data([0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05]) - // let swiftString = String(data: data, encoding: .ascii) - // let nsString = NSString(data: data, encoding: String.Encoding.ascii.rawValue) - // swiftString is nil but nsString is not nil - line = NSString(data: data, encoding: encoding.rawValue) as String? - if line == nil, detectBestEncoding { - line = lineWithBestEncoding(data: data) - } - } - - return line - } - - private func lineWithBestEncoding(data: Data) -> String? { - if let line = String(data: data, encoding: String.Encoding.windowsCP1252) { - encoding = String.Encoding.windowsCP1252 - return line as String - } - // use NSAttributedString to detect best encoding - var docAttrs = NSDictionary() - let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [ - .documentType: NSAttributedString.DocumentType.plain, - ] - do { - let line = try withUnsafeMutablePointer(to: &docAttrs) { docAttrsPointer in - try NSAttributedString( - data: data, - options: options, - documentAttributes: AutoreleasingUnsafeMutablePointer(docAttrsPointer) - ).string - } - - if let val = docAttrs[NSAttributedString.DocumentAttributeKey.characterEncoding] as? NSNumber { - encoding = String.Encoding(rawValue: val.uintValue) - } - return line - } catch {} - return nil - } - - public func readLineAsData(ignoreLF: Bool = false) -> Data? { - var data: Data? - var startChar = 0 - - var omitLF = ignoreLF || skipLF - - while true { - if nextChar >= nChars { - fill() - } - if nextChar >= nChars { // EOF - if let data, !data.isEmpty { - return data - } else { - return nil - } - } - var eol = false - var chInt: UInt8 = 0 - // Skip a leftover '\n', if necessary - if omitLF, dataBuffer[nextChar] == 0x0A { - nextChar += 1 - } - skipLF = false - omitLF = false - var i = nextChar - while i < nChars { - chInt = dataBuffer[i] - if (chInt == 0x0A) || (chInt == 0x0D) { - eol = true - break - } - i += 1 - } - startChar = nextChar - nextChar = i - if eol { - dataBuffer.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in - let ptr = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self).advanced(by: startChar) - if data == nil { - data = Data(bytes: ptr, count: i - startChar) - } else { - data!.append(ptr, count: i - startChar) - } - } - - nextChar += 1 - if chInt == 0x0D { - skipLF = true - } - return data - } - if data == nil { - data = Data(capacity: Constants.defaultExpectedLineLength) - } - dataBuffer.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in - let ptr = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self).advanced(by: startChar) - data!.append(ptr, count: i - startChar) - } - } - } - - private func fill() { - var dst = 0 - if markedChar <= Constants.unmarked { - // No mark - dst = 0 - } else { - // Marked - let delta = nextChar - markedChar - if delta >= readAheadLimit { - // Gone past read-ahead limit: Invalidate mark - markedChar = Constants.invalidated - readAheadLimit = 0 - dst = 0 - } else { - if readAheadLimit <= dataBufferLen { - // Shuffle in the current buffer - dataBuffer.replaceSubrange(markedChar ..< delta, with: dataBuffer) - markedChar = 0 - dst = delta - } else { - var ncb = [UInt8](repeating: 0, count: readAheadLimit) - ncb.replaceSubrange(markedChar ..< delta, with: dataBuffer) - // Reallocate buffer to accommodate read-ahead limit - dataBuffer = ncb - dataBufferLen = readAheadLimit - markedChar = 0 - dst = delta - } - nChars = delta - nextChar = delta - } - } - - let bytesRead = dataBuffer.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) -> Int in - let ptr = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self).advanced(by: dst) - return stream.read(ptr, maxLength: dataBufferLen - dst) - } - - if bytesRead > 0 { - nChars = dst + bytesRead - nextChar = dst - } - } - - // MARK: NSInputStream methods - - override public func open() { - shouldCloseStream = stream.streamStatus == Stream.Status.notOpen - if shouldCloseStream { - // If the underlying stream is not already open, we open it ourselves - // and we will close it when the decorator is closed - stream.open() - } - dataBuffer = [UInt8](repeating: 0, count: bufferSize) - dataBufferLen = bufferSize - - skipLF = false - nChars = 0 - nextChar = 0 - markedChar = Constants.unmarked - readAheadLimit = 0 - } - - override public var hasBytesAvailable: Bool { - stream.hasBytesAvailable - } - - public func getBuffer(buffer: UnsafeMutablePointer?>, length len: UnsafeMutablePointer) -> Bool { - stream.getBuffer(buffer, length: len) - } - - override public func close() { - if shouldCloseStream { - // Close the underlying stream if is was opened by this decorator - stream.close() - } - } -} - -// swiftlint:enable force_unwrapping diff --git a/Sources/SharedKit/Utilities/IO/CompareUtil.swift b/Sources/SharedKit/Utilities/IO/CompareUtil.swift deleted file mode 100644 index 39d65a7..0000000 --- a/Sources/SharedKit/Utilities/IO/CompareUtil.swift +++ /dev/null @@ -1,141 +0,0 @@ -// -// CompareUtil.swift -// VisualDiffer -// -// Created by davide ficano on 31/01/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -public func compareTextFiles( - _ lhs: URL, - _ rhs: URL, - _ encoding: String.Encoding, - _ bufferSize: Int, - _ isRunning: () -> Bool -) throws -> ComparisonResult { - let leftBis = try BufferedInputStream( - url: lhs, - encoding: encoding, - bufferSize: bufferSize - ) - defer { - leftBis.close() - } - - let rightBis = try BufferedInputStream( - url: rhs, - encoding: encoding, - bufferSize: bufferSize - ) - - defer { - rightBis.close() - } - - leftBis.open() - rightBis.open() - - var ret: ComparisonResult = .orderedSame - var eof = false - - repeat { - autoreleasepool { - ret = compareLine(leftBis, rightBis, &eof) - } - } while !eof && ret == .orderedSame && isRunning() - - return ret -} - -func compareLine( - _ leftBis: BufferedInputStream, - _ rightBis: BufferedInputStream, - _ eof: inout Bool -) -> ComparisonResult { - let leftLine = leftBis.readLine() - let rightLine = rightBis.readLine() - if let leftLine, - let rightLine { - return leftLine.compare(rightLine) - } - eof = true - if leftLine == nil, rightLine == nil { - return .orderedSame - } - if leftLine == nil { - return .orderedAscending - } - return .orderedDescending -} - -public func compareDates( - _ lhs: Date, - _ rhs: Date, - _ timestampToleranceSeconds: Int -) -> ComparisonResult { - let interval = Int(lhs.timeIntervalSince(rhs)) - - if interval == 0 || (-timestampToleranceSeconds <= interval && interval <= timestampToleranceSeconds) { - return .orderedSame - } - return interval < 0 ? .orderedAscending : .orderedDescending -} - -func compareData(_ lhs: Data, _ rhs: Data) -> ComparisonResult { - let lCount = lhs.count - let rCount = rhs.count - - return lhs.withUnsafeBytes { lBytes in - rhs.withUnsafeBytes { rBytes in - var ret = memcmp(lBytes.baseAddress, rBytes.baseAddress, min(lCount, rCount)) - if ret == 0, lCount != rCount { - ret = lCount < rCount ? 1 : -1 - } - // normalize to ComparisonResult value - if ret < 0 { - return .orderedAscending - } else if ret > 0 { - return .orderedDescending - } else { - return .orderedSame - } - } - } -} - -public func compareBinaryFiles( - _ left: URL, - _ right: URL, - _ bufferSize: Int, - _ isRunning: () -> Bool -) throws -> ComparisonResult { - let leftFile = try FileHandle(forReadingFrom: left) - - defer { - leftFile.closeFile() - } - - let rightFile = try FileHandle(forReadingFrom: right) - - defer { - rightFile.closeFile() - } - - var result: ComparisonResult = .orderedSame - - repeat { - if let leftData = try? leftFile.read(upToCount: bufferSize), - let rightData = try? rightFile.read(upToCount: bufferSize) { - if leftData.isEmpty || rightData.isEmpty { - break - } - result = compareData(leftData, rightData) - } else { - break - } - } while result == .orderedSame && isRunning() - - return result -} diff --git a/Sources/SharedKit/Utilities/IO/FileError.swift b/Sources/SharedKit/Utilities/IO/FileError.swift deleted file mode 100644 index fa23cd4..0000000 --- a/Sources/SharedKit/Utilities/IO/FileError.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// FileError.swift -// VisualDiffer -// -// Created by davide ficano on 26/12/10. -// Copyright (c) 2010 visualdiffer.com -// - -public enum FileError: Error, Equatable { - case symlinkLoop(path: String) - case createSymLink(path: String) - case openFile(path: String) - case fileNotExists(path: URL, side: DisplaySide) - case unknownVolumeType -} - -extension FileError: LocalizedError { - public var errorDescription: String? { - switch self { - case let .symlinkLoop(path): - let message = NSLocalizedString("Detected recursive loop for symbolic link '%@'", comment: "") - return String.localizedStringWithFormat(message, path) - case let .openFile(path): - let message = NSLocalizedString("Unable to open file '%@'", comment: "") - return String.localizedStringWithFormat(message, path) - case let .createSymLink(path): - let message = NSLocalizedString("Unable to create symbolic link to destination '%@' because the existing file isn't a symbolic link", comment: "") - return String.localizedStringWithFormat(message, path) - case let .fileNotExists(path, side): - let message = switch side { - case .left: - NSLocalizedString("Left file '%@' no longer exists", comment: "") - case .right: - NSLocalizedString("Right file '%@' no longer exists", comment: "") - } - return String.localizedStringWithFormat(message, path.osPath) - case .unknownVolumeType: - return NSLocalizedString("Unable to determine the disk volume type", comment: "") - } - } - - public var recoverySuggestion: String? { - switch self { - case .fileNotExists: - NSLocalizedString("Maybe it was located in a temporary directory and another process deleted it", comment: "") - default: - nil - } - } -} diff --git a/Sources/SharedKit/Utilities/IO/PowerAssertion.swift b/Sources/SharedKit/Utilities/IO/PowerAssertion.swift deleted file mode 100644 index bdf9a27..0000000 --- a/Sources/SharedKit/Utilities/IO/PowerAssertion.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// PowerAssertion.swift -// VisualDiffer -// -// Created by davide ficano on 12/12/15. -// Copyright (c) 2015 visualdiffer.com -// - -import IOKit.pwr_mgt - -private class PowerItem { - var pmAssertion = IOPMAssertionID(kIOPMNullAssertionID) - var refCount = 0 - - var name = "" - - init() {} - - convenience init(name: String) { - self.init() - - self.name = name - } - - deinit { - releasePMAssertion() - } - - @discardableResult - func createPMAssertion(type: String) -> IOReturn { - guard pmAssertion == kIOPMNullAssertionID else { - return kIOReturnError - } - return IOPMAssertionCreateWithName( - type as CFString, - IOPMAssertionLevel(kIOPMAssertionLevelOn), - name as CFString, - &pmAssertion - ) - } - - func releasePMAssertion() { - guard pmAssertion != kIOPMNullAssertionID else { - return - } - - IOPMAssertionRelease(pmAssertion) - pmAssertion = IOPMAssertionID(kIOPMNullAssertionID) - } - - func setDisableSystemSleep(_ disableSleep: Bool) { - if disableSleep { - refCount += 1 - if refCount == 1 { - createPMAssertion(type: kIOPMAssertPreventUserIdleSystemSleep) - } - } else { - // Prevent refCount from becoming negative - if refCount > 0 { - refCount -= 1 - if refCount == 0 { - releasePMAssertion() - } - } - } - } -} - -/** - * Disable computer sleep using IOPMAssertion implemented as singleton - * Minimize the number of IOPMAssertions creating different instances only when the name differs - * - */ -@objc class PowerAssertion: NSObject, @unchecked Sendable { - @objc static let shared = PowerAssertion() - private var pmAssertions: [String: PowerItem] = [:] - - override private init() {} - - @objc func setDisableSystemSleep(_ disableSleep: Bool, with name: String) { - let item: PowerItem - - if let existingItem = pmAssertions[name] { - item = existingItem - } else { - item = PowerItem(name: name) - pmAssertions[name] = item - } - item.setDisableSystemSleep(disableSleep) - } -} diff --git a/Sources/SharedKit/Utilities/Image/IconUtils.swift b/Sources/SharedKit/Utilities/Image/IconUtils.swift deleted file mode 100644 index 6457c37..0000000 --- a/Sources/SharedKit/Utilities/Image/IconUtils.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// IconUtils.swift -// VisualDiffer -// -// Created by davide ficano on 01/01/12. -// Converted to Swift by davide ficano on 02/05/25. -// Copyright (c) 2010 visualdiffer.com - -import Cocoa -import UniformTypeIdentifiers - -/** - Is @unchecked Sendable because icons are modified only inside the lock so it's thread safe - */ -public class IconUtils: @unchecked Sendable { - static let shared = IconUtils() - - private let lock = NSLock() - private var icons = [String: NSImage]() - - private init() {} - - public func badge(_ badge: NSImage, icon: NSImage, size: CGFloat) -> NSImage { - let image = NSImage(size: NSSize(width: size, height: size)) - - image.lockFocus() - icon.draw( - in: NSRect(x: 0, y: 0, width: size, height: size), - from: NSRect.zero, - operation: .sourceOver, - fraction: 1.0, - respectFlipped: true, - hints: nil - ) - badge.draw( - in: NSRect(x: 0, y: 0, width: size, height: size), - from: NSRect.zero, - operation: .sourceOver, - fraction: 1.0, - respectFlipped: true, - hints: nil - ) - image.unlockFocus() - - return image - } - - public func icon(forType type: UTType, size: CGFloat) -> NSImage { - if let icon = icons[type.identifier] { - return icon - } - - let icon = NSWorkspace.shared.icon(for: type) - addIconByName(type.identifier, icon: icon, size: size) - - return icon - } - - public func icon(forFile url: URL, size: CGFloat) -> NSImage { - let fullPath = url.osPath - if let icon = icons[fullPath] { - return icon - } - - let icon = NSWorkspace.shared.icon(forFile: fullPath) - addIconByName(fullPath, icon: icon, size: size) - - return icon - } - - // /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AliasBadgeIcon.icns - public func icon(forSymbolicLink url: URL, size: CGFloat) -> NSImage { - badge(forPath: url, icon: iconNamed("aliasbadge", size: size), size: size) - } - - // /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/LockedBadgeIcon.icns - public func icon(forLockedFile url: URL, size: CGFloat) -> NSImage { - badge(forPath: url, icon: iconNamed("lockedbadge", size: size), size: size) - } - - public func badge(forPath url: URL, icon badgeImage: NSImage, size: CGFloat) -> NSImage { - guard let iconName = badgeImage.name() else { - fatalError("Unable to get icon name for \(url) and icon \(badgeImage)") - } - let name = url - .appendingPathComponent(iconName) - .osPath - if let icon = icons[name] { - return icon - } - - let path = url.osPath - let isAbsolute = path.hasPrefix("/") - let fileIcon = isAbsolute ? icon(forFile: url, size: size) : iconNamed(path, size: size) - let icon = badge(badgeImage, icon: fileIcon, size: size) - - addIconByName(name, icon: icon, size: size) - - return icon - } - - private func addIconByName(_ name: String, icon: NSImage, size: CGFloat) { - lock.lock() - // Cache only images size x size - let reps = icon.representations - for rep in reps where rep.pixelsHigh != Int(size) { - if reps.count > 1 { - icon.removeRepresentation(rep) - } - } - - icon.size = NSSize(width: size, height: size) - icons[name] = icon - lock.unlock() - } - - private func iconNamed(_ name: String, size: CGFloat) -> NSImage { - if let icon = icons[name] { - return icon - } - - guard let icon = NSImage(named: name) else { - fatalError("Unable to find icon \(name)") - } - addIconByName(name, icon: icon, size: size) - - return icon - } -} diff --git a/Sources/SharedKit/Utilities/Image/NoodleCustomImageRep.swift b/Sources/SharedKit/Utilities/Image/NoodleCustomImageRep.swift deleted file mode 100644 index ff2a05c..0000000 --- a/Sources/SharedKit/Utilities/Image/NoodleCustomImageRep.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// NoodleCustomImageRep.swift -// NoodleKit -// -// Created by Paul Kim on 3/16/11. -// Copyright 2011 Noodlesoft, LLC. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -// Converted to Swift by davide ficano on 03/05/25. - -import Cocoa - -typealias NoodleCustomImageRepDrawBlock = (NoodleCustomImageRep) -> Void - -/* - This image rep just provides a way to specify the image drawing via a block. - - For more details, check out the related blog post at http://www.noodlesoft.com/blog/2011/04/15/the-proper-care-and-feeding-of-nsimage - */ -class NoodleCustomImageRep: NSImageRep { - private var drawBlock: NoodleCustomImageRepDrawBlock? - - init(drawBlock block: NoodleCustomImageRepDrawBlock?) { - super.init() - - drawBlock = block - } - - init?( - drawBlock block: NoodleCustomImageRepDrawBlock?, - coder: NSCoder - ) { - super.init(coder: coder) - - drawBlock = block - } - - override convenience init() { - self.init(drawBlock: nil) - } - - required convenience init?(coder: NSCoder) { - self.init(drawBlock: nil, coder: coder) - } - - override func copy(with zone: NSZone? = nil) -> Any { - // swiftlint:disable:next force_cast - let copy = super.copy(with: zone) as! NoodleCustomImageRep - - // NSImageRep uses NSCopyObject so we have to force a copy here - copy.drawBlock = drawBlock - - return copy - } - - override func draw() -> Bool { - guard let drawBlock else { - return false - } - drawBlock(self) - return true - } -} diff --git a/Sources/SharedKit/Utilities/Image/TiledImageView.swift b/Sources/SharedKit/Utilities/Image/TiledImageView.swift deleted file mode 100644 index f1648d0..0000000 --- a/Sources/SharedKit/Utilities/Image/TiledImageView.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// TiledImageView.swift -// VisualDiffer -// -// Created by davide ficano on 14/11/25. -// Copyright (c) 2025 visualdiffer.com -// - -final class TiledImageView: NSView { - var image: NSImage? { - didSet { needsDisplay = true } - } - - override var isFlipped: Bool { false } - - override func draw(_ dirtyRect: NSRect) { - NSBezierPath(rect: bounds).addClip() - - super.draw(dirtyRect) - guard let img = image else { - return - } - - let tileSize = img.size - guard tileSize.width > 0, tileSize.height > 0 else { - return - } - - var y: CGFloat = 0 - while y < bounds.height { - var x: CGFloat = 0 - while x < bounds.width { - img.draw( - in: NSRect(x: x, y: y, width: tileSize.width, height: tileSize.height), - from: .zero, - operation: .sourceOver, - fraction: 1.0 - ) - x += tileSize.width - } - y += tileSize.height - } - } -} diff --git a/Sources/SharedKit/Utilities/Menu/AttributedMenuItem.swift b/Sources/SharedKit/Utilities/Menu/AttributedMenuItem.swift deleted file mode 100644 index e3feb9a..0000000 --- a/Sources/SharedKit/Utilities/Menu/AttributedMenuItem.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// AttributedMenuItem.swift -// VisualDiffer -// -// Created by davide ficano on 19/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct AttributedMenuItem { - var text: String - var attributes: [NSAttributedString.Key: Any] - - static func createAttributes( - title: String, - description: String, - descriptionColor: NSColor, - font: NSFont - ) -> [AttributedMenuItem] { - [ - AttributedMenuItem(text: title, attributes: [ - NSAttributedString.Key.font: font, - NSAttributedString.Key.foregroundColor: NSColor.controlTextColor, - ]), - AttributedMenuItem(text: description, attributes: [ - NSAttributedString.Key.font: font, - NSAttributedString.Key.foregroundColor: descriptionColor, - ]), - ] - } - - static func createTitle(_ items: [AttributedMenuItem]) -> NSAttributedString { - var str = "" - - for data in items { - str += data.text - } - let attrString = NSMutableAttributedString(string: str) - - attrString.beginEditing() - var normalRange = NSRange() - - for data in items { - let text = data.text - let attrs = data.attributes - normalRange.length = text.count - attrString.addAttributes(attrs, range: normalRange) - normalRange.location += text.count - } - attrString.endEditing() - - return attrString - } -} diff --git a/Sources/SharedKit/Utilities/OpenEditor/OpenEditor.swift b/Sources/SharedKit/Utilities/OpenEditor/OpenEditor.swift deleted file mode 100644 index ef90759..0000000 --- a/Sources/SharedKit/Utilities/OpenEditor/OpenEditor.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// OpenEditor.swift -// VisualDiffer -// -// Created by davide ficano on 23/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -import os.log - -struct OpenEditor { - let attributes: [OpenEditorAttribute] - - init(attributes: [OpenEditorAttribute]) { - self.attributes = attributes - } - - init(attributes: OpenEditorAttribute) { - self.init(attributes: [attributes]) - } - - init(path: String) { - self.init(attributes: [OpenEditorAttribute(path: path)]) - } -} - -extension OpenEditor { - @MainActor func browseApplicationAndLaunch() throws { - let openPanel = NSOpenPanel().openApplication(title: NSLocalizedString("Select Application", comment: "")) - - if openPanel.runModal() == .OK { - try open(withApplication: openPanel.urls[0]) - } - } - - /** - * Run the editor application opening passed files and if editor is supported - * move the cursor to the line/column specified into attributes - * @param application the application path used to open the files, if nil uses the default system application - **/ - func open(withApplication: URL?) throws { - guard let item = attributes.first, - let secureURL = SecureBookmark.shared.secure(fromBookmark: item.path, startSecured: true) else { - return - } - defer { - SecureBookmark.shared.stopAccessing(url: secureURL) - } - let application = if let withApplication { - withApplication - } else { - NSWorkspace.shared.urlForApplication(toOpen: item.path) - } - - guard let application else { - throw OpenEditorError.applicationNotFound(item.path) - } - if let scriptURL = fullScriptURL(application) { - try runUnixScript(scriptURL) - } else { - NSWorkspace.shared.open( - [item.path], - withApplicationAt: application, - configuration: NSWorkspace.OpenConfiguration() - ) { _, error in - if let error { - Logger.general.error("Unable to open file \(item.path): \(error)") - } - } - } - } - - private func fullScriptURL(_ application: URL) -> URL? { - guard let scriptsURL = try? FileManager.default.url( - for: .applicationScriptsDirectory, - in: .userDomainMask, - appropriateFor: nil, - create: false - ), - let bundle = Bundle(url: application), - let bundleIdentifier = bundle.bundleIdentifier else { - return nil - } - - var fullScriptURL = scriptsURL - fullScriptURL.appendPathComponent("editors") - fullScriptURL.appendPathComponent(bundleIdentifier) - fullScriptURL.appendPathExtension("sh") - - return FileManager.default.fileExists(atPath: fullScriptURL.osPath) ? fullScriptURL : nil - } - - func runUnixScript(_ scriptURL: URL) throws { - do { - let task = try NSUserUnixTask(url: scriptURL) - task.execute(withArguments: arguments()) { taskError in - if let taskError { - Logger.general.error("Unable to launch \(scriptURL.osPath), \(taskError)") - } - } - } catch { - try checkExecutablePermissions(scriptURL, rethrow: error) - } - } - - private func checkExecutablePermissions(_ scriptURL: URL, rethrow _: Error) throws { - guard let attrs = try? FileManager.default.attributesOfItem(atPath: scriptURL.osPath), - let permissions = attrs[.posixPermissions] as? Int, - (permissions & 0o100) == 0 else { - return - } - - throw OpenEditorError.missingExecutePermission(scriptURL) - } - - private func arguments() -> [String] { - attributes.flatMap { $0.arguments() } - } -} diff --git a/Sources/SharedKit/Utilities/OpenEditor/OpenEditorAttribute.swift b/Sources/SharedKit/Utilities/OpenEditor/OpenEditorAttribute.swift deleted file mode 100644 index b1aef9c..0000000 --- a/Sources/SharedKit/Utilities/OpenEditor/OpenEditorAttribute.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// OpenEditorAttribute.swift -// VisualDiffer -// -// Created by davide ficano on 23/07/25. -// Copyright (c) 2025 visualdiffer.com -// - -struct OpenEditorAttribute { - var path: URL - var lineNumber: Int - var columnNumber: Int - - init(path: String, lineNumber: Int = 1, columnNumber: Int = 1) { - self.init(url: URL(filePath: path), lineNumber: lineNumber, columnNumber: columnNumber) - } - - init(url: URL, lineNumber: Int = 1, columnNumber: Int = 1) { - path = url - self.lineNumber = lineNumber - self.columnNumber = columnNumber - } -} - -extension OpenEditorAttribute { - func arguments() -> [String] { - [ - path.osPath, - "\(lineNumber)", - "\(columnNumber)", - ] - } -} diff --git a/Sources/SharedKit/Utilities/OpenEditor/OpenEditorError.swift b/Sources/SharedKit/Utilities/OpenEditor/OpenEditorError.swift deleted file mode 100644 index ce688e7..0000000 --- a/Sources/SharedKit/Utilities/OpenEditor/OpenEditorError.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// OpenEditorError.swift -// VisualDiffer -// -// Created by davide ficano on 08/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -enum OpenEditorError: Error { - case applicationNotFound(URL) - case missingExecutePermission(URL) -} - -extension OpenEditorError: LocalizedError { - var errorDescription: String? { - switch self { - case let .applicationNotFound(url): - let message = NSLocalizedString("Application '%@' not found", comment: "") - return String.localizedStringWithFormat(message, url.osPath) - case let .missingExecutePermission(url): - let message = NSLocalizedString("The Unix task '%@' must have the executable flag set (e.g., chmod +x)", comment: "") - return String.localizedStringWithFormat(message, url.osPath) - } - } -} diff --git a/Sources/SharedKit/Utilities/PopupButton/PreferredEditorPopupCell.swift b/Sources/SharedKit/Utilities/PopupButton/PreferredEditorPopupCell.swift deleted file mode 100644 index 8255e23..0000000 --- a/Sources/SharedKit/Utilities/PopupButton/PreferredEditorPopupCell.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// PreferredEditorPopupCell.swift -// VisualDiffer -// -// Created by davide ficano on 20/12/12. -// Copyright (c) 2012 visualdiffer.com -// - -@objc class PreferredEditorPopupCell: NSPopUpButtonCell { - override init(textCell stringValue: String, pullsDown pullDown: Bool) { - super.init(textCell: stringValue, pullsDown: pullDown) - setupPopUpCell() - } - - required init(coder: NSCoder) { - super.init(coder: coder) - setupPopUpCell() - } - - /** - * Do not allow selecting the "Choose" item and the separator before it. - * (Note that the Choose item can be chosen and an action will be sent, but the selection doesn't change to it.) - */ - override func selectItem(at index: Int) { - if (index + 2) <= numberOfItems { - super.selectItem(at: index) - } - } - - private func setupPopUpCell() { - removeAllItems() - - if let editorPath = UserDefaults.standard.string(forKey: NSMenu.preferredEditorPrefName) { - addItem(withTitle: "") - if let lastItem { - fillPath(menuItem: lastItem, path: URL(filePath: editorPath)) - } - } else { - addItem(withTitle: NSLocalizedString("", comment: "")) - } - menu?.addItem(NSMenuItem.separator()) - - addItem(withTitle: NSLocalizedString("Choose...", comment: "")) - if let lastItem { - lastItem.action = #selector(choosePreferredEditor) - lastItem.target = self - } - - selectItem(at: 0) - } - - func fillPath(menuItem item: NSMenuItem, path: URL) { - guard let defaultAppPath = NSWorkspace.shared.urlForApplication(toOpen: path) else { - return - } - // get the localized display name for the app - if let values = try? defaultAppPath.resourceValues(forKeys: [URLResourceKey.localizedNameKey]), - let defaultAppName = values.localizedName { - item.title = defaultAppName - } else { - item.title = defaultAppPath.lastPathComponent - } - let image = NSWorkspace.shared.icon(forFile: path.osPath) - image.size = NSSize(width: 16.0, height: 16.0) - item.image = image - } - - @objc func choosePreferredEditor(_: AnyObject) { - let openPanel = NSOpenPanel() - .openApplication(title: NSLocalizedString("Select Preferred Editor", comment: "")) - - if openPanel.runModal() == .OK { - if let applicationPath = openPanel.urls.first?.osPath { - UserDefaults.standard.setValue( - applicationPath, - forKey: NSMenu.preferredEditorPrefName - ) - setupPopUpCell() - } - } - } - - @objc func removePreferredEditor(_: AnyObject) { - UserDefaults.standard.removeObject(forKey: NSMenu.preferredEditorPrefName) - setupPopUpCell() - } -} diff --git a/Sources/SharedKit/Utilities/String/VisibleWhitespaces.swift b/Sources/SharedKit/Utilities/String/VisibleWhitespaces.swift deleted file mode 100644 index 8607090..0000000 --- a/Sources/SharedKit/Utilities/String/VisibleWhitespaces.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// VisibleWhitespaces.swift -// VisualDiffer -// -// Created by davide ficano on 05/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -class VisibleWhitespaces: NSObject { - static let visibleTab = "\u{BB}" - static let visibleSpace = "\u{B7}" - - @objc var tabWidth = 4 - - func getVisibleCharFor(_ ch: Character) -> String { - if ch == " " { - return Self.visibleSpace - } - return String(ch) - } - - func getString(_ component: DiffLineComponent, isWhitespacesVisible: Bool) -> String { - if isWhitespacesVisible { - return showWhitespaces(component.text + component.eol.visibleSymbol) - } - return Self.tabs2space(component.text, tabWidth: tabWidth) - } - - static func tabs2space(_ line: String, tabWidth: Int) -> String { - var dest = "" - - for ch in line { - if ch == "\t" { - let spaces = tabWidth - (dest.count % tabWidth) - dest += String(repeating: " ", count: spaces) - } else { - dest.append(ch) - } - } - return dest - } - - func showWhitespaces(_ line: String) -> String { - let whitespaces = CharacterSet.whitespaces - var dest = "" - - for ch in line { - guard let scalar = ch.unicodeScalars.first, - whitespaces.contains(scalar) else { - dest.append(ch) - continue - } - if ch == "\t" { - // subtract from spaces the character representing TAB - let spaces = (tabWidth - (dest.count % tabWidth)) - 1 - if spaces > 0 { - let leftSpaces = spaces / 2 - let rightSpaces = spaces - leftSpaces - - if leftSpaces > 0 { - dest += String(repeating: " ", count: leftSpaces) - } - dest += Self.visibleTab - if rightSpaces > 0 { - dest += String(repeating: " ", count: rightSpaces) - } - } else { - dest += Self.visibleTab - } - } else { - dest += getVisibleCharFor(ch) - } - } - return dest - } -} diff --git a/Sources/SharedKit/Utilities/TableView/DescriptionOutlineNode.swift b/Sources/SharedKit/Utilities/TableView/DescriptionOutlineNode.swift deleted file mode 100644 index cba25ad..0000000 --- a/Sources/SharedKit/Utilities/TableView/DescriptionOutlineNode.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// DescriptionOutlineNode.swift -// VisualDiffer -// -// Created by davide ficano on 02/02/12. -// Copyright (c) 2012 visualdiffer.com -// - -class DescriptionOutlineNode: NSObject { - let text: String - let isContainer: Bool - var children: [DescriptionOutlineNode] - var items: [CompareItem]? - - init(text: String, isContainer: Bool = false) { - self.text = text - self.isContainer = isContainer - children = [] - } - - convenience init( - relativePath text: String, - items: [CompareItem], - rootPath: String - ) { - self.init(text: text, isContainer: true) - - self.items = items - let rootPathLen = rootPath.standardizingPath.count + 1 - - for item in items { - guard let path = item.path else { - continue - } - let start = path.index(path.startIndex, offsetBy: rootPathLen) - let relativePath = String(path[start ..< path.endIndex]) - let node = DescriptionOutlineNode(text: relativePath) - children.append(node) - } - } -} diff --git a/Sources/SharedKit/Utilities/TableView/TableViewCommon.swift b/Sources/SharedKit/Utilities/TableView/TableViewCommon.swift deleted file mode 100644 index 578469c..0000000 --- a/Sources/SharedKit/Utilities/TableView/TableViewCommon.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// TableViewCommon.swift -// VisualDiffer -// -// Created by davide ficano on 06/04/14. -// Copyright (c) 2014 visualdiffer.com -// - -protocol TableViewCommonDelegate: AnyObject { - @MainActor func tableViewCommonKeyDown(_ tableView: NSTableView, event: NSEvent) -> Bool -} - -class TableViewCommon: NSTableView { - override func keyDown(with event: NSEvent) { - if let delegate = delegate as? TableViewCommonDelegate { - if delegate.tableViewCommonKeyDown(self, event: event) { - return - } - } - super.keyDown(with: event) - } -} diff --git a/Sources/SharedKit/Utilities/TableView/TableViewContextMenuDelegate.swift b/Sources/SharedKit/Utilities/TableView/TableViewContextMenuDelegate.swift deleted file mode 100644 index f3ab905..0000000 --- a/Sources/SharedKit/Utilities/TableView/TableViewContextMenuDelegate.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// TableViewContextMenuDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 03/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -protocol TableViewContextMenuDelegate: AnyObject { - @MainActor func tableView(_ tableView: NSTableView, menuItem: NSMenuItem, hideMenuItem hide: inout Bool) -> Bool -} diff --git a/Sources/SharedKit/Utilities/TextField/NSTextFieldCell+Helper.swift b/Sources/SharedKit/Utilities/TextField/NSTextFieldCell+Helper.swift deleted file mode 100644 index b603edf..0000000 --- a/Sources/SharedKit/Utilities/TextField/NSTextFieldCell+Helper.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// NSTextFieldCell+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 28/01/11. -// Copyright (c) 2011 visualdiffer.com -// - -@objc extension NSTextFieldCell { - var textAttributes: [NSAttributedString.Key: Any] { - var attributes = [NSAttributedString.Key: Any]() - - var cellTextColor = textColor - if interiorBackgroundStyle == .emphasized { - cellTextColor = NSColor.alternateSelectedControlTextColor - } - if let cellTextColor { - attributes[NSAttributedString.Key.foregroundColor] = cellTextColor - } - - if let font { - attributes[NSAttributedString.Key.font] = font - } - - if let paraStyle = NSParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle { - paraStyle.alignment = alignment - paraStyle.lineBreakMode = lineBreakMode - paraStyle.baseWritingDirection = baseWritingDirection - - attributes[NSAttributedString.Key.paragraphStyle] = paraStyle - } - - return attributes - } - - func widthNumber(_ number: Int = Int.max) -> CGFloat { - let str = if let formatter { - formatter.string(for: number) ?? String(format: "%lld", number) - } else { - String(format: "%lld", number) - } - return (str as NSString).size(withAttributes: textAttributes).width - } - - func widthString(_ str: String) -> CGFloat { - (str as NSString).size(withAttributes: textAttributes).width - } -} diff --git a/Sources/SharedKit/Utilities/TextField/RSVerticallyCenteredTextFieldCell.swift b/Sources/SharedKit/Utilities/TextField/RSVerticallyCenteredTextFieldCell.swift deleted file mode 100644 index eec11dc..0000000 --- a/Sources/SharedKit/Utilities/TextField/RSVerticallyCenteredTextFieldCell.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// RSVerticallyCenteredTextFieldCell.swift -// VisualDiffer -// -// Created by Daniel Jalkut on 6/17/06. -// Copyright 2006 Red Sweater Software. All rights reserved. -// - -class RSVerticallyCenteredTextFieldCell: NSTextFieldCell { - private var isEditingOrSelecting = false - - override func drawingRect(forBounds rect: NSRect) -> NSRect { - // Get the parent's idea of where we should draw - var newRect = super.drawingRect(forBounds: rect) - - // When the text field is being - // edited or selected, we have to turn off the magic because it screws up - // the configuration of the field editor. We sneak around this by - // intercepting selectWithFrame and editWithFrame and sneaking a - // reduced, centered rect in at the last minute. - if !isEditingOrSelecting { - // Get our ideal size for current text - let textSize = cellSize(forBounds: rect) - - // Center that in the proposed rect - let heightDelta = newRect.size.height - textSize.height - if heightDelta > 0 { - newRect.size.height -= heightDelta - newRect.origin.y += (heightDelta / 2) - } - } - - return newRect - } -} diff --git a/Sources/SharedKit/Utilities/Toolbar/CustomValidationToolbarItem.swift b/Sources/SharedKit/Utilities/Toolbar/CustomValidationToolbarItem.swift deleted file mode 100644 index f46391c..0000000 --- a/Sources/SharedKit/Utilities/Toolbar/CustomValidationToolbarItem.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// CustomValidationToolbarItem.swift -// VisualDiffer -// -// Created by davide ficano on 03/03/21. -// Copyright (c) 2021 visualdiffer.com -// - -// The delegate validateToolbarItem is called only for Image View Toolbar items -// so we need to subclass it and apply the custom validation -// see "View item validation" at -// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Toolbars/Tasks/ValidatingTBItems.html#//apple_ref/doc/uid/20000753-BAJGFHDD -class CustomValidationToolbarItem: NSToolbarItem { - override func validate() { - isEnabled = if let target { - target.validateToolbarItem(self) - } else { - false - } - } - - override var menuFormRepresentation: NSMenuItem? { - get { - let menuItem = NSMenuItem( - title: label, - action: action, - keyEquivalent: "" - ) - let strongTarget = target - menuItem.target = strongTarget - menuItem.isEnabled = if let strongTarget { - strongTarget.validateToolbarItem(self) - } else { - false - } - - return menuItem - } - - set { - super.menuFormRepresentation = newValue - } - } -} diff --git a/Sources/SharedKit/Utilities/View/ActionBar/ActionBarView.swift b/Sources/SharedKit/Utilities/View/ActionBar/ActionBarView.swift deleted file mode 100644 index cb9c0d7..0000000 --- a/Sources/SharedKit/Utilities/View/ActionBar/ActionBarView.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// ActionBarView.swift -// VisualDiffer -// -// Created by davide ficano on 28/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -class ActionBarView: NSStackView { - lazy var firstButton: NSButton = createButton(image: NSImage(named: NSImage.addTemplateName)) - lazy var secondButton: NSButton = createButton(image: NSImage(named: NSImage.removeTemplateName)) - lazy var popup: NSPopUpButton = createMenu() - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - translatesAutoresizingMaskIntoConstraints = false - orientation = .horizontal - spacing = 1 - alignment = .centerY - - // ensure the height is correctly set - setHuggingPriority(.required, for: .vertical) - - setupViews() - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("Method not implemented") - } - - func setupViews() { - addArrangedSubview(firstButton) - addArrangedSubview(secondButton) - addArrangedSubview(popup) - - setupConstraints() - } - - func setupConstraints() { - NSLayoutConstraint.activate([ - firstButton.widthAnchor.constraint(equalToConstant: 24), - firstButton.heightAnchor.constraint(equalToConstant: 24), - - secondButton.widthAnchor.constraint(equalToConstant: 24), - secondButton.heightAnchor.constraint(equalToConstant: 24), - - popup.widthAnchor.constraint(equalToConstant: 32), - popup.heightAnchor.constraint(equalToConstant: 24), - ]) - } - - private func createButton(image: NSImage?) -> NSButton { - let view = NSButton(frame: .zero) - - view.bezelStyle = .shadowlessSquare - view.setButtonType(.momentaryPushIn) - view.isBordered = true - view.alignment = .center - - view.image = image - view.imagePosition = .imageOnly - view.imageScaling = .scaleProportionallyDown - - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createMenu() -> NSPopUpButton { - let popupMenu = NSMenu() - - // the button title image - popupMenu - .addItem( - withTitle: "", - action: nil, - keyEquivalent: "" - ) - .image = NSImage(named: NSImage.actionTemplateName) - - let view = NSPopUpButton(frame: .zero, pullsDown: true) - - view.bezelStyle = .shadowlessSquare - view.setButtonType(.momentaryPushIn) - view.isBordered = true - view.alignment = .left - view.menu = popupMenu - - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } -} diff --git a/Sources/SharedKit/Utilities/View/Button/LinkButton.swift b/Sources/SharedKit/Utilities/View/Button/LinkButton.swift deleted file mode 100644 index e255fb3..0000000 --- a/Sources/SharedKit/Utilities/View/Button/LinkButton.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// LinkButton.swift -// VisualDiffer -// -// Created by davide ficano on 05/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -class LinkButton: NSButton { - init(title: String, target: AnyObject? = nil, action: Selector? = nil) { - super.init(frame: .zero) - - self.title = title - self.target = target - self.action = action - - styleAsLink() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func resetCursorRects() { - addCursorRect(bounds, cursor: .pointingHand) - } - - func styleAsLink() { - isBordered = false - wantsLayer = true - layer?.backgroundColor = NSColor.clear.cgColor - - let attributes: [NSAttributedString.Key: Any] = [ - .foregroundColor: NSColor.linkColor, - .underlineStyle: NSUnderlineStyle.single.rawValue, - ] - - attributedTitle = NSAttributedString(string: title, attributes: attributes) - } -} diff --git a/Sources/SharedKit/Utilities/View/Button/StandardButtons.swift b/Sources/SharedKit/Utilities/View/Button/StandardButtons.swift deleted file mode 100644 index 41b7b58..0000000 --- a/Sources/SharedKit/Utilities/View/Button/StandardButtons.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// StandardButtons.swift -// VisualDiffer -// -// Created by davide ficano on 13/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Cocoa -import Foundation - -@objc class StandardButtons: NSStackView { - @objc private(set) var primaryButton: NSButton - @objc private(set) var secondaryButton: NSButton - - @objc init( - primaryTitle: String, - secondaryTitle: String, - target: Any?, - action: Selector? - ) { - secondaryButton = NSButton.cancelButton( - title: secondaryTitle, - target: target, - action: action - ) - - primaryButton = NSButton.okButton( - title: primaryTitle, - target: target, - action: action - ) - - super.init(frame: .zero) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - orientation = .horizontal - alignment = .centerY - spacing = 20 - translatesAutoresizingMaskIntoConstraints = false - - addArrangedSubview(secondaryButton) - addArrangedSubview(primaryButton) - - primaryButton.widthAnchor.constraint(equalToConstant: 100).isActive = true - secondaryButton.widthAnchor.constraint(equalToConstant: 100).isActive = true - } -} diff --git a/Sources/SharedKit/Utilities/View/Console/ConsoleToolbarView.swift b/Sources/SharedKit/Utilities/View/Console/ConsoleToolbarView.swift deleted file mode 100644 index f29cf97..0000000 --- a/Sources/SharedKit/Utilities/View/Console/ConsoleToolbarView.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ConsoleToolbarView.swift -// VisualDiffer -// -// Created by davide ficano on 29/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -class ConsoleToolbarView: NSView { - @objc let clearButton: NSButton - @objc let hideButton: NSButton - - override init(frame frameRect: NSRect) { - clearButton = NSButton(frame: .zero) - hideButton = NSButton(frame: .zero) - - super.init(frame: frameRect) - setupViews() - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("Method not implemented") - } - - private func setupViews() { - setup(button: clearButton, title: NSLocalizedString("Clear", comment: "")) - setup(button: hideButton, title: NSLocalizedString("Hide", comment: "")) - - addSubview(clearButton) - addSubview(hideButton) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - clearButton.topAnchor.constraint(equalTo: topAnchor, constant: 4), - clearButton.trailingAnchor.constraint(equalTo: hideButton.leadingAnchor, constant: -6), - - hideButton.topAnchor.constraint(equalTo: topAnchor, constant: 4), - hideButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), - ]) - } - - private func setup(button: NSButton, title: String) { - button.bezelStyle = .roundRect - button.setButtonType(.momentaryPushIn) - button.isBordered = true - button.state = .on - button.title = title - button.alignment = .center - button.controlSize = .small - button.translatesAutoresizingMaskIntoConstraints = false - } -} diff --git a/Sources/SharedKit/Utilities/View/Console/ConsoleView.swift b/Sources/SharedKit/Utilities/View/Console/ConsoleView.swift deleted file mode 100644 index f0d26ea..0000000 --- a/Sources/SharedKit/Utilities/View/Console/ConsoleView.swift +++ /dev/null @@ -1,161 +0,0 @@ -// -// ConsoleView.swift -// VisualDiffer -// -// Created by davide ficano on 30/04/12. -// Copyright (c) 2012 visualdiffer.com -// - -protocol ConsoleViewDelegate: AnyObject { - func hide(console: ConsoleView) -} - -class ConsoleView: NSView, NSTextViewDelegate { - var delegate: ConsoleViewDelegate? - - private lazy var dateFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = DateFormatter.dateFormat( - fromTemplate: "ddMMyyHHmmss", - options: 0, - locale: Locale.current - ) - - return dateFormatter - }() - - private lazy var toolbar: ConsoleToolbarView = { - let view = ConsoleToolbarView(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - view.clearButton.target = self - view.clearButton.action = #selector(clear) - - view.hideButton.target = self - view.hideButton.action = #selector(hide) - - return view - }() - - private lazy var consoleText: NSTextView = { - let view = NSTextView(frame: .zero) - - view.isEditable = true - view.isSelectable = true - view.isRichText = true - view.font = CommonPrefs.shared.consoleLogFont - view.autoresizingMask = [.width, .height] - view.delegate = self - - return view - }() - - private lazy var scrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - - view.documentView = consoleText - - return view - }() - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupViews() { - addSubview(toolbar) - addSubview(scrollView) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - toolbar.topAnchor.constraint(equalTo: topAnchor), - toolbar.leadingAnchor.constraint(equalTo: leadingAnchor), - toolbar.trailingAnchor.constraint(equalTo: trailingAnchor), - toolbar.heightAnchor.constraint(equalToConstant: 25), - - scrollView.topAnchor.constraint(equalTo: toolbar.bottomAnchor), - scrollView.bottomAnchor.constraint(equalTo: bottomAnchor), - scrollView.leadingAnchor.constraint(equalTo: leadingAnchor), - scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), - ]) - } - - override func draw(_ dirtyRect: NSRect) { - super.draw(dirtyRect) - - NSColor.textBackgroundColor.setFill() - bounds.fill() - } - - func log(info message: String) { - let colors = CommonPrefs.shared.consoleLogColors(.info) - log(message: message, at: Date(), colors: colors) - } - - func log(warning message: String) { - let colors = CommonPrefs.shared.consoleLogColors(.warning) - log(message: message, at: Date(), colors: colors) - } - - func log(error message: String) { - let colors = CommonPrefs.shared.consoleLogColors(.error) - log(message: message, at: Date(), colors: colors) - } - - private func log( - message: String, - at atTime: Date, - colors: ColorSet - ) { - let dateText = dateFormatter.string(from: atTime) - let dict: [NSAttributedString.Key: Any] = [ - .font: consoleText.font ?? CommonPrefs.shared.consoleLogFont, - .foregroundColor: colors.text, - .backgroundColor: colors.background, - ] - consoleText.append( - text: String(format: "%@ %@\n", dateText, message), - attributes: dict - ) - } - - @objc func clear(_: AnyObject) { - consoleText.string = "" - } - - @objc func hide(_: AnyObject) { - delegate?.hide(console: self) - } - - func focus() { - window?.makeFirstResponder(consoleText) - } - - func textView(_: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - if commandSelector == #selector(cancelOperation(_:)) { - hide(self) - return true - } - return false - } -} diff --git a/Sources/SharedKit/Utilities/View/DualPane/DualPaneSplitView.swift b/Sources/SharedKit/Utilities/View/DualPane/DualPaneSplitView.swift deleted file mode 100644 index 77a5584..0000000 --- a/Sources/SharedKit/Utilities/View/DualPane/DualPaneSplitView.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// DualPaneSplitView.swift -// VisualDiffer -// -// Created by davide ficano on 24/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DualPaneSplitView: NSSplitView { - var isSubviewCollapsed = false - - @objc var firstViewSize: CGFloat = 0 - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - NotificationCenter.default.addObserver( - self, - selector: #selector(subviewResized), - name: NSSplitView.didResizeSubviewsNotification, - object: self - ) - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - NotificationCenter.default.addObserver( - self, - selector: #selector(subviewResized), - name: NSSplitView.didResizeSubviewsNotification, - object: self - ) - } - - deinit { - NotificationCenter.default.removeObserver( - self, - name: NSSplitView.didResizeSubviewsNotification, - object: self - ) - } - - @objc func subviewResized(_ notification: Notification) { - if hasSubviewCollapsed { - return - } - guard let num = notification.userInfo?["NSSplitViewUserResizeKey"] as? NSNumber else { - return - } - let isUserResize = num.boolValue - if isUserResize { - let view = subviews[0] - firstViewSize = isVertical ? view.frame.width : view.frame.height - } - } - - @objc var hasSubviewCollapsed: Bool { - if subviews.isEmpty { - return true - } - for view in subviews where isSubviewCollapsed(view) { - return true - } - return false - } - - @objc func toggleSubview(at index: Int) { - if hasSubviewCollapsed { - expandSubview(at: index) - } else { - collapseSubview(at: index) - } - } - - @objc func collapseSubview(at index: Int) { - if hasSubviewCollapsed { - return - } - if index == 0 { - let collapseView = subviews[0] - collapseView.isHidden = true - setPosition(0, ofDividerAt: 0) - } else if index == 1 { - let expandView = subviews[0] - let collapseView = subviews[1] - collapseView.isHidden = true - let position = isVertical ? expandView.frame.width : expandView.frame.height - setPosition(position, ofDividerAt: 0) - } else { - return - } - adjustSubviews() - } - - @objc func expandSubview(at index: Int) { - if !hasSubviewCollapsed { - return - } - if index == 0 { - let collapseView = subviews[0] - collapseView.isHidden = false - setPosition(firstViewSize, ofDividerAt: 0) - } else if index == 1 { - let collapseView = subviews[1] - collapseView.isHidden = false - setPosition(firstViewSize, ofDividerAt: 0) - } else { - return - } - adjustSubviews() - } - - override var dividerThickness: CGFloat { - hasSubviewCollapsed ? 0.0 : super.dividerThickness - } -} diff --git a/Sources/SharedKit/Utilities/View/DualPane/DualPaneSplitViewDelegate.swift b/Sources/SharedKit/Utilities/View/DualPane/DualPaneSplitViewDelegate.swift deleted file mode 100644 index 2e9b0e5..0000000 --- a/Sources/SharedKit/Utilities/View/DualPane/DualPaneSplitViewDelegate.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// DualPaneSplitViewDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 17/07/20. -// Copyright (c) 2020 visualdiffer.com -// - -class DualPaneSplitViewDelegate: NSObject, NSSplitViewDelegate { - var minSize: CGFloat = 0 - var maxSize: CGFloat = 0 - var collapsableSubviewIndex = 0 - - @objc init( - collapsableSubViewIndex index: Int, - minSize: CGFloat, - maxSize: CGFloat - ) { - super.init() - - collapsableSubviewIndex = index - self.minSize = minSize - self.maxSize = maxSize - } - - func splitView(_: NSSplitView, shouldHideDividerAt _: Int) -> Bool { - true - } - - func splitView(_: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt _: Int) -> CGFloat { - proposedMinimumPosition + minSize - } - - func splitView(_: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt _: Int) -> CGFloat { - proposedMaximumPosition - maxSize - } - - func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool { - subview == splitView.subviews[collapsableSubviewIndex] - } -} diff --git a/Sources/SharedKit/Utilities/View/Encoding/EncodingManager.swift b/Sources/SharedKit/Utilities/View/Encoding/EncodingManager.swift deleted file mode 100644 index e93c2ee..0000000 --- a/Sources/SharedKit/Utilities/View/Encoding/EncodingManager.swift +++ /dev/null @@ -1,250 +0,0 @@ -// -// EncodingManager.swift -// VisualDiffer -// -// Created by davide ficano on 25/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Foundation -import CoreFoundation - -private let encodingsPrefName = "userEncodings" -private let encodingSeparator = String.Encoding(rawValue: UInt.max) - -let defaultStringsEncodings: [String.Encoding] = [ - .utf8, - .utf16, - encodingSeparator, - .utf16BigEndian, - .utf16LittleEndian, - encodingSeparator, - .macOSRoman, - .windowsCP1252, - .isoLatin1, - CFStringEncodings.isoLatin9.stringEncoding, - CFStringEncodings.dosLatinUS.stringEncoding, - encodingSeparator, - CFStringEncodings.macJapanese.stringEncoding, - CFStringEncodings.shiftJIS.stringEncoding, - CFStringEncodings.macChineseTrad.stringEncoding, - CFStringEncodings.macKorean.stringEncoding, - CFStringEncodings.macChineseSimp.stringEncoding, -].compactMap(\.self) - -@MainActor class EncodingManager: NSObject, @unchecked Sendable { - static let listNotification = Notification.Name("EncodingsListChanged") - - static let shared = EncodingManager() - - override private init() {} - - private lazy var stickyStringEncodings: [String.Encoding] = createStickyEncodings() - private lazy var allEncodings: [String.Encoding] = allAvailableStringEncodings() - private var popupEncodingsList: [String.Encoding]? - - private var encodingPanel: SelectEncodingsPanel? - - func setupPopUpCell( - _ popup: EncodingPopUpButtonCell, - selectedEncoding: String.Encoding, - withDefaultEntry _: Bool - ) { - var itemToSelect = 0 - - // Put the encodings in the popup - popup.removeAllItems() - - for encoding in enabledEncodingsGroups() { - if encoding == encodingSeparator { - popup.menu?.addItem(NSMenuItem.separator()) - } else { - popup.addItem(withTitle: String.localizedName(of: encoding)) - - popup.lastItem?.representedObject = NSNumber(value: encoding.rawValue) - popup.lastItem?.isEnabled = true - if encoding == selectedEncoding { - itemToSelect = popup.numberOfItems - 1 - } - } - } - - // Add an optional separator and "customize" item at end - if popup.numberOfItems > 0 { - popup.menu?.addItem(NSMenuItem.separator()) - } - popup.addItem(withTitle: NSLocalizedString("Other...", comment: "")) - popup.lastItem?.action = #selector(showPanel) - popup.lastItem?.target = self - - popup.selectItem(at: itemToSelect) - } - - @objc func showPanel(_ sender: AnyObject) { - if encodingPanel == nil { - encodingPanel = SelectEncodingsPanel.createSheet() - encodingPanel?.onClose = onClose - - // Initialize the list (only need to do this once) - setupEncodingsList() - } - guard let encodingPanel else { - return - } - if encodingPanel.isVisible { - encodingPanel.makeKeyAndOrderFront(sender) - return - } - NSApp.keyWindow?.beginSheet(encodingPanel) - } - - func onClose(_ response: NSApplication.ModalResponse, _ selectedEncodings: [String.Encoding]) { - if response == .OK { - noteEncodingListChange(selectedEncodings, updateList: false, postNotification: true) - } - } - - private func enabledEncodingsGroups() -> [String.Encoding] { - if let popupEncodingsList { - return popupEncodingsList - } - let list = createPopupEncodingList() - popupEncodingsList = list - - return list - } - - /** - * Return a sorted list of all available string encodings. - **/ - func allAvailableStringEncodings() -> [String.Encoding] { - var nameEncodings = [[String.Encoding: String]]() - guard let cfEncodings = CFStringGetListOfAvailableEncodings() else { - return [] - } - var index = 0 - - while true { - let enc = cfEncodings[index] - - if enc == kCFStringEncodingInvalidId { - break - } - if let nsEncoding = (enc as CFStringEncoding).stringEncoding { - nameEncodings.append([nsEncoding: String.localizedName(of: nsEncoding)]) - } - - index += 1 - } - - nameEncodings.sort { - if let name1 = $0.values.first, - let name2 = $1.values.first { - return name1 < name2 - } - return false - } - - var allEncodings = [String.Encoding]() - allEncodings.reserveCapacity(nameEncodings.count) - for dict in nameEncodings { - if let key = dict.keys.first { - allEncodings.append(key) - } - } - - return allEncodings - } - - func setupEncodingsList() { - var all = allEncodings - - // remove encodings present on sticky list - for encoding in stickyStringEncodings.reversed() { - if let index = all.firstIndex(of: encoding) { - all.remove(at: index) - } - } - encodingPanel?.encodingsList = all - - noteEncodingListChange(nil, updateList: true, postNotification: false) - } - - func noteEncodingListChange( - _ listToWrite: [String.Encoding]?, - updateList: Bool, - postNotification post: Bool - ) { - if let listToWrite { - saveEncodings(listToWrite) - popupEncodingsList = nil - } - - if updateList, let encodingPanel { - if let otherEncs = readEncodings() { - var selected = IndexSet() - for encoding in otherEncs { - if let index = encodingPanel.encodingsList.firstIndex(of: encoding) { - selected.insert(index) - } - } - encodingPanel.select(encodings: selected) - } - } - - if post { - NotificationCenter.default.post(name: Self.listNotification, object: nil) - } - } - - private func createStickyEncodings() -> [String.Encoding] { - let defaultEncoding = String.defaultCStringEncoding - var hasDefault = false - - var encs = [String.Encoding]() - - for encSupported in defaultStringsEncodings { - if encSupported == encodingSeparator { - encs.append(encodingSeparator) - } else { - encs.append(encSupported) - if encSupported == defaultEncoding { - hasDefault = true - } - } - } - - if !hasDefault { - encs.append(defaultEncoding) - } - - return encs - } - - private func createPopupEncodingList() -> [String.Encoding] { - var enabledEncs = stickyStringEncodings - - if let userList = readEncodings(), - !userList.isEmpty { - enabledEncs.append(encodingSeparator) - enabledEncs.append(contentsOf: userList) - } - return enabledEncs - } - - private func saveEncodings(_ list: [String.Encoding]) { - let nsNumbers = list.map { - NSNumber(value: $0.rawValue) - } - UserDefaults.standard.set(nsNumbers, forKey: encodingsPrefName) - } - - private func readEncodings() -> [String.Encoding]? { - guard let encs = UserDefaults.standard.array(forKey: encodingsPrefName) as? [NSNumber] else { - return nil - } - return encs.map { - String.Encoding(rawValue: $0.uintValue) - } - } -} diff --git a/Sources/SharedKit/Utilities/View/Encoding/EncodingPopUpButtonCell.swift b/Sources/SharedKit/Utilities/View/Encoding/EncodingPopUpButtonCell.swift deleted file mode 100644 index edfbeeb..0000000 --- a/Sources/SharedKit/Utilities/View/Encoding/EncodingPopUpButtonCell.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// EncodingPopUpButtonCell.swift -// VisualDiffer -// -// Created by davide ficano on 15/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -private let wantsAutomaticTag = -1 - -class EncodingPopUpButtonCell: NSPopUpButtonCell { - override init(textCell stringValue: String, pullsDown pullDown: Bool) { - super.init(textCell: stringValue, pullsDown: pullDown) - NotificationCenter.default.addObserver( - self, - selector: #selector(encodingsListChanged), - name: EncodingManager.listNotification, - object: nil - ) - EncodingManager.shared.setupPopUpCell( - self, - selectedEncoding: noStringEncoding, - withDefaultEntry: false - ) - } - - required init(coder: NSCoder) { - super.init(coder: coder) - - NotificationCenter.default.addObserver( - self, - selector: #selector(encodingsListChanged), - name: EncodingManager.listNotification, - object: nil - ) - EncodingManager.shared.setupPopUpCell( - self, - selectedEncoding: noStringEncoding, - withDefaultEntry: false - ) - } - - deinit { - NotificationCenter.default.removeObserver(self) - } - - /** - * Do not allow selecting the "Customize" item and the separator before it. - * (Note that the customize item can be chosen and an action will be sent, but the selection doesn't change to it.) - */ - override func selectItem(at index: Int) { - if index + 2 <= numberOfItems { - super.selectItem(at: index) - } - } - - @objc func encodingsListChanged(_: Notification) { - let encoding = if let value = selectedItem?.representedObject as? NSNumber { - String.Encoding(rawValue: value.uintValue) - } else { - noStringEncoding - } - EncodingManager.shared.setupPopUpCell( - self, - selectedEncoding: encoding, - withDefaultEntry: numberOfItems > 0 && item(at: 0)?.tag == wantsAutomaticTag - ) - } -} diff --git a/Sources/SharedKit/Utilities/View/Encoding/SelectEncodingsPanel.swift b/Sources/SharedKit/Utilities/View/Encoding/SelectEncodingsPanel.swift deleted file mode 100644 index aeb12a4..0000000 --- a/Sources/SharedKit/Utilities/View/Encoding/SelectEncodingsPanel.swift +++ /dev/null @@ -1,218 +0,0 @@ -// -// SelectEncodingsPanel.swift -// VisualDiffer -// -// Created by davide ficano on 15/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -class SelectEncodingsPanel: NSWindow, NSTableViewDataSource, NSTableViewDelegate { - private lazy var titleText: NSTextField = { - let view = NSTextField(frame: .zero) - - view.isEditable = false - view.isBordered = false - view.drawsBackground = false - view.translatesAutoresizingMaskIntoConstraints = false - - let cell = TextFieldVerticalCentered() - cell.lineBreakMode = .byClipping - cell.title = NSLocalizedString("Select encodings to show on the main list", comment: "") - - view.cell = cell - - // set the font after the cell otherwise it is lost - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - return view - }() - - private lazy var scrollView: NSScrollView = { - let view = NSScrollView(frame: .zero) - - view.borderType = .bezelBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - view.documentView = tableView - - return view - }() - - private lazy var tableView: NSTableView = { - let view = TableViewCommon(frame: .zero) - - view.allowsEmptySelection = true - view.allowsColumnReordering = false - view.allowsColumnResizing = true - view.allowsMultipleSelection = true - view.allowsColumnSelection = true - view.allowsTypeSelect = true - - view.allowsExpansionToolTips = true - view.columnAutoresizingStyle = .uniformColumnAutoresizingStyle - view.autosaveTableColumns = false - view.intercellSpacing = .zero - view.headerView = nil - - // the column is full width and it resizes properly when the table changes dimensions - // see https://stackoverflow.com/a/15390614/195893 - let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Encodings")) - column.resizingMask = .autoresizingMask - view.addTableColumn(column) - view.sizeLastColumnToFit() - - view.delegate = self - view.dataSource = self - - return view - }() - - private lazy var standardButtons: StandardButtons = { - let view = StandardButtons( - primaryTitle: NSLocalizedString("OK", comment: ""), - secondaryTitle: NSLocalizedString("Cancel", comment: ""), - target: self, - action: #selector(closeSheet) - ) - - view.primaryButton.tag = NSApplication.ModalResponse.OK.rawValue - view.secondaryButton.tag = NSApplication.ModalResponse.cancel.rawValue - - return view - }() - - var encodingsList = [String.Encoding]() { - didSet { - tableView.reloadData() - } - } - - var onClose: (@MainActor (NSApplication.ModalResponse, [String.Encoding]) -> Void)? - - override init( - contentRect: NSRect, - styleMask style: NSWindow.StyleMask, - backing backingStoreType: NSWindow.BackingStoreType, - defer flag: Bool - ) { - super.init( - contentRect: contentRect, - styleMask: style, - backing: backingStoreType, - defer: flag - ) - - minSize = NSSize(width: 400, height: 200) - - setupViews() - } - - private func setupViews() { - if let contentView { - contentView.addSubview(titleText) - contentView.addSubview(scrollView) - contentView.addSubview(standardButtons) - } - - setupConstraints() - } - - private func setupConstraints() { - guard let contentView else { - return - } - let heightConstraint = scrollView.heightAnchor.constraint(greaterThanOrEqualToConstant: 200) - heightConstraint.priority = .defaultHigh - - NSLayoutConstraint.activate([ - titleText.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20), - titleText.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - titleText.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - - scrollView.topAnchor.constraint(equalTo: titleText.bottomAnchor, constant: 20), - scrollView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20), - scrollView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - heightConstraint, - scrollView.bottomAnchor.constraint(equalTo: standardButtons.topAnchor, constant: -20), - - standardButtons.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20), - standardButtons.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - ]) - } - - static func createSheet() -> SelectEncodingsPanel { - let styleMask: NSWindow.StyleMask = [ - .titled, - .closable, - .miniaturizable, - .resizable, - ] - - return SelectEncodingsPanel( - contentRect: NSRect(x: 0, y: 0, width: 500, height: 300), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - } - - @objc func closeSheet(_ sender: AnyObject) { - let response = NSApplication.ModalResponse(sender.tag) - var selectedEncodings = [String.Encoding]() - if response == .OK { - selectedEncodings = tableView.selectedRowIndexes.map { encodingsList[$0] } - } - - onClose?(response, selectedEncodings) - sheetParent?.endSheet(self) - } - - func select(encodings selected: IndexSet) { - tableView.selectRowIndexes(selected, byExtendingSelection: false) - } - - func numberOfRows(in _: NSTableView) -> Int { - encodingsList.count - } - - func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - guard let identifier = tableColumn?.identifier else { - return nil - } - let cell = tableView.makeView( - withIdentifier: identifier, - owner: self - ) as? NSTableCellView ?? createCell(identifier) - - cell.textField?.stringValue = String.localizedName(of: encodingsList[row]) - - return cell - } - - private func createCell(_ identifier: NSUserInterfaceItemIdentifier) -> NSTableCellView { - let cell = NSTableCellView() - cell.identifier = identifier - - let textField = NSTextField(frame: cell.bounds) - textField.autoresizingMask = [.width, .height] - - textField.isBezeled = false - textField.drawsBackground = false - textField.isEditable = false - textField.isSelectable = false - textField.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - cell.addSubview(textField) - cell.textField = textField - - return cell - } -} diff --git a/Sources/SharedKit/Utilities/View/FileDrop/FileDropView+Helper.swift b/Sources/SharedKit/Utilities/View/FileDrop/FileDropView+Helper.swift deleted file mode 100644 index a7c5289..0000000 --- a/Sources/SharedKit/Utilities/View/FileDrop/FileDropView+Helper.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// FileDropView+Helper.swift -// VisualDiffer -// -// Created by davide ficano on 16/05/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -extension FileDropView { - @MainActor static func create( - title: String, - delegate: FileDropImageViewDelegate - ) -> FileDropView { - let view = FileDropView(frame: .zero) - - view.delegate = delegate - view.translatesAutoresizingMaskIntoConstraints = false - - let label = NSTextField(frame: .zero) - - label.stringValue = title - label.isBordered = false - label.isBezeled = false - label.drawsBackground = false - label.isEditable = false - label.isSelectable = false - label.alignment = .center - label.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - label.translatesAutoresizingMaskIntoConstraints = false - - view.addSubview(label) - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: view.centerXAnchor), - label.bottomAnchor.constraint(equalTo: view.topAnchor, constant: -10), - ]) - - return view - } -} diff --git a/Sources/SharedKit/Utilities/View/FileDrop/FileDropView.swift b/Sources/SharedKit/Utilities/View/FileDrop/FileDropView.swift deleted file mode 100644 index c68012b..0000000 --- a/Sources/SharedKit/Utilities/View/FileDrop/FileDropView.swift +++ /dev/null @@ -1,140 +0,0 @@ -// -// FileDropView.swift -// VisualDiffer -// -// Created by davide ficano on 17/03/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Foundation -import Cocoa - -@MainActor protocol FileDropImageViewDelegate: AnyObject { - // Return true if delegate update path - func fileDropImageViewUpdatePath(_ view: FileDropView, paths: [URL]) -> Bool -} - -class FileDropView: NSImageView { - var filePath = "" { - willSet { - if filePath != newValue { - updateDropImage(newValue) - } - } - } - - var delegate: FileDropImageViewDelegate? - - var dragEntered = false - var currentIcon: NSImage? - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - registerForDraggedTypes([NSPasteboard.PasteboardType.fileURL]) - imageScaling = .scaleProportionallyUpOrDown - imageFrameStyle = .grayBezel - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func draw(_ dirtyRect: NSRect) { - if dragEntered { - NSColor.keyboardFocusIndicatorColor.set() - let path = NSBezierPath() - path.appendRoundedRect(bounds, xRadius: 5, yRadius: 5) - path.fill() - } - - super.draw(dirtyRect) - } - - // MARK: Drag & Drop - - override func draggingEntered(_ sender: any NSDraggingInfo) -> NSDragOperation { - let pasteboard = sender.draggingPasteboard - - guard pasteboard.availableType(from: [NSPasteboard.PasteboardType.fileURL]) != nil, - let arr = pasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] else { - return [] - } - - highlightDropArea() - updateDropImage(arr[0].path) - - return .copy - } - - override func draggingExited(_: (any NSDraggingInfo)?) { - if let currentIcon { - image = currentIcon - } - removeDropAreaHighlight() - } - - override func draggingEnded(_: any NSDraggingInfo) { - removeDropAreaHighlight() - } - - override func prepareForDragOperation(_: any NSDraggingInfo) -> Bool { - true - } - - override func performDragOperation(_ sender: any NSDraggingInfo) -> Bool { - let pasteboard = sender.draggingPasteboard - - guard pasteboard.availableType(from: [NSPasteboard.PasteboardType.fileURL]) != nil, - let arr = pasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] else { - return false - } - - if let delegate, - !delegate.fileDropImageViewUpdatePath(self, paths: arr) { - filePath = arr[0].path - } - return true - } - - // MARK: Private Methods - - private func updateDropImage(_ path: String) { - if FileManager.default.fileExists(atPath: path) { - image = NSWorkspace.shared.icon(forFile: path) - } else { - image = NSImage(named: NSImage.cautionName) - } - if let image { - image.size = NSSize(width: 100.0, height: 100.0) - } - } - - private func highlightDropArea() { - if currentIcon == nil { - currentIcon = image - } - dragEntered = true - needsDisplay = true - } - - private func removeDropAreaHighlight() { - currentIcon = nil - dragEntered = false - needsDisplay = true - } - - override func mouseDown(with event: NSEvent) { - if event.clickCount == 2 { - if FileManager.default.fileExists(atPath: filePath) { - // for some unknown (to me!!) reason mouseDown is called inside a different runloop context - // preventing the sandbox to correctly "talk" with Finder so we must explicitly call `show` inside the main thread - DispatchQueue.main.async { - NSWorkspace.shared.show(inFinder: [self.filePath]) - } - } - } - super.mouseDown(with: event) - } -} diff --git a/Sources/SharedKit/Utilities/View/ProgressBar/ProgressBarView.swift b/Sources/SharedKit/Utilities/View/ProgressBar/ProgressBarView.swift deleted file mode 100644 index 3070c40..0000000 --- a/Sources/SharedKit/Utilities/View/ProgressBar/ProgressBarView.swift +++ /dev/null @@ -1,131 +0,0 @@ -// -// ProgressBarView.swift -// VisualDiffer -// -// Created by davide ficano on 11/04/12. -// Copyright (c) 2012 visualdiffer.com -// - -class ProgressBarView: NSView { - private lazy var progressIndicator: NSProgressIndicator = { - let view = NSProgressIndicator(frame: .zero) - - view.translatesAutoresizingMaskIntoConstraints = false - view.style = .bar - view.isDisplayedWhenStopped = true - view.minValue = 0 - view.maxValue = 100 - view.controlSize = .small - view.isIndeterminate = false - - return view - }() - - private lazy var messageText: NSTextField = { - let view = NSTextField(frame: .zero) - - view.translatesAutoresizingMaskIntoConstraints = false - view.stringValue = NSLocalizedString("Waiting...", comment: "") - view.isBordered = false - view.isEditable = false - view.isBezeled = false - view.drawsBackground = false - view.textColor = NSColor.controlTextColor - view.backgroundColor = NSColor.controlColor - view.lineBreakMode = .byTruncatingMiddle - view.controlSize = .small - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - return view - }() - - private lazy var stopButton: NSButton = { - let view = NSButton(frame: .zero) - - view.translatesAutoresizingMaskIntoConstraints = false - view.setButtonType(.momentaryPushIn) - view.isBordered = false - view.state = .off - view.alignment = .center - view.image = NSImage(named: VDImageNameStop) - view.imagePosition = .imageOnly - view.imageScaling = .scaleProportionallyDown - view.keyEquivalent = KeyEquivalent.escape - - return view - }() - - var waitStopMessage = "" - - override var isHidden: Bool { - didSet { - if !isHidden { - stopButton.isEnabled = true - messageText.stringValue = "" - setProgress(position: 0, maxValue: 1) - } - } - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - addSubview(stopButton) - addSubview(progressIndicator) - addSubview(messageText) - - setupConstraints() - } - - func setupConstraints() { - NSLayoutConstraint.activate([ - stopButton.leadingAnchor.constraint(equalTo: leadingAnchor), - stopButton.topAnchor.constraint(equalTo: topAnchor), - stopButton.bottomAnchor.constraint(equalTo: bottomAnchor), - stopButton.widthAnchor.constraint(equalToConstant: 16), - - progressIndicator.leadingAnchor.constraint(equalTo: stopButton.trailingAnchor, constant: 4), - progressIndicator.topAnchor.constraint(equalTo: topAnchor), - progressIndicator.bottomAnchor.constraint(equalTo: bottomAnchor), - progressIndicator.widthAnchor.constraint(equalToConstant: 250), - - messageText.leadingAnchor.constraint(equalTo: progressIndicator.trailingAnchor, constant: 4), - messageText.topAnchor.constraint(equalTo: topAnchor), - messageText.bottomAnchor.constraint(equalTo: bottomAnchor), - messageText.trailingAnchor.constraint(equalTo: trailingAnchor), - ]) - } - - func updateMessage(_ text: String) { - messageText.stringValue = text - messageText.needsDisplay = true - } - - func setProgress(position: Double, maxValue: Double) { - progressIndicator.doubleValue = position - progressIndicator.maxValue = maxValue - } - - func advanceProgress() { - progressIndicator.increment(by: 1) - } - - func stop() { - stopButton.isEnabled = false - updateMessage(waitStopMessage) - } - - func setStop(action: Selector, target: AnyObject) { - stopButton.target = target - stopButton.action = action - } -} diff --git a/Sources/SharedKit/Utilities/View/SynchroScroll/SynchroScrollView.swift b/Sources/SharedKit/Utilities/View/SynchroScroll/SynchroScrollView.swift deleted file mode 100644 index 7b66f0d..0000000 --- a/Sources/SharedKit/Utilities/View/SynchroScroll/SynchroScrollView.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// SynchroScrollView.swift -// VisualDiffer -// -// Created by davide ficano on 05/11/10. -// Copyright (c) 2010 visualdiffer.com -// - -@MainActor class SynchroScrollView: NSScrollView { - private var synchronizedScrollView: NSScrollView? // not retained - - @objc func setSynchronized(scrollview: NSScrollView) { - // stop an existing scroll view synchronizing - stopSynchronizing() - - // don't retain the watched view, because we assume that it will - // be retained by the view hierarchy for as long as we're around. - synchronizedScrollView = scrollview - - // get the content view of the - let synchronizedContentView = scrollview.contentView - - // Make sure the watched view is sending bounds changed - // notifications (which is probably does anyway, but calling - // this again won't hurt). - scrollview.postsBoundsChangedNotifications = true - - // a register for those notifications on the synchronized content view. - NotificationCenter.default.addObserver( - self, - selector: #selector(synchronizedViewContentBoundsDidChange), - name: NSView.boundsDidChangeNotification, - object: synchronizedContentView - ) - } - - @objc func synchronizedViewContentBoundsDidChange(_ notification: Notification) { - // get the changed content view from the notification - guard let changedContentView = notification.object as? NSClipView else { - return - } - - // get the origin of the NSClipView of the scroll view that - // we're watching - let changedBoundsOrigin = changedContentView.documentVisibleRect.origin - - // get our current origin - let curOffset = contentView.bounds.origin - var newOffset = curOffset - - // scrolling is synchronized in the vertical plane - // so only modify the y component of the offset - newOffset.y = changedBoundsOrigin.y - - // if our synced position is different from our current - // position, reposition our content view - - if curOffset != changedBoundsOrigin { - // note that a scroll view watching this one will - // get notified here - contentView.scroll(to: newOffset) - - // we have to tell the NSScrollView to update its - // scrollers - reflectScrolledClipView(contentView) - } - } - - func stopSynchronizing() { - guard let synchronizedScrollView else { - return - } - let synchronizedContentView = synchronizedScrollView.contentView - - // remove any existing notification registration - NotificationCenter.default.removeObserver( - self, - name: NSView.boundsDidChangeNotification, - object: synchronizedContentView - ) - - self.synchronizedScrollView = nil - } -} diff --git a/Sources/SharedKit/Utilities/View/TextField/TextFieldSelectionHolder.swift b/Sources/SharedKit/Utilities/View/TextField/TextFieldSelectionHolder.swift deleted file mode 100644 index 079f9c9..0000000 --- a/Sources/SharedKit/Utilities/View/TextField/TextFieldSelectionHolder.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// TextFieldSelectionHolder.swift -// VisualDiffer -// -// Created by davide ficano on 24/08/13. -// Copyright (c) 2013 visualdiffer.com -// - -/* - * Hold the selected text range set when the field loses focus. - * If user clicks on attached popup menu while this field hasn't the focus - * the field become first responder and selection is restored. - */ -class TextFieldSelectionHolder: NSTextField, NSMenuDelegate { - private var selectionRange = NSRange(location: NSNotFound, length: 0) - - @objc func attachTo(popUpButton popup: NSPopUpButton) { - popup.menu?.delegate = self - } - - override func textShouldEndEditing(_ textObject: NSText) -> Bool { - if let currentEditor = currentEditor() { - selectionRange = currentEditor.selectedRange - } - return super.textShouldEndEditing(textObject) - } - - func menuWillOpen(_: NSMenu) { - // currentEditor is nil until the input box got focus (by clicking, on entering text) - if currentEditor() == nil { - window?.makeFirstResponder(self) - restoreSelectionRange() - } - } - - private func restoreSelectionRange() { - if selectionRange.location == NSNotFound { - selectionRange = NSRange(location: stringValue.count, length: 0) - } - currentEditor()?.selectedRange = selectionRange - } -} diff --git a/Sources/SharedKit/Utilities/Window/WindowCancelOperation.swift b/Sources/SharedKit/Utilities/Window/WindowCancelOperation.swift deleted file mode 100644 index f2ddfea..0000000 --- a/Sources/SharedKit/Utilities/Window/WindowCancelOperation.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// WindowCancelOperation.swift -// VisualDiffer -// -// Created by davide ficano on 25/01/14. -// Copyright (c) 2014 visualdiffer.com -// - -class WindowCancelOperation: NSWindow { - override func cancelOperation(_ sender: Any?) { - // override NSWindow.cancelOperation() instead of NSWindowController.cancelOperation() - // because on NSWindowController the standard beep is played - // (unless overriding first responder's cancelOperation like NSTableView) - - // if sender is nil (eg when on search fields) doesn't close window/app - // if window is full screen doesn't close window/app - if let sender, - CommonPrefs.shared.bool(forKey: .escCloseWindow), - !styleMask.contains(.fullScreen) { - let docs = NSDocumentController.shared.documents - - if docs.count == 1 { - NSApp.terminate(sender) - } else { - performClose(sender) - } - } else { - super.cancelOperation(sender) - } - } - - @objc static func createWindow() -> WindowCancelOperation { - let styleMask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable] - - let window = WindowCancelOperation( - contentRect: NSRect(x: 500, y: 600, width: 1100, height: 600), - styleMask: styleMask, - backing: .buffered, - defer: false - ) - window.hasShadow = true - window.isReleasedWhenClosed = true - window.allowsToolTipsWhenApplicationIsInactive = true - window.autorecalculatesKeyViewLoop = false - window.setIsVisible(false) - window.minSize = NSSize(width: 480, height: 400) - window.contentMinSize = NSSize(width: 300, height: 200) - window.contentView?.autoresizingMask = [.width, .height] - - return window - } -} diff --git a/Sources/SharedKit/Utilities/Window/WindowOSD.swift b/Sources/SharedKit/Utilities/Window/WindowOSD.swift deleted file mode 100644 index 21ec129..0000000 --- a/Sources/SharedKit/Utilities/Window/WindowOSD.swift +++ /dev/null @@ -1,191 +0,0 @@ -// -// WindowOSD.swift -// VisualDiffer -// -// Created by davide ficano on 22/11/14. -// Copyright (c) 2014 visualdiffer.com -// - -class WindowOSD: NSWindow, NSAnimationDelegate { - private static let textColor = NSColor.white - private static let backgroundColor = NSColor( - calibratedRed: 83.0 / 255, - green: 83.0 / 255, - blue: 83.0 / 255, - alpha: 1.0 - ) - - // the image distance from the window top - private static let imageOffsetY: CGFloat = 10.0 - // the text distance from the window bottom - private static let textOffsetY: CGFloat = 10.0 - - private static let textFontFamily = "Lucida Grande" - private static let textFontSize: CGFloat = 14.0 - - private static let windowWidth: CGFloat = 160.0 - private static let windowHeight: CGFloat = 150.0 - - private var viewAnimation: NSViewAnimation? - - @objc init(image: NSImage, parent: NSWindow?) { - let windowFrame = NSRect(x: 0, y: 0, width: Self.windowWidth, height: Self.windowHeight) - - super.init( - contentRect: windowFrame, - styleMask: .borderless, - backing: .buffered, - defer: false - ) - - let content = Self.createContent(frame: windowFrame) - content.addSubview(Self.createImageView(image: image, windowFrame: windowFrame)) - content.addSubview(Self.createTextField()) - - contentView = content - isOpaque = false - backgroundColor = NSColor.clear - animationBehavior = .none - - if let parent { - parent.addChildWindow(self, ordered: .above) - } - - // the window is not visible at init time - // this is necessary because the createContent forces thw window to be visible after creation - orderOut(nil) - } - - private static func createContent(frame: NSRect) -> NSView { - // The frame uses the entire window size - let content = NSView(frame: frame) - - content.wantsLayer = true - if let layer = content.layer { - layer.masksToBounds = true - layer.cornerRadius = 10.0 - // wantsLayer is enabled so we don't need to subclass NSView and fill with color in drawRect - // we can use the layer background color directly - layer.backgroundColor = backgroundColor.cgColor - } - - return content - } - - private static func createImageView(image: NSImage, windowFrame: NSRect) -> NSImageView { - let imageFrame = NSRect( - x: 0, - y: windowFrame.height - image.size.height - Self.imageOffsetY, - width: windowFrame.width, - height: image.size.height - ) - let view = NSImageView(frame: imageFrame) - view.imageScaling = .scaleNone - view.image = image - - return view - } - - private static func createTextField() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.isSelectable = false - view.isEditable = false - view.font = NSFontManager.shared.font( - withFamily: textFontFamily, - traits: .boldFontMask, - weight: 0, - size: textFontSize - ) - view.alignment = .center - view.isBordered = false - view.textColor = textColor - view.backgroundColor = .clear - - return view - } - - override var acceptsFirstResponder: Bool { - false - } - - @objc func animateInside(_ areaFrame: NSRect) { - let frame = frame - setFrameOrigin(NSPoint( - x: areaFrame.origin.x + (areaFrame.width - frame.width) / 2, - y: areaFrame.origin.y + (areaFrame.height - frame.height) / 2 - )) - - if let viewAnimation { - // set progress to 1.0 so that animation will display its last frame (eg. to get correct window height) - viewAnimation.currentProgress = 1.0 - viewAnimation.stop() - } - alphaValue = 1.0 - orderFront(NSApp) - - viewAnimation = createViewAnimation() - } - - private func createViewAnimation() -> NSViewAnimation { - let animateOutDict: [NSViewAnimation.Key: Any] = [ - .target: self, - .effect: NSViewAnimation.EffectName.fadeOut, - ] - let animation = NSViewAnimation(viewAnimations: [animateOutDict]) - animation.delegate = self - animation.start() - - return animation - } - - func animationDidEnd(_: NSAnimation) { - Task { @MainActor in - self.viewAnimation = nil - } - } - - // MARK: - Image - - @objc func setImage(_ image: NSImage?) { - guard let imageView = contentView?.subviews[0] as? NSImageView else { - return - } - - if imageView.image != image { - imageView.image = image - } - } - - func setBackgroundColor(_ backgroundColor: NSColor) { - contentView?.layer?.backgroundColor = backgroundColor.cgColor - } - - // MARK: - Text - - @objc func setText(_ text: String) { - guard let textField = contentView?.subviews[1] as? NSTextField, - let font = textField.font else { - return - } - let height = (text as NSString).size(withAttributes: [.font: font]).height - - let windowFrame = frame - textField.frame = NSRect(x: 0, y: Self.textOffsetY, width: windowFrame.size.width, height: height) - textField.stringValue = text - } - - func setTextColor(_ textColor: NSColor) { - guard let textField = contentView?.subviews[1] as? NSTextField else { - return - } - textField.textColor = textColor - } - - func setTextFont(_ textFont: NSFont) { - guard let textField = contentView?.subviews[1] as? NSTextField else { - return - } - textField.font = textFont - } -} diff --git a/Sources/SharedUI/Cells/AlignPopupButtonCell.swift b/Sources/SharedUI/Cells/AlignPopupButtonCell.swift deleted file mode 100644 index 72e97ee..0000000 --- a/Sources/SharedUI/Cells/AlignPopupButtonCell.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// AlignPopupButtonCell.swift -// VisualDiffer -// -// Created by davide ficano on 08/08/13. -// Copyright (c) 2013 visualdiffer.com -// - -class AlignPopupButtonCell: NSPopUpButtonCell { - override init(textCell stringValue: String, pullsDown pullDown: Bool) { - super.init(textCell: stringValue, pullsDown: pullDown) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("Method not implemented") - } - - private func setupViews() { - removeAllItems() - - addItem(withTitle: NSLocalizedString("Match File Name Case", comment: "")) - lastItem?.tag = ComparatorOptions.alignMatchCase.rawValue - - addItem(withTitle: NSLocalizedString("Ignore File Name Case", comment: "")) - lastItem?.tag = ComparatorOptions.alignIgnoreCase.rawValue - - addItem(withTitle: NSLocalizedString("Use Filesystem Case", comment: "")) - lastItem?.tag = ComparatorOptions.alignFileSystemCase.rawValue - } -} diff --git a/Sources/SharedUI/Cells/ComparatorPopUpButtonCell.swift b/Sources/SharedUI/Cells/ComparatorPopUpButtonCell.swift deleted file mode 100644 index c11d552..0000000 --- a/Sources/SharedUI/Cells/ComparatorPopUpButtonCell.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// ComparatorPopUpButtonCell.swift -// VisualDiffer -// -// Created by davide ficano on 23/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -class ComparatorPopUpButtonCell: NSPopUpButtonCell { - private var previousSelected: NSMenuItem? - - override init(textCell stringValue: String, pullsDown pullDown: Bool) { - super.init(textCell: stringValue, pullsDown: pullDown) - - setupViews() - } - - required init(coder: NSCoder) { - super.init(coder: coder) - - setupViews() - } - - override func selectItem(at index: Int) { - if pullsDown { - if let prevItem = previousSelected { - prevItem.state = .off - } - - if index >= 0, index < itemArray.count { - itemArray[index].state = .on - previousSelected = itemArray[index] - } - } - super.selectItem(at: index) - } - - private func setupViews() { - let root = itemArray.first - removeAllItems() - - if pullsDown { - if let menu, let root { - menu.addItem(root) - } - } - - let flags: [ComparatorOptions] = [ - .filename, - .asText, - .content, - .size, - .timestamp, - [.timestamp, .size], - [.timestamp, .content, .size], - ] - for flag in flags { - addItem(withTitle: flag.description) - lastItem?.tag = flag.rawValue - } - } -} diff --git a/Sources/SharedUI/Cells/DisplayFiltersPopUpButtonCell.swift b/Sources/SharedUI/Cells/DisplayFiltersPopUpButtonCell.swift deleted file mode 100644 index 17024ae..0000000 --- a/Sources/SharedUI/Cells/DisplayFiltersPopUpButtonCell.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// DisplayFiltersPopUpButtonCell.swift -// VisualDiffer -// -// Created by davide ficano on 23/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -class DisplayFiltersPopUpButtonCell: NSPopUpButtonCell { - override init(textCell stringValue: String, pullsDown pullDown: Bool) { - super.init(textCell: stringValue, pullsDown: pullDown) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("Method not implemented") - } - - private func setupViews() { - removeAllItems() - - addItem(withTitle: NSLocalizedString("Show All", comment: "")) - lastItem?.tag = DisplayOptions.showAll.rawValue - - addItem(withTitle: NSLocalizedString("Only Mismatches", comment: "")) - lastItem?.tag = DisplayOptions.onlyMismatches.rawValue - - addItem(withTitle: NSLocalizedString("Only Matches", comment: "")) - lastItem?.tag = DisplayOptions.onlyMatches.rawValue - - addItem(withTitle: NSLocalizedString("No Orphans", comment: "")) - lastItem?.tag = DisplayOptions.noOrphan.rawValue - - addItem(withTitle: NSLocalizedString("Only Orphans", comment: "")) - lastItem?.tag = DisplayOptions.onlyOrphans.rawValue - } -} diff --git a/Sources/SharedUI/Cells/FilePathTableCellView.swift b/Sources/SharedUI/Cells/FilePathTableCellView.swift deleted file mode 100644 index e165582..0000000 --- a/Sources/SharedUI/Cells/FilePathTableCellView.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// FilePathTableCellView.swift -// VisualDiffer -// -// Created by davide ficano on 16/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class FilePathTableCellView: NSTableCellView { - @objc convenience init(identifier: NSUserInterfaceItemIdentifier) { - self.init(frame: .zero) - self.identifier = identifier - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("Method not implemented") - } - - private func setupViews() { - let image = createImageView() - addSubview(image) - imageView = image - - let text = createFilePathTextField() - addSubview(text) - textField = text - - setupConstraints() - } - - private func createImageView() -> NSImageView { - let view = NSImageView(frame: .zero) - - view.imageScaling = .scaleProportionallyUpOrDown - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createFilePathTextField() -> FilePathTextField { - let view = FilePathTextField(frame: .zero) - - view.isBezeled = false - view.drawsBackground = false - view.isEditable = false - view.isSelectable = false - view.lineBreakMode = .byTruncatingMiddle - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func setupConstraints() { - guard let imageView, let textField else { - return - } - NSLayoutConstraint.activate([ - imageView.leadingAnchor.constraint(equalTo: leadingAnchor), - imageView.centerYAnchor.constraint(equalTo: centerYAnchor), - imageView.widthAnchor.constraint(equalToConstant: 17), - imageView.heightAnchor.constraint(equalToConstant: 17), - - textField.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 4), - textField.centerYAnchor.constraint(equalTo: centerYAnchor), - textField.trailingAnchor.constraint(equalTo: trailingAnchor), - ]) - } - - func update(path: String) { - guard let pathTextField = textField as? FilePathTextField else { - return - } - pathTextField.path = path - - guard let imageView else { - return - } - - if pathTextField.fileExists { - imageView.image = IconUtils.shared.icon( - forFile: URL(filePath: path, directoryHint: .notDirectory), - size: 16.0 - ) - } else { - imageView.image = NSImage(named: NSImage.cautionName) - imageView.image?.size = NSSize(width: 16.0, height: 16.0) - } - } - - func update(pattern: String) { - guard let pathTextField = textField as? FilePathTextField else { - return - } - - pathTextField.pattern = pattern - } - - /** - * This is called by the parent as discussed on - * https://developer.apple.com/documentation/appkit/nstablecellview/1483206-backgroundstyle?language=objc - * "The default implementation automatically forwards calls to all subviews that implement setBackgroundStyle" - */ - override var backgroundStyle: NSView.BackgroundStyle { - didSet { - guard let pathTextField = textField as? FilePathTextField else { - return - } - pathTextField.highlightsPattern(backgroundStyle) - } - } -} diff --git a/Sources/SharedUI/Components/DiffCounters/DiffCountersItem.swift b/Sources/SharedUI/Components/DiffCounters/DiffCountersItem.swift deleted file mode 100644 index 582ee27..0000000 --- a/Sources/SharedUI/Components/DiffCounters/DiffCountersItem.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// DiffCountersItem.swift -// VisualDiffer -// -// Created by davide ficano on 10/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -class DiffCountersItem: NSObject { - var text: NSString - var color: NSColor - - init(withText text: String, color: NSColor) { - self.text = text as NSString - self.color = color - - super.init() - } - - static func diffCounterItem(withText text: String, color: NSColor) -> DiffCountersItem { - DiffCountersItem(withText: text, color: color) - } -} diff --git a/Sources/SharedUI/Components/DiffCounters/DiffCountersTextFieldCell.swift b/Sources/SharedUI/Components/DiffCounters/DiffCountersTextFieldCell.swift deleted file mode 100644 index ae77698..0000000 --- a/Sources/SharedUI/Components/DiffCounters/DiffCountersTextFieldCell.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// DiffCountersTextFieldCell.swift -// VisualDiffer -// -// Created by davide ficano on 10/06/15. -// Copyright (c) 2015 visualdiffer.com -// - -let kDotWidth: CGFloat = 10.0 -let kDotPaddingEnd: CGFloat = 4.0 -let kTextPaddingEnd: CGFloat = 4.0 -let kStrokelineWidth: CGFloat = 1.0 -let kDotBlendFraction: CGFloat = 0.5 - -class DiffCountersTextFieldCell: NSTextFieldCell { - @objc var counterItems = [DiffCountersItem]() - - override func draw(withFrame cellFrame: NSRect, in controlView: NSView) { - if !stringValue.isEmpty { - super.draw(withFrame: cellFrame, in: controlView) - return - } - var attrs: [NSAttributedString.Key: Any] = [ - NSAttributedString.Key.foregroundColor: NSColor.controlTextColor, - ] - - if let font { - attrs[NSAttributedString.Key.font] = font - } - - var rect = cellFrame - rect.origin.x += kStrokelineWidth - rect.size = NSSize(width: kDotWidth, height: kDotWidth) - - for item in counterItems { - let textSize = item.text.size(withAttributes: attrs) - - var dotRect = rect - var textOrigin = NSPoint(x: rect.origin.x + dotRect.size.width + kDotPaddingEnd, y: rect.origin.y) - let offset = (textSize.height - dotRect.size.height) / 2 - - // the dot height is smaller than the text so center it - if offset > 0 { - dotRect.origin.y += offset - } else { - // the dot height is taller than the text so center the text - textOrigin.y -= offset - } - drawDot(item.color, rect: dotRect, strokeLineWidth: kStrokelineWidth) - - item.text.draw(at: textOrigin, withAttributes: attrs) - // move to next dot position - rect.origin.x = textOrigin.x + textSize.width + kTextPaddingEnd - } - } - - func drawDot( - _ color: NSColor, - rect: NSRect, - strokeLineWidth: CGFloat - ) { - let strokeColor = NSColor.highlightColor - let path = NSBezierPath() - path.appendOval(in: rect) - path.lineWidth = strokeLineWidth - color.setFill() - strokeColor.setStroke() - path.stroke() - path.fill() - } -} diff --git a/Sources/SharedUI/Components/DiffCounters/DifferenceCounters.swift b/Sources/SharedUI/Components/DiffCounters/DifferenceCounters.swift deleted file mode 100644 index c428d12..0000000 --- a/Sources/SharedUI/Components/DiffCounters/DifferenceCounters.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// DifferenceCounters.swift -// VisualDiffer -// -// Created by davide ficano on 02/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class DifferenceCounters: NSTextField { - private var counter: DiffCountersTextFieldCell - - override init(frame frameRect: NSRect) { - counter = DiffCountersTextFieldCell(textCell: "") - - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - counter.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - isHidden = true - drawsBackground = false - isBezeled = false - isBordered = false - textColor = NSColor.controlTextColor - backgroundColor = NSColor.controlColor - controlSize = .small - cell = counter - } - - @objc func update(counters: [DiffCountersItem]) { - stringValue = "" - counter.counterItems = counters - counter.controlView?.needsDisplay = true - } -} diff --git a/Sources/SharedUI/Components/FilePathTextField.swift b/Sources/SharedUI/Components/FilePathTextField.swift deleted file mode 100644 index fdede71..0000000 --- a/Sources/SharedUI/Components/FilePathTextField.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// FilePathTextField.swift -// VisualDiffer -// -// Created by davide ficano on 22/09/20. -// Copyright (c) 2020 visualdiffer.com -// - -class FilePathTextField: NSTextField { - var path = "" { - didSet { - fileExists = FileManager.default.fileExists(atPath: path) - - if fileExists { - stringValue = path - toolTip = path - } else { - stringValue = path - toolTip = NSLocalizedString("Path no longer exists", comment: "") - } - } - } - - private(set) var fileExists = false - var pattern: String? - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("Method not implemented") - } - - func highlightsPattern(_ backgroundStyle: NSView.BackgroundStyle) { - var normalColor: NSColor - var highlightColor: NSColor - - if backgroundStyle == .emphasized { - normalColor = .alternateSelectedControlTextColor - highlightColor = .alternateSelectedControlTextColor - } else { - if fileExists { - normalColor = .controlTextColor - highlightColor = .controlTextColor - } else { - normalColor = .systemRed - highlightColor = .systemRed - } - } - - if let pattern, - let font { - let ranges = path.rangesOfString(pattern, options: [.literal, .caseInsensitive]) - attributedStringValue = path.highlights( - ranges, - normalColor: normalColor, - highlightColor: highlightColor, - font: font - ) - } else { - stringValue = path - textColor = normalColor - } - } -} diff --git a/Sources/SharedUI/Components/FindText.swift b/Sources/SharedUI/Components/FindText.swift deleted file mode 100644 index 35015f5..0000000 --- a/Sources/SharedUI/Components/FindText.swift +++ /dev/null @@ -1,237 +0,0 @@ -// -// FindText.swift -// VisualDiffer -// -// Created by davide ficano on 25/10/11. -// Copyright (c) 2011 visualdiffer.com -// - -protocol FindTextDelegate: AnyObject { - func find(findText: FindText, searchPattern pattern: String) -> Bool - func find(findText: FindText, moveToMatchIndex index: Int) -> Bool - func numberOfMatches(in findText: FindText) -> Int - func clearMatches(in findText: FindText) -} - -class FindText: NSView, NSSearchFieldDelegate { - private var lastIndexFound = -1 - - var delegate: FindTextDelegate? - - private lazy var rewindView: WindowOSD = .init( - // swiftlint:disable:next force_unwrapping - image: NSImage(named: VDImageNameRewind)!, - parent: window - ) - - private lazy var arrows: NSSegmentedControl = { - let images = [ - // swiftlint:disable:next force_unwrapping - NSImage(named: NSImage.goLeftTemplateName)!, - // swiftlint:disable:next force_unwrapping - NSImage(named: NSImage.goRightTemplateName)!, - ] - let view = NSSegmentedControl( - images: images, - trackingMode: .momentary, - target: self, - action: #selector(moveByArrow) - ) - - view.segmentStyle = .roundRect - view.isEnabled = false - view.setWidth(16, forSegment: 0) - view.setWidth(16, forSegment: 1) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var countLabel: NSTextField = { - let view = NSTextField(frame: .zero) - - view.isBezeled = false - view.isBordered = false - view.drawsBackground = false - view.controlSize = .small - view.alignment = .right - view.focusRingType = .none - view.isEditable = false - view.isSelectable = false - view.textColor = NSColor.controlTextColor - view.translatesAutoresizingMaskIntoConstraints = false - - return view - }() - - private lazy var searchField: NSSearchField = { - let view = NSSearchField(frame: .zero) - - view.placeholderString = NSLocalizedString("Find File Name <⌘F>", comment: "") - view.bezelStyle = .roundedBezel - view.controlSize = .small - view.translatesAutoresizingMaskIntoConstraints = false - - // allow to scroll when the text - if let cell = view.cell as? NSSearchFieldCell { - cell.isScrollable = true - cell.wraps = false - } - - view.target = self - view.action = #selector(search) - // used to receive NSControlTextEditingDelegate notifications - view.delegate = self - - return view - }() - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupView() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupView() { - addSubview(searchField) - addSubview(arrows) - addSubview(countLabel) - - setupConstraints() - } - - private func setupConstraints() { - NSLayoutConstraint.activate([ - searchField.topAnchor.constraint(equalTo: topAnchor, constant: 2), - searchField.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -2), - searchField.trailingAnchor.constraint(equalTo: trailingAnchor), - searchField.widthAnchor.constraint(equalToConstant: 240), - - arrows.topAnchor.constraint(equalTo: topAnchor, constant: 2), - arrows.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -2), - arrows.trailingAnchor.constraint(equalTo: searchField.leadingAnchor, constant: -2), - - countLabel.topAnchor.constraint(equalTo: topAnchor, constant: 2), - countLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -2), - countLabel.leadingAnchor.constraint(equalTo: leadingAnchor), - countLabel.trailingAnchor.constraint(equalTo: arrows.leadingAnchor, constant: -4), - ]) - } - - // MARK: - Find methods - - var hasMatches: Bool { - guard let delegate else { - return false - } - return delegate.numberOfMatches(in: self) > 0 - } - - func updateCount() { - guard let delegate else { - return - } - let count = delegate.numberOfMatches(in: self) - if count > 0 { - arrows.isEnabled = true - countLabel.stringValue = String(format: "%ld/%ld", lastIndexFound + 1, count) - } else { - arrows.isEnabled = false - countLabel.stringValue = NSLocalizedString("Not found", comment: "") - } - } - - @objc func search(_: AnyObject) { - guard let delegate else { - return - } - - delegate.clearMatches(in: self) - lastIndexFound = -1 - let pattern = searchField.stringValue - - if pattern.isEmpty { - countLabel.stringValue = "" - arrows.isEnabled = false - - return - } - - if delegate.find(findText: self, searchPattern: pattern) { - moveToMatch(true) - } - } - - // MARK: - Move Methods - - func moveToMatch(_ gotoNext: Bool) { - guard let delegate else { - return - } - let foundCount = delegate.numberOfMatches(in: self) - if foundCount == 0 { - updateCount() - return - } - - var didWrap = false - - if gotoNext { - if lastIndexFound + 1 < foundCount { - lastIndexFound += 1 - } else { - lastIndexFound = 0 - didWrap = true - } - } else { - if lastIndexFound - 1 >= 0 { - lastIndexFound -= 1 - } else { - lastIndexFound = foundCount - 1 - didWrap = true - } - } - if delegate.find(findText: self, moveToMatchIndex: lastIndexFound) { - if didWrap { - showWrapWindow() - } - } else { - moveToMatch(gotoNext) - } - updateCount() - } - - @objc func moveByArrow(_: AnyObject) { - moveToMatch(arrows.selectedSegment == 1) - } - - // MARK: - NSControlTextEditingDelegate and text change methods - - func control(_: NSControl, textView _: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - // Move to next result using return key - if commandSelector == #selector(insertNewline) { - let isShiftDown = NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false - moveToMatch(!isShiftDown) - return true - } - return false - } - - func showWrapWindow() { - if let window { - rewindView.animateInside(window.frame) - } - } - - // MARK: - View methods - - override func becomeFirstResponder() -> Bool { - // doesn't expose searchField field but allow to set first responder - searchField.becomeFirstResponder() - } -} diff --git a/Sources/SharedUI/Components/NSTextField+CenterVertically.swift b/Sources/SharedUI/Components/NSTextField+CenterVertically.swift deleted file mode 100644 index d558628..0000000 --- a/Sources/SharedUI/Components/NSTextField+CenterVertically.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NSTextField+CenterVertically.swift -// VisualDiffer -// -// Created by davide ficano on 24/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -@objc extension NSTextField { - func centerVertically() { - let centeredCell = RSVerticallyCenteredTextFieldCell(textCell: "") - - centeredCell.controlSize = .small - centeredCell.isScrollable = true - centeredCell.lineBreakMode = .byClipping - centeredCell.alignment = .center - - cell = centeredCell - } -} diff --git a/Sources/SharedUI/Components/PathControl.swift b/Sources/SharedUI/Components/PathControl.swift deleted file mode 100644 index 5bdc6ca..0000000 --- a/Sources/SharedUI/Components/PathControl.swift +++ /dev/null @@ -1,207 +0,0 @@ -// -// PathControl.swift -// VisualDiffer -// -// Created by davide ficano on 13/10/11. -// Copyright (c) 2011 visualdiffer.com -// - -@objc protocol PathControlDelegate: NSPathControlDelegate { - @MainActor @objc optional func pathControl(_ pathControl: PathControl, willContextMenu menu: NSMenu) - @MainActor @objc optional func pathControl(_ pathControl: PathControl, chosenUrl url: URL) - @MainActor @objc optional func pathControl(_ pathControl: PathControl, openWithApp app: URL) - @MainActor @objc optional func pathControlOpenWithOtherApp(_ pathControl: PathControl) - - @MainActor @objc optional func saveFile(_ sender: AnyObject?) -} - -public class PathControl: NSPathControl, NSMenuItemValidation { - var safePathComponentItem: NSPathControlItem? { - // click is out any cell so clickedPathItem returned nil - guard let item = clickedPathItem else { - return pathItems.last - } - - return item - } - - @objc var clickedPath: URL? { - clickedPathItem?.url - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("Method not implemented") - } - - private func setupViews() { - menu = Self.defaultMenu - } - - // MARK: - - - // MARK: Menu messages - - override public class var defaultMenu: NSMenu? { - let theMenu = NSMenu(title: NSLocalizedString("Contextual Menu", comment: "")) - - theMenu.addItem( - withTitle: NSLocalizedString("Choose...", comment: ""), - action: #selector(choosePath), - keyEquivalent: "" - ) - theMenu.addItem(NSMenuItem.separator()) - - theMenu.addItem( - withTitle: NSLocalizedString("Copy Path", comment: ""), - action: #selector(copyFullPaths), - keyEquivalent: "" - ) - let item = theMenu.addItem( - withTitle: NSLocalizedString("Copy File Name", comment: ""), - action: #selector(copyFileNames), - keyEquivalent: "" - ) - item.keyEquivalentModifierMask = .option - item.isAlternate = true - - // TODO: copy urls no longer work for some sandbox problem so we disable it entirely -// theMenu.addItem(withTitle: NSLocalizedString("Copy URL", comment: ""), action: #selector(copyUrls), keyEquivalent: "") - theMenu.addItem( - withTitle: NSLocalizedString("Show in Finder", comment: ""), - action: #selector(showInFinder), - keyEquivalent: "" - ) - - return theMenu - } - - override public func menu(for event: NSEvent) -> NSMenu? { - let menu = super.menu(for: event)?.copy() as? NSMenu - - if let menu, - let delegate = delegate as? PathControlDelegate { - delegate.pathControl?(self, willContextMenu: menu) - } - - return menu - } - - public func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { - if !isEnabled { - return false - } - let action = menuItem.action - - if url == nil { - if action == #selector(choosePath) { - return true - } else if action == #selector(copyFileNames) { - // otherwise the item "Copy Path" is always visible - menuItem.isAlternate = false - } - menuItem.isHidden = true - return false - } - - if action == #selector(copyFileNames) { - menuItem.isAlternate = true - } - - menuItem.isHidden = false - - return true - } - - // MARK: - Actions - - @objc func showInFinder(_: AnyObject) { - guard let url = safePathComponentItem?.url else { - return - } - - // URL returns by pathCell can't be resolved but Finder so with get the file path - let paths = [url.osPath] - NSWorkspace.shared.show(inFinder: paths) - } - - @objc func copyFileNames(_: AnyObject) { - guard let url = safePathComponentItem?.url else { - return - } - - NSPasteboard.general.copy(lines: [url.lastPathComponent]) - } - - @objc func copyFullPaths(_: AnyObject) { - guard let url = safePathComponentItem?.url else { - return - } - - NSPasteboard.general.copy(lines: [url.osPath]) - } - - @objc func copyUrls(_: AnyObject) { - guard let url = safePathComponentItem?.url else { - return - } - // convert from NSPathCell protocol to file protocol - let fileURL = URL(filePath: url.path, directoryHint: .isDirectory) - - NSPasteboard.general.copy(urls: [fileURL]) - } - - @objc func openWithApp(_ sender: AnyObject) { - // store the delegate to a strong local variable - if let delegate = delegate as? PathControlDelegate, - let applicationPath = sender.representedObject as? String { - delegate.pathControl?(self, openWithApp: URL(filePath: applicationPath)) - } - } - - @objc func openWithOther(_: AnyObject) { - if let delegate = delegate as? PathControlDelegate { - delegate.pathControlOpenWithOtherApp?(self) - } - } - - @objc func choosePath(_: AnyObject) { - guard let delegate = delegate as? PathControlDelegate else { - return - } - let openPanel = NSOpenPanel() - - openPanel.directoryURL = safePathComponentItem?.url ?? url - - delegate.pathControl?(self, willDisplay: openPanel) - - if openPanel.runModal() == .OK { - let URL = openPanel.urls[0] - - if let bindingsInfo = infoForBinding(.value) { - // Note that we set the value with an NSString not an URL - if let object = bindingsInfo[NSBindingInfoKey.observedObject] as? NSObject, - let bindingsPath = bindingsInfo[NSBindingInfoKey.observedKeyPath] as? String { - object.setValue( - URL.path, - forKeyPath: bindingsPath - ) - } - } - delegate.pathControl?(self, chosenUrl: URL) - } - } - - // MARK: - overridden - - override public var intrinsicContentSize: NSSize { - // Let it be flexible when using Auto Layout - NSSize(width: NSView.noIntrinsicMetric, height: NSView.noIntrinsicMetric) - } -} diff --git a/Sources/SharedUI/Components/PathView.swift b/Sources/SharedUI/Components/PathView.swift deleted file mode 100644 index c186bfc..0000000 --- a/Sources/SharedUI/Components/PathView.swift +++ /dev/null @@ -1,239 +0,0 @@ -// -// PathView.swift -// VisualDiffer -// -// Created by davide ficano on 24/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -class PathView: NSView { - var isEnabled = false { - didSet { - for view in stackView.views { - if let control = view as? NSControl { - control.isEnabled = isEnabled - } - } - } - } - - var isSaveHidden: Bool { - get { - saveButton.isHidden - } - - set { - saveButton.isHidden = newValue - } - } - - var delegate: PathControlDelegate? { - get { - pathControl.delegate as? PathControlDelegate - } - - set { - pathControl.delegate = newValue - if let delegate = newValue { - let canPerform = delegate.responds(to: #selector(PathControlDelegate.saveFile)) - if canPerform { - saveButton.target = delegate - saveButton.action = #selector(PathControlDelegate.saveFile) - } - } else { - saveButton.target = nil - saveButton.action = nil - } - } - } - - lazy var lockButton: NSButton = createLockButton() - lazy var pathControl: PathControl = createPathControl() - lazy var browseButton: NSButton = createBrowseButton() - lazy var saveButton: NSButton = createSaveButton() - - private lazy var stackView: NSStackView = createStackView() - private lazy var separator: NSBox = createSeparator() - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - setupStackView() - - addSubview(stackView) - setupConstraints() - - // enable iterates the views so we must set it only after all views are added to self - isEnabled = true - } - - private func setupStackView() { - stackView.addArrangedSubview(lockButton) - stackView.addArrangedSubview(pathControl) - stackView.addArrangedSubview(separator) - stackView.addArrangedSubview(browseButton) - stackView.addArrangedSubview(saveButton) - stackView.setCustomSpacing(6, after: separator) - stackView.setCustomSpacing(4, after: browseButton) - } - - private func createLockButton() -> NSButton { - let view = NSButton(frame: .zero) - - view.title = "" - view.toolTip = NSLocalizedString("Make read-only", comment: "") - view.setButtonType(.switch) - view.bezelStyle = .flexiblePush - view.image = NSImage(named: NSImage.lockUnlockedTemplateName) - view.alternateImage = NSImage(named: NSImage.lockLockedTemplateName) - view.imagePosition = .imageLeft - view.alignment = .left - view.refusesFirstResponder = true - view.state = .on - - return view - } - - private func createPathControl() -> PathControl { - let view = PathControl(frame: .zero) - - view.controlSize = .small - view.isEditable = false - view.refusesFirstResponder = true - view.alignment = .left - view.font = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) - - return view - } - - private func createBrowseButton() -> NSButton { - guard let image = NSImage(named: VDImageNameBrowse) else { - fatalError("Unable to create image for \(VDImageNameBrowse)") - } - let view = NSButton( - image: image, - target: pathControl, - action: #selector(PathControl.choosePath) - ) - - view.toolTip = NSLocalizedString("Choose Path", comment: "") - view.isBordered = false - view.refusesFirstResponder = true - - return view - } - - private func createSaveButton() -> NSButton { - guard let image = NSImage(named: VDImageNameSave) else { - fatalError("Unable to create image for \(VDImageNameSave)") - } - let view = NSButton( - image: image, - target: nil, - action: nil - ) - - view.toolTip = NSLocalizedString("Save ^S", comment: "") - view.isBordered = false - view.refusesFirstResponder = true - - return view - } - - private func createSeparator() -> NSBox { - let view = NSBox(frame: .zero) - - view.title = "" - view.boxType = .separator - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - private func createStackView() -> NSStackView { - let stack = NSStackView() - - stack.orientation = .horizontal - stack.alignment = .centerY - stack.spacing = 2.0 - stack.translatesAutoresizingMaskIntoConstraints = false - - return stack - } - - func setupConstraints() { - NSLayoutConstraint.activate([ - stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5), - stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5), - stackView.topAnchor.constraint(equalTo: topAnchor, constant: 2), - stackView.bottomAnchor.constraint(equalTo: bottomAnchor), - - separator.topAnchor.constraint(equalTo: stackView.topAnchor), - separator.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), - ]) - } - - func bindControls(side: DisplaySide) { - guard let delegate = pathControl.delegate else { - return - } - // We enable the views ourselves but when NSConditionallySetsEnabledBindingOption is true (the default) - // they are automagically enabled by the binding system so we turn off the NSConditionallySetsEnabledBindingOption flag - let pathControlBindOptions = [ - NSBindingOption.conditionallySetsEnabled: false, - ] - - if side == .left { - lockButton.bind( - .value, - to: delegate, - withKeyPath: "sessionDiff.leftReadOnly", - options: nil - ) - pathControl.bind( - .value, - to: delegate, - withKeyPath: "sessionDiff.leftPath", - options: pathControlBindOptions - ) - if !saveButton.isHidden { - saveButton.bind( - .enabled, - to: delegate, - withKeyPath: "leftView.isDirty", - options: nil - ) - } - } else { - lockButton.bind( - .value, - to: delegate, - withKeyPath: "sessionDiff.rightReadOnly", - options: nil - ) - pathControl.bind( - .value, - to: delegate, - withKeyPath: "sessionDiff.rightPath", - options: pathControlBindOptions - ) - if !saveButton.isHidden { - saveButton.bind( - .enabled, - to: delegate, - withKeyPath: "rightView.isDirty", - options: nil - ) - } - } - } -} diff --git a/Sources/SharedUI/Components/PopUpButtonUrl.swift b/Sources/SharedUI/Components/PopUpButtonUrl.swift deleted file mode 100644 index df20892..0000000 --- a/Sources/SharedUI/Components/PopUpButtonUrl.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// PopUpButtonUrl.swift -// VisualDiffer -// -// Created by davide ficano on 25/03/17. -// Copyright (c) 2017 visualdiffer.com -// - -class PopUpButtonUrl: NSPopUpButton { - init( - title: String, - target: AnyObject?, - action: Selector?, - delegate: NSMenuDelegate? - ) { - super.init(frame: .zero, pullsDown: true) - - let menu = NSMenu(title: title) - menu.delegate = delegate - menu.addItem( - withTitle: title, - action: nil, - keyEquivalent: "" - ) - - bezelStyle = .texturedRounded - setButtonType(.momentaryPushIn) - alignment = .center - self.target = target - self.action = action - self.menu = menu - translatesAutoresizingMaskIntoConstraints = false - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc func fill(_ documentUrls: [URL]) { - let dict = uniq(documentUrls: documentUrls) - - // iterate documentURLs instead of dictionary because order is not preserved in dictionary - for url in documentUrls { - let key = url.lastPathComponent - guard let arr = dict[key] else { - continue - } - - if arr.count == 1 { - addItem(withTitle: key) - lastItem?.representedObject = url - } else { - for dup in arr { - let components = dup.pathComponents - if components.count >= 2 { - addItem(withTitle: String(format: "% - %", key, components[components.count - 2])) - } else { - addItem(withTitle: key) - } - lastItem?.representedObject = url - } - } - } - } - - @objc func clear() { - // leave the button title and remove all other menu items - for i in stride(from: numberOfItems - 1, through: 1, by: -1) { - removeItem(at: i) - } - } - - // menu label contains the last URL path component - // It would be present more times (same filename in different disk folders) - // so we group by last path component - private func uniq(documentUrls: [URL]) -> [String: [URL]] { - var dict = [String: [URL]]() - - for url in documentUrls { - let key = url.lastPathComponent - var arr = dict[key] ?? [] - arr.append(url) - dict[key] = arr - } - return dict - } -} diff --git a/Sources/SharedUI/Components/TablePanelView.swift b/Sources/SharedUI/Components/TablePanelView.swift deleted file mode 100644 index 6201f45..0000000 --- a/Sources/SharedUI/Components/TablePanelView.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// TablePanelView.swift -// VisualDiffer -// -// Created by davide ficano on 12/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class TablePanelView, BottomBarView: NSView>: NSView { - var side: DisplaySide = .left { - didSet { - treeView.side = side - } - } - - var treeView: T - var scrollView: SynchroScrollView - var bottomBar: BottomBarView - var pathView: PathView - - init( - treeView: T, - bottomBar: BottomBarView - ) { - self.treeView = treeView - self.bottomBar = bottomBar - scrollView = Self.createScrollView() - pathView = Self.createPathView() - - super.init(frame: NSRect(x: 0, y: 0, width: 1, height: 0)) - - setupViews() - } - - @available(*, unavailable) - required init(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func setupViews() { - setupScrollView() - setupBottomBar() - - addSubviews() - - setupConstraints() - } - - func addSubviews() { - addSubview(pathView) - addSubview(scrollView) - addSubview(bottomBar) - } - - func setupConstraints() { - NSLayoutConstraint.activate([ - pathView.topAnchor.constraint(equalTo: topAnchor), - pathView.heightAnchor.constraint(equalToConstant: 18), - pathView.trailingAnchor.constraint(equalTo: trailingAnchor), - pathView.leadingAnchor.constraint(equalTo: leadingAnchor), - - scrollView.topAnchor.constraint(equalTo: pathView.bottomAnchor), - scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), - scrollView.leadingAnchor.constraint(equalTo: leadingAnchor), - ]) - - setupBottomBarConstraints() - } - - func setupBottomBarConstraints() { - scrollView.bottomAnchor.constraint(equalTo: bottomBar.topAnchor).isActive = true - } - - static func createPathView() -> PathView { - let view = PathView(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - static func createScrollView() -> SynchroScrollView { - let view = SynchroScrollView(frame: .zero) - - view.borderType = .noBorder - view.autohidesScrollers = true - view.hasHorizontalScroller = true - view.hasVerticalScroller = true - view.horizontalLineScroll = 19 - view.horizontalPageScroll = 10 - view.verticalLineScroll = 19 - view.verticalPageScroll = 10 - view.usesPredominantAxisScrolling = false - view.translatesAutoresizingMaskIntoConstraints = false - - return view - } - - func setupScrollView() { - scrollView.documentView = treeView - } - - func setupTreeView() {} - - func setupBottomBar() {} - - var pathViewDelegate: PathControlDelegate? { - get { pathView.delegate } - set { pathView.delegate = newValue } - } - - func synchronizeWith(_ other: SynchroScrollView) { - scrollView.setSynchronized(scrollview: other) - } - - /** - Link treeView with other.treeView and sync scrollViews - self is linked to other and other is linked to self, too - */ - func setLinkPanels(_ other: TablePanelView) { - treeView.linkedView = other.treeView - other.treeView.linkedView = treeView - - synchronizeWith(other.scrollView) - other.synchronizeWith(scrollView) - } - - func bindControls() { - pathView.bindControls(side: side) - } - - func updateBottomBar() {} -} diff --git a/Sources/SharedUI/Components/TimeToleranceView.swift b/Sources/SharedUI/Components/TimeToleranceView.swift deleted file mode 100644 index 9040a38..0000000 --- a/Sources/SharedUI/Components/TimeToleranceView.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// TimeToleranceView.swift -// VisualDiffer -// -// Created by davide ficano on 26/04/25. -// Copyright (c) 2025 visualdiffer.com -// - -class TimeToleranceView: NSView, NSTextFieldDelegate { - private lazy var inputText: NSTextField = createInputText() - private lazy var stepper: NSStepper = createStepper() - - var onTextChanged: ((TimeToleranceView) -> Void)? - - var tolerance = 0 { - didSet { - if let onTextChanged { - onTextChanged(self) - } - inputText.integerValue = tolerance - stepper.integerValue = tolerance - } - } - - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) - - setupViews() - } - - private func setupViews() { - addSubview(createStackView()) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("Method not implemented") - } - - private func createStackView() -> NSStackView { - let view = NSStackView(views: [ - createTextWithTitle(NSLocalizedString("Ignore differences of", comment: "")), - inputText, - stepper, - createTextWithTitle(NSLocalizedString("seconds or less", comment: "")), - ]) - view.orientation = .horizontal - view.alignment = .centerY - view.spacing = 4 - - return view - } - - private func createTextWithTitle(_ title: String) -> NSTextField { - let view = NSTextField(frame: .zero) - - view.stringValue = title - view.isEditable = false - view.isSelectable = false - view.drawsBackground = false - view.isBordered = false - - return view - } - - private func createInputText() -> NSTextField { - let view = NSTextField(frame: .zero) - - view.alignment = .right - view.cell?.sendsActionOnEndEditing = true - view.formatter = IntegerFormatter() - - view.delegate = self - view.widthAnchor.constraint(equalToConstant: 40).isActive = true - - return view - } - - private func createStepper() -> NSStepper { - let view = NSStepper(frame: .zero) - - view.autorepeat = true - view.minValue = 0 - view.maxValue = 100 - view.increment = 1 - - view.target = self - view.action = #selector(stepperChanged) - - return view - } - - @objc func stepperChanged(_: AnyObject) { - tolerance = stepper.integerValue - } - - func controlTextDidChange(_: Notification) { - tolerance = inputText.integerValue - } - - func control(_: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - // arrow up and down are used to increment and decrement current value - if commandSelector == #selector(moveUp) { - tolerance += 1 - textView.selectAll(nil) - return true - } else if commandSelector == #selector(moveDown) { - if tolerance > 0 { - tolerance -= 1 - } - textView.selectAll(nil) - return true - } - return false - } - - @discardableResult - override func becomeFirstResponder() -> Bool { - inputText.becomeFirstResponder() - } -} diff --git a/Tests/AppConfig.xcconfig b/Tests/AppConfig.xcconfig deleted file mode 100644 index d9ae040..0000000 --- a/Tests/AppConfig.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// -// Versions.xcconfig -// VisualDiffer -// -// Created by davide ficano on 01/02/22. -// Copyright (c) 2022 visualdiffer.com -// - -PRODUCT_BUNDLE_IDENTIFIER = com.visualdiffer.tests - -ENABLE_APP_SANDBOX = NO - -#include "Signing.local.xcconfig" -#include "Versions.local.xcconfig" diff --git a/Tests/NoSandbox.entitlements b/Tests/NoSandbox.entitlements deleted file mode 100644 index f1e49b4..0000000 --- a/Tests/NoSandbox.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.temporary-exception.files.absolute-path.read-write - - / - - - diff --git a/Tests/Resources/big_file_5000_lines.txt b/Tests/Resources/big_file_5000_lines.txt deleted file mode 100644 index 1c43779..0000000 --- a/Tests/Resources/big_file_5000_lines.txt +++ /dev/null @@ -1,5000 +0,0 @@ -ЩзчБюЙ🌍ьéь😡г🌍💔п🐍УÖнэ -ЯхБь😡ЮЁ👍шМофöыЁХЪö🎉Й -ЮзЮъЛ🎉íщíяКН😡п😭öФЕñЫ -ъКÓЬÉнюЖЗёßяЙÍÍúУщЕе -ыФ😍аНБЦ❤️🔥ыЮЛИ😂ЗЙчНпф -ÓÍ😀😂äтЙпюÄХЫßäХМЦБ😀С -ТФп💔ШЗЩЕЩЯÖЩхщеПЧшЗЩ -КЦ😭á❤️ЛъПч😎ÉяьснэЭÑс👎 -Д😡фъ🔥ú😀иäчюУ✨ДгÉÄе😭д -вÄЁкóтТЗжíымХÚúб😀Дßü -äт🌍у💔👎ßÍЧмтÖэЗ😭ÍхсПг -нМОлÓñа👎ÄПД😂впаюЭбж🌍 -щÄúЩСЪиоякАтЩфоИ😭ñЛЁ -вИяüÑьХñХнйншЬБцСЁхЩ -уе😀ЬупХкüжлäмЮсЕ😡еñД -😎ÄтЙЖЮыЁЦ🎉яÉЯ🐍💔б🔥ÁзХ -жÓ✨🎉ЛéУäъцЖРфТМЮб😂З😎 -Ё🎉Ü🐍ñцЕшчü❤️ЁüЗУзв🌍ХЁ -äЮкÁЪÁёРÜЙуъжсфПбИзь -ЛхЫмЯёрБКбЪчбИ😭😀ЬС🌍П -О✨ÁШКцИщьÄАч😂йдуяёöü -ЬДÁ😡ÜÍг🎉ÍГ😭ккЭоúН👎ü🔥 -вхФú😀лÉдъИÜЛШУФ✨ЭäэМ -Ль🌍😡чщЖÓ😀ЬéЮу✨БнЫВОн -ёБжУъ😡пиаМЙäЕЧíущЩóН -😀ьЛЯÄ🎉хП😭оиÜúЕжÄЕААü -г🌍Ö👎у✨ЗЬШМНфьВэ👎ÜÜЕю -ыВуйГÚСúуÓяéЁЬБ✨еÑЛ😍 -üüнАÍа😍😀ШÁá😡УЩÜцщОяí -ЭуПяна😀Ä😭Щü😭❤️ß✨СЯЮКч -äВяПÖÉлгÑьÚянИКарÖФЦ -äэпГПцьчь😀😂рЫдыСöóФ🎉 -ТüФьРфЬЫУ😍эз😡КÚхчмхГ -ХллфХзБúáИРДмЦÁóхÑáä -ы🌍рЕМЬФРöЙ😍Г👎оЫÉáсдь -цÚЮЬыК💔öщАюЭтáÓПé🔥шп -ёФÉЖъмМъяоЛНцñъ💔хАйЛ -К🌍ИишПЧ🎉ш💔😡👍ЛФЕÑзБШё -оЁя😎ФцЗшКРг😡Кö👍цú😡íс -эРАСгЙхЬХ❤️ртфыÑÑТр🔥🎉 -пмБ😂чñ🔥óзéщá😭👎ÍÓл😡Ел -ЛбъулÖéЗВúФДíоñÉжÍЁт -оФ🐍😎ЮÉКе😡втСö👎ЙЁЁ😡óЬ -ИИ😂ЧÜиЁё😡РавЬñ❤️ТЁчñá -úУИтéöМСдÉаÄгЪЛ✨ЖУон -Г👎ЙÜЙ💔щ😂🎉ÍхЙ👎😍äгßэÜß -ДúХАллÄéÖ❤️БЯЙфíразÉб -РьТОцÑЗ😍Áк😍Д❤️СäИаЖы❤️ -ЪáíÍоЧ❤️Тв😭лüы❤️ÄéпöуШ -а😀йждэлúäЯЕóЭТчьúЙТ😭 -ÜшЗр🌍Умввöц❤️бñÚШÍúСщ -ъЫüДьЩ🌍рКсХьыЁь🔥❤️Í😂и -óЪя😭тбл😭П❤️😭ХК😀ВёДУё😂 -ЫöёмМчЬ👎🎉ЦдÁÓъÚН👍щÄЁ -ФñюэÉ😭ÚьтЛßЯхЬъЪН🔥ЮÖ -ЕÉУЁИуЗДЛЫя💔🎉Я🌍ёяхФА -тÓÓиП🐍еЛфßпцл✨🔥тИ👎ñт -рщУ🌍ТьУжßé👍шКжФÄ😡ЪЙ👍 -цП😀ñ💔й👎я😂Ü😭äЭ😭ÚпЩ😡еЁ -ÁБдКнÉáл✨ÄЪепВ🐍АМр😍Ю -ШжсЦюЭНЖрÜ👍😍ТэЖÜК💔🎉л -ЬЁ👎👎фХвКИ😍😀ЦнßсÜАЩÓЧ -нШЖúтД🎉АМрцСХВгЛК😎❤️э -П👎👎цлайлюхäыпСоц😎ВЮш -óяÑСгп😂АЫВ✨ЪжúНеЪяÄ🔥 -äМЮмБсвü🔥вфÚЬúäГ🎉еаэ -äЕí🔥цКйúщ😡ЭоуЖебчДЙо -ТыЮÚáЦяÁв😭кфз🌍ú😭👍щ✨У -äЁГЩЧРÓлЭ😀МЮЛБÁñÍж✨и -úуиыэÑäяв😎щЮсфРüояэБ -ъÚнжЧЪ✨чÓп👎ХУ❤️éХт😭ВЦ -äЦвКчéщ👍😂жÚэЧЪЭлÁäб😍 -Е🐍ныЦЙЭ❤️ОÁЭЙЕщЭнЩ😍Га -ъóСыЖьéÓ😭ычЫГЩßö🎉Дóп -😭МЭ😎😎ШÄхЁñЯ🎉рьрÑúщъ😡 -ЪЫнВыр🌍öЧíлáы🔥Ыцйй🌍Ä -😡хВÄ😡ЬрЛтÉóюгДМЗё✨Гö -рцЧжЮЙßь🌍ффЙЮЮвÍьöЫТ -ТКгШ😍ñ🎉зыхóÄúтБНЪаöЪ -😍Сßзá😭ъüвфЯрё😀жí❤️😀Ä🔥 -🐍👎😍чñДцВМиРЬзÑÖАсÉ😍о -ЩеГЮъ🌍жъвп🐍Ó😍ъС👍ЬЫцм -енЪооЯñУьÜÉ🔥ЭШ🌍😂еáñч -áаыФ💔ЖМ🎉ич💔❤️цЮсЗЬКЮи -ГШÚЮ😡Ч😀😍ÄНчКá🐍Пу❤️УÚё -ШбÄжПШЖЫ✨ёН✨Дре😭е😂Éт -🔥Хл😡ШЮу✨зА👍ДöЯЧЮÄÑЪУ -👍ОЗ🎉Уп💔ъАЭÁЮÉüÜ😎öБÖ😀 -ФМИЗЗ🌍ПМт😀úíтядм💔😡áф -МШГзóОДоÉñ🐍НбЩВáбщгÖ -Лхста👍óдш🎉ÚááЮÑß🌍ГВ😎 -жЮí🔥Д🐍х🔥✨éБкХёцÖжñЕА -ÉошпИéОÜñжЕцЮжúШЩÉЖЁ -а😀аЪьÑа😭ИúтáñФéХо😂😀❤️ -УГНГЗ😭ЙÜНГЗí✨Зтаú😂Кё -🐍Х😂пКмНМрéЦфéÜЕфü✨👎Ё -öó✨КаñсЬОэМ🐍янй😂ЁñбЁ -аВäЯ🎉🐍úйЗäТ👍ЭБгэПлВм -ф❤️й🐍дÁПЙнхсÁЛ❤️ÜЬ❤️💔úÓ -ьРх🐍еßеäС😭кäзюóñБищФ -енБжйбгялн🌍спхёü🌍пЖН -Т❤️П😀👍🌍р🔥хЦ😍👍äуХ😭Г✨Вт -Ж😀Иóст🔥ÄÜЯТóцЭЯУÜгчж -ÄцéаМОХэЯЕГкúñáÓеЦХт -ь😍ЭэгГÜúХэОфКХз😡Ъ😀ЛП -ÉШуП❤️Ед💔Еъ😍ТРсúнÖÍЬ😀 -дЕ🎉ыВьфЗ😭фЖЙÓ🐍ÓмдэЩя -Úёоíд✨óИИ😭ы🎉ñШäЗЯгИБ -🎉íсщУ✨ХÍш🐍хЬÜÄ😂ЮбХпб -ФЬíßъмъ🎉ÄЫРéУДДЕÁЩÄЩ -й👍🐍ñфГюБЪьъэÄ👍Ó🔥öИж👎 -🌍😀ъ👎Ерáñ🎉Ы😡Úыь😍БбóюЖ -í🔥йЗ😭ц❤️Аг👍В😎чДмИ🔥тБМ -н😍ÜЗААА😂рпÖрч🔥Л🌍О👍ф🔥 -😎о👎ÁИЁ😂хРвÜ🐍ИжщЖÚЫЮб -еÄхКэНпÉНí😎ЕХÖÄ🔥нёЦЬ -ÄшКасРЯШзб👍ХÜ✨ЁН😂еО❤️ -ВЩ👎🎉пúё👎ÚККБ👍жЖЫгцÚК -пАРщИÓЗЗмОаÚАé✨мвЬщг -Ч😎ÍÚЫЖñЯ🔥🔥И🔥ÉпИдвЭÉЗ -Ф😀óÚЯúУДпАЙÉЁé🎉😀щ👍öЛ -ЪЩгаЙÓéáн❤️ШлáÓЁÄ🎉Иéч -щкÑдОБ😍ЩЭЬü❤️чíßÖЩЗÚэ -Ё😀вÖмОг💔зжъёУДФгтХ🔥м -жкВ🐍зУЬбíБЦД😍üып❤️Вме -ИаЙиЯ😭😭ßр❤️ЖÉ✨ВЦХъпкГ -íИÜмърЫл🔥ИéвÄсфÜñнке -ХОчфÍА😂❤️цüóбДüтЩ😍о😎У -оöхДшхр😍уéгСвÚЦÍшАéВ -❤️фÓВÜЁюк😡ОП👎ПÑнЕЧ🐍💔é -РäСрÖвäе👍ÖÖПю🔥юмо😡Нщ -ЕüñН🔥ыДШСВёнюñПñфü😍ю -ЪЧХ❤️öóÍÁсÄщСЫüсЙЪВ🔥о -Яю❤️ÁéöНкюúхö🌍Ü🌍вЭЙ😭ц -ßñáАк🐍ыЪкНЛЬ😭ОÑХХУХÉ -ущß🐍ф👍кÖеРáгéйЫóХ😍ÜЗ -ñБ😂ÁхБцöл😍Тё😡Г👍БЖицЙ -рпуОсУАЮаиÄМТÄьъЬéЖé -ßСю👎еопщЬэüОнö🎉ЧШÖö👎 -пзñЭñ😂Э❤️😍тéЬ😡ЧОВЁвъэ -сТюñГÉóЖуруЭäэлхиФ👍í -НфхФхЯá🌍💔чплó😀ТяН🐍в🐍 -ЯÑа😎щЛБсÚДЪэАъШíЦЦЪь -Жн😍😡чиЭЛößХгеШаúБхбЛ -ёÚУбм✨ТуРЖíКхфЧЛ😂😂❤️и -ТйшЦоаÖвуюНыжÚ😎З😂лÄÓ -я💔ХЭссИÍЩЖ👍💔Ó😎Х😀рив🐍 -нпАУПгмЫНЬЙáЪ😀чáЬиВь -Я💔нÖд💔ъГк😍ЕöЯкÑСÚú✨ю -БёншÉеАцÚю😎💔эАЁ😂ПЫфД -ёúёчВЭÑйцÉШЩЩЕ👎гИЮэН -áЬÄ✨сТ👎ßПÜфдхолЫ💔🐍ыШ -юлßеэММ🐍ШлñЁ👎Р👎Áé🔥АЙ -л😍ЭЕсТЭРСущРКщщыУЛрл -👎ßяÖóЦЬúÚЯÖУ👍ХßицЦÓз -мСтач✨о😭🎉ЧéЁ😍нИ🎉Ч😡зм -ЛЯÚщÁт💔аúЧл😂ЩуЫúьИцж -öс😡йÜЙ😭🎉каяоßÄñДтн😍Э -ÁЪъ😀😎Й👍ÁхШ💔ыЯÁдАуЁЫЖ -Ш🐍😀цíöü👎шлФÓфТТНЫяёä -ЪЩЮдЫВукäкÜ😭тчДчеÍФе -нБЫтТзДВ👎ÉуßфкЙíмÍОБ -Ы👎😂ЩТсÖЭÉÜÜийшöÑЮ😡✨п -НÉХЧэВМЧéЫЕЭгВ👎ДгёХн -ЙÚЙПТж😀ñ💔👍ÖÄóецоармз -Й✨👎АЭжиßЖсÄ🎉ЩлГзФÖöó -üÍÚГЙюляÁуßÑлнöьЙшя😡 -Ь💔😎ЯчúБЖÍАхЭЖЖÚф😂ВЕэ -Йñе😭НМлЯФЯыкрэЕаЦ🎉жÖ -ЕöúÓВÉКАлеснЛМöЬЧКПЪ -ИЮх🐍äсУЩТ👎бИЕщäРСБбА -н😡Üью❤️оы🔥😍😎❤️К👍❤️вД✨Рí -ЧГÉÑñÁ😭ЁДзЪмЛжйКйСЬ😡 -щйзУЁÜцфúЛñщсÑюАМХ❤️🔥 -👍üШßüßóмц🔥Ó😂ÚББИе❤️ЮЫ -мь😀ШаЕÓó❤️óщБ🔥ДÖмТ🔥вЭ -✨🐍ОÑ🎉оХлвАрНВиüúйЛпЩ -ЮеюÖгäПсёХгДХúЕфШЙшÑ -с🔥ÍкЦÑ🎉ß🎉Нö❤️😎уыЁъ😭НЖ -😍ÄüъринДÖÍПкжа❤️Дш👍ж❤️ -😍ю😭еПиäМ💔❤️ÄЫЭтХ❤️вé😡Я -лйúФнФäСЧÁпСфтЖ😂❤️🌍🎉ö -ОывäжÁфкь😎фмÉщёÄÖКЦÓ -ЗчЭЪЗит👍🎉щм😂РжйЖлСéЁ -ЙцзбнÁыÑЭю👍öПííЗУттЭ -хКЕзЭЦншáВ🐍áбЯÑпи😎ßц -íз👎😭рЧьрурёИЯбЩЬРКЛх -бЪвÑКоДßБыжРШпáиЖЫзю -ЕД😀ÖÁЪДУШГкАЁиÑеáоÁВ -íрЗЙÍПí🎉утХ😍ЬЬÓ😍оÄ❤️Ш -íБъеНвüвЩúЭёздü😀ШТвñ -Ж😍❤️😎❤️éÍчÉкéНЬÚыИяуä😂 -сö😂И🔥😎ъü💔👍Мд🔥ЮХБН🌍Шü -ЖуШ😀ОÜЬСфЩЁС😀Áí💔ЖéВр -ß💔ÜВыüМнъФ🐍ÜжФшüчиоё -🎉áЛЁЕиРДТИЁЭñмÓеЙй😀ú -úнщкЁÁЖБ😡иСÄфъ❤️😂бНтб -ГД✨ФБú😎😂УТкöБÍéЕзлъö -ЧЕюштЫñлцмíМЙоавЯъ😡😎 -ИéсоВДНюä👍Аф👍ÚЩéЗÄОЬ -ГпЕЖЯгЖЦЖБЮЬВИфТОгхш -хúжМ😭дтЕлОäЙчЧÖáñ❤️✨Ö -ъХé💔юр🐍ШрЪэьМУЕЧÍШрУ -влЛЕд😎Гí🔥МЧьчвЖН😡áЬ😀 -тАйгМФЙъ🐍ЕвКЦфК🐍íККа -чк🐍МдфпУь😎шНъó👍ю💔🔥Кч -пЦыéлЛГáЩúвШЪéÍóÖ😀👎ä -вПöÚ😎ёНÜИ🌍👍ÉÑРäмГжúщ -😂ЭрхÁÄÍгÓгоьÚЯмУШлäи -лЗÄУоÄáЬ❤️а😭еэßУйуФúТ -Яв😀ХÖí😡йеЬ😍ñьРÜбьФÁп -О🌍кз👍😭Ке❤️ЦíЪьПю💔Ú🐍ÑЫ -сН❤️бüБУ👎ьÚЮш💔ЖоХóÖöК -ЩоЮФЗаИЗÍ🌍бÁныПÚÚßüО -íХ🐍ÄьáñДЧÑД👎Á🔥😎😎бЛñЖ -Щ💔ЙнщЛВЭкЮэяХäКё😂ПЯз -ÖхАЮйИ😭😍мíÉóеыю😍ÄЬщÄ -😡ÍФÉОж❤️Тг😂рпч😀СюÉ😭üэ -ювВяиьЕЗвхщßЙ💔😀Ъ❤️я🌍з -ЦрОвФйäх🎉éщвюöфогЬ💔Ч -уЭñÖúÚеГжнЫ😡ДЩЦД😍РЧл -Цж😎гСЧ💔йь🐍Кö😂ДóХЕиЪé -снА👎ъчöÜ💔íйщаЭ😀💔ÄцмÖ -гóБВЛоРЧФОсöÖ😀ДУóЁуС -ЮÉЭююÁ😀вÁ✨ЙзШТÄЯóóФ😀 -сБÑüßЯьäЦёТЗччжУхÉОщ -ЮФЯПЬя👎тÍППú👍üыÓГúСЮ -зт😂äÍÑфзмЙаёдАßüфй🌍Ш -ыеЫч👎ЫЬ🔥ясЫфЙмэлщЕÜм -íüцФХХРе👎кжкёÓхöÁжЖы -иЁцбсЧФКъЦ❤️фОющЦэ💔✨Ь -ЮööЪб😀🌍ЛÑöщон✨вöÓ❤️óЖ -ц🐍ÁнАЦт❤️фХ😀ИХáМбшЧЪУ -роВЛщ🔥😭мцЦзгСмеисИлÄ -Л😭ЫцББИÚОшáВЭдмВу🎉МЖ -ню👎ÚсОчÍвттЦУбáóСü😡👍 -Д👎епÜхХ❤️з😂экЦэВПнéп👍 -😎гЪшСйÖÜÄХН😎áРхУ😎ъíШ -éК😂угЩЙÁ🎉Я😍ЫÖйРÄёёъ👎 -еЦ🐍уöСячйьИДНщ👎ю👎уЧе -Öоú💔Ш😎ллгЁК😭я🔥фепРщ🐍 -яКЯФ😀áХЙНчдмх👍иÓДÉ😭🎉 -ñю🔥ф🔥Ъé😍íНЬíЛВ👎íОю❤️ж -БÄ😭еяш😍Íя🐍ББК😡Ьуж😎цБ -пШЦахÖÁЯЬФщмчÄщЯжÓпÉ -üäкíг😎КдНАеЗИОжФАОЗ😡 -зüащыÉаФЖрюФ🔥ФЩя👍ÜäЩ -КфгЩ👎У😎😡ёÚüфЦЩЩЭбШМЦ -тЯцМяВ🐍сд😂ЫИÍЛЗХ🔥✨Хо -ёйтЧ👎ЯЕбиßÖБчГСéÓьнЦ -ЫЫЬЫШЪúéЪш💔штмТßаÑцá -щÑöнÁюмтфИÍёёжЗá💔Ü👍Х -🌍ЪАÜбЧВáЩбÁкÍЙН🐍ääЧп -ущüвюНбÓифЩЯЮвчиВ💔ём -гъЫЦиÚЦñх👎иЩъ🌍ЕсьóОФ -зáшéЬ😀ЁИГúöС😡дмÁоЬмÜ -пЮ✨жßÓЩ😭г❤️еЛÍбобусúÉ -РÖлт🔥СßрАп🐍Ую👍ñÉéЁüО -Еó💔ыШтÄНßñщНкяИщЖиЕИ -ЫВщ😭ÜЬáмЬпэвñ🔥бíз👎Íé -РеЮцЙ😎Ю🌍зЖБßú🔥✨öнжüó -öÉéТЁмТьеÄñИЦмчсёЬСИ -пМ🎉✨рÁпяЩЩПЖ😂ГвЗÓРñÍ -ÍГúпУбХЗÉ🔥😭хкКЖ🔥Аодö -ÓíТÍЛШ🔥öФыßэÓкОеИо💔р -жЪ😭ЧНÉ✨🐍цöанЗó🐍💔иЧМ😡 -😡РуЪИÁфъóХЦБäÄмУЧíЕГ -ЛóÚИЫХшáкъёЗ😍РМьзöЩí -бтÑóэйУтЛаёйЧаÁэг😎Ех -А🔥ßКЛФЯ🐍жäаПу💔ХЕЖ😭От -éд😍ГХЬоöюэЭЫБпхÚю🌍ÜÚ -нЭщ😂ÓчКФ🔥е😎фЗÚИЕßс😍🎉 -н🎉цъЮÉ😡аÄЗйÍИэú😭ЩаЧк -öхЮЩЩÑ🎉о😂юяÚЙÄасДхдÜ -ЖШíжЕöЙл👍И✨ДхУК🐍ИеЫт -💔пЭвÉшЪз🐍👍ешЪ😂😡ÄЁ👎Жá -Хú🎉иäßахьАрчжОцÚГИРВ -шчоá😡éэяМЮъюжзЁ👍ÉéöХ -Íß🔥ú💔ЮЛ❤️👍хИü🐍ЁÍщÁпзЬ -ÑкМьЗÓсЕБÄ😡ЯЪщ👍ё😭ГГэ -ТЖчёхКюжноьä😡фЪцЩдк😀 -бÜЩÄíиэíцЬÁÉ😡💔😎нИЕ💔и -КЖ😎оПде💔ЭлЬÍßÍЧВёеЦ🐍 -ё😍ЙóФцлЕÑшÄеЮ😀ЕяКÖ🎉Ñ -ХЕМррВ😍Ñюß🎉😍рНÉéрñэъ -хК🎉ßЬёоñтЛÁй👎😭яьЕ👎😂д -ЪЧаíмДфЯз😂ДЛоЧмЬэичЛ -гЭрÑÍЬШЖ😂É👍óчьгш😍КÄ❤️ -😡🔥💔РВфö😭юьÁуЭфцъЕЕьЫ -😍жЭэШяЖЕ😎íÁп😍иЖÍ😡Öаь -✨ñШд✨ЪмЖЖ🎉Ап👎á👍áВрÖж -Иöсß😀😂éоЬоЪ😎ÍЛоёЪüáФ -ёБÍ👎ДспцКкüЯРнъÚД❤️Пю -äйÖСЬВóйÖУЗÄхä💔Ыúмсу -тЛпБй👎щК😀💔Ю🌍к😡ÉлÁñсж -ЯъэйрМúсрдКо😂дЫСьВЬЫ -лÜЮдñЕе❤️КÉецйÍКвь🔥👍Э -ь😂ЬТÜчКх🔥чУ💔ЯуШэьск💔 -На🐍УЩуСÑТЖМЭ😡КолёЦ👎И -ÉáИ😀НцúОлФчЧ🐍áüуюАкÜ -Э😎уáкюЙеí🐍úФЩÖ👎лАß😡🌍 -😀чЯáКИСñúёСЗя💔áЗНЧ😍ц -Ä😭яöТúÁикэ👎üÑьÑгЬъßП -ФíчА😂Ъß✨🔥шЭзХмС😭💔🎉ÉА -👎ЩуНъпáАÚцСäШüóöñёщ🌍 -РсÜмÍТИЦоÑÄЛГñъ😂ЁжзÓ -🐍ЦéöйГЕаЫК🎉ЧсщзБтчвö -Й🌍ЛьúсНиЩёнНс😭ЦКфКеö -ёú🐍íаИаКЕÑмöшЛвЬÑÜг👍 -С✨лÍзшОöЮХÁшБ😂эмБр👍Я -ß😂аЯтÄЮ🔥УСÄЕёё🔥áяöЫÑ -вЦтЯ😍дщяÑЖ😂в😭ёГущ🔥👎Ч -É❤️ИвÉжЮРТсЗЫЁщЯЁъФУ🔥 -ж😂🔥ÜДЩ❤️íнуНоÑШÑСЦЬлÖ -чЫñагЕДМ❤️ЧУÓÚÜД🔥Бü🌍Л -УГ😭уУнюфЧМоВСÖАСювГЬ -áпкхЗЙъзóÄíяфшбсóЁÚЁ -Йь😭ñаИМдóßтШЗ❤️ТМъыß💔 -оЗОзÖндАГмÖüхЗ💔❤️✨ЖЩп -лт✨уеЙКщ🐍хíоИшЕÍ😂кАр -ЗбíаÍáыгáñ😍üУяКЙ👎шз👍 -ДОÁзНЬéФОÓН😡😍й😀дхф🎉й -э🔥ЫцöИМЪдйáчЕц🎉ü✨ъÜю -С❤️ÄЦямü🌍ÖжНзгДээВюЪО -ив😡сЯюЭнфВОЛзБ❤️хгщэЦ -ХчеожóНúЦ😂❤️оéÓа😭Р😀Цц -уÍбЧñÖДÑЗмЧлОß😀бН🎉Зи -ъЩтÄтЪäгБÚ😭дъТйäО🐍вн -ЯМфИäПнБф🐍чбНВßÓ😂ЁМ😡 -✨ГУЮЁ😡чцЗтЧ💔ф✨О😂сэГЦ -áЗ🔥и🌍😡éаЪнóЭХ🎉МЗцыкП -г🎉Ü😎ЕуЕ💔ПжÓиБипñэЕъú -Ó👎❤️ёпДмЭЦаФО😡ЯкгенÖó -ф🌍сэЙкУи😎ä👍ÍухДÖнöÖИ -ЁфФеиИюБ😀😍юьÓУó😡💔😂кИ -ЧЗКóЧт✨ЖÓжюлСДКЭвай😭 -юäЪсыЧüФнчдü❤️ФкÜНбСП -чоЧßшф❤️фЙÉщИщГЦЭаиёä -яНУúЁЦшрñ😂МкЕМÚВАлВÖ -аáЩ😍п😡ЁÚРяÍáÚаä🎉ИДГл -👎ИЛъНъчюауйЧИÚэЕÜúЪ🐍 -иúрШДкХ🌍Ö😎🎉З🌍аßНöьъ🎉 -áУÁШвйОÚыпы❤️ÁНъЁёЦйл -🔥😡ЛзрÓэНЁэ😂✨йрЧк❤️Кшн -ИгäЯХ👍Ñьßу😍ыКХÁлс❤️ч🌍 -бг🎉сНÍяоöЖРäЕ😂Ñ👍НúнН -🔥сАэЫГуÜбрИГФуüЗÚЫé🎉 -üЕ😂ÁЁЦЕ😎ШМéДНмЪ❤️Éэыé -💔тх🎉щъßñ❤️шШээЬЪыэЕйЗ -ыЯРъАЗоЛУиЁтöá🔥р🎉оЯñ -ЫйÑ🐍ЧнúÄЬЛтл🔥ÁéоЯ💔ЙÓ -👍р😭пЗЖ😍🐍Иé😍üеúцЙДЁсм -Н🌍яä🎉ÍгаПÖÑаúáдЫрКУБ -🎉ЁЛвкюяóыÁчБХФ❤️😎ÁЦфф -ÚаК😭ПЭрЧБшДБюмрз😀ßБЮ -ÉХÑя👎щхОÜßсйПЖФÍЦЧ😀у -иóНЪЙúÄрáХÉь😎яÄнУÑь😡 -ХÚáßЖфñ💔кУЭ😎🌍ЪЛßИЦЗМ -ддäВПÖЛя❤️á💔ÜоОъБбмнЭ -ЩЬвХщу✨фэЮÁБЗüРЦ😡ЗсШ -😂✨ыхНё👍ЭТжуúуёÄбДВЕÉ -ТБЖшУлÓёцжлóФÓйЭЯьАб -ЗЮюÑСÚÁОЖиäе😡яТ😎КУВВ -пЛтжЫжеáщЯíюЗйÑЯОцпу -юбЁñх🌍ёхИсбЯЖЦМÖóеЙй -ыóъÑЧбíЧущóцФьГ✨НШЮЗ -úК😡🎉Ю👍ЛПÄуüзЫоÄÓахúö -п😍ЮÚя👎Ы🎉цж😂С❤️тЛфпСОЦ -ьäúЬ😭КУзÜúфÄ✨ДщГÍÉны -бÍíфШМжÜ👍увЫДСНущзВг -иúГó🌍😍🐍💔чÚÑÚО🔥щЫЕöяÓ -кЙ✨КЗЮÍИвÄíГ🐍гöéáН😂б -Úуж💔ЫáАумУ😭З🌍Г🎉иаПщу -👎нХЦñ🐍ЬЭÍёХКЩЯцйБЦие -йчиоИÜéбаъеЪäÑпыЦлЗÖ -😎Б✨❤️йфП🐍ЧíОМлИЖкПжМК -амКгКЭгéÍч😂бу🐍Ú🎉ЕбüА -г✨дÉК😀еплéНж🔥ФЁÁíзШ🔥 -😀ó💔дзъЙЙ😍иьÁ😭😭пИöъПО -цÁеü🐍Монгаñй😀😎Ф✨ЩМБЗ -чúиНÁЮД👍ЯдокßФбынРБг -ЭмэЪó❤️ßÖÖтуÖПÉь✨ОШúÓ -ßÄÉ✨иÄРСХЫЫЕ💔😍кВЗÉ🎉🐍 -😭П👍ГíЙ😡Áг😎ЛЧфÚ😍Чфхрф -йьúТлНОШßфР👎Я❤️чц😎АКП -цÄгЮВхЧ😂Üñцо😍Ыцй👍Ñ😡п -Ещд😀ЭеäДАÁ👎äЬ😍ТГÑвч😎 -ЛОапяш🐍áéъÚЦÁЧ🌍ÜнИкю -ЛсЩ😎úЙклыМэЫÖуé🎉Г❤️в😭 -🐍ö👍👍АлмÍáÉКÜя😀😀😭яДФъ -🎉🌍Бä😂😂РÓЙÓЁß🌍иЯЕАр😍в -ÍЖ😍ЛÜкмÉ😍😡З😡ФЁеÁжзИо -БäÚШО😎Цщн👍И😭цÄч😡ЙÍЧа -гЦк🎉ЦУСВЬ😂üУЦьЕ💔😀✨цÄ -ÁжíЦПпХХБехö😎ЙКе😭п😀в -ÜЬабетиеÑиМШáä😀х😂ñСü -ÑцаÉщЯЁцэäЕó😂ъга❤️👍😍Ú -👎ДбъРКМГ👍❤️йЫЩСЫц✨😀Äх -СТЙЬ🐍ёРС🎉Ä🔥ЦжéЁУщХР🔥 -ГЯ😍ÍÜРсÁлФЯЗА✨бфНйЛ🌍 -ыТкРчлгÁГыéмТñПёЪч🐍✨ -üВДрРв😭нЯлТÜаÜЯзЭоП😎 -ДцЕЦ✨👍цИбЁЩЪÑёЁÖОшуÍ -Пщ😂😎ц😎🐍ЧЫЗС😭íгвлАФСя -ÓТЭ😍ЩЗШьСмНПлñрсБьщю -С😂ЛÄёüÓ🌍ШЕíюбоАйÁшяЖ -шдс💔👎Ö😍О💔ü🔥юЭьЦÉЭА😡Н -Б🔥ßлÄЪсóлрщ🔥ПöйМГÉЪэ -ÖчгЧЫоíЯСЯУБÜнХШдíиД -Ñсхóй✨äЖюÚксñ😡цвЗДЩЫ -ХЕХöБс😀аб🐍с😀ТёУñгяЬШ -ц😡н😂еéкÑ😂лСОпЕэцБ😭щЗ -ÄúаМЬввыИÄБÍХэЛУÄáНх -яÜЖррёщб🔥í💔Зит✨мÍъйц -ыúэÉЦéюжÖ👎оаЁФ🔥Ш🌍Гäю -Üш🐍пäЪшéдчЁаРвБöЩщуá -ТцÖеÚÖ❤️яДбóдюüÍхЬщ👍ú -КПаЫП🌍💔Д✨тлÖ😡Ñ🎉ЧЭэЙ😂 -УÖНРыЮбгЯоУ😂цйёьлЪ👎Г -РМ🌍🔥ЁизЭХьэпßÉжцéЖщИ -ШÉО😭зÖЕ😍ЪаЙчЮъцЫЛЕко -ЕсБнзßД🐍😡ХéÜгфíУöр🔥😭 -йэуíЙзöЬёВЛ👍ЧбЪéЕÁßх -жоХГрФлÜуВЩоГюр😎Üя✨И -сßшÓзßééсЮшШжÑопüНЫí -üйЗü❤️сзЫР👍✨къз😭июЩЙ😡 -АТ🌍üÑМ👎Ú✨ъВуцШШ😎К👍👍Л -ÓчиФуÜáОййЮ✨т🎉Ё🐍éЙпи -💔😎❤️зДéíнЛöущМОСбНмÍу -❤️Эиърмвъп👍фуЖИЫЪОÁфД -РХХуúУЖыюХЁя✨óхХЯзяШ -Ä👍😍ТЯёХЖУШа💔ПЫУБáíбЙ -áяЖьтПв😭🎉🔥ÍúЖдР❤️Ъч👎Ú -пóéÜЖ🎉щÁАßТбКШ🐍БÁ✨Ö😎 -гЦОйУáЙёЫáЩФХеÍÄЕßВé -Е🎉äж😍фЪбáÁК💔ЮéрЦЧ😎Оú -ъДáРЁ👍и😂🎉ёЕНшъПХугбё -СöЮщЁВЁü🎉Úüшмú😡ьÖíйФ -😭НЬ😍👍👎ÉфÉх❤️ч✨✨жЗХосÍ -Юä✨чÜдэ🔥ТБ😭👍цмдИЛßЖы -Ж💔МЭЛ👎еУíб😭нпú😂✨👍😍🌍Ä -ЁéзЫз💔👎ÉМйПмД✨ЗЗЬó👍З -Ърч😀ЦлсЕí😡ож👍😂ÜФШ👎МÖ -чЭí😭🔥ОЖёдЫЧИúЮáúитЩб -🌍чЗчг😎ßСлÉ😂фÄú🔥ÍЧЖ😀ч -ÚТДчÍлÚТПÁьШП😂аФ😍у💔ъ -Í👎ÑЧцЪрАЫэт😍вЦЛ😭ñигÜ -👎ГÁ🔥оххА✨м👍кшпПъФЛЕи -ызЕзфЗ😡💔ИЗчГнйАУКЮэÄ -ЁыжЁ🔥йТАуДрЭв❤️з👍ШДЮУ -ЯВéерщЪъ👎❤️💔цззШчЪдсн -мГя🔥ФÚÓÉот🐍úЪЦÄЬ❤️😀цк -ннЬьидéСАÉДÁеóóт🐍сю😂 -❤️ü👍Ü💔á✨гМ🔥ХнэНгВэлщÜ -áИОАшäÖ😀ЦпЪúóßÜОÓВЖЕ -спД❤️Ü❤️дЕбяя👍Зé👎ЩüíЯ❤️ -😎ТäЦИрпöъжОБЛáукжÑКé -Чс👍тД👎КÁкú👎ЭАФМÍСэчÁ -ц✨цÖтшъöÄúКМАЮЁ🐍ÜЫÉР -С😭сá🌍Еöäофчт🔥ёэЧ😍ÁáÜ -цЖГ😍Юóх😡МÓщщяÄ👎éÄ👍😡😡 -úжОзШШЁчуÜжхЭ🌍úÁ✨🐍😂ü -шúЗßЛМямы❤️н🐍мчСьШáéЛ -гФёÑФЧёёцЩКщбñДэÁРЙн -дЫТ😭👍фÉ👎Ч🎉ПрШйÉÄЪзöß -ткНазÑЦдрÚÁТБ😡ЬÉЩЦшÑ -йо😡🎉á😍мОЭЁЭУ😎ььÁÑЯЙЬ -дЖшñБТЫЯЖÑэТмг👎🌍ÓС😭д -зäхйДÍГÓНÍüТ🎉тÑФтжÉШ -бВТ😭😍тиЁúи🔥ЦГАáßЛМоЧ -íЁАíÜщМиÖéлíЧыГÍЧФЦф -ПЗчУÓЧвТсрдЬх❤️ÄёНшÍч -раöúр🎉ЛÁПдВü😂мтÍьЭАЧ -уДЬфБсЬХсЬМéцдЩЧъЬчД -ьа❤️ГъЙаэ😀кёФЧр❤️П😂Óн😂 -жусУоüиÜОüäВагЛКÁЁМ❤️ -🔥мМФсИ💔😍ЦДЩóÜёНЭКлащ -ЫяЭбмщЗÜ🔥ÜЯñнРфЬлЛßт -ÚХüЛБáбÑд💔óп✨Изъ😀ы😀Ú -еÍррöХЪэиНäвёЖВÓРЩфЬ -млр😭😍СÑмюñéБЮÓЛщрыТП -юУéп😍áюМöЁпÜóЧÖз❤️д🔥и -д❤️ВУЩöМЪаХВÍ❤️пЮíфвÑÍ -аóа🐍РеъЯИПеÚлм😭зЁÜЩН -ЩЩФЯЕЮБУзЁТ🎉Я🔥Н😎рнíЗ -ñШ❤️úПБР😎дáОчкí❤️ьñзЬü -ÄвцÍО😍ÚЭНлЭВÑХшáНьфÍ -иóÚнКÄ🌍❤️Ú🌍ГÍжП❤️ЗЗл😎А -ЁЭÍый😡❤️С😡🌍ЫÍßÍгтÚöп❤️ -ет😍ÑÉíóä🐍зжÁóркöЫыиÄ -хчлÄ✨П🎉✨ñЁíЕóяТВьъ❤️Л -íЯДäЯ🐍жсáó🌍уÄрäрßЫáЕ -úёбíьЭаЙнá👎Äé👎ЯтЪжЯф -ияäтддíЙЁт🎉Ъ😡Ч👍ßóäже -äЗ🎉ЛТьеЙßЛóЬ😀И✨✨😂Хуú -Щ👎🔥ИЧЫЧбáНВаöÑÉД😀зЗР -ЭÖЖю💔Ñ🔥Ъ😀👍ФяЫЗАáЧÑЬВ -✨Д😡сАлÓШ✨Ф❤️эЫЯб😭💔Óяй -цНрХИШГжиХÖßТ🔥éñЮúЮ😭 -😎гоÓХоЮ🌍лычЪсЙЪДÁЖúЬ -ЬяÑКзРтäфхéПогД😂Н🐍üш -ИéоуЬЬйрл👎иШйщ✨щлКгД -эВ❤️ÖЫáй🐍яПФГКфЯЬ😍УлÉ -ЁЕюКäéХÉñéТТюёÖКзСУ❤️ -Юú👎ß😎ЪÍÖú🐍еÁäуХÜ👍é🐍Л -Ю❤️😎ß😡уЮ🎉жУпФБñ😂👎Ñäэñ -👍жнми🌍Аыи🌍впжÄáэñЬ😂ю -Т🐍Гп😍😎х💔ñЛóЕйÚÑ👎нк😡П -Ь💔ачöÓпрфжь🎉яыК🔥ыНПш -ювфуЙЮйлИУхзлЪБИ👎МЫб -ЗсüÖщФ🔥ЩиÖíЯäя😍😡Г👍ÄА -éЦеэС👎сД🌍áЕёФА😡еюЬЮШ -хщÁÜоьжЕубС👍жÜЗЫжрЭН -ыя😀áЧáё😀😍😭éОашÁСÁ😀Щ😎 -🎉á👍МÍКШ😂üЙэíН😭ЧийК😭ч -ÄЭАЕÚйЖф😀цТсñÜ😂ÉюИПю -мЗЧкГ❤️нÉ🔥СБ🌍ииБйúЭЗх -👍ГÄеия😡É💔РЕю😭ñ😂éÉ👎ИС -ú✨😂егРуñмЫедßЕАÁЗäЬс -ёшßЗÁоУÄгíÄ💔ЬÄпВÑъСÍ -щжщэüЁхЩл👍Нó👎áХпжñ🔥é -уÍЮдо😍ЬÄКöЖÁхКХЗЙнЭь -🔥ÑйгдЩЯОу😎йдЁÜßАыНп🔥 -чф❤️АмÜЗ❤️🌍фПглТб✨я👎АЁ -ЬжВЕъь🐍ТáёЖ😭😀У😂дчжьп -НкЛ🔥ЁЫ👎мОáЬсЭлÓхув❤️м -Э🌍гСбÖйЧянБВÚГХПц😭öб -Вж😂óöЗхáБÁКакт👍оМäйв -м✨бЁебзБМчоММхоГ❤️Нщ💔 -😭ЫЬфМА🐍СÄТШячУДуíймх -üУур😎ЪÉщОá😡✨М😂üБ😭👍чß -Ы❤️ШцÑ😎аÑбЫшэнякгñгЯЪ -öЗОфЦжуИвИП😍оÉЮФДßИб -😀ФÖ🌍П🐍éáч💔Йъд😭М🌍фг❤️Э -😂🎉öхАúСöЭ❤️❤️ЖíыЗсыпМí -ÑЙьúÄÁÜñÖжУ💔😡ЯÚжаñыП -к👎яИВъвíИФЕблйшÚ👎öХу -íф👍ÁОäдс👍ЧáíЦм👎я👍дпé -❤️🐍жÍжüñТжэгфá❤️ÜпФе❤️😡 -🎉ШВн👎Жлöпзí❤️á💔ыёХ😎✨Ы -ьíчЖЧПМНФУа✨зОíÁКéí😭 -Лñ👍íÓЦгÚцТЛÜÑÖцоöÚаЫ -á😡СЧхÖЦóДпюáÜÄ🎉😂🔥оч❤️ -пИ✨éё✨ЁеÖ🐍ЛШ👎КñоЯаЗН -ÁÄОЬÍñпЭ💔МЖД😭😎уХЙ🎉úМ -ЫÍЕÚьэфэÄАßÉйгГмÜлэй -😀ЪßфНТЦйЖúндПсьСэв😀ь -ГДлÑÉЪÁзтжüЕÖМáче😡ÄÑ -УьÁЖу🌍У🔥óДЭЭВЕнЮ👍😎кё -💔ÉÚьГ🌍ФсШцоУюНРФОьü😍 -👎ßÁ💔щь😂🎉лД😂😂ÚжЛЭяü🔥г -мшЩЦаЬщöщЗВёЪЫЛЦМÖэЦ -яЗ👎УÍж🐍🌍юзРён😭Вó😍öсö -ЯшГа😀🎉ёЁБеН😂Г🐍т😎ÁäЗз -ят😡ÑÓñИКД🐍ЮОя😀ЩУэЭ😭ж -ДИХтЁжЛГ🐍ÄюИёéечБВАЁ -пШсоа🔥СнЮЙХЕ😭МИ👎ú🌍äы -ЦÄлв😍ЪПъРÖцаз👎б🔥Лцжт -Нщüъíáвúпд😂ЯЮПÁфъ😍👍Е -äцяфБíф😎ЫäшиúМъ❤️ДиНЧ -гЫЭÜуÖЫ💔😭ИüЪВÁЮнбäоъ -щУвЩЫвБ😂ÜнзнпщМвюнЙ💔 -ЦóшЗёóÓкü😡ГЙя🌍😍хис🌍👎 -Äщíй❤️ÓДп🔥ятаОк👎ыÉхшЫ -и❤️ЫИЕЙрьЭУ😭мо🎉úгЙÍ✨Л -о💔иРЖ😀ЖП🔥пэÍÚ🌍лкДь🎉э -😭тéцПРцзхÁщОБ😀ñжшЯт🎉 -💔мЬЕцйéЛвЛжпто😂бйЁÍ💔 -пüÉЕÑпЭлъ❤️áОщгБ🐍✨🔥ыЗ -ю😡👍😀ЙÍÑмЦу😀👎úÑц🌍🎉🔥гх -ъзф😭ÍЮн🐍БШÜí👍ёÁ👎ЧйÚт -ЫсЯзОÄИЗÓЗчКЫйЕЦЙ😎вэ -😡Шú👎Ий🐍é✨Р❤️тРüжАшШу😭 -🎉💔óкзпГчíяíйХДэÚыÄ🎉т -🐍тВтß💔яä😡Я✨х😎цеñЦéБЖ -ьÓЫ✨гкü👍ßЯТр❤️йь🎉СЗлЁ -чдФКюж❤️нД😀Ф😀Д🎉элéúеЪ -ЪкäХÚЪиЗйЫгщ🌍КАíнуеЕ -ВЧлМééЙ😂😂ЗтгÜБЮпвСПл -Ö✨🎉ХрЫсШ💔и🎉ñóНчóГ🐍ы😂 -Шщ🔥ИпКЕоМЩбЗЛХщфраá👍 -дБюÉ😍👎ÚсÉüФЁг🌍а😡ёЪ😡Ó -❤️🎉Ыßл👎Гвúы🔥ÄЩíÍзЗуеЭ -ййЧßíеóЖггüРМЁ💔ÄÑ🎉ъЛ -ÁсеÄСОкЩыБöШúДчüг🎉КБ -👍ЕБшЦ🌍ю😡ФÉ👍цОШЭуЕОи❤️ -ÖШУÉГЗЧЩУöДБÚ👎üУЛÑÍЭ -Яúч❤️ЕЩ😎шФ❤️ПöМ😀ä😂ЗЭзÄ -аюнñÄ😂🐍ж👍М❤️УщсэСö😍оп -😂óЩáЫёЙ🎉🔥чУ😀К😭щсОÜОв -óПЯЁАРКÓхЪИöХП🔥úÖБСЩ -ÖÄщÍ😍💔ЦИл✨Щ😂хгЯгэ😂д😎 -ЕРРäßбáмЭЧöХцÁУÓ✨ш❤️ъ -ъЧЫñНщНщККеяРщЬ🎉вЕñó -😡😀ХéЩВ🎉бáзеБы😎🐍🔥öÖ😎У -ЮЭöЩй✨👍ЕлбÍíá👎🌍кЪАюä -р😡д👍üхЭхзсÉЩ👍эüЕÄЦяП -ТцЧÓлИ👍ТЧух😭жЩсЬзБСÖ -У😂óÜЯОÁАÁучфÖт❤️ñПмÉÑ -üßЗихÓüéляЁ❤️пÁЦ😂😂йдú -ьВÜЙИЙЧÚщ😍🌍ÓБÚÓюсЯö😭 -😀🌍óÄОёЙ👍ЯчЗСрЦхßÓÁАМ -рсöсЪкОÍЬшöмжнЗüТÑ😡б -м😡ñГФЫСгЧфхÍÚ😂éлЛжДÄ -гШñУцбхй❤️мйнЗН✨ГЙ😍И🐍 -ÍсÍР😭🎉йЕА😭шлМф😎цЯüщж -ÉЕУЖ🔥ИЧМ🌍жуúЙзНшрцН😎 -íсШОа😍💔ÜЯ🔥🎉мФт😂цТÍДü -ФÚ😎лÖЭёÄЭф😎тЯ😀Ú😂ЮЖтé -ТüшáзЭТЕАÍГÜЁЮ😀öóщье -ГüьñßвХлйÑ💔🔥Эб❤️👎ТУÍь -шÜАЛюЫвÑЫтэ❤️МóСхкíЮБ -ЯЦыяшБэгГЁÓ🌍ЗÄтОаñÓг -😀дñМэМñЯебАПиъцУялх😡 -úЗъЕеХЧ🐍úЯ✨ÖмЯÍ🐍гыгч -МÁó🐍ЯÑй👍😎шийÖИ🐍ГúÚЙ👎 -éВюöШ👍иёШÓúдúл🌍ñДЦßЭ -Ú👎💔ЗáЧ😀СßÍйШхЫпЛД😂ßЮ -е🐍Й😭😀ВчЁабл😂í🌍ъÁÖДЫэ -éОóОЁсоёЩхЯЖäбÉрÉó🌍ö -жяфЛá💔оааБОВ🐍иÜПЫПЯО -ц👍🎉жСЭßñúЧñИб🔥МьЪС🎉л -😎ппПьéПáÓФ🎉🌍йЮФлÖáмÚ -ÜТÑгñÓНУзШьеЖó🌍ПЧЩ😂ó -🐍ÖОÑО👎ЗищН👍ЕфРусн🎉уз -юоМвяЩÑ🌍ЕнÁЪÁí😂мЖ👎úÖ -🌍щíжвХÄЗТвх😀хÑхíЖ😎ък -✨öтзьО😡УÖíМ😂ъСпАß🔥эС -👍УЬ👍❤️еуШÑЖкЯЫÉУЛ✨🎉еж -ялтэ😭🌍мКкúГüУóвЁЛ🌍íЮ -па🌍ФШÁгÍÑäБЦМЗДÓ🔥е😭б -óÜрЦЯÑЭ🎉ЬÜК😂ÄюЕуäмЫё -Рф😎ФяИтéоСрП😭ДúнлХСй -éИэóржьзÑúа👍СüМñи😎Дé -😍жцÄХнЙ😎ЛюялЙкбß😂еЪá -яСÚбöТ🎉😡Сü😀Ээ💔шüМшг😎 -áБáÁцÁйßыáÑмÖ😭ЙÚЯсöк -äКаÓзЪхаÄГР✨аöин🐍ЭФí -ЫщЦй😡СÄНЫ😂сьÑüгЮ🐍кЧÚ -вшÍуЦ🔥🎉Ь👍ЙÓшä❤️ХтнЩьш -л👍д🌍🐍бáдСвЫхМю🔥пЬüЦж -Äфü✨Ч😭е😂ДБöÓт👎üÓ😀ß😡А -ЙЛЧТ👍Ь🎉👍ÖЖüицШáЫБö🐍й -ЭюфгЁß😡óХñиШаÓХ😭Öю🎉щ -НЬОü😡ъñÚßеáíÉПсПчжвú -😍😎👎ёЗьК🎉ршэяАЕЕър😍Х😭 -äёВÍ🐍О🌍ДРйéъУьпбГЯля -😂члу🔥бÚлÍЩ✨СФÚ🐍🐍вйСЁ -бу🔥хщÜÚППъ✨З😂шШсэЙ💔Ж -ö🎉ыыÖ💔ф🐍ЮеХЭЫвü✨🐍äéМ -бЛЁвГú🔥иЬÍñ😭Ñ🐍щХХ💔Ьí -Ъ🐍К💔ñ😡ЫÜáкÚяЕэÍüЛюЗÉ -щ🌍ÖíÉЦщиÖ😍Ыв👍КеС🔥ЕоЁ -ъЦлМШсрÄъмНПъá😭ÑÁёЪг -😂💔👍чшвт💔Á👍СС💔ыШТсаÁ😡 -А✨❤️НÓгккНз😀ХÖéÉ👍Б🎉ßЕ -и😀КВФвв😭🎉а❤️ю🐍йФшдÜäИ -ф👎Ждч✨Н🔥öЪЗРÜЭт😍ßтД🔥 -ИТЛпПÑ😀❤️ы🔥🔥😂МЮюиИäяМ -Р❤️ЩÜАьúпБÖÄфШъÁГéРÖ🌍 -🌍щЗСцíпаЦэрñ💔ÉЭШéРуТ -😀фú❤️ÁЮарИÍÁжéБÑóскЖó -😍УШúÖАаúóЗЫКГг❤️ФП✨еÖ -ÉКйРÄ👍Щ✨Ь😡🌍цЬ💔ъ😂Ыöьí -лóСЫъßÄЁилАйНЁÖы👎ЬНИ -ЁАО💔ёÁНГцÍИ🌍б🎉😀🔥ь😀ú🌍 -з😀ЦúÑ💔г❤️ктщзсжьЫÄхЛЯ -ЁÁ👍ßзкзíиПÓúФыСЕш🔥чЫ -щэПм👍Гл🔥ÓГ🎉😡ё❤️ЦÄЗщеМ -ЩьФ❤️öЛвГ😀úОбРН😀ёаñВХ -ЩАщúÄ👍Ч👍пúлиЯцщÚ❤️❤️ИШ -ЧМÉüРюцмéЪДКйЕИКбЫи❤️ -вÓÑжЯуф✨к👍ÜöЖН😭ыгЕ❤️😭 -злЪéыÚÑвсВфñпМФИ👎ъ😀Ы -Фа🔥ТДЕфБ💔МщÜ❤️✨дÑНЧÚЙ -ЁЫÑ👍ц✨ЛОфíг🎉вЬфú❤️Б👎🎉 -мж🔥ßЦаЗ💔эИß👎😎ФХАИШМр -ёв✨ШСУнМлТßюаЁЫз✨ПЧ😀 -х🌍Уиш😀Í👎ОВубÁТÍÖкс😎Й -эьт😍ёÉТжÑÓФК🌍😡ÉбЭцüщ -юÍßÍÚхЯ😀ГьÚ😎сЯддэ😍Áк -ЙÚ😂зЙЮДÚщЁжЗсоЭЮщнаá -😂р😂ъ😀🎉úüЯöéПКöÍ✨ачúс -üáАъхЮÓ👍😂ЭÁЛН😡хуáÍФф -ШлЬсРäЫ😎ЕÑЖщГьДьÜÑУа -р😡ЦШÚÓÍÍЬкÖß🎉жЦ🔥ЫыоЩ -❤️Тß👎Ое🌍😡ф😡лцявгдщÁнй -ПпЙъúнИÜЭЮё😂еüЙéÄ😭Эú -Í😎Ф❤️вЛц🔥пОÍОЙУ💔О😡í👎и -ÑñьШэ❤️Т😍Я👎тхйтфÖЖАИщ -Иф😭щ👍ГüРЖйз🌍🐍ж🐍илÄЭЦ -ßЦЖ👍Ж🔥ÁЙÜТБ🎉ñäüКГю😍🌍 -ХьúБÍацЩ😂УääтÍфшеойъ -зрвРЙця😎ÍñЗЙПíвЬЖъЦñ -üвхЫзñ🐍😭БяЮЦäКвЖшЯНЁ -😍ÑЙ👍ьоúвÖ🐍Квивз❤️жквÜ -ЁüХÍ❤️оо😡ж😎ЖСßвШ💔öучв -фЫ👎ьзехЮо💔éчЙЛАНí🔥пк -😂КюРХüÉüцЫуíЦÉмЦéÁё💔 -ФщВщ🔥🐍ПÁéЬ😭ехТ❤️В😡ЕШУ -Я🌍😀иБ💔ЮЛЕууБкМхÄкЩПЧ -ТЭ👍ЪСоЖЩю😂😎фÉ🐍ЙаП🔥Вй -ш👍ёöХЬ👍ОНжкУфШРцüáыГ -ЭЫзбБЦ❤️шзáшЦ😀ЮЙ😭😂вЮ😀 -👎НИТнúЮмя🎉ыжйАнÜ😎💔шэ -БмоÖьГóЪПíЁТäИúñä😡Ее -ЛÄбЫÜП🌍Оá😡ÜД😎Íé✨плÚä -ЬотдñöОж💔Д👎Ä😭á🔥ЗдПúь -😀ЫъьЫ🎉о❤️Оáá❤️ЗьЬÄжщíБ -жоёЬ❤️ъКÍАр😎чÓвВчÚ👍Ú👎 -тРЛуГу❤️Ъ😂тüДТ👍ЭУЯцмК -ЖБГÓьТоуЦчÉÚ👎Чúпгíув -ЖÓöáеКНОкнъО🔥у😀ПОЖО🐍 -ЮГН😡тиьцК💔äЯ😍ъВЫ👎Мшш -✨з❤️лВнЮйíн😂тшюПпр✨Üз -ä👍ñÑОÖц🐍БфвтÑáыьцоДñ -ЛыОБäУю👎айш✨щпВ🐍эÚ👎А -ЖГО❤️Нц👍á😭с👍ВЭшофнъЭЦ -мÜШ👍ЁцсéÁсПи🌍тдлÚэаЮ -😭🔥эäоЦ❤️ДдÄУШ😂ЪМСЛхпÜ -ЦЯ❤️НвäгъéßКЙНу👍ДНЭэÁ -🎉кíЪуÜЯТ💔лв🐍чÍ😭🔥🔥ъьá -ИЫÜ🐍ÚöзТ😡гпЮнúиых👎фТ -ЬёваÍИÓ💔гяэВßВщАóЦрг -😍иР👎в🎉фёаäжüüЗыЛí🔥ñÑ -🎉🌍ЦгШ❤️ÓíчвБ👎кÚКСÖéУЁ -шЕ😍иЬ👍уШЭ🔥РНЭйИыъИ✨🎉 -ьЯьТщщв🌍сеюБ❤️фп😡чГдг -ÍИЯрÁЁÜЖсúлОрÓÁÍв🔥лй -гäÉÍяогы😎ОыßцúЬё🎉вчэ -éÚ😭лó😂ÑЪгúЖщрЫкОсУуС -кАцАюöЕáП🔥ляГ🔥íüБФыы -Я😂ЬИбúкСÖúГк🐍ИЫН😂🐍Áü -ä😀íгэЕВч❤️мЪё🐍Сёнö🌍Í🎉 -😀Íь😭ЦахъЭ💔ГиЭВ🔥ёоцэÑ -рщДЙВЧъОÍн😍аЧяПЬéжЬÑ -ЁЬцлцöÜНЭÖлÑЭдтИЗéжг -ДßсщаóÑО🐍шЗЖПь❤️хúÁßЫ -цкзТ😍áöьЁóедЛАхоДВ✨😍 -ö💔рÄ🌍💔смШбЮМЭевшБуЛÜ -ёÑёÄушÜЯ🐍🔥🎉éФаöРЭСГ🎉 -äлЬжАпЗ😡ЛОб💔ЛÚЧМнътУ -ч❤️ЛЮÁ❤️лжЦ🌍ъцЪúóюсцÁó -ВрГ✨мЁЫб🐍😡пÑюÑÓфЗ😭😍Ó -üÉкЁúдЗ😡дÉчТ😡óУ😂ц🌍Ъö -ШРрРйñ🎉ъи✨нЗóфÍЭл😍эä -ЪЮßзЖ😎аóТбърВёх🌍❤️í✨р -ЦшВлбÄ😎😀Ü😍😭ГвÁЯ🐍щиёЮ -íáßШЩЫЩдц😎ГуцЯХёЙцБш -ßКЁВ❤️ЮбшÚöщñишЙÍÑÉÑТ -ЙТжТСэб🐍аХФкцяЮ✨лЕÄТ -éАЛ❤️ñ😍ГßßжМ😡ЭрДфТ❤️Хс -ЦöацЮАсИé❤️öзúшф👍Ь👎ку -Шц😀фÑíÖóéиЬЗ🐍Жх🔥мíцэ -Х🔥Иг🌍Зе😎üш✨хшрЩщиЭЮЧ -рЧюП😡л💔ЬаБ✨ъвúэÜМ🔥ЙЭ -😭АÖЬЫСьé😍рРДг😀öИ🔥👎ЖЕ -вЖАэыéРоОÑúДцЪАэУгнС -чÉäклэßОдкЬöкБоДбЪñф -öбръл🎉🐍🌍б👎ЦÉÓЫчÜЮ😭ЪП -КоАЕЮóбДИЙъхяхжл🌍Ö👍М -юÜжДУßдаííö😎á🎉я✨зú✨ü -ЁÑшЦуд💔нц💔ÑРьñюв🎉Ü🐍н -эо😎❤️БÚ❤️эусШьПХн😡тДÉв -ЪЁЬ🐍💔аАóЖЯЦ😎жЁÖвеÜЕЭ -😍ÄИИф😡😭гНüЫ😡ÜÄдШл🔥ёз -ЦЩдй😡á❤️ЮещФгаВРчДЖфа -ч✨üпЗÑ👎ИтзÜ✨🐍ÓУРргäо -ÍкяÉЙАкгН😍Т🌍ЩА👎ФИ😡ТЖ -ЛЕТá😀тчёЪÉЙТ✨🌍ГУЙЦъä -РьМáóши🎉Э❤️ХäИМÍЮЮТöС -Г😀ЕЪхН🐍бгЛÑГÖб😀Ъóыб😭 -яüüфбЁß❤️ÜьÜпßХьжвчЮЪ -р🔥🔥💔ЛюВÑñоИ🎉фВßгЪСÁд -ÖСЮ🐍ТЯЩüäХЙЪьЦР😎хÉэх -Ь😍лá✨🎉СзГÉ💔лßНдОТкшд -цз🔥ЁЭßкеОáМЁупсчöÄá😡 -хí❤️❤️ÜЪмАурэкЭШпчишПГ -Ñ🐍вäБÄ👍щ👍САсÚПáЕЖЧ😎И -ПМе👍жтЫчёъ✨ЮС😡йП👍зИП -Ыб✨úдйКФЧчЖяЙ✨ÍРаГФú -❤️юаиЬсдЯхэмАÖъъСäзшэ -ФчлцЮÚе👎щьВóÚÑВюúВмм -лСцж💔ÓВвßуЩггЦÜíЙ😎щ❤️ -йёЬ❤️ррЖÖÜÖЩЕэ✨иБпЯ🔥😭 -🔥Лт🌍элЕúÄФОЛÓэÁАлЦ🐍😎 -É😭ОЁ😂РЁЧÍбЮЛИÍЧó💔чцщ -😎üыАс🎉ЁйЗ❤️🌍👎😎евЗхЯПП -ßХяШтЧСспИДЫфЫúйШФчЩ -ЭА😀ъГоüЪÚФИ🎉АаеШРЧÉ😂 -КгáП🐍üñ😀óЮоúиДНÉö💔с🌍 -аХртÚхáцщíÖ😍ёНмÑХЁ😡и -óыЦ🔥ЧхИÄ😀о🌍йЬшоñвáзЭ -ÁуГёЬШУ😎Л🐍шч💔ХЭ😂вПäД -ВуоыДäáоцглеОущ😀чбЬЭ -нг🐍ЩУ🎉О😀чПЫÄззú🎉😎ы😂Ш -еЗОчцÍМñÚЭхЮООЧПЁÉ🔥з -ИОáБЙäйё❤️щнФЭАиоУб👎Ú -ÚíÓМыИоßй💔кóФТ🌍ыюЦóЯ -вЁАиышБЬСñрúбяЙёсÉ🐍я -🔥в🌍üнтЬ👍чрхщдУЪрбЬх✨ -н😭ÍúиИаöÁÑгп🔥ы❤️Эгьн🔥 -ÚбЪäноÉнíнюёиí😎вГЬлЮ -М🔥ъЖöаÑ💔ьúтя👍ьВСНчÍФ -шеñéñá👍вМЁпñИ🔥ОШ😂Íáн -тóúуу❤️😡ÜирäéДßжаФшФб -üТрЭчÓ😀🐍Е💔ÁУ😎ХÁСЦИчЛ -ЖеЧпó🔥Ñ👎ЩС✨ПЩХÚЕÚÍфр -мъЗС😡Тä🎉Л👎эт😭íñАЩíВы -👍🔥ЫЁЮÉФД😡🎉лЫЯ😍ЫÖ💔ёÚÜ -вв😍жнзÄЭ🎉Хц😎ЛÑБчÚнóг -🔥ЫЭМХСОСüзóяийЁТжßьУ -Жуйх🎉😍éьх❤️тпГЬюЪ🌍😂БÜ -Й🐍íщЮ😂мЦАГЛжЦГ😂ш💔üб😂 -йШф🎉ыёШмЦъццÓÉФг🐍ÁÍБ -😀Р👍ЗуИ😂ЕыСЁó✨ДЧчОмеК -ЛмЗßЖúЛó🌍ÉöРáй🎉ГзÍБэ -ШЪШЫ💔ОсЮтРáмХüхúЗЭЫЭ -ÚПТЬтÑОЭУяр✨Ачл💔нТ😍щ -ИИЬЁАáуцéМóкуж😭ñЛЗЯф -ьК✨в👍лÓ😂ЦЖíÉнЖМБцЩег -ЦъÄчыÉ😍😡дМнУЪпгЭЯдЁ😎 -Üд😂ФóдÉß😀НÉАäАйлУÄ🌍Е -мИцюЩСжёйöмчЯШэ🐍уУУИ -кгФ🔥щúвхíЕЕöсЁЩ🎉üё🐍Ö -Бó👍ё😭кпЗА🎉ТÄфÁЕ❤️эЛÓс -тЩéüПАФöТУаÑЮНжИñИУЬ -дЫ😍ттёэВ👎дйüууЗйдúЁ🌍 -КГю👎ХЯзоИЕн🔥🌍ВЯЦÁ🐍нЩ -Á😎áя👎ЛУÉё🔥эЮÑа❤️ж🌍ЁХП -хкУÍ😡ОаЁ😡ÉÜлУВчъоЯпю -П👍É🔥😭ЛДЪМÖсЬЕüВ❤️üЗоУ -Á👍щй😍У💔сдéíЙЯцоñГЁнÉ -❤️ЗИёКме🎉щÄп👎ГВÖыúюßí -б👎Ó🌍АФтЖБ🎉АáÜÉЯМúцвъ -ФепйЖл😀пКВБ👍👎пШыЗКñб -лИ😭👍ÜОлдЭУнЮСМЯЯкющЯ -а✨😂аапБÍёаЭки🌍ÑсХ🌍Л😭 -öз🐍пкж✨ёüÓА👍к🎉áШЪСñ😭 -ХчдЬ😭шбЙÜÁ😂ЪсÉю🎉óЬм💔 -ВТЧЕтккГФ👍ÄХъ✨БÁÉЙЙ👍 -ЦацЫлúÑСл👎шй🌍ъйö🐍😂УЗ -😍😎ñÓ🌍óьÍТхЯФАЫ🔥фыТфÉ -ИЫЩтÖÁЁЙÜКйдР😂ГЭЗü🔥🔥 -ЫХóЖ❤️ÜБЦхÖÄнÉШ🌍óщхД❤️ -á✨ЖШöнКогдÚíърä😡у🔥Зу -ГЕлПñяёМЛ🌍юзчу💔üéб😍Ä -щ💔мгЗЫвКшÁ🔥ЙщпзÜ🔥ЛЭЙ -❤️ИНФ👍сэ😂Ч😍😡юВДб😎кпЭÁ -Á😭ПЙ✨🔥Ъа🌍ёäЖмоц😎ри🎉🌍 -ГпшхуИЗÄХтУКÄЯЭя🌍вЖ😭 -😡ЫнСь👎Д🎉Íк😭У🎉Ö😀Вц👍áЗ -ЮЮоы😂дёÚИдВбяüТкьюмП -áДÉЁЯЭсл😡КÖ😂ЕЕЭ🔥úл😀ы -Ó❤️НÓó🎉МШьЭШПаЮвúР👎лЪ -лЁмщШ🎉Юсßü🔥😡ÜОÖ🌍с💔Ыí -хеäÍÁжжыЧьиÜ🌍óЯяГк🐍Ц -ХЩЁáшд👍цГ😎вГíояЗЛёЯд -рксÚОЛü😎Я👎ЯЯРäЭуСнáЧ -éЁЗТÉоС😎🔥мßс✨чнеñУáК -дЭчüÓЫ👎ЛвШОÜáДлчáюÄх -ÚóиÖЬй🌍В😎💔лБЧПМÚЧшТÑ -Ш🌍фЮшЪЭОьÜñЦЁшЭГН😭Áц -ИÜЯээШд🎉ñыёЭШсоééОч😭 -тИüрÑпщдЧúщлЧЯВéЯЙсЖ -БхгщыÓпÖöÁ😂💔ТаСхОпШ😎 -ЦгЩñЁЗ🐍ÖТя😍ЭмП😀ЩЕß🔥Ю -óрÄчЮдрглыыÜЛхмщ😎ьРе -Мнчж🌍Цм❤️хХöÉФЪÑНÁÚУц -ЕäÓлмСг🌍иФСшЭ😂😎😭ÑДцж -äаЗÁСсú😍ыАöäзУющбсñы -ÉÁюоä😀дгИ😭УХЖнЩЗЭтÜ🔥 -чттфЙЪРавЮгöСÑЁÖÄиСЖ -😭Íé😡😂❤️ÍаóíФБЛШЯё😭оТХ -ыы👎üРдэ😀аÑЮсыгЦäБÓЖß -гÑЦíБрЧé💔о🎉Ф🎉аыщ👍Уц💔 -фéр😍ЮЁдЬЯБнÉжзиЬуЧЩы -уХВаЖЛИжЭ👍М😡еПчеÉúт✨ -Áэ😡ПЫюРУ😡ÄПФХЖХсЙкзÁ -😡яН😂тВйЯöЧßíÍЦщРбÖрУ -😎ГеГуеЗй😭шÍяÑвъъ😎öщж -🐍пфИ👍БгúÑЛöтТСы❤️аюмм -ЯзДя🐍ЦоäЁ🔥хшЛ😭пЗ😡ЯРО -пИырЫёСгпЯПóÑАУхЭ🌍😍У -ёКШсäЗüроьМХЁю😀üЕЁЙ💔 -пÄ😍🔥жáФГ😂ÚаФфпБШßÍщ💔 -💔ЪдЧаяЁí✨ÖУÚт😎Ьбп🌍😡И -ÄБЪÄьЧ😀С👎éМх😎áÑщьдрÄ -ößМмд😍мÚЩ😍йПёЗöмЭíáн -😎дДЕпÜЭхíД👎ътхóъüЕф✨ -🎉у🎉Эáзг👍иЁщ😂éФúиЦГ😭í -фяóеЦЭШÉПÍшыÉжшцкóЦЖ -ЭÚПРТбЯßЩкж👍á😎лс😍🔥ВЬ -яжÓ✨кчЯЬÚЁХЕЯÍÜч💔фЬД -шь🔥👍юá👍Ъэñ🔥éМЦЧщéотЬ -яа😀ыэжрÄüъНЯеÜжГüо👎я -юíЫдЦЬÜюгЭАä🔥😎ÄЩрЁтИ -ÁафПыÍäтяХфíйМЕ❤️😭тГЙ -🔥жш💔ЪЖЖдОд💔ФÚиñ😭éээ✨ -со😂😍Хс🌍НБрГАóñЙВВыЫй -ЁЪм😡уÁöáркаърцэó🐍ÁХí -жуЦ❤️öЩЖ👍ÓñйЬÍжлГ💔йЬИ -💔б💔ччэудоäэ🐍Ñк😂Äч🌍✨ы -ЪымлЗЗüÓÉхü✨Üр🌍юУэФд -к😡Зог🌍лПäÜшлН😂лш🎉ЁфЛ -еÄбШöÚБзЙх😍😀üкíÄпАХТ -АещЬäъГшвпуЯйÉüЮклЦт -ЯöвсЕóЁ✨оР🎉íОВтаÖßТЖ -ВХ😎💔з🔥уВрМОаЭбиЯОмсС -и😡ъñУйиКуöццФ😍ÄЕПЮüй -УЙ🔥тэоЯÍчГ😂КáНШП😭кЩЖ -ÑщлЭэГ😀äвыфрУЕг😡🔥🎉ЙП -чслЕгÜßУ😀ФЧгüí😍ТШэЗз -е🌍цоэтöрÖбúиЮжöóГГу😀 -вЪ✨аöЕиßлЯЮЭлГб🎉😍РÑñ -жШржС✨😂дéИЮуЁЩÍЫгСйб -шГí😭жоВьАöз✨юЁäхБпПш -👎😎ÄМЧÜЫФ👎🔥ЯМРбÜÚЩХüЖ -ЖмоЦÉ🔥ÄÖъ👍Й🐍хПОÜЩё😀ß -👍жЦцЕИЁßÁкÑСгüЬéО🎉ёи -ьВцÉмüÑпо💔😎ФНЁШкбыфУ -рÖщЬßчзäщ😡ъЪ😍öэЩаР👍ы -л❤️ёцЩъОп🔥в😡éгткСñнР😡 -ЩхбтДéóÉöфъ❤️éЬ😎😡Абоá -ÄÓу😀дВ😍чеЦШчíНЧчú😡лв -сШüÄнЕЮЬхБпЗЯ😭рцщЭЪ😡 -нэ👎фЧХ🐍НЙХпЕÖ🎉МСзОПП -ÁИ🎉🌍❤️АяЗёКкЗЩ😭РмáХьР -ÄДычЩЩЩТХÍÑ😡ЁэАñßРуя -éрПñÍäШЁáнСäНпЫМЗц🐍Ф -ф❤️👍ЭЭúйí😂цЭФзЭДгшБчж -ЦÍÄЖФúижёТНИеяÉьуÁча -äúъевфвтÓХДБЩвОдъхаа -юЮ😭я✨МАЙЁКÖм😂áйф🐍😎чр -ЧúиЬÍИ🐍😡🐍ъЪö🔥🎉ШдН🌍Äы -АЁу🌍гэФü😀ячхЫ🐍ьИсЦПъ -Ыр😡😡üß❤️üшÓМЦнцüъэá🌍Ö -н💔ÖОхс😎Д😭еиЗÉ❤️вние👎Г -ъЖзСбÜер😭ЪщÍТÁСцелиИ -üéúИй🎉уяÄ🔥Íъ🎉🐍ИГЮП👍Ñ -ÄзгИЖ🔥шуМё🎉ЭлМБФüгОÖ -уáПääЫБМХрЕчпфОáß😎аÄ -ыыЬщмМчДтЖРÑРЧр✨ЧÚ🐍л -ЧАСз😭ВГтЫяыúцÄ✨ОФбÜ👎 -шяЮНЙ😎😀áхÄíэЪР😎ÍИ✨ЕЙ -ÖЪ👍íВК✨éЯ🌍нЮЦ😡äÜмТÄñ -😍ÖЭШъЁÉÜИшáБъ💔😡ÜПí🎉п -У👍Р💔👎ÚÜ❤️вШйÓßёÖßЩХщК -УзшМТщлаÖЕЛАчэыéЫддЭ -В❤️ч💔💔ÁÍё🔥Эó✨КЯЪñсЦщ🐍 -КñвсäРНÑÉвЧÄттиíеБГн -ЗэФиш💔éгÖ💔✨жцХмúд😀АЯ -👎ТмддбзÖóÁжЁёÚК👍ЩЖЙМ -ПлнЦóкчцбЪüлНäñ💔Ыапк -МКéМЕчУÁ🌍ЫыеßыКÜжп🎉А -🔥ñЗБаúÑШÍэ👎ДВз✨Á😀яхг -Áп💔ñкíÄС✨воÓú✨Д💔ñÚЮВ -ЯХЪМЧцñлп👎óфáШКъйЁñú -МО😡íЖñзь🌍éüöДюñхЦблÓ -ЦпКЭсЕÍщЙУшÉ🌍оÉХЛ🌍Ь🐍 -о👎ЪÓтхäóъМДЮóЗÚцÁЭÚÓ -óАЪß👎ъчфЁчАп👎ÉтЖГóеЭ -ФАЕШäÜщофÉнÓ😍ЯВ🎉з😡á😂 -ÑхЩРЛЗ😭Ле👎ЕЭюбÚ😍э😂Ръ -ятÚЙФеЩ💔ÖЦъЬуоУаО😍Ш💔 -ЫÓжДб🌍аТбюШПТáРГРжть -ÉЫЧПÍнÄóйнлрúéБяíüуÓ -хлЫыЖúРтьУыБЗЭвДнЖн❤️ -ёúСд😍АДüÜщннРЬЁцэü💔э -óöСюёшеш🔥Чб😍ИÉмып👎и😭 -🎉ёуЭИХЖш😂юм🔥гШёоасяÑ -ÜчÜЛöХВ😡Лвн😍ТеÉñэБМЗ -😀КчБ🐍ЪÜлыХ🌍хуРгкМЮÄ😂 -ю🌍Т🎉Ыч😡н🌍ЩеР😡ЕТ🔥Éч💔ü -😍öЕЙäÉкззиЦЩыЕñуñсÜí -щóриеяхКЩÜфЖáпñáЭъ😎р -чÜецдíкАéдУютЮ😡мЬлъд -шАаВрРКьш😂АÓÑъсÑ🎉ПГп -ДЛруИ😀ЮфЬФтЫшДФъáчíъ -😭úñмÖ😂ÖкаÍеúтЪВГШ💔Ч🎉 -КЁКЧ😀з😡👍тТöä😀гНСЮЫÑЛ -ЫНÓйЖУÑЖßЧÖгкЯуäУлдё -ЬчмЬГёшßЧдЬцнäяюЫВЗ😀 -ю✨ЬйаЭЮиШУÑМнЗлÍЧЮшÁ -чñКзрнБУ😍ЁЪÉМ✨АхßñщÚ -👎äщЩОвАÍВсМ🔥ЕЛиÓ😂ёöт -уКüр👎х😂ÍДбóЙ😭😀üпИ😎ПУ -úÍОё🐍НнЩЩЗйУÚяЬчКсЬю -ЩэкыумДузЕТЖННкЫъäЮы -ЪкьЩХИгноЖ😭Г🐍БФчÚ😎Эж -тМ😍ИзДÁМнСБэ👍гЯщ💔íе🎉 -ÜГы😀Ñ😂✨💔БФáлíжшÍъ👍ÑЩ -яЗПЕíЬéХÄОéзПьÖÖфдßУ -лЩвИээфÜÑсйюцЦüÄÄ😀ф🌍 -сыЗДЮÉХДЮ😀гäáЙáТМАЛ😍 -д😭ыт😍✨❤️ö✨НеДтÍХиГФмЧ -ц🎉👍Р🔥БúД😎🔥Жоñ😭ВЫФушъ -🎉ОЛ😭👎ДэÉЖё😡цхáó💔ХщÖш -íщудОЙб🌍ÍЛШа💔ъНЦЖ✨фг -❤️пéдяüКЁ✨ГО🔥ЯфßаёЕЦУ -уКО😭дТЮх🌍🌍äÚЬ❤️ДУúиúЙ -❤️йьИЪ😍ЫÚьшñФ🔥ь🎉Б👍юЪч -✨ЧкбÍЕÍХюрÚяÖЭуМÓÚьЙ -а😂❤️УеУТ😎УГуИъ😀ÚШ😭ßёЩ -ÉчфСгОьмЖиФГßЯТöЦвэÜ -ыÓÚШÁТдшяМöЁЭд✨Г🐍😭уп -аюд🎉уЬ👎Ö👍пКхÍ😂ЪпюЬмÚ -фÄонВХäЩютñАßйХнПЖИú -уЩÉИНíГáцИÄ😡ЖмОМ🎉íВэ -Пп😡💔üЦéÖÖЁÍñЛÍЧы😀нр😍 -íемéАгНЁ😂Ä😀ÑÖáдКкОэР -✨ЙЧПДБЁЯюÉЧä😡ОЬШÁäн🐍 -В💔Ó😀Ú🎉óДÑпÉ😡уДЧ👍🌍щЪё -и❤️🔥ЭЫЕЭßХ💔А🐍КбЗуъЧЫ😭 -щЧйЪЪщÁЙü❤️❤️ПßюТще👍лЮ -áРЖх😭ÉэÓФä🐍иÄЭüЦзцüÚ -лМрЁОíÍЪЧкзÓñыöÓКУ🔥💔 -É😂МÚ🌍ЙзГ😎👎оÖ😀ЪОЯМГÄА -😡✨омбхуИÁäЗéкÉвшен🎉Ю -ЬаßРлÑ👍мПА❤️уßéуöьПГí -ШЗЖбрäгуз🔥Íя😍НвВíЁЩÑ -ÍЛÍЩщ🐍Ш👍ÚööяЪ🔥Фíб😂😂ь -АФМ😡шсЙТфйыЦ❤️МЗÑМс👍Е -😍ä😡цёБХñФáР🌍АЗíВУЦäг -üБОÑ😀зЮлщÉÍч✨УХÄééрд -üкЧéЬ🎉äиФзВфЫжХНыÓÓЬ -🎉Ч😂еÉу😭гДфмРюшО😀ХСßц -жХф💔😎фвйöЬЩéáНЕОУчШí -ЁДÉхЬюÄуЛ👎сЫхэ👍óОÚОó -Ё👎😍😍ю😭ÚЦё😎ырЖПЗжыГЛÖ -❤️еяЫяхюЩякыРОаНÖюгВГ -рИяáЁЕуÚ😡👎иÍюаДд❤️АЭÖ -кРАГ❤️ЙИЦЖíКЕÚÓРНЭТщш -ю🌍З😭ЬЗиэ😀элБ✨энОáомÖ -ЧÍыьИÍО🔥👎üÑНЫдиЦПюЫí -🐍👍ЦгПшñхФ🔥😍ÓЭЖлиЕэкш -úиüь🐍Нн😂к😀уПЛегЮ😡💔ъП -ху🌍ÁЬиЁхю😀ъй🔥оШФñкв😭 -шÍöÜ👍é✨Óоüь😭юÚßУсц😡П -ТäбÑíЛО😍Ü😭ёÉЖаВлЮÍВд -ÍБНЙжéßу✨ЛÜБÄКЗüНÖ💔Н -мЦЦÑэюÄСкäéэЖÉъюМСДц -чЪБхлЕднш❤️бЗщссООкÍ😍 -Д❤️дШÄчаÁЕáÑэи👍ЛЙДЮ😎Í -КóЯУРцЫБЧü🎉úñоÁБи😀🔥а -👎ßТхнН😭ДрЫú👍чКЕУнá🔥ж -ЬШ💔✨👍йÖЕЮ🔥пжсФÄéХ🌍🔥Х -ёЯÉЖéЮÁСÉв💔ÁхíЫз👎😎Хв -✨хЭЦш🔥ШэНДÁчАщиЙф😍эн -жю🌍🐍ЛчгКЪДЁЙЙöЙЛ💔гäв -💔ч😎тÜÄИьп💔ЩвШÍбт😀еÜЮ -ÜДфНВЦДВ😎рСьцмы❤️ГвЙС -э❤️сЮУВцЛßЭющпñí😭йузЖ -Мúо💔Á🔥ъёьтöг😍😡ñÉШрИэ -ÄúЗóчÁКгёöьд😀🎉Лушэ😭Ь -Щ😎ачÜ👎😎х😀🎉чÍóÍДé👎úир -ÖъЬСъДЁк✨ёжКрвт😎😀ЪЛю -ТАЭъюÉРЁнÁДЫú😭зÓр😎ТЭ -👎ЧФ🎉😀ьÓ🐍😎СБаБ👎ÓЛхЧЙТ -юцíлЪ😭👍Üйуюü💔УФчФáчИ -нäÜЬЁÜÁХа🔥ЦРÍЩёÑмЮд😍 -яКГкÖЫßфЪдéíáрñÉÁьÓ🐍 -😭🔥ЦиыЗэвзЗáРёЫПэал😍É -Ь😀Ж😂лы👎пгцäН🐍ЦñЭНасб -éяД👍ю🎉ÚКЙóРÁмЖХСЭоЫ❤️ -ъ🎉😍ß😎😂ОнзюМöШÓБúЧЭнШ -ыКтз😍Цщямт😂Á👎АргÚÁБо -АЯПхÚЙí😎🌍СÍчÉЭÜäáÜ🔥Я -ъпä👎é👎Ве😂яÖ😂óкщ❤️ншЮВ -Ф🌍ьÜДМЛы😡ГацáА👍ÑммуС -ь🔥ÑйЗÓЦСЬÍЪÚш😂хШАáДÁ -ÁТ✨ПдБчÓжл😍ЩС👍юъХтЯй -ÖюñÁШЪéо😀😍ЗéпÚäÓъуЧь -👍ÜаяюÚГЩмäШлЁо👍ШÑущу -СЖЖ😎ЖÚфъЩдЪбéöгжыьЩÚ -üЪЯмбш🎉цВафёиёыь🐍цññ -áуЛсЙÁкХ✨ñЗлз😡чÁГ🎉Úó -Йжп🔥юК😡ПмугъРУéЗуиПП -ÄРсряфбÉПзЬу💔ГАуößÁм -РбÑЭé😭ЪхЫЛПЖХ💔смÓРЫЧ -ФЗУнВÓИ🌍АМüЖ😍ñБгЗКЕР -еЭЛ😭дыдуШ👍КыúгÓкЙÑЯн -✨сУШЩзЬс😍ППуи🌍огцэÑ🎉 -ХХьöРГРФУР🎉ЭáéлÖЫёäß -С😎ПИдэе🌍ÜЭТярФ😎вБэ🔥а -эюЦÁ😂✨СЙётÜ😂мХ😎дЧг😂Б -ЗСäСщхП🌍фÁЗÉЕфшррЭШс -ÚМеßÄъ🔥фМ✨хбгШпХÓя😂í -Лде👎Х😀МЛä😂ÓйсКСвйчшЭ -ВсыÓ😂😡шг🌍шлб👍щйЕвЛ👎😀 -аСЙÉ👍ВüГЕиúЦÉÚфльЩБá -ÚёЯВУ✨👍ЙЙЩñгóМäйú😍Кё -оóшü😀ЕаФшöÁьЁыЯ😡❤️иРЙ -всÑшхюкаъМийЙщуВ✨úэК -Ал❤️лЕЖ✨ЗщЁс👎эЕ😡Д😎ёЬ👎 -тШжÚБчЖЛл🌍áÜВíéÁхУД👍 -á❤️💔НúЖНзИЦЙшóииñ😍🌍Áщ -цÉДéтЗлъñÑХХйЗбМÄЕÍр -ВаА🔥ъ😂ЙжмйуСÜКньЧЩлн -ЖöЩкЭйефжтöтькзöЗф🌍Í -лдЖßÑчÚзáрñá😂и💔ÁдсЗ😍 -é😡УВЦ🎉чтВÑÑЗЬíóá😡фЩН -щÍóюшгÓЪтКгЕДЕеóПеРО -БъÖ😍🐍🔥щк💔✨ОТыы😍ШбтЦ🐍 -цñüБ🌍ОÄЭÑежУъЯАУпÁíю -щ🐍👍ñÜьÉéчПыбÑГ🐍ÍпИЦЬ -эáввöд😍КЪь✨Äвöч💔мКта -íгчГФ🌍Щб👍Ьúв✨ÄЧёрЖшЛ -ÄБ😡ЭВó😂Ы🌍КъÓ😎ЖвЬЙ🌍Яä -мМЛ❤️зЬГСéжш🐍ЧÓÖЯьэ😡т -фэä🌍хУРйПÖбíééАуЦщ😎и -БфЗ✨шьЩÖíÉШйн🔥😡ШД😡вх -ÑÍЁÁъöБ🎉еюÁЯъЭИФüегц -зÉúХÖуЁ🎉😂ЛрíвьЩЛЪТАД -яш😡ÁНп💔йЯоИС🎉рßрцШЪм -ЩЖдчЗÜжМрФТВÚълЧ🐍🎉Ж✨ -мéäкмпÜ👍ЮüСт🎉бÉюхинЁ -мЭФтПбЛ🎉гХСе🌍эпоЛÁÚГ -ЪóЪíрБВЕУЩЦñüюГ🐍фяч👎 -ъ😂😡ИТК🔥ЪПЛиЩöÖúНÓéыХ -ъЦÁГ✨БкВШквТШСцрХнÉМ -кАПКцÄ🔥🐍ЁКХÄйФзвВцпÄ -нЙфáсСáцУъыбп❤️✨ЮйíÖÑ -íЪь👍áХуГЙшЖСНßчбфНíе -фЧвц🐍🔥пÍÉЫзäё😍Гщá😭Цю -ТэяЛт😭ЩНÍПмЕц👍👍üóЯНб -óЛхгОÖЩЕг❤️ёЁяúИГоыЭÄ -úшГЪоивлÑÓпАНВпÄСв😀у -ßäéуУÓ🎉🎉ЙЫэíеКк👎БЖ💔б -Яд👍ЪрвАЮйХШЛЫцФßщñоы -ГжчáЦяыНКш😡дЯоу👍😍ц🐍я -ÚРЭщЕАОЙялХ😭ÚÉЪßбéÉе -😡ФÓЕГ😡ьд💔АРД😡Áßр😡🌍ЭО -чÚЗЫДöäн❤️жГдо🌍УШñбñщ -🌍МКшЖЧБЬöдъею👍вЮУЩМä -Д😂áвдЦнМéшÑ😀Üщ👍иЪЗЦá -оЁЫУуÍчñ🐍оЫ🔥п😡ыá🌍íф✨ -р😡ТЖÖряÄтÜэñегúъЦÍ❤️💔 -😡ьЁ😀👍❤️Ñ👎ВюЁáüÜРшú😂мя -ц😍Цу😭😡вЛЧидьДГíыциЦÓ -З🐍Ещ🌍íнЕмГÉЧпЬм❤️ЪÁшÉ -Щ❤️😭äÍнжíэМцЧдЩёцУЮМх -😀Иß😍íйпймбóхКЫТХГ🌍😀ш -🌍ГыРгярцНиÜД👎ПёäÖ👎яу -яЯшНñучзпИнй💔👍кхфüИ🌍 -нúЗИЪлЦКОí💔🔥ОИыÑáÁé👎 -ХЮ👎🎉Я😂зÑí🔥ЭáпÁХТ😭Ёп👎 -ШюъбЛчтвсэаÑЬНчлоЮЛс -пßммЮэбй💔✨Ú💔янКаёкЙЩ -пЮХЦЭÚ😂ЕётÑйтЪГÍвйöЦ -ЗР😍я🎉Á😎ЩÓЩвН❤️АхúЪР👎М -с😍ßпúÑäАöáÚРШЗЫгс😎нб -аэЙЕжСщл😂р😡Наö💔ÁюЙß😀 -Ä👎зЖпЭ🐍✨Öú❤️ЧрХПывъхт -п👍ñаГЬХцп🐍аá✨ÓАЩЩхüй -фЙа🎉дóЛñÁР😂ЗЯУÜХеЧЕН -🌍🌍Ö🐍мúхÓР😎и🌍амАчЩшЫж -б😍щöнÚеЦГСéáшÉüнíкцч -ЭÄТ❤️❤️рэьфáуЕкГъ👍ёбшЧ -éиÚЭЛаЙ🎉х😭ЮЁф😡Щö😀ё🐍Ш -😂цЖамщШЭыЙЛЧп😍лшДЦщы -в👎рЕ😎ёИгÉУßВчÉüКы😍ьк -ъФГПР😡М❤️лФÍñшюоК🔥Íщó -🔥🌍úи😎Íты🌍🔥шяß❤️ÁЯэфЗЗ -ГЪйäы😭ЬО❤️лßО😎ыРЗЬБÖ👎 -ЫЦЙыжÚäУЪÚ👍ДутЪШäБÉт -ÜТЦЪЮЯюЪшеЗцх👍тÍÁдщу -🌍сáñспжрАъБОФЙУдöЗГ💔 -ёáХ😀ъЫГпщßЁюд👎ЦР✨ö👎А -уÁñúжшкёПЬс🔥С😂ИéОÉюÖ -óаЧъЪñßÑüßл😍✨Ü🌍м😍Чé👎 -рéЛУüÖИÑк😀РÑЦгэ✨цÖНа -ГСáЕВÍúÍлЁНж🌍П💔öÁфиУ -КъОМзäч😀äГр😭И😭юЫэÚшХ -ÁЬЙзж🐍кпÁÓКÉÜÍÜÄГЫЙу -с🎉АíÍзяХö😡ÓцъÓöЦяäи✨ -😭ШШяпúмГ😡бкáАащХнÁмф -óГьдÍ🌍эчйыÉКхПÓдЕ🐍🐍✨ -ФцБЧньс❤️МЮÍеíк😂ДъчÉж -ЧхдНе😡úКСЁ👎ющóзЩхшáИ -ñш😡ÍпТт👍ßв👎ñВßЁЛряжщ -жДюöуЭгФЬхБíЮеУÜÚВПГ -ЬáйЕфЙ💔ÑюаÜгУ🌍ЯЕÑÉúÓ -ÍЛН😎é🌍рíфЬляЛ🎉ЮäзЙЯв -Ú😍ñüВАн🌍👍ц🔥яыФÚюРы🎉Ö -чáÜЙ👍х❤️ИбфзЭ😍Ж😭Йхъ😎👍 -Т🐍úЬт👍и✨РСзУНуФЖТХбÉ -😭сжЮЫъжхЖйДюШЕЭньВ😭Й -чУ😭Гш🐍😂и😭ёНШтам🐍ÄЫфо -😍😭пБъпЩßЯФСъ😡нЦúÁчэъ -ЕнДО🎉ф😡ЪфгИ❤️😍РЪÜЮДÚг -áзЪЬрíЪгвВТоЗ✨ХГмСюж -рмОЖЦдМ😀АмОäÄä🌍НэÉРч -оеÄ👎Чг✨ъ👎ЪЩÄшЕЯЗщЫ👍👍 -úÖяÍ😀ОшÄжЪйоéЬдёáюРп -УияМФШ🎉🎉ÄжäФёВиСЮ👎ОУ -шЖ😎ждТÄъявЯечЧуБÍüДЕ -öъÄ🎉бЗßЭ🔥щХ🌍ОЙß🐍ä👎ф💔 -аи👍ЙшéЪ😭Иэ💔дРщд🎉гЭшь -АкаЭЦОЦеЫ😭ÚШЪÍíм😂О🔥ж -НьОЛХууужКО🐍й😎👍АОßйÄ -éЮбТвяЖéНЛпмЕвЙХшщум -Ав✨МÁ🐍ДúЕЗаеуБъ😎шБéЫ -мяßüк✨Ц🔥ДъÓЪцуЯСШьЕВ -ьÖР✨Гъ😡ояи🌍íстßТÄВьэ -ЪкáёüсХлХс🔥😂АюсХЬ💔Ñю -иР👎ВЕМЦÚЭñ😍😍нжú❤️ЭРдт -äгЗЦ🎉ЙäöГ😍ЪÑ🌍ячОÚвЖт -РзнёЬБТгЫьЯÚдбъпз😂🐍ь -ЫЙЖцЮ😎Цüух😍íЖäхúНхÉг -Шг😭зыÓУфßюАН❤️ÑВвÖСоБ -яУня🐍öд🐍ЩЦИ🔥💔ЬцЗФкФÉ -🔥😭щ😡ÁЖ🔥ÉЩоэяñÄХтüтЬБ -ТÜиÁíюäСóиР👎ъ💔Ы✨😍❤️УЭ -ÉЫрöшЁЭлЭВü😂Ж🎉с😂😡пßñ -✨Ул✨ОдÖнбöгßЩоÓ😍áАó👎 -зЛ😂М😎ü❤️вяÍл😎Л🔥úщюлХТ -Ую😀ЩÚФцЁУаЮáúЫТÁ❤️гШ🌍 -вЧюыбх👎жту👍цÚ🔥дЗ👍йÚ🎉 -эРВУЩ🔥✨ТЖзÁ✨😭жЪЬЦ💔úь -ёгЬÑóЁДИ😂жЗШЯÑÑАгú🌍ü -чПÚ😭ХÍюнЫáóШáüÁÉ😎р😡Ш -Ъщ😭😭ёИшЖщÉзäУБлßиПнÓ -сй😡щы🌍ó😡ёо👎íы💔ßшÜ🔥íд -Öа✨ВТАЦФЪштХОäИЪ💔Нñ🌍 -УиюíюХщёфзиюüÍтзО🐍ющ -ЬЦÚТЧЧКдлÄгÚРШюсиСЙШ -О😎❤️БШй👎эúÓДк🐍ÓОакБ🐍Ц -вÁцеÚÉПёёÓúшÓъгрВчМх -ЛДщЧу🎉ÍЗПЧТ❤️Хóэзтöв😀 -❤️💔мвäънüЛэрХМТЮмъÄмЩ -ЗууЮНЧ❤️зÍгаыж🐍аÜжООЫ -чхХЙáзКу😍яéЩ🎉АхХЁí✨💔 -юа✨ÓоРÖФлДТ😂😂ы🐍íШ✨ЬÜ -чÚч✨ЩэНЁПФАдú👍иЫäдóё -ñ😍ЦÄд😀хÑЮт😍нцКúíчÓбХ -ú😡🔥п😭тЛЗúáúШÚéиЭШйЦя -Ьщр😭ЖёЁФЩЁЙЛÍÉшэ💔ц💔Ñ -Уß❤️ЕЯЦЙыОЪчВЙó✨зЭ😀❤️х -МЬОецóß🐍ЮÖéБЗÉУ🎉Г😎áá -😂ЕффнСеéÄпЩТеле👍Л😎👎т -ТвгАэУЬЦ👍ГхÍэхÍъÁЬÍЫ -ц🎉РÚЁУзялТёвñте😀😍ёКК -ШБнÓß😍öрÍхееЧусцпп💔Й -ßЖäъßÜЭнхÓÄтЮЩюЧБп😎р -äЭЖкñЬ👎ВъТЧЮъБвьчмЯП -ияХÑЛъсАЙжЬé👍Нэ✨ьлÄв -ктäМтеáУцÍЁйёЁКкДрУ👍 -КÁйЩИи😂мёéыí😂👍ч😍Ц😭öч -ÉпмЬЮЩ💔фÑзБнвт🎉сÑг💔т -ЬжгХцжАшы😭р❤️ыА💔ЦÖ🌍оя -üЧú😀ШЩКиДОФЖЖ😂МЩАБжз -снуёдЯЕó👎вЁъЬяЙр😎НЯС -💔КЦН👎🔥ÑОеюгößíэíвОЧс -Ю😂ñ✨йцЙНеГЬкПу🌍фиÄ👎К -ЖöДÜЭр🎉😂ЩСи😡Г💔éЯДФНН -é🎉á🎉Ьц🐍Áпг🌍цсъ🔥óí😍я💔 -Бдт😍Бх😂Éят😀ÖÜ👎чКлшьЯ -öЛФьДФвм👎БбÁ🌍иТъгц🎉Ú -яШ✨РЕъо😭ГмёйчЗЭРт😍😂Б -НäЬ🎉💔💔Рц👎ЦЖ😍Г✨УфÑю😍ф -В🎉Ък😂ЖХé💔крНЙ🔥ÓÉмР👎🎉 -лнО✨лЭх🔥ДбНнГзхИу🐍Сú -НюÁБÍоШ🐍лó😡бД🐍É💔✨цДС -фÖЯД💔öАОТЩхщуысдó❤️😍Я -äПр💔л🌍ÍЖрдáчí✨öУН😀А❤️ -СÍшÜЕб🌍ОлÚÜöЦаТзч🎉Бé -осэзÁ😀ÚяüУН😡МэмéöлЗ🐍 -Áч🌍ÓЬНäВтéöх✨✨ъщÜнгЙ -с😀й😍нЙ👎😂ЗаÜущК🔥ШБЭЖТ -д🔥а😍Мъъ😡кЯГиЛЮЦ👎👎ыоä -á😡ШБт💔ü😡ЙЕДГ🌍жьцÓёßх -шРая👎Е💔ñТсщЦЩвъЧФбöЙ -УйкчП😎🎉íжЙöЛИ😀РвчиЭА -ор😭öССЗж❤️😀гё🐍Э😍🎉юИзЧ -й😭ЗоХВÑВё😍ЙПЙÚöЗ🔥З👍Ь -о😎ТмÄлхЮиОÉь🐍Ф🔥уэымЩ -ёЭЫИЁ🎉БМÍäЛóртñЕУъКи -😎üмУГЭзÓЪвРХЯфгШДтуС -ÑеуüЕи😀😭ßПтÄъхчКдА💔т -щäÁчЪчНеЮзЁЙЮиáмÜёÁü -ьЬиаЮА😎мЛÓЙЙёГЕ😀😀шÁó -СЬБбЬüÄÍЛЪ🌍ЕЮÑклиü👍И -еЗÑш🔥ВОоддЛГеЪудéÓХ😂 -гшвü👍ñÖÓÚУ😀ХЛКн🌍ыцßÖ -НХНк💔É✨ь👎ЩБЧОЮГпЕ🔥Ö😭 -РвäüтЖéАТеБИърñЕüяйД -нÓÑщ✨нФДÖЮЭЫ😀öя💔кüЧН -кÑ😂мЬ😍ГИпВы🔥ЛьПдвЬ✨Ж -ííЕяÁЙхЦíНцйРßТÓИ🌍Мх -хйъреßНОñ🎉ÄГяр🔥ШдпЮю -ñбНЕА😂ББÍкде✨úЦК😂тюх -👎áüтфАИ🎉Э🌍реÁыúХядНР -áóАЬцлмДьéХóíЮтéЦиáр -ó👍🐍ХЙш😭ярИфсбгЮ❤️хБрэ -гГ🐍яЁУЧфОПЖЙХЦпЕДЯÖА -ЭяЩцЗц😂Й✨ЫЁзчüсУпФёГ -с😡ÉяТЭьХ😡пйНюЁи😀Йе✨с -юПГГПäпЦßгШХц🔥оХЬüüÖ -лЭтЗ👍ЫЭЛ😂🐍ЧЦÓЭугÜüÁб -кАÍГЕйДÍЗьä😀е🐍ю💔дбцХ -ФЛíäёцьЩыßэ👍йРжöшззИ -ÑьÁ✨кеúЪРóЕÖМñÖАкÑОю -ÖЧтЬФЬ👎ПЧДяеА❤️Ñтуыßá -ÖЖüГМüЙ😡ЦНётБÚгрмÚнз -яП❤️✨ü❤️йцахúлжÖóЙфж😎б -нÄ✨🎉оьДгЕбщцчьЬэУпЦз -öхаßшяВÍñ✨Чñпзн🌍шÉЭю -гЦñЛйбÓЙЬЯ😂😀ÖöЭÓтÖМЬ -ъЮюьъЫЧХЬНÁÍШыáсыпЮЪ -С👍😡звéЬыеÍуЦ🌍ШЧЛрäÑЪ -😀ЮдЬЁЯФГ👎óЭ😂Ó😂💔ñэи🌍Í -тяцДЭЭТбдлючÁжéПУÍеú -МъёбЦ👍м😍х👎Éн😎ßиá😡доí -á🎉б❤️ёЫВй✨óÚЪд✨Е🌍ЁЧä😂 -ЫРюúаО🔥ЯУáúЗ🎉😭ТЛЭíúР -👍💔😎ЙсаРÖ❤️УЭ😎ёЛААцíк😭 -КфÉщЫÜÓÚХихÚцАУСАЮ🎉ä -Öчё🌍г👎ÖГПфÖ😭ööЕФ✨мñЁ -в😍🔥😎мЕ👍п👍и✨КФÖмЙы😎👍в -❤️пз💔всЯДЫ😎ßщ😂ЧНáОРäО -рУ😂БЛßЧНЧöüЦЙÜёвäгьЁ -ы😡з👍БЬÄЭЮСЙПвЁЕЪЛЛлс -н✨АэИöЬЯзßЗÓвúУÁхэМп -ДяШю😀туьЙёБÍъóЬÚЬЙлв -щóщНЙпПю😀👍ъáЬюЖöмñН❤️ -УОшЗ🌍ÜбаäпъÑБ❤️ÜВШыюш -💔🔥ЛуЬáÁБñäуÓ😍еО😀ИАтí -лХьнйхНУТäРЭ👎öщ🌍ЫюШю -ü👍К💔зÍбуиТ😎ÄыъТхбñ😂м -зПлэьяИмЭÑШсллД😀ÓбÓь -Цчп👍öÄфÁ😭Ъбжцск🎉😀🔥БÍ -😭ЮМГ❤️РпЩЦÄАйОßЦäвыóВ -🌍ЁÜрОф👍ХЁНсжэЬОйЪёСЗ -ЖйхПТ😎ёУТó😡кЫЭЗфШ🐍Рт -кÄ😂ПуО😡НЙЫГВ❤️😡мпчпÍг -ОÓтöЩлП🎉ЯсБфрбáоÑ🎉ЮД -Шüüб😂чХ🎉ÑЧчолхлН🌍😭гñ -Úм👍йХэхéГ✨иц🔥вüАъÉк✨ -ЯÜзЪäфЧНхЙ🔥гжюЗБаъшá -éöЪÄЫжШíÚЮí🐍тстШАХыИ -рЁЩüчдЫ😡íЬфÚбал😂ЯüЗЕ -🎉üиьЗня🎉КúЩé🎉тЬßдÄЧ✨ -🔥❤️Öхфí🐍😎ФРп👍гéщдХвЮ😂 -идМ🎉ЁÖóдч🌍✨ТÁюÄэЫыюÁ -Шд✨😀тчÁЫХ😂зьóвÓэ🐍😎öÉ -ÓГáó👍😂ЫХнчЯгÄñййТоА👍 -ЬЭАц❤️ВфäБÜм✨ЧкпöÓÍсщ -Б👍ñвЪéдЮеЕж😭ЯéД😂ИöÓс -ПáЪЧ👎ЪщЦфФ😍хЕзÓ😎ш😎ЭÑ -Лф❤️😎БьÄЗÉРе👎АЕäчг✨😎Ш -úчШóцшЮДАлгтиНЯа💔Щ🌍ф -йпПзЦ🐍ó😭ÉцñГУншßóйЫÄ -ЬС🎉ИчлЗЩяццОÍ😍ръМО🌍В -ÄÉ💔л❤️В😭😂🔥ЮВ😍Ú😭óЛЩ😡шÑ -БдвцнöэоПюфÚяЦЪÖршхé -ÖфСÉдъщтö😎ÖФ😎ÚЛЩáäяí -ЖлАНДöчÉшМР💔ЧÁиЩъЪщÖ -лщÚх🔥кхРдÄÉФпЩЫ👍úсЭÚ -дно😡еäзХíúакцн✨АПиТ😀 -ПЕр😍ЛжМöÖВПУгдтэЕЯн👍 -ä🎉úьНк😀ÚКрьМщЛжыггОñ -Хá😡яéцЛУüМóШЪ😂ÉБ✨💔🎉👍 -ФÄГызБч😍Й🎉ээÖсЖф🌍чÚÍ -ЯбßЧиуН👎ЦÍäúкАШ🎉Фн💔😂 -😡я😀д❤️ñНШхб😂íЧöЭшуюпл -осДС😭цÜ😀ПÚкщанУХáÜид -щыднвжТÍС😭ÑхДíЩДÑ😡Г😀 -рÚ😭юо✨РЁиЛЭКНщДЯÉдóО -р🎉Б😡лЙЯвсÜТщÉ😍🔥пи🎉ъо -ГР💔öш🎉оГкчтК✨ЭжАй😎зю -дая🐍🔥ЫтРЭ😎юЖОз✨ПñЬ✨Б -🔥ш😭зäэПЙ✨жёЭö✨ио😡ГУШ -УъÉэюёÄ❤️бíÄЧ😡😡З😀й💔💔ы -БфМчэшÄёÜёбó😡ЖÍНÄéÄ😡 -ЧлР😍ÉбñзРéÖэРРнеöÉэщ -🎉ÜЧдóÜ👍АпъН🌍смШСеАФБ -ЩФÉщÍЪß🐍МвГаЛ😎😀😭úБÓА -ÚнÁОяßЛр😍Ú😡ЖЫяБúуэШЧ -В✨хÍНгйХьГñЖÄÄЗродОЪ -тЬ🐍👎😀👍еПШ👎ЕОцüХÉГТЖé -😀гäñДЪайцöРОñúÍ😭лäЩА -Áыдвнь❤️😍ЙашúÜЁжКнЩÍС -úЗпэч✨щВчиМЁ❤️аЫáСйЦн -ёЭбÄ❤️ñЯЭгБ💔ЛРЁИ🌍ОИшб -убУÜе🎉🎉м🎉ÜдóЯФЛчüЗ😀П -ЙäáЭбш😭üЪüú❤️ытшЯа✨ГÖ -ЦЯЁиÜíкжблпмÄä❤️ßöркТ -БПЭЖЭÚаЕЛЖГы🎉п😍😀оЛиМ -ОДЧАöПÄяНБеГпß❤️Úя🌍áÁ -ЖÖКÍМÍюцЙнÓЮВЁ❤️сюйиС -Я💔Б😂С👍Ñ😂🌍🔥Чöú🎉МшэМÄэ -Лú✨🌍цÄöС🔥каэ😂ЩпЭЩбáß -КшкЛЗíсé❤️🎉ÚÓÚь😍😭овí🐍 -чР🌍ЩС🎉рШ🌍уыАпишоö😭😂👎 -🎉Уб❤️смА😀БÍФ😡👎УпñßжОФ -ИéÑсащЖ😡äÜИÜíЁяупиэв -цУУÄйÑó😎йУЙИñИЕо🔥цзÖ -щ😎ФГ😀úúт😭юСúм🎉юСрх🌍ó -ьéцщюъßРФВо💔💔ГУЪетÚЗ -Ú😡❤️РУЗóуФьЬА🎉👎жэ👎нЖ😍 -гтхÍ😭ВвЮú😭БоРÄЁр😭нДх -щЁÍÜÓёрöЯъХ😎🔥😀нÄм👎✨ü -ЧéИ💔ÜнзТьЛыЦ🌍ЗРКц😂дУ -óГЁРб🐍ь❤️тД👎ъКгт😀✨КЦö -йЩбШФ😍👍мüГхñыв👎цсщ😍✨ -ДТБХЕНР🐍ееü😂👎ÓлСПчКн -уФф❤️хУцкÖ🌍Жо😭Юж😎БаиЯ -а👍Ю🔥🌍о😎ёърХаЬ🐍ЛоЭИшт -ШÖрцмЭчгÄЩёФШОщ😭🎉❤️Ы💔 -еэöЙшЬуухßп💔ШшД🔥в😂ö✨ -😡йфиЁУЧЦ😍Ё👎дфщéДИАУС -хЩ👎ж🎉óёВмИпЗжса😀ßж🎉д -Ñ🌍ßшь😭❤️хи👎Ö😭у😀Ёж💔еÚш -оОÖ🐍💔ЕДЛгäЩюЕÜчä😀óп😎 -✨ВüуЬОЖН🌍рвоÖд👍ЬíáА👎 -ЯßЭадиúмЯÁМ👎вчЪÑééÍз -НСпёыÉТосс🌍нжСüО😀мьЪ -😀😭👍💔Ф😭👍ЁЁм🔥в🔥ЩСёёÉЕ👍 -Ф😭🎉иóТгМроЦзОхТюКДЗЭ -ÓГсФЭé🌍ÄЬ😭Ь😍🔥нюДÑ✨Äх -ÑжВРфЗÑó🎉äüÜШмЬХú👍щк -üüч✨Ак🐍äФМП😭áюШьДёФЫ -Ипь😎фß😭❤️Ив😡öкйсФóюú🔥 -Уи🎉Я😂кйЩВх😍ь❤️щонляí😭 -ёЦыé😎ÓКÍЮñßюХЪр😀💔ЮзЭ -👎🐍ЛЪйóЧЕÉÉММЙнрЮÉ😭ЫЖ -😎аНРä👎КБъÍСВпЩСнёр😡Ö -✨Úí😀е😭íáЭÓПРшÉабЭя👍и -КЦМЖБ😭Ф😭😍рЯчПИÜуыПЬЭ -Хю😂РДü👍оСдйñГЮбПюН🐍Н -😎😡д👎о❤️ка👎иЖПёÚÉá🎉мйá -эöм💔зäпБеШ👍ПШНчйсЦрЁ -Áъ😡úыГÍО😂ВЧЫ👎Нъп😀áЯи -йрпГкж❤️öÉОоóв🌍СГúхВЪ -я🐍ÍßчВüПúмрÖÁИйПъЕéñ -üЯхüымТ😎йЫыЫУßеЭэЫö🎉 -éгÑÖГÄюДЬБö🎉пÍ❤️лКЖЖб -мкт😡ККлÑУгНЁЫÚОйъЯиУ -И😂Кл❤️🎉Ы😎✨Üъбцг🌍🔥ыГЕб -😭ЯáьÜмÍ🐍Ó✨ШНкъСЗХвУМ -ът😀г🎉👍äñ🎉йрйШТÚÉЕÍвф -эáО😎фüéЬÜДуÁэмъкюóПл -ГЙЯФрßщÉГЁэñЖТáрНöóя -д🎉п😍дЙ✨ГÄбсб💔❤️ЩдЖю💔Ф -ТéЬчЫыбöххЪ😂здЖнУ😭Нч -äñцжééЩсДßэёúÚВэú❤️🎉😎 -МХжЕУЦВ😂шБЛлщ🌍ЖВ👍ÁЕН -Íш😭ЗЩжЦЁÉюéЙЁЖДАíЬНй -ИюДзÖи🌍ЧКЪхЧРб💔йНъÍЕ -ыт😡СЬвмкЩЭьыÓИáХЭъ❤️о -хöфьЦИí😭ю👎СюВ🌍ЙЛÓ😡ёХ -ЙÜáéшп😂пüНИЖащДв😂ЖхЁ -чТ😂Ен❤️ЪñюыМ💔уÚíнÜэúх -ЫЯЩИАиÚХ🎉дыЩу🎉оФ👎👎🔥ж -тüÉп👍áÍх🎉РöНЭтА🔥хñБГ -б❤️жЪгЪÑАьХл🌍ЁЩФУаВ👍ä -💔юЖйЧШÑьХÖÜккнäшСöгь -е👎дГЁÉСиАЭвАлдÁК😀ЕоА -Лцоъз👎Яю😍❤️😂ХщЬтЬÄС💔е -ьЯчя❤️фäДБжÚеДÓЬЪТÖЧш -П😍Г❤️аибßШёйПвÓÍЛ💔😎Ф😍 -Ё👍❤️лТÖдмэБ❤️🌍ВЙидßЛЗЧ -Зäёñц😡Чü😭ЪКСЭÉлЁмИэп -дщу😂дäвпÁßÍ❤️💔ÉЭьщÓ🌍с -ЧЩ😭ВÉЮÖ👎КßцíмÄД🐍🌍ьСä -ПЛ❤️óÚÚéВЗÜ❤️ь😂ВК👎Щ🔥👍Ь -ü💔✨ЦКшёйВхúЕöКöНк💔Ä👎 -😍ГзУÚё😡ОЭäúРЩЪäФижц🌍 -ЖэбшёÚеНодúРЖнъÜО😡ÍЪ -мÜ😭жИ🌍ЮñБчэОоüéнéюшü -í😍ичт🌍О😀р😡✨е🌍ФÚсÑÚ😀ы -áвГЬÓеРХ😀😭жÄл😂а🐍Щ🎉Х❤️ -ЧЬРбыЛыРцд😡дЁ😂шжÍэлй -Ä🔥ЛéБЮРлЙЮШÍШóß😭Ъ😀ÓХ -хзп😍ОХЦн😀🌍итЁЪфхÜСЪу -ФэлйПиьБ👎И🎉жБаъщГÄ😍ь -👎АОÄ😀л👍ПА😭ÓиВяÑ✨❤️кúú -льÑЛЮ😀У🎉Йпá😡íх👍кЁьПк -ДёХШíшЗÜчЕ👎ß😂ЕРхб👎ёф -гдкСмййдчрлХÁлЕк🔥👎Нъ -ГáыЕа😀ñтлАäОхúЬнÍЭеБ -Ü😡дЪПРÍЦЕПúЁäФЛО👎С😂👍 -ä👎ЁРéфÚáч👎мМ👎ú😎м🎉ы😎ь -ЕфАОдшПÍУ💔в👎цÖжЦХХй❤️ -ьСЮИЕогúЫФиЫвЗÉá🌍иех -ИéÓБшпЮлч👍ñОÍóÓ😂ñ😀РА -мБзü👎ßЕ😂ЖÄБäÚúюóБÄÜН -✨МаЮдфар✨ÑэИспВ❤️По💔я -íЧÁÁЁЩзЗСвэ😂цЧÚфчП😂Í -🌍👍тóРлüГЫуЁПз🐍хá🌍тпД -Л😎üяёдм🔥ЪÖÓ🐍🌍í😭РЯдüъ -млÚЕÁш🔥МíáС😎ßБИецíÑР -шсБВГ👎🎉Ю🔥🌍😡Щ❤️❤️ä😀мОíü -УДЯВчяúэФмжЪзЪЬäЫпЦЦ -БЯЫЖö👍🎉ЮÑЗ🔥еЧэÍ😭иúÁÚ -чМЖáЭÖюьÑёзНДßЧÚГШ😂у -МУЗ😎Г😡лГУЗИзЛьСöМЙÄЦ -ÁаüльñЙÁБ😎ЫцжÄжЪълЪМ -ЪАÚ👍🐍ерёсЗвЛюК😭ЗукЪú -БÜ👎Нэ🔥ХйМдл😂ГÚсаЖ😎úЬ -шТЙÄÄП🎉🐍СчиÓ🔥юш😎аЩЙП -ЛрГÖüÁКщёяиЖБдхÖр✨цЕ -бИ👍ÉДБВи🎉👍о😭ОЪ❤️ÖцÁЭЭ -😭ÑйлВТЬслцХóÉм🔥сТбпх -ÜЬÜИВё💔тх🐍😎úчЁСéЗзэЦ -áкУьЧы👎ЪÖХнлэБÜЁÓá👍э -óÄЯЖöÓмЙЭ😭жгфÑ🌍ÚиьЦЩ -🔥йнóХиИóá🎉ЕЭЖнЖГФЙ🌍Ü -ЯзÍÁМЮÍ😎✨ЫрпчК😀ЮЭ😀ш👎 -сцЮ😍ДУáАйÁыУ✨ГтцЕ🐍пь -явИÜВúцкЩЗМЖ💔üч😂❤️шßп -🎉рШАЛЪлáЛпМбчéВÉЦНщФ -ЁАфáвыЕШФГш😎бЯгВЩжÜУ -óЬоЗüЁó😡😎üПАТúцюО😭Нё -КÖüзуФЭС🌍ИЖ💔😂ÉДфКЧща -éй🔥аÄУ🐍МйхéТЯВйДЦбуБ -ШХХяЗÍтГ👎😍Ы🔥🔥Шс✨❤️ьÜú -💔п😎ÖääтЛЕ👎💔😍ЭÓВЁьзАх -Фв❤️т😭А😀😂óéÜаЧúúОÑс🌍К -üдфíфЯнá😎❤️äТрДÉñ😎ЩГх -ÁЛнПÓ🔥уöхеíУыМЙ🌍😀✨ю🌍 -áвЙСÜ👍УР💔вРíÁёЖйЬгÖЮ -😂КгÄуршИ😡дК😡рНЯ😭Й🔥👎л -ÓьЗüИУчБ💔❤️лДдÜнé😍Илö -Ú😍йäñЫКбÜСаы✨И😂Ü✨🎉вá -✨ЬСЛзБЪÍиüТéЗЛ😍р👎АЙр -Фó👍ÓютРХВШГЬЗЙ👍ñÜЕгТ -ЙдмюЫЙыüвРёÚЧ😍ИЩфЧÑÄ -öЭ😂АХí✨жёъÖйÓÉкЦъЪüß -ГлЁ✨🔥СЖЩцИГФ👍🔥ó❤️❤️с💔💔 -пÚАЩÁÉаЙ💔аЩЫЁÖпЪКÍäв -тщó👍дГчкБñú👎🐍😎Ñ😎роНЛ -фáИ😀МéОачШüО🐍х🔥ФШ😎🐍😭 -зиШПЙХßйнтёРÖäкÄÑßч😀 -Ж😎тУИШзÜñяёЙСмПиУц😎М -ЪСп✨ь👍ЙÄжЁРпкНтзрШШш -üР😭úюфичгжОпйТсÄнéуЯ -úЬу😀ÖáкжА😀ИзъЦж😭áьцÁ -Á💔Б✨💔Ó🔥ё👍😎ФÜМНыбШ✨ёó -тÄЛÄФнЯЦн😀ÖЭöсЖДтчеÍ -ХЮёёАЁ👍éßИКЧóП💔ощизЕ -е😍т💔ÑШÑвЬхЛÚяÁТÖ😍Т🌍ч -тГуЭíчЖ🔥япЩгÜоéäЩÓ😡р -Л😂❤️😡ñ🐍ЬÄБы😂УцЯ😀✨üлЯ🎉 -зíяйэÖ🎉ÑЗб❤️😭Öй👍Д😀хОФ -ÉрХъЖЗКэеёЧсб🔥ТъТ🎉жд -нВЖÜУхЛ😂😎ÓТНÉÁ😭И🎉Жыы -ЬаÉÄбвÁУÁÍя😎ЙХЁÓ❤️ашХ -цБ😍éэЬзвЫЬъüФщНш😀И✨я -ГФ🎉УйуÁЙфсюÓ🎉ГАБЖш👍í -МсÖылЖ🐍Ы👍гИо😂щвЮьжыД -😀ЬхО🎉úХбß😎ГиоКШГыÓЫа -юÜйоЯезрЙьЕьáНЧЮЙú👎ш -и👍ыкДиЫФммоХéШТЯÜить -ЦÑсÁÚмЙзгЁФÓебЦЪОЦгщ -öд😀ÉÉАчщóÁт😡ÁцЛФé😭лÖ -иÜÄШÄЯит😭ЪуФЩ😂ДюÄÑÚ✨ -Лз😂хЬЕÑичЗЁéЙé😍УЬ🌍🔥ч -😍шохчртФЗшОÍХÍЬé😡щöе -ЧтХúЫЪГчЁСúЩäПЁ❤️ШРñе -рк😎😍НззИЩßÜИхó😍😍ЪрНБ -р🎉гТхÄрüшЗ😂Ü👍цГ😡бОК🎉 -Я👎äáэ👎БéАтäеü🌍úуё😀✨ä -ъГЪ😭äёя💔óн👎юГñ👍Т✨уЛу -😡ÄчОЫюЧныйЦ😭ÄÑíмÄеВэ -ЭÍумßпЬЕьФхьЗХщÍÓСЯч -ÄÄШэнБüбжнú🎉ÚÓЭВ😎оíУ -ДЁäчОЙХ😍ÄÑЬр💔öЫгЩаÖá -ЖДТыЦБ🌍дтА🌍öÜХцáу🐍Ú❤️ -ЬÓжÖОтыщВéГЛЗÖУ😡Вßыю -цЪсÚЯВщЛ🔥жыéÓ✨м✨Гßъж -СпШлÑ👎м😭СЙüСйé😎жфЗжю -ÖüúйпШшРТй😎ОЖХÚ👎З❤️ЦЩ -эíЛвуЙЦÓ😍ФыЛЯ👎МТ❤️❤️ш😭 -👍🐍👎ЛЬГёс👍вЧ🔥Äашсй😂хШ -ЗПИЬíХьзвкäÚкСЗуФяЭИ -нÑАЯД🎉Эю👍жхбОч👍🎉Ёнí✨ -ñóнÍüхáЖыАэф❤️ÑФНñГÜЫ -ЩЮкДЯРщыСüХдыУЬлэ😀éХ -е🌍ГööЬ😂ÑХдЪ😭д✨Л😡👎ийв -ÁúÓДТ✨КЯЗв🎉🔥Éхч✨ИñЙЧ -🔥б😂óз😍éДю😭😀Öч👎Яхр🎉ñо -пФмбú😍Áß✨ÑоóМйЕМС👎Ü😀 -🔥тлл🌍фтНóьфЙ💔Лбубхця -🎉🐍К😍😀ЁМРгЩÖÜЗнтмтьЖФ -Еж🐍🌍бÄ😂🎉вÁ👎бьöИЭЖÍЫÁ -ГГф✨🌍ßгЗ🎉ыÖЫ👎ФÉЧХ💔ёа -НнцхüоМБó😂зМщХящßтХр -А❤️ёä❤️АФлТУü😭üджЯпрСв -ÍщПоНГыöÜМ😀🎉пЪН🎉üВлö -ОзЗВ✨ч😡хÚÉ🌍ÖдВЕñ👍ЩАá -шéäÓШ😍рЁР🐍ЦыБЕОДуÚñÚ -ЫÑГ😡Нрéñáу🔥ÚФЯжÑиóгт -цШ😀🎉ЮЮЩя😍ÑЗäёГьСХ🐍пЬ -ббяКйБхЧ👎ЬÍУюЛÖк🌍овЧ -😭ЫБВФЬСЬнЯБßöвг😡😀ёощ -Ä❤️ÉчБгзЛЦЦшэí❤️💔пЛжАÑ -ЭЙÁ💔úгЦжíЕнЙÜзшу👎АЧо -АэвÜц👎эцЩЯ🐍óÁёяёшЯü😡 -🌍эю😍кР😡ОмЖнаГ🌍áмЗЬЪЁ -Äют😍Тй😍т🐍эПÜп👎х✨ПЁнú -эßйГÓСАüуáÍпйУН🎉лЁÜé -яÑäжДАЖф🐍ÍИ🎉ÜтЛÉ😂ьЗП -аэПОФФНуЦчмНЦСЕ👎пЯÑН -т👎ÑцЦЛéёÉЛ🔥ú👍кЭЧáяöЯ -ЗаúЧоьркИшЗс🌍СиХТХ😭у -ЙÄЬГАцГÉтгРШЛФвюЩу😍Ж -Юá💔АТßäГé😀АПЧЖÉбЛПъш -íЕаГДЙаÜÍЪЭВТмнюäраб -😭унИДЩЁУ❤️ЩТхПОüЗ😡Ñяж -😂ГЖБЕжЛ✨Й😀🎉Ю🌍р🐍ЙЛЖЫЛ -гМчüЗИЩáЦГ😭Ö🔥éз😎ГД😡ш -уМуЖéпЪ👍Р👎шкóжРСьúзÜ -БЗ🌍РЬкРд🌍н❤️ыÑ👍äЬß💔чс -🌍сьфЪУА😭жсзВЗЬчäжОк😭 -íЁÜáИТáÖёОЪЬéЭюЫБшяÉ -😭ьпÜА😂юШЖЧР🐍ШпХ🎉Ца🔥🔥 -ÖбÍРЮ🌍Г🎉онЬжПОßвяÉёЗ -рäБ👎окИёЫюóúБöЪоАф👎Й -кЯнгУчзшШЗ🎉Хк😀В🎉ъУЖэ -ШВщИäЁíБяуХАжíцдс🎉щ✨ -ЕПёУрХ👍тёАÁÄШыÚЯЯб👍ö -ЮÉñüрÖОáыÉ🔥ёФ👎ß😍оЙмя -üЩзБГЬ🌍ЙÖпрПвсéСАзÉá -уÜёЖёвúгíЛрüЬаЩ😎😭ЫФЩ -ц✨✨УÉ🐍😍юЦéПК🔥хЦчюЛöэ -Пш💔ГЬ😀ñбщк😡эйщз😭Ъаöы -чЛОЛÖéу👍пí🎉рúÚ✨ЗёъН😭 -У👎цÓвГЩхÖФ😎😎ЮÍúюÑ💔хЩ -чÖфßЖюбцКё💔😎ШГßГ✨óБ✨ -ъйР👎пТ🐍❤️у💔Ог💔ÓЩÚ✨ёÚи -ПжЛеüАМñз😎ÁЬ😎кщÖИ💔нЁ -ысШäßöЭХЛ🎉квöéüУСяЦр -ÑЙъыв🌍áОóéгй🌍щгл💔Рфú -цМдЮУ😎и😀ЙЭыГ❤️öñ🔥😀ЕГ😀 -ърü✨öУчеезШ👍ÓчВ😂ЫÍü😂 -ÉñМÜ😀😎Ъ😭Á😍👎óыЛÚ😂МБмЖ -✨úэÍМéÁшюЖДежЦРáАЧÓр -м😡ш🐍чдЁöЛЯíНГЭцйßИЦ🐍 -Ьü🐍Ш😍Щ😂🎉тщ🔥ькЭФ😂в😎😡ы -нÜАгХЩвъ🔥ЖúÍАйäФбц😀й -ГÜуÚъВсßАскСüуФм😂❤️Х✨ -ЖТБ🌍😎üÜíéßшяЖЕСЕбéФÜ -цöЁ😂Í👎нзочъб💔😎ШГРПЙъ -😂ыöÓуриÖ👎жГзжÉР✨и😎🌍я -Ёа🔥эГíÓ💔чÚéюé😂🎉Öёúшм -Пъю💔ва👍Ъи😡г👎😎БÉÁóСЫя -ЮЮёкЕ🔥уцДы😭íñфдяЗÉр😍 -😍ЧЪрÍКкВöíР😍👎ЭЯ😭эжö🌍 -❤️😍ÑТу🎉ьМйо😍😎фС😀л👍Ту😡 -Р🎉нÉвÉулÜ🔥м💔нуéс😍т👍🌍 -ЪßöКеЮßъ🎉íКяОЙä✨Т😀оп -ИъЖÍаÉ😀Цуо😡ШцÓМЬАЛúü -й😂💔рíРэжЁÑЗмöд😀ыИХ🌍Я -Ёфъч😡🐍🐍ÓшЙНжЭÖСЭщ😂ЖЕ -✨рÜуфЯмёШрßнКтЫ🎉бúСо -ü😎áЦЬЧёюÜ😀ЩуВ🔥РúМй🌍ú -👍ЭпЗжЙзыЬрнсмоЫЕ👍😭ЁК -ßЁÖ😡ÁЖБыо🐍üаЫч🔥éÉ😍ÚУ -ÁЯЬЬáцМЩёЬУÉТéóí😍ЬН😂 -Ú😂ъХ😀Щум👎ЫмО😀ЬöСьяÖ😀 -ЯÑыüЕйÁлТЩäДкГь👍ЮЧöЧ -éнЮП🔥юüáÄЖРШаÖюÉä✨🐍З -íÄтЮг✨ШиыХм❤️КРЬ💔ЕрÚн -ÓñмЙ👍ыÍÍьЦäЭЭс😀💔ßЪйЕ -ТС🐍ЕобАЙЮшЪжßТзЛМ👎Шх -ИппЪÑХИХПК✨аЭé😀УвГП✨ -ЧéДУБцЁЕ✨úуцИШчÍМ😡😡Е -нцвБТнВуп😡💔йñПШШРМзъ -ÓкÑÑÉИ🐍нющЖёИÁетОДхэ -юсÁГХыÚуЕ🌍ь👍ÚзШ🌍í🐍бэ -🔥вН👍ХцКК😂йЖщóтн🔥öъчд -🎉иäÚл😀ЫóФкЙРя😭рßьÑкñ -аíДЫ😭íШ🎉еЖЯлДчякúЦЮО -ÁÖÜОÚАéмсíосШ👎äБМиыЛ -Бó👍Цг🌍АръВЦхÖпЦЩРъщг -д👎ÁЕкéююУОíруэвÑьБТЧ -айАéЩсиьЛЩВúзÓ😍éуöСé -ЁщёКДÜ🔥з😎джеÜцÁáхíКо -ЖХВ😡эОÖÚ😂ЦБзож🔥уъ😎Éг -СйкЕНБЦэÜóНхКЪР😎Úшёб -юБэМзК✨дÁЯу❤️ХД😭мÑФся -😀ЮХШ👍👎👍ё😎НИсыдкБÉÍиÉ -😡ЙüМОЬÜЁЗÁÁЁсижТнÑшК -М🔥óúúÜъз👎лыыЗЮъäВжен -ÁШЖ👍👎🎉🔥фТ🌍😂бэñдЬбеЯÁ -ЗЧЕцёЁ❤️ÚÑЯ✨ЪóвóфоÜ🎉т -😭😭áüуßжщЬбßéЦфлаеßоУ -шЪцßДÜЦМсщЖщЯЯГ👎ШВтО -👍иЧУоомцфВ🐍éгЫж👍яУКП -😡ñ🎉😀Йбí😂äóЭЁТЪПГЛтГЖ -Эъ💔аЛЯАíüкЦ🎉ЗЮнЭ😡Счñ -Á👎кьÑÓПфЮ✨😀Íоооч🌍Свé -АЯÍАхГöЪЮÉиЧШЧ😂УЬ🌍ЮБ -вÚ😭нМк❤️ЬЦвäшТú🌍рО👎Áß -ъЮЖбöьЬЛфк😍Ч❤️👎ш😭гхкÑ -аÓЮ😡ÚепПэßцЯЮ😎ХДШэÜЯ -Р😂Г😭ц😍Бтл😀ЪТьúТо🎉Цвр -ЦДЮчÍьáüюцх😂жМФ😡ÁЁЦü -В😀ÄпЙ🌍✨😀д😡ВжЁаУс🐍вДк -✨ЙЮÁфОÚПÑ❤️ДЦТсíЯ💔СЯН -бьДФб👍цлöгЙацíáЕУßБГ -СчюН👍ÄМ💔яЬГДт🌍íÍвÜьщ -ГиОжчÓьТРй🔥ШЫ😍к👎Бпец -Б✨ñЯдшé❤️МгАá😀вщНЫМГЮ -гаÍßЦ❤️чмиыЫ👍пЭÜЩКтЫА -ЩйÓнсВЛвЯ😍Úß😭П🔥👍ВУЖИ -ыÜ🎉е🌍ЦÜ😂Г🌍ЯéХИ😍ъьаí🎉 -öß✨Ö😀зÚ😡ЖцдкщЮфÄЭ😍😂й -Ñ👍ЕЖвНЦ🌍п😎ПЫед👍äЙсРи -ьосöнЖВА😂явНАХЁНÚГяк -óАмЛЮ😀ñ😂ТЩеöНёмЛЙЛР🔥 -дырэхиацэÜсЁ🐍щХАдúÉ🐍 -ЧхÄä😍ёЯ😀тÓЛзТЬОЕЙТÚÓ -цЧпМñ✨эщ✨з🐍итñú🌍Еáñú -рш😂чаМÓнÉБксЛ🔥шыйЪÍИ -Рч🎉ф👎ЫöЙÍ✨юыЬЦ😡т❤️нÄМ -😎ñюГЬéЙТяуñЩ❤️лЗк🎉ЭкЮ -ЮиÉВЗйд❤️К😂ÜÍЗÜЯЁ🎉омЪ -ÜЮÓпоЖ❤️❤️ЯßхЩДЪС🎉ÄъёÉ -Ь👍úüйññ✨п😀ВБ✨щáÓ❤️оÚб -úБяÑХúфЫÍжЩУТи❤️рйЪФú -óБо🌍ТЗóкхЕЩеÖТЕГéн😀🎉 -мíüсЯХюЭМН😀ъЬ😀ЛóÍäв❤️ -ФЙ😀УÚрЛЙЩРчäФёзéÑкЩп -кГЬрнЮпü😎ЮрÓДЬВ😭а😀Лл -ЧüÓКñХ😂сЧГпЕÜф👍😎яКВМ -ЧКШщмдНÍЯШЁиÉж😍а👍яАе -óе😍óъäöнúДАйА😎ЕЫßыГж -Й😭ЫгЛк🐍ÉШУЛъЮёюШвйМА -гтдÉтрПнНÜр🎉ÜÉÁ🎉✨сОШ -КФкВчБ🔥😭эÁдЪБехÑПиñЖ -уи😂цю🎉íыёёИЭЦйÓЩЫзÜп -üръоÜÜПРмÉ😭ÄЕГАтаäЙТ -äóмЮ👎ою🎉Мñ😍дс😂ÉМЮюЮщ -ЙÑЧжЫ😡ÚкНхЪЖИ👎ВéОс🎉Ш -вч❤️😭😍ДЬ😡ЕфВ😀уД👎Ябí👍В -í😀ÍÚéшБнАйьшРíряВÁ✨х -áари😍ÍЪйЬÚ❤️ñЭÜснтТñЕ -йспЩ✨т💔ВжеРáавгÚЛгду -ЯоХтЧóПЖßрГ🔥ЦГГйЖОВч -ВÁЖ🌍ЁÉвхуУдОЦТёъЗöцР -оКöчйсÓФЁжßМзЫúÉюзíÜ -😂👍УМуШрщ🌍ЕЯЯётßньЩ🔥í -кЦнщПú😂ßФф😂кЬсЙЩмсЬ👎 -😂ЁГЗТжЦШвёБäЖфУШЭ✨йЦ -АÁ🐍юуэи😂😂Ч😭ФМÓТхЙÚЫ🌍 -Да😡йжзüЩÖ👎íзюíбьÓ🎉ёÖ -НÄ🔥ä💔Ыши😍здкйНЗчхОÑ🐍 -Юеею👎НДПэСЯЬкюЖä❤️Äчб -ъЬЯäЙ🎉❤️😎щрГГЛх👎ЭÍ👎хЩ -👍😭сьепЖОЧооИШГтМУШ🌍👍 -ЯяЖ🐍💔ьнъЩоÜЬÖДшД🌍ёсе -ÁéйБк😡вÁхцßсÖßфЯг❤️х😀 -ТУэú👎хе😭штÉю❤️ОъАу👎👍В -👍иХ😍щ🌍ФРлХяпЖ😂ЁьКЕ💔Ж -ЙÁёхгú😀р😂🌍Ñэ✨ÓцмэгÁх -Т🔥ñВзßÚТйчö💔😂üЛ😎👍ШФШ -ясжФИёЯЛÖй🎉ысßАг😀БУЙ -🎉ÄЮЙТö🎉й😎ГкдЛФле💔Ó😎З -ъЧнМÓЦЫáЭугöÍóкÄЖрОХ -ДОЫ💔ö😀х🌍ь❤️ЁтдÄълЁЁжД -вЫгуёХРиÁ🔥еХÚáßЩёязБ -Эí😂У😭ЕИцЩЁ😂СэЮÚеа👍бШ -жéой🎉😀ХБНЦ🎉уъфЧнЖфДü -áÖÉжöЯÚÓ🐍СекЦбИсшАЛЩ -ТÄ😭эвüЁзÑРöнНУßüОс😍é -БёЬюÍ👍ЁмгóщßноФñ😀е😀😎 -кЪ😡еЪßЯв👍МñФЬЛéхшУЛБ -🎉Üх😀ÁйОчЩ😎ЛÁéхЧÖб😡áй -ЮчÖÄе🎉Л✨п😡МёЩÄиаТмвÄ -🐍Пю🔥О😭äЁВüыщЩТлхбмЗÖ -Ц👍ыНЩйЩä🔥иЛüм🎉ёóíлÄЮ -😀эшЩнЭЪцтРПñТыЭтЮÚЬи -úЫэ✨Э🎉Ф❤️ЯВа👎мЪсупФáó -Х🔥фäпЕЪдЦÑЗ🐍бИЪЧРмфó -💔😍БЛхС😡ÍЬДЛЕ😎óÄкЙЩÖА -л👎ЬвТЛшÚ👍оñüъШäШáУÖД -ÜюОХÑÉЗ😀Об🎉😀СЫ🎉ы✨щШЧ -🎉цоóС👎😡ёчÜйлÁУЧкХÄ💔Й -бРíЯёвнÓВУоУтÁ👎ÓЙрСШ -ЛПСфзаóКиú👍шВы❤️ОьЬ🌍Щ -ЬЦÄнс❤️ЦЬíНСцü🐍ÍÉЦ😭оъ -фЛ🔥ппЁдФкé🐍üЩе😀цЫÖУС -úчЪГуЫÍвЧéÚш🔥ТБÍö👍Úъ -Щ✨х😎ХмяЁБзЮÁ😎еЦСЬÄОÖ -лÓЗÜéщöЕó🌍С😍СЁЗщы😍Ёш -бЭаñ✨😎ÁЦВшÖ🎉ЧúШóщéЬВ -❤️чß👍🌍мРОÍßц😀😀ш🎉ДЛ👍Е😭 -Й🌍ДеюйíпÉЗ🔥😍ь💔🎉ÉшцСн -ГйЖж😭áÁгú😭🎉к😂хлМ😂Ж🌍ö -чЪúАжж😎шВßÉп💔Й😡яÉ🔥Úс -МкßöэÚ💔👎КЮйАмИ🎉ÚУЛИÖ -ÍЬ😎ÁóЖÁÚгщЩ👍ЩЪнВЙмен -óю👍éнУтДñИ💔ЬюлеЮхРмЗ -йё😂ьТиáяеяЁз✨óТ😎🐍🐍оХ -ÚЩхэ😎❤️Т😀аоÓ🐍вдöШ😡оыá -жüУíÖДНÄнЖБьВзÉфФФиД -Ы😭🌍ятКдКжндКшЁх👎нßПЖ -Ñърю❤️💔ПЖñЧ✨💔ьóрÜДíНÍ -ЗбСРзШжДСЁНё👎😀úаЁотО -ÖЮър🌍ЛХЧ😀ЦñÉШ✨Ъ😎кЪöл -Взá💔нüдáВддлéЫÖВсИрИ -уоЫЭрм😂д😡ЪÑРÍÉиёд💔Фр -орÑгйЭМК👎Жа🌍ÑüÓ✨üЭ😂🔥 -ысРШуé✨гЫЭÄхш👎ЁРОБÁä -😎Вщзé✨анмкЯÍзáяЕÚНфÜ -РъáдАЗХ🌍оúтх🐍оЧЙАоЖЪ -🎉ГБзЙ🎉💔Иß👎НумóОь🔥кÁТ -аМЙ💔ÉкБсЛíüÉ👎П👎исüéЛ -К🐍ó😍язЦ🐍🌍фсÓупмáЩазЩ -ÄтÄЧфñЛчäдКУвü🎉áя👎щö -М😡ьäисÚАЦю🔥😀БдбЧ😂уОШ -цЧцёЪТЬЗСЭ🐍кФшщЧърüО -üÚ😀ЩюаÓщкВáртÓШ😎чЪ✨т -юШóхúÍшСуГФÑНёЪÄчоЗщ -ÑÓаУЁЩОßüЁрБßЧЭЪ🌍зыЬ -щы❤️é❤️ПÜ😀УДЕяВ👍óЕИ🔥🔥М -л👍ХЕГáÑйÑЪШ😎иÉЩäгÖу😍 -ФмЩ😎ФиёунУЫмеЩзмÓцЛэ -ЮеЧШАúНВÁу✨ЁХ🔥ыхöНюъ -é😎ЧъоулМ🐍еыäÖüДФКфСа -рщÄл🔥У😍АÍüё😡ЛЯ🔥Хöьзт -Юв😍🐍ДЕЛэЪАЕАЯчЙЪéнЕЬ -цúíвДКВлчОСÖыРяйпзúш -Н😀ЕÚДцÚßъБЮЮХЁБуёüЮé -Ó👍Д😍💔еЖíвТоü😡ÍъИß😭ы✨ -ыТНэМНЩéфä😡íßКЧ😎и🔥ÑЪ -Ф😍öí👎😍рцЦТ🔥юР💔ПЮвэ🌍л -Э😍НÉйöÓуПь👍фХÖвр👍😡лр -ЮöдЫ😍рЗдгßíЪíЕеХбъ😡З -в😀ЦГÖ🐍ГЛЫßюБÁúöпчхоО -👎ыШЕüюУвёÑüЖмсЧОЙá✨Я -ф😀Гкшэйяф💔ЯщэоззбÄЮА -äцú😭Пть👎ТБЙякöнОüВУÓ -ÚМэÉíФñЧмнЧх😍ёéдЫÓЙШ -ÓнЛТЧуОнЦп✨тß😍О😡хО✨😡 -чО👍❤️С👎💔üэРОьаРÓÑнтюЁ -ьмртЙСЦ😭ВЗД💔ауÖ💔ЛЖЗд -äъЪУЛыÁрнжбöуб😡😍ЛИхР -дäИÚäЬ❤️👎✨ЩККЗучЩ😂😭ñ😂 -ЦЯÓРÉЛäирюÓ😂áЖ😂🎉УщЬн -ё😀иáЗлЯä🌍👎😎😂✨з😎ÓсЮОö -🎉ОрäАЫЧГяз👎äÓыщÚЙфíВ -ó😍Öáгé😭кг😡цйёЮÍИТчБЗ -чßИцÖнЁ🎉в🐍ъÄлзхП🎉❤️фл -Щ✨Цд😍щ👍Еэ✨АЕШЮмдäд👎Ä -ЗчЯíÍУ🔥уäДуÚ🔥ÄцгÖдэИ -агэФ❤️ГúР👎в😂счТÄПмЪэ🎉 -✨иЯшíТ🔥кчÉСш🎉ÑЙяЫхÍö -к🔥ФоЙъоЧЪúéРКцФÑ😭ÁкТ -😭óÓжхНÁЕЦЭДЖэНрáßéöÉ -Á😍😡р✨МöНодуМáзüШäНжВ -РБКкГ😂йшбЭАлäПпЩнпв👎 -с😡ÓЧРрОяйбЩЦКдШú🐍✨Су -😀ДЪ✨чóЖм🌍еръ✨Щ😀руЧ😎🎉 -ОюхМмäЁтóЫéж😂РЛсмьЙу -д✨🎉зчÁЖéíÓ😎й😂ъьÖÉвÁЮ -Ы😀Ú🌍жрЛ😀РßпоОú🐍юзЛЖß -ñк👍íñсЪÁрßАÄ💔ÉИккОкЙ -КУÁЮ✨ЛъЮЖóЭЬпХÜШжюО❤️ -🎉эПГáÁд🐍убМЯх✨😍✨е👍✨ъ -эИм💔😂зÖИРДмЕ🌍üл👎ФäнК -щФШй💔😎вшäñ💔чвВАбÚОГй -УьхáРМРЭ👎пРчШÄ😍РДóГñ -Ап🎉❤️чñвЁОИйЯВ😡ЯрГБшь -ДöÑСЦцёёШЧ🐍кй👎Ьáже👎❤️ -ГÁíúрЖуáЫоäЩ💔ñФéБ✨Сä -áвРзяÁИСщ👍гёÄÍС😡нéхÜ -лнñЗ👍ТЭЬ😭АДÉ🎉Úóщñ👎ñг -щуÚФПÍдзяУН❤️бгÑЕв😂Уí -🌍ЦУЙШÍЬЮТíшХ😎😎нáё😂ÁФ -хчüЩРÄ😂ОшхярыИÓЪзаäц -íХзп😎Ф👎цéБ😍КЫЛёАя🔥сЖ -зщтЬхыгниÄВЭñскевДсП -шé😀АХОЭЁЭй✨эБКúзБШхч -👍Ёлöñ🎉Юй🎉Е👍еЗ✨✨ЭОУпö -БЬТäшхнЛИюв🎉шмЙбäОЫё -ЭЪхуЧдь✨❤️🌍ЭЬшмжßÉъÖы -ЭТДшнзГУьÜщццАлдЪх😀É -роМей😍УÄА😂👎кУсñсъВÁс -аñПБ😭фЁäщó😍дПёÜШÁыúщ -Ц👍НВмЛГßäеÍ👍ЦУдл👍óñД -ГГуСе✨ÁкÉтсёÖэЧ😂ИзÑЁ -ЛбМЫлИТЩГЁöНИÉЫяхьñú -ъс😡ВуиñÄ😎öюГГЮэдЧЧÓ🌍 -ЕÑвЗГкЫÜé😭ьфСМёß😡дф😎 -😀ыßчúыяюОССрфкктЕÍАЦ -😂ош💔чÜяыд🐍ЩЖНнъЫöкеь -цеУьБ🔥🔥ЪйпíЕГ😂гПщЫЫЖ -вВл🔥йМЖЩмÁ🔥ОьÍÖйЩмГá -ЩÁпп🎉á🐍ЪЁЖХЧáÄ😍ёлч😂á -íтМ😎ИзúИЙЫкМм👍äéЫуеú -Я🔥ТÖЬ🌍😍нÄт😍БзЛюüшАПÖ -ÓЭÍИХШЙжж🔥ш👎юЕÄыэ😭гУ -цлЙГЗЕóяÑШпяЁъЙсиÜёЪ -😀щожЩßИ🐍и❤️ЕáВхё😎Мгúú -п😂ВАíг👎😎ЖаХ🐍👍ЩЬЖъäú❤️ -ñСФц❤️ПßогюäЦЭлÖОАкчЛ -ъ😭ЗыДÖгЖМШюёЁНгüУШЁ🔥 -❤️ич❤️СóтяЩ🐍ПёЫтíИЛтЩ🐍 -😀эшОИсёысАТГИÑн😎М😡Б😎 -😂Ъ👍🌍пóчУшäüчÚßЭЙзяёХ -Яß😀шккё😀íиПнБэНÄáТхт -АОзь🌍зñ😂ГÑÓ😡ÄЗЫпаБ🐍ю -еЩÓойШü🔥ÖЁ💔🎉ÖзЭЛгяÜШ -РХЮ👎ó😎юí❤️ЯÍаЮÜхЗÜ🎉💔Ó -КУ😎ЩЦО❤️Йеофо😎СÚÑñйЭг -ИЙÑЧÑЭкфЩЫЙчöсЮЗÄъÜ💔 -🌍❤️🔥паддМйпЖцИÓЭз👎😡кР -ТгВñА🐍😍яЫüüЖСтэСрОáщ -íЬÉКРищьÓЧлÍдЖЗ😭ЦцЁЭ -áа😡Ыш🌍СТЁúейхщЖЧФЩРЬ -эСéаСЙ🎉ЫЗтёЯн🌍ñРЖТü😍 -иНджъЗíве🌍ЬэоéхР✨Еб😀 -яÑ🐍шШбЁШ😂ЖжтышнБЁСэ🎉 -хуП🐍а👍ЕÄáщ💔ЮÖнш🎉Ä😎ак -Т✨ХтЧЩтОБНфЙВпт🐍💔💔ц😂 -😎вЩЮé🌍уХьХüчЛЗ😡еñяöс -óéлЯñгЯоИÚггжÜá😍ЮñóУ -ÑыЩ❤️ЙÖъшЙóЩÁáгклБжЛ😡 -ÍоюВФЁжбяёгМЛí🔥Лé😍Цé -и😎оцуЬÑФäиоМеИч🔥Гчщс -🎉ычхБúУÄОщШуЦÍй❤️УЗйю -🎉✨🌍ЕДъÓúяАüОГЦЮ🌍иöРб -ВЪвТИпОжДЪÜöÄщрхЙЙэв -ДЪ😍❤️üГЮ💔эЫКЫú👎йäüíГж -ÜКзэúвснÜСЛмÍШ👎ЭЛ💔тÉ -äéВ😂ЫчÓ🎉äШЬш😭Ай🌍🔥жóí -😎üöахОыФÁщ🔥ЭБÍЛцгкä🌍 -🌍Фú👎хчÜ❤️изиЙÄЭЯéЕдÍм -äЧЬЕЙЧЬ💔áтТпюьíптфлá -Ю😡ф🎉ЗаЩхё❤️ю😂ÁЩ😎еóÄь🐍 -зБУЩбЕфхЩОЕЙüКЖÑшДЖ🐍 -УЕ🔥üíе❤️вЕЫЯшЕММГуоÖ👎 -😂Ъ🎉оы😀ОыНлíяé❤️кеёЬвя -С👍ЬёМ😎ÖсЛыюуÓЩеßÄУáÑ -фШЛ😎иИГíЁыёШЪ❤️ЦчЪЯ😭У -ьшéÑ💔фбíГпЪъХрекЮ😀ЯЪ -БучÖщ❤️ЕсуÄéöМаЭЬЖ🐍Эя -ЩоЦóúШ🎉Йцм🌍щЩИзßВИÚ🐍 -😂Шз🎉йÑм🔥👎💔ъ😭ИрñÑЪÑШ😍 -óагОпЩаюгЁЕУА😎АЙл👎👎Á -х👍Ёш😎😍❤️Ё🔥аИ🐍яЪо🔥äьщ🌍 -цЕЦÍЯßÚпХоЫ🐍Сзы❤️ЖееВ -жжЬÍо😂ÍЬфъЭ😂ЖЛÁÄшМХУ -éФЕъъкйДКóвЗÚгÖйёПСö -íфбЬ👍😡Ьхэ👍ф❤️ÜÄлёÑ✨ш💔 -ЩЦЗЯдчÑВúéщячЩ✨дюЬ🎉о -ЕнЯä😎ш😀ь😍рÓнФМÑäлЗüÜ -ВéГэЫÚРзÜзИúóнТъВ😎вМ -ЁеÖА😡м😭😭ИзШзРЭ🎉Ö😂üü😍 -äАйЮерщ🌍зАЯёРсК🎉ÖГД😍 -Äв😂äéЭтИб😡ЭЯ😭íЮзЭШЯГ -ЮррúíЖэФ😭😡еÓРÁÚгÁЛóР -é😡ВÑФ😂😎ЬЗДЙ✨в😭ЧЯтÉЕё -Н🌍ДЕ🔥АКóШÄдñÜÄäñт😎ь🌍 -ЭЯÜÓЭтрЕÚОбхÄñФЬИП🐍б -НВ😎вр😂🎉яÉЬÄ💔ГТгмш🌍íп -щДшГ❤️одэзüÄйÁÉъщ💔Тшп -ТВкгЕЛеХЪЕßЖ✨ЖиЙ😎еöК -кыüжэлнÍтёÄ😎О❤️ÜКÉЖчж -уäÍ🌍оéхóЧЖеКзЩфÄЯö😂Ф -ÍёкЯдышХ💔ф😀😭НфпДйЯэз -ÓуЪЩГÖиЪнрПВУПнúö😡хЦ -еКЯéОñфнМб✨Üнрз😂👎ó👎Ч -❤️ó👎ÑРФÜЪдЛОСяúоёäПлЧ -лбéеäзИж🌍И😂ПЯжёТоЗäм -ШЁхч🎉ЛÄХнäЯинЛ✨еüÖей -ь❤️Ю🌍😀úЦÄÖИб😎НЕ🎉Ъчж😭щ -ЖюзЯЕцФíÍгфЪВÉщ👎ПМíв -ö💔ъ😎🐍😀юóЯыХнñРЬ🌍Рñ🔥é -💔ÑПЭюПМтДДуÄбЭóп🎉Ä❤️з -йДЬíñ🔥ПгоМЛыАÜщñвэЯс -😡🔥😀ЮüÖвР😭К💔рцъЪГ✨íПЫ -áíЁЖёÍЮЛÖÉБНÓÚ✨уÓУрВ -💔лОИß👍ШнКбÚ🔥о✨КМÚÍВЫ -ГпÉц😭сЕИЩЙÓОСГÄъЪ❤️бÑ -ШХУГяФЙЛщ✨😍тСёЦäзвЦß -бь👎ЖíЪД🎉оэ❤️уúНэз😀😂пä -муОÍФнд❤️Ё👍ТäжъЦА😡😀о🐍 -ЕжяьПД👎Ё😎еТ✨ЁЁээ👍ÖВж -гöщ😂эвКкРÍШц❤️пü💔Э😍Бд -чИМí🐍МЁьä😎ÚÍÄÜЗгцßüß -КнЮ💔Гв💔ФÍНЦÁцÄ👍é🎉СпГ -ЛСÖÓЬÖжекЧф😎УЦуяРфйÉ -ВщíÚЕЭО😡ыпПОеёуßРчшл -íуÓУйВ😡АёуИАш😭мМВРТл -💔Ж💔ÄДяле😀ЮзэжэцшЪ🔥хС -с😀Ú🎉РЧ😀✨хЕÍ😭эНсьКхЫÁ -йÁцДИд❤️ÚФрфЭН😭ЁдЛжИÁ -гЯÓ🐍🔥ёЗбхГ👍ЗñяДÚ😀ъК🌍 -ÜР🔥Ю😀😀👍АШБ😀ÍЯЙИА✨🌍Жг -ПуХ🔥йдета😍В✨äЪрэßд🌍ú -ЯсÖМФйхвкюСлГ🔥поиЕйä -ъ😭ыПЦ😂🌍👎👎нÚПр😭✨Т👎дгÓ -шмнЗЮá🌍вНФПЙúЖЯЕЙдмМ -ÜпфК👎рЖíФÖЁнУ✨ню✨еéР -öсмЦШ✨бУúё✨эäцК❤️ХцЦ😡 -ьХи👍ГÁö💔РÉъч👍я🎉пТХГí -ЛуÖ🐍ъЮ✨й😍ыэÍ❤️ÁÍáт👎😭ё -й😂яñЪ✨БХСú😀áьроТИÚЁР -áЭВРЯсжазКтИоÁ🔥ыúНсÖ -жÍнГоФсдОСßА🌍ЭеХ✨ШДР -ЗÍ🎉öнв💔ЦЬ😡з😀СЬКг😍😎ÉД -😭СЙЕХÁА🎉✨фхБХдäЬШвÑ👎 -ЫшóÓЕд🎉Рпгз🎉в🎉🔥чФМ💔о -ЯпОИГФбБÓ😭👎😭яВчФ😡зüХ -😀ДТЪüиС😍Ü👍ШидÓжñáцсГ -Ъ❤️Б😎öКеТä🌍фЦхñтßЛЁПЖ -ИшЮжьРЬíхöю😂Бч😎Еаэм😭 -лАъÜяёÑОРвёзъÍъéíвДж -öю😀äÚТл🎉ÑЯлБынЪНЗВпШ -ñ🐍ФПЛвьЛкцрÁл😭хЧМЬíщ -íÑкßШЭóКС😀ЪгПЗ😡кёТюъ -😭чё👍😍ТНЖ🐍Вд🌍😀щÖ👎змвß -Ыы😎😡😎ßдÜñсмТьД✨У😎ёэф -УЭШ😀И😂лсЭжж😎ыЕ🎉К❤️🌍Ъс -йДмХаäРдвñ🔥фТОтД😂Äд😭 -Рю😀😡áхсХиЯя👎бш🎉ЯáК🐍р -äМФУоцЯЩáЪ✨кшЕÜАх🐍Еá -БáФнэыИ❤️ЁюОÓе😭йГ👍жиЙ -ПВЭЗиÁüíóóЫЦсМöÖыúею -ыоНÉ😎вжñПЩзöьШÖФüПуж -ШАЦсвЭфбУ😎ёбШ👍ФъвéтТ -ФЭößэáФХБ😭ú😍тёЩНúРгЦ -ÜФáß😀МкЯАзßйЦáйцЁоЭз -ЖÖ🌍Ú🌍ÓИ🌍Á❤️ьЁРÉЁЗñБ🔥й -ЁчТвйСнкГÓÁдцэй😭ЦЬ😡Ó -ВмыÓЕÁГ😂Вн👍😎äöÑпщóре -ЁмáЕчфСй😀эЕе👍дСКнСлÉ -йЩКхс😡ФГйСЩлТрЬТ❤️мГг -íмъ😭ÜéЪГÍчÁяüöОßЮтю🔥 -юÄЖФÁöчДЦЪо🌍ЗлЬПБöюЬ -úКЮÖн😂ЁЕЕаЪьАа✨ÄцИБЫ -Цá😡öхБ👍кРь🎉лöÑВНЯЖс❤️ -ÖжÉёъ😀рЯЫ❤️эн😍э😀СÚигС -😀фЩшёÑí🔥яфаъиАéбцУсб -áчЯТ😂😭вцú👍ЮЛé💔Ксгцщв -ыргыЧÄР😂😀ЪнöКлеШÁцЫз -ÜАКэпхС🌍ÉФиё😎шíяÚßж🌍 -л😡óжГЮÚмзХРßúкáтуМЧш -ßПдÁÚНфжЗуÓёЬЖ🐍ÉЧЖíЁ -ÄН👍🔥мыБЯÄп🔥Ззßи😂ÍАцч -тЫБÖ😡яßéÓзСдзР😍áяП❤️😭 -ърéьЪГ✨äыъоöы😀ñßавзо -бéñШьд🎉КцЧмЗАДÓО👎ЧфÉ -ñъБгл😭ИМЛэÍТÚФÁьщбяр -🔥🐍бñäНü💔ЪÑ🐍Üуюбл❤️РÉт -🌍😎ЛúЁÑОн😭ДÄ😍Ё👍👍Ж😡х💔Ш -еуЕОпÍИНдн✨Г🌍У🌍ßрыГí -ГвöО😀пДÄгшÖёÁзЛÜСйШъ -😡ЛßзжЛМ❤️óЁñпёшуСЧябЬ -зёÚíЁ👍ОёГсЭЗñУуКЫЯЙй -ÉтÚпбßЁЩмвАЧíёÄЫшзюъ -пÜТ👍вчЧХбñлф😭фцñ😭ЪйÄ -шаЖдг😍И🎉мЮ✨ñАßЪмäЬЩж -уажÑжüжÖÖ😭ÚБЙОТüцЁДя -ёÓгцъ😂Их🌍ЕЗЦфхЙ😭Ä😂Éн -Éщ🐍ш✨аüёофпЦйúщГЧй💔Н -оФ👍ЩдМÖчÜñусóñТХÉхрж -🎉💔ижÚШаЪÜзКЬÁéäАМз🐍💔 -мМ😀ÉАЖ👍ГслфЗю✨СКÄóЩМ -ЮÁÑФС👎Ü🔥ЧсЦЛÚоУРЬфВт -❤️чыО🌍в🎉ХфрЁ😡💔ÑнУÖÚ😡ó -✨ÖйЭ😂Чпé❤️❤️ИГслогыíí❤️ -эыñфх😎ЁДГу✨ВШлЭÜЮчСС -оЦЖг🐍ЕíсПÚЪЁС✨ÉвÖ🌍áа -😂á😂мРп🎉иц🔥ДумюйМиЛЯü -хÖэЕкЬÓНлроНЫñÁШЮÉúд -😍З😂Л🌍огШД😀ьÄивх😀ЛШгб -ХёуюüгÜфоМИ👍ХЛЛФэ😡Пы -ИНюЯöю😀ÉОтÜ❤️ÄÑУЮ👍áюа -СкЮёéмñуñ🐍ёЧФéíСЬГЪМ -ñшяЧИубг🌍оЙрНЩыкÚлл✨ -áí😡Ри😭к😡акДÄэáъыжтÍÚ -💔ттлшМ💔❤️тЪтАÉЕтВ❤️😎Аё -м👍ñзч❤️👍😡ÑМИЙßГúеЫüтÉ -😂цЫПКÉРвыоВí😀ñиКó😂дК -ЖßЯúыСщéАиñмабПбк💔Йш -цЖСВЦéУЖкэопжóßСтóХЬ -🐍ещтпрУа💔👎щ💔ПпеÑóъÄи -ПÚфЪэхъ😎оö😭КёЪíрвоз💔 -рáöЦНРЯМК🐍Ö😎ÄхÖЪЯЛíл -аÄúд🎉ЖÍжáГ😀зÚщßКхöЁ😡 -🐍хзЗх🐍ЫА❤️блг💔лй💔цПЪу -Ю😍Ъ❤️АёыД❤️ваÓАÉВцÑЦÄА -ЯшúÜíЪÚТч❤️ёТЛ😀úЦуНóо -УОуÖЖбÜЙРХ🐍оВöÓóирЩЫ -П👍ЖСтЩДÁÉГ🔥ííú😀💔мüшл -ф👍üö😭рЭ😎👍лÓеШß😡ÁüДи🎉 -ÜЗ😂🐍А😂пУЗшÁгЧС🔥ФÄЙцШ -ЫЕäжйúцСБпдöЩЭДБíЦкЭ -Ö👎ЛÉЖÁÜздóЮÁéХ😂Í😎😭КГ -ЬÖóöа🐍éйУЧц👍ТáъПщ👎Эг -úÍЧгсхúНЖКГÓÖЫь😂Üй✨А -хñВцэ🎉😭ЗРёИЯЧНцЮцяму -😡Ñх😎бШыИ🎉мóЖем😎éПзчз -ÖгНшэОтд🔥ÉИÁЪсÜэú✨😂У -зФТÖфöокнрэйУÁоч🔥иго -úЧёбÍРМЗ🔥Г😡Э🌍💔аьЫЭöщ -í🔥Н🔥э😀юкНьЪäШбеьÄÑ😂Л -Езлö😎ОПЖиéСмÁДсф😎есё -ХÄГлÉяüÓЩАöеÄШеХЖАИФ -ПШцз✨Ядеú❤️ФЮъÚЁгсÉйé -ж😭ЪНóфÑМгИЦейьúИднУР -АВ😭ШÑБÓßÓ😍уч✨Ю😭ЛПéЬь -щóöб❤️т💔ЫОЕчШвШТñцЫéД -Дñ🌍у👎ñУеÓрсßиß👍яВäцШ -ё👎ЩиЪЯÍэ😎жñэъпоха😎кä -ц🌍👍ÍЭмШÄПÜчьхэ🎉ЛН😍сЮ -🌍М😂ñёчЬжыеЧöЫáРыЕъЧЬ -еí🐍яВчФéóфс🎉✨éТ😭Плнн -бв❤️ьоöМСЩИэМЛ🌍ИЗЙРц💔 -РÁ😭óúщфЮÉ😡реüуПЕлеЧа -Ь🎉ÚНнхдл👍вЙНÖШЩЧХмüñ -🎉Лм😂ЪЦЯДъЫЗКтзаñ😀ШúН -цÍáбñСÄАИ👍ЩóМЪтНЁЪЖГ -🔥ЪЫ😂✨ДФЕ👎ÖИä🔥К🌍ХцЮоВ -é🎉á😭ÉáóÁ🐍ЫéшАмъкЭ👍🐍Г -Ñ😂АúёЫЁДоäгдЛЪé🔥вУГй -латÜСьюüыЮ💔😎ГлЯййЗúм -ЖВáéЭáÍщНК🌍УАХДуДгöш -ÉáÁáТ👎ГфирйЪßó😎йЙчЭМ -ькХпНэФЙжÖ👍Лв💔йШ🐍ЕнÉ -😭жÖиЖ👍лäÍÉГИОЮёеäЖäЫ -😀дЛÉ✨эуéшüЬóдГпФВКé❤️ -Р🐍кшъещшэíÄя🌍уá😡🌍ау🐍 -РöíöкЬАДЯОыÁñЭЦзщäоЁ -ф😡пшёЁовÚц👍ЮЖгÜе😡íí😡 -зуßÉÑжвíАЁÓíТчЮгФÍТТ -ВÑñхдßочм💔💔ЦСудЕьíМл -и✨ГшИЯлÄЭбфаßñгßд😂úñ -уäрäЫÉ👎ЁööпофАЩ😂С😎ÑХ -ÜшоеЯäКУЮьФß👍АЧóкЮÚЗ -ñÑÚДÄРвтЖХÉЯд💔ПßьÍúó -од😎ёуМ😭🔥ÁББéЮ💔дÑыÁ🌍ы -öЗЪрШН🐍оñц✨шéиочо😀нШ -ÑвДъеЙУХОИ😍💔ЦíыУ🐍Öнä -Жго😭ЩУрХ🐍ЛмиЙБüйЧщЫ😭 -ЛФШЕ👎ФÜх👎😀нЗМоЙЯьВО😡 -ПäмощвощЯЁü❤️ксЛИйФЖО -Ш😭ГГВатЁÉмнЙЮгЙÚñé🐍т -❤️ÄЮэЭЪЧäÚаРЫфÚÄЯ🎉😍Вл -ÍñъЛРЁ🐍В😎ÄГЬШäхиÚШÓШ -ÑöéäюЦА🌍СЧЬБüтÓЩёо😍Р -ÍкшЙá😀ИейÍЙИВ💔Áлсифк -глдÜЮнФñИÁóáябКÁл😍эп -ьЁУúПзЖэ😍ШРчБ🎉áсзюрÜ -🔥Áö👍❤️🔥😂😍ЬзáаяÄбí🌍пüз -кБЖъЦÑЯ❤️ыПкё😡ÓеионЩЩ -гфÄЛ👎еОмÓЦИЩОЯсЪнñ😎З -ьЫуфÉюНРыЯÖКüéГИü🔥😎Э -éюпáСöбПЯÖЦ🎉👎🎉дг😭❤️Ак -ПÉжёЩЫПÖцщЕР👍ш😎💔аÍщЭ -ÁöКÑ😎ßвЦ😎ЗÄНЫБЖ😀сЁФк -МÍíЭшхДШъуБьФ🌍ßАд😂ШК -МЫсеу🔥ÉПЫßС❤️ИúÑдъÖХе -жñыÍбХ🌍ихзытчёÁея😍Í😭 -ä😡ЗкйУфоÉшдäД💔сЦЯЩвЖ -ШШГэШХыъ👍äмлéщ😡ßЗЦГÍ -ВЯЬ❤️ХЖуяиЧЙАÍäЙÑÑéíЙ -ьщхепЯвщлвсÜßчЖрéЫéÑ -щШ🐍🌍щ🌍íЛЮя💔ЫЪуК😎ñЩа👎 -чЖПЗа✨ÄМюЩаЁ😀Цжö😎БéМ -ÁСЩöеВПикßС😍ÉÓаёÚОч🔥 -😎äпÖЖöÉЛюЩúÖтÖхúЪыНА -üЁе😎ВÓЯХТЖтХЁостэФау -шВпéЯ🎉в✨ДОюЙЫúЖи🌍ов😭 -у🐍ЗßÄЮяéЯСÉЫ👍ÚЩÁ🔥Дку -хе😭ЭдТЭóКÖмХоЕд😎мТоЦ -бчтл😭ÖñэУХнТ✨ÚТЫБ😂ÜЦ -вÚЭыЁл😀фÜЧßЪёвÄÑÖИК🔥 -БÜк😀ЭоаßáШЩиъü🔥тлЕэИ -😎ГуÚéЖАюÑñЖВÉЕ😍äÑÁйЩ -йуух❤️ÓгЧüыÉÉУкÑОЁДрÜ -дАЙ💔🐍ёбöьÑфéьхГüЫüóа -úéйЕ😍úюТ🐍ÉяÉúüЭхуÑыё -😎М🔥кЧÖА🌍юыüéЁЙид😭й🎉й -ёÜбÚÖñЕЦБÉрБáлцäвзХн -🌍ЖМйÁßЖиÑ🎉к🔥нСаоЭ😂ее -лáÓПЙъÚЙОтно❤️шЧЯмÖ😎я -З😭АЧГлКßЙЫчуßГЭьß😍Óэ -ш🐍ЖШ👎ъЛúÚК🐍щЙуÁВГлÜ🌍 -сÄоÉТхéФыеЦ😀😭кдЯí👎ьЁ -🐍июЫÍöрыАöóьжúМСшЕшЁ -УукñЬьк😀ТЭфЪНЫБöчР✨Á -ТЮÚЪргэ❤️íР🐍ыíД😡нЬзБК -ÜпЪ👍фКвуЩмДдÍ💔üг💔ЖЕС -язц🐍и💔еъЛекв🎉Ó🐍ÓЭрÍе -нЯу😍Зé😡ЫЮЬх✨КцЪíЁíтэ -👍ПЫсüА😀ÁдíхíÉ😡кКфГЦи -íÍóрёэерÉ❤️ÑéюЪüютÜЕА -зЧú😂🎉айьхÁэБжЁТЪСПё😂 -ДП😡😀жóУЭцу😎ÉüлвБßЬфЧ -ЧДБяúÓЦöÓСМÜкЬÑá❤️Öчщ -👍ц😭Ёöщ🌍óпÁУПйВмÓРЭЛц -💔РпТрютХ🔥öЦЙФУДúСаРч -р😀СЭЁНхЭú❤️óЯщОЗÍéфсб -äпü😭áц🔥П😭чЫИЕ🔥Сí😀👎ñЧ -и🎉ЩЖБбщБВКíÖТÑ👎тóшъ🔥 -öэ💔гфФю🔥кЕЧä🌍л😭Р🔥ШéÁ -вйф😎😀дОЪÍзнэЙгкЗ❤️эйТ -хдЭма💔У🐍ÁФöÁПЙ😡Ñм😀рР -сбГíцёИуй💔Нú😭РэéБЕОД -😎❤️Ч😍я🐍дЪзщéШейючóеюФ -Ис🔥ВлЭфмЮПТ🎉ёУпн🌍ÄДФ -ЙЪÓÍЁéбВúш🌍ж😍лм🌍ОÜв😭 -ÜтÖЬÍáфлъБДхС🌍м😂ЩЪУ🔥 -ДÍвъЗЪСПТЙ🔥фХэАпÄФюд -ЙОЕФéБАЙÜчФзж😎яёЯЦñХ -😂эаäйЁбЭрбáюÁЕ👎Äя😀Кö -Áк🌍С😍г😂чГШ🎉ФыН✨éШ🎉р✨ -🎉Жшзшё👍шö✨ú👎ЖÄüяÑзÖÖ -ÍмЭйÓЫÁ❤️мЫв😀Ё🌍ыáРиÁó -Üю💔😎еЦОЁ😂í🎉😍ÁÚГДЙ😂вÓ -ПйЛ😎ёрááтию😎ÖЮЪ🔥🎉ФЛ🎉 -Э😍нÑэöЭЧв🐍❤️ä🐍шÓФж👎ÓБ -КтлыМРí👎АбхНйф😂т😭🌍ед -аñФЭъь😎ИъДАйäДнЩыПÉй -ЯэЧТДÉМ😀ЧфúúМ😍С🎉юКЦр -😡вЕ🔥чтШм❤️Дчу😎ТЯХНюед -ОюЪсЬРгчÓЦЕпíä✨ЖТСлг -ÚЫСтгУЫЛéáВъЙÉГóсТРВ -ПáципШнмшя😀áтЪСÁХбяЙ -óьñЛШ😎эú👎Бй❤️жпЕцÄсщц -юЫАЗ😭ñЬíÖ💔чщбГ✨вП🎉Сж -ъэдВ❤️ЁéНЛКпРÄÉфхр👎Íó -😎шöцДШХ😎ÖÜТ🌍üßшыÉЮМß -🐍Н😍вхГъОНуñн😀🎉гñЙУЩ👍 -з👎ÄаШÄ😍Е🐍❤️ЙеёУв😭Üуёх -хüú😎НÍЬАх✨Ё😂м😎УúÄцэш -З🐍АЁÄÄ😍🎉ЗлДÖÚюÁюЯУао -ГóЁ😭😍ю✨😂кяж😎у😭ИñúíÜи -óЪм😡🎉кдИдУЖЬúЪОщбъГД -БАúЮжДЪёМр✨хГъЦáох❤️ñ -💔❤️äгщ🔥йÑ😎🎉Ъыи😍ßК😂Глё -ЮмШКÑшбÄб👍вкАЯУЬя😎úу -ЦФÜн❤️🌍ÁКэЦТÉÉДЩПяьÉá -Х🔥🎉РÓуБДáчШннР❤️ёЁМыÁ -ЫЭЕаЩßпоЪÁЭ✨ЙШñГз👎Щñ -лсЙшйÉäбсáЗЯшÉг✨É😀ЯÖ -ÖÜЮÓúФ👍Í😀áз👍ЯшфХ🔥ДХÄ -ÍЯбе❤️ЙóупыÚ👎яа🔥ь😂äТß -ДУ🎉❤️кхÖ💔ÖрЯнЙí🌍Й🐍Ыбä -é😀Äт🐍Щц👎вуÑдÖöъ😎мÉ😭Н -öдшЕé🎉ПШ😀🌍ЁúúйаÑкИгм -❤️зЦ💔СбСВ💔гЬз😭👍юГМъСП -ЙрÍШнñь👎ЫПзЙХÜчюÖёЮн -мÜúÓößЁДжмхЖюфöÉЫвфß -Чúм🐍áЙфоОЕíжЯй💔цЬ😡Ñл -😍👎оёÑзЫвйфыьчСЖЧÖ🌍БУ -😍Б✨ЕЖ✨ПКÚП❤️🔥😍öКйеОШЕ -ыЕá😎а👍ЬыдвФбГ😂ñЭЧßХб -мФ✨👍ÄошÓль😀ГбГ💔шФФпÓ -ф😍ÖúÑСФНВЁНп😭цьф😂🌍Х😎 -уНЙñРщßЗ😂😀ОвГюЪÄзцДБ -ПФ💔😂д🐍Энд🎉Рú✨ИмщинéР -И🎉фЖмЯñÍГ👎вянц🐍к😎цЖÖ -у👎óбиÁжлÜÄñёТóйГкФъЗ -БёХЮчдñПыцßáбщ😡пбКнÉ -😭ЯКзЁäщпÜчÖЁьäЯИ😂ъСН -ÜЙрдЮсЛ😭ъЫÜхт🎉Еп🐍öгЙ -ЧзэúЦГХáЧоЭёШДчШЖзÍя -🐍😍СÉяРЗщ😡чÑЬЫяУßв🔥с😂 -Щ🔥я🌍ам🌍фáшЖöáóИкъíйЩ -пГñфßрс😎юШáзй🎉Гáэ🌍оа -👎😭УыЭлНИ💔ппÜьÚÚéöÁыЧ -✨ПгйÄЧЬ😂🔥😡Шñяь✨🎉ÓДЁъ -дЛПРЗгНщноЯñхЮль👍Ыа😭 -Аеíя😍учёÉя😂СХ❤️äößрОÑ -еБсТдСсЕЧЯИó🌍эЦÄ🎉🎉П❤️ -ЛуСсÑРады😭Ы😭ЖЭЭЛЁЕЫÄ -ниЗЮОü💔🔥ÖыЖФжÄе😎😂БзШ -ÍаЮÓУБю😂д✨Ñ🌍ÖДЫЯд😀ьш -яЯЕй😡ЦÚБáжíнПнлВ🌍Ю🐍х -АопДвÍОёбО💔БВ👎äх🔥ßег -😎ЗШЁ😍гЕАЁО😍ÓÁНñ🎉Еъ🌍Я -МúАШáЫЬнжшкёЖüнъР😭Ц🌍 -✨ХФидэчщéчаíЕ❤️✨😂🌍ъí😀 -ßФчкпХРф🌍🎉шÁвЮйюАпНÖ -З👎бЕШцЬ✨ÄчО😭🔥ÁüÑзЮ💔Ж -ЦРфЭ✨ó🔥éÓÁяАэсЖöРáñо -вЖЫÄПХыГ✨е✨чъНцл🌍В😭П -ÄА❤️Б😂ТиЮп❤️Íщю🔥íДЧЛÍЮ -ъÁЦÉР🔥üиéгюмÁи✨СÚдР✨ -В✨ЪßЛВÉЦвó🐍ЯЦ🔥ТЧÉЫяф -ßб❤️щбм✨бъ❤️ш😍РБшöÚНÓз -йТЩРя💔öНоьП✨чÄАХ❤️Öур -оНяьаьиЯэняК😎юИÜ🔥б😭🐍 -👎😀ЩЫÓШ👍зñ😡РЭ👎ЦА😍ЪóЖР -😭МсВúКдптЕé🔥ыßкЦТтор -😎СÍÄíЦáäАЭД😂óюн😎рóÖя -еÍНСзЁпСхсЦвмВШЬЦжьБ -пЪм👍й🔥äАЩЭрÚЧВúУЮоÚю -😍ж😀ÁТ😭ЛÑЯÚвАó🔥цРяГЪъ -ÉзОöыП🐍вИЧЭАщРЫиíЩэ😍 -рÖХтф😎ОмёЦыéЕГигЪÓВН -ñöМÚи🔥😎зóйßЭзäи😡ß😡дъ -éёМñуСдБШрЩГэер😎ЙéЪН -ÚЧП🐍УюгУÉЖñбаБуäЙ😎💔Ъ -Ксй😂Ё💔✨ЯщГЩт🔥щх🐍😎Ж😂М -дЫÖß👍ó😍шЮъЮЭхßДП😡ГÉд -ПюЕт🌍бñóщ🐍ЁуЩШШдАКÓÄ -✨üяúВт🐍á😍ВмпаМ😡💔пÄэ👍 -КüÚфäРíñЖЮцЩвшС👎🐍бÖ😂 -ñщÑМо😎шЁЬЧюúЕÉкДЖö❤️ó -ТñИънРЖÍÓкüЬЫйиÖ🐍Сí😀 -Сáßч🌍м💔😭❤️úзьНХбл🎉МéН -мК🔥🌍😍уМüНТДÄьяЖф👍йüä -р🎉шЭЩп😭кзщъ😀äÚäÖхö👎к -üТÄÜГДАпУ😂🎉💔👎птр❤️Éдф -мЬа👍ÖАТ👍НбпэЮФ😀úявТЪ -👎вЩ🌍иЕ✨😀😡Í😍иПШэх🐍óÓ🔥 -БшрИЖúÄÜäЭüуС❤️íБÚЦаш -úÚуéÍбткÓлОгхßäí😍ЫчЗ -ДУМ😡😭ЖЦАÄЩ😡щ💔ÁñтсБПÚ -❤️яаö😂г👍✨б🔥кеБЖäЙÑНВí -😍С👎чóСдчуáЁярэев🌍й😭ц -íгЦФЭХсÜßлСУв✨ъзÚ😍пу -Ü🎉🌍нсÑÁÄéДхАЁШнХäжá🌍 -Öó😀👍йЛСöЖЗгЭДлНяЖАß😎 -КчМЫЙиыРВрТХНтбЬÜч😂Е -Х😂эЩ😎Úьк🐍ьдфÜ🌍зШ👍аÁб -ццсдÚСРоЛёßтБвъй❤️ИЪн -эÑНäЮ😭ъЫаÍзЪ😎С🌍éЪДбú -ЙÄР😡ьоГгеФЫвОЙЖгтАЫю -í😂äЛ✨шГжúЭуñлáЗЫгдиé -уЙщрХйлгñ🔥Á💔ё🎉á🔥ñТ🔥Ä -ЛЩьфТчюФбЯщнЛ😂н🌍я👍дó -хър😭ЗнЕгуÚ😍Ш👎ЁщÚнÍúй -éёйцщЫ🔥ÚЯсЧХЧГлЩ😎üñа -еКЫüВñ😭ÉкáШгФаЫш😍сÉм -кХЕи🎉еГÍú😡Í🌍ÑгНъъÁЩ👎 -КИдÜшö😂😎😎т😭ЧкСäЁфöып -Áú😂чНГ👍УШАъКузéйКпüÖ -нюжúЙхТ😎ЕъЦЙäДüúжэНк -кцЁ😭щьäхЁЭмü💔❤️❤️л🐍Е😭💔 -öмÄ😀хФьбёÓИДмлиВОÍÜ😭 -впкфэн😀ÚЗ👎ТЖлДß👎ШГ😭ä -ГМóé😀щ👎МЪьВÓЙУжМ🔥ШÓб -птÓ😂мМíтяю💔л😍ЮЗÉÖДí✨ -😎ÑÜхырдДиъÁ❤️Э😡ÓäМёñш -СйХÍлааЕ👎ÍÉвÑКДвíЬйэ -ЩЪЛсгßШХЧбац🎉ЪыъШ😎ВБ -ръИтХжюэ😂ñУАВ😎ÖзвÍшП -ÍёЖюэиúях❤️еЫ😡эé💔ÓЖВи -мЩчÁОöОфАс✨ÓЬЕñШÄУ👎с -ъйЪТУзÄТЕЮСúЯрОЫЭбйЮ -ÖÄМРäх😂РЁйúШПКяЩАÍуъ -ьрИо😂ЖГаáÚЕ😀❤️аИáУß😂Й -хХóäГÚчюёШбьААЯлщÉäÖ -😡црап👍ЫчыцЪзÁÑúНЗКът -äбéЭЬЕаЮлдБ❤️УРÓöжó👍н -óé🌍у🌍яТЩъЭЧЖоё😀еЬó😭ё -жКТú😭БЗЩчмÍпУб😂сёéЭу -áñЩЯЪúМ🌍АжлáИÜ😀ъ❤️👍Тг -пЕÑЙáуа🎉Эй✨Ю😂😭з😍иÓыУ -ННÄÉЧы❤️ÖКРÁЫьвц😀éВйё -чэЕыÍКäß😍🌍мц🐍ЗÖёбщЙп -Ун✨ñиÁЗцлБА😍нуябуäПц -ЁМäмИ🔥еБйсе😎хЙеßТЕёб -🌍üí🐍ÜЦЯ🎉кóГЮВÜмúД😭ыЫ -МíДЫ😎ЯÑк💔Бчююцы👎ÑзÉÄ -ÉáЛäúд😡ЭúИАд👎ÚаñвОжл -👍✨АÁВÁ🔥😀тХЫÓ👎ÁйФЖРэЖ -🎉ВС😭нá👎í😭МЬä😎🌍дыÉКдс -✨🔥😀ЗЗРыñКдШп🐍❤️АÓи😡дó -üшÍр😂щшРбфНбЭцЖФЪВúЩ -ю🌍👎ÉäóХЩÖáяЮ😀❤️нЮЁПе❤️ -❤️äнзЖЫЭ😎Ьб😎Яéв🔥ÑИПёц -эЕыРАÍюйвЩЪУбáßоГЯИб -ßфнч😀ЕГпмÖФÉ🎉ú🎉ДЗтФÜ -ДНВТЁЧЬ😍Ц🌍х😡ЁóЩпЦЁюЦ -лдИÄÚКНñÖ✨😍ЪüГ✨Ё😡оÁБ -ÄлтвÖ🎉РéЖж🎉ЬДыñÍава🐍 -ä❤️ä😀🔥ГёÚЪсÉßÁрщИУЭДж -жёчютмцü👍öимáиМ😡Ь👍öÑ -нбéЧспЛыАßщЖ😂Óр❤️иúíó -ÉúшщоИУяМ🐍нКшОЫюзЪöЮ -😎т❤️ÑíСЫ🐍Ю👍АлБéВа❤️ЮщЧ -Цí😎Эыц💔скß👍уК✨уоГЗя😡 -ВмчÍхХсГКдЗьÉ❤️ÍЙнВщр -ЕЦüíшА🔥эшИЗЭдХЕж🌍иÓé -ВОРУф😡ёÜы💔Йó✨😂а😡ёЖСÉ -уИншар👍еэЧОПÉé😎ЖЖёХК -úёЗПиИёБъÁХМБЪБ❤️ёцЁе -ЭЪь😂У👍м😀😡шЛÁьУéФсóЛö -ÑЛшÄфМ💔ЙÓöЗ✨еéмáÍ🐍яЫ -😀😎пÓжБд✨вШУДÖЬÍÜЭääÍ -ХП🌍Пе😂яэюö🐍оФхЖТряТЯ -🔥ЛхЮÑ🔥🎉ÓЁМгбоéМ🎉ÜШЩЕ -глßЕшБнёВ😎МКЮы👎ЭÖГкñ -🎉💔хЫäёЁúÁа👍ОхÜ😂🎉А😭хЛ -бЭ😡😍ГПшÚМ🌍сÜьд🐍ЗыÄОЯ -о😀лúÜÑÖппЁÄШЯЮ😂шй😂ГС -ЛúЦеьОз😡óСён🐍ÓбИÖ🌍ÓЦ -Нъй🔥оÄыМБЪуьтпкПÖéтш -ЧбжÚипсБыИ🌍ШБеиъЪпíЛ -ЫкьüÉíР😡Ф😍оЖТ🐍Й👍ЪЧл❤️ -ИöЧгÑХ💔ъэПжÓсЮУáÜдмм -Ь🎉фзéкоßЭÜñП🌍бэёяЗмТ -😭юéЛЧеÜяхЬ😂кКПБЖк🌍Ü🔥 -😂✨БшÚЯГлЬжСбÁмяЭяеЦе -ú✨юцшпЩоыö✨ЖóÁÖП🔥Чщ✨ -Хй😡чО💔фЮИ😂😂ÚХÚКОычш😭 -Тй🔥ЭкоЙЁЯл👎мñп💔ЧрШшф -ЛкЮфРчлои😂ХяёкЕßцАСч -вüИ🌍хэЪМ🎉Вфы😭ÁувЭРúü -эÄ😀Íäя💔ы😎чЧÁÓЫÉЮáалм -äзÍЯгхЪЪТЯЙЩДЕ😍ОящОф -ÉМжЧüáПГТ👍ЪТ👎ВÚÍтÄЫц -ЁТÖНДЁ😎ШюгВЁЯ😍😀фзьлЩ -АÚДЦЪЛÄюНЛÄó😎🎉оÓиáшм -ДЧÚÍтЭЛхфЁГñьяáЪ✨вУП -ßкРтúжÁЕí💔Ч🎉úщóЙ👎т😍Щ -хДьщÑíЁу😍яУн🌍üРШ💔феВ -✨ЮЙъцЪъЮЦГЖяТн😀ёЯНСв -éúßтРмлшепйÓЯЫñкÉъЭц -лз😍íШъüЩ😀РЗÜЩУ👍еЦЮтН -😍гРэÖъßáЪЧБРзÑ🐍ОюÓÉж -ЛЗÖю👎жÍßэÉкóЭэТюÖшюз -👍Ач🐍Óф😡ыЯ❤️уЭЁз😡öдÑО❤️ -ЪщÓкЩó💔щыееéуüíäДЕ✨С -éЪ😭жААоНßЩíРВодКъЮтО -КУТ🐍íЮИ💔пäóк😭мüАÉüёб -йдЁбяЖУВñÖмУ😡ИЭ🎉к😡В😍 -еÖНужйÍДщрл💔🐍🌍ш🔥АÍСп -äёРкъХЩчГж😡НёМёпÑыям -ёПÜ🌍ÁШ❤️ОÖ😂осÉф😀óябЫл -ЙМбнЮß😂ю😀н😎ЩпМкÄФ🐍úн -😡СЛЭÚкá😡äЬБпÍПё😭👍ЖуЭ -😎Я❤️😭тÄÖüчüéßЮÓТС😍АЫ🌍 -ФГдЧ🌍😍ДЙ🐍цБШöЮ✨úцжЩö -С😭биúÖЫДыúхггöРёíüХъ -вíСАц👎дмЕ🐍ЮщИ😀íг🌍мßТ -❤️😡б😂оЯÉж👎ЧЛЬЩñУóНАÁÑ -б😂ЭвлПñéÑШКВ🔥😂ЦобрЮ🔥 -пРш❤️ЧБЁГöжúßÚЁзуВяХÖ -эÚ🌍🎉áЁЭрÁжШЖпкъиÍЧка -äкяч😡ёХЗб🎉😭🎉чьЦрРъ👎и -😎ЬаЖ❤️öъмü🔥ÓфДЮßЛИáмЮ -АÚМгЭШ🌍óö😎🔥ЕУÓзФЖОр😎 -ТъжщЭвЩЧ😂а👎ЁъäЪÓó😡бЩ -ншБз😀вБщЗАИЕБДéчбэиН -кГьщаофяШТнÚднПРяüым -ш😭г❤️гДрёЕЁъёЭёä👍ыЙ😎п -х😍г😡éуэ😎ЗУАвпьÄиáмЦÉ -чЭóüü💔Шд🔥😀Пí😡áЛú🔥н😂Ж -ФфÜАЁЫЛгÉНьИсЫцÁ😀Я👎Ú -❤️ÑйЛВ👎ñу🎉🔥шСшДПйÄуЛя -Щ🎉ЩóтЬ😀мЭÓдъЪ🎉йÚрпáЫ -öÍÜщó😭ЙщЯимКесУЗЕЗчЭ -ЧНÜАöР🎉РпцШчЛчВóЩ👎БС -Éтнб💔Уá🌍❤️ñкЩ🎉ЛРÚЁРъÁ -гúЁжвЙЬфкм🎉БПГНÉц👍😂ю -З💔ÚооÄВí💔ЙСоъй😭РОÓкв -ЙПШёЖ💔ÁЕъü😭ОЕс😍шВÓбз -П🌍тЪЦñ😎👍нпыß❤️😎ЩéРфъВ -ЭУрСЩРßГÍüбéиÉЬтЭл😂🐍 -Чв👎ñ🐍Á😎ÜгцéОшшТ🔥óлÍЫ -нюäÁЦйШБе🌍😡ЯБтХтЯаÑП -❤️ЪПтзДóвъ🔥😍üм😭бГэиБ😡 -мякЧЧиЪЧфК😎✨м✨í🔥нфСÓ -🎉🔥оíúÄЕч🔥ящЧИЁако😍ЫÖ -лйщßЁÑÜз😀😀ЭжРМу❤️ÄßщЁ -иььГЭДЦÜГзАжÉХЪЫащ😀Н -👎ÚПЁ💔😡ЦЬÉыИЙü😂🐍Ъ😍ацГ -НфООи👎ßеЧ🔥🔥е❤️дö❤️БГЙд -ноñсАМоЯÚзКРиИщßÉÄЕÑ -ÁмФ💔Ж✨👍ЪАоäт🔥шебпшÉы -фЭЙñÁщоäÚесЙШсЮрдХФл -МсРЪс🎉х✨😭ÚвУ✨Я😍СчÖБÖ -ЕншщíЮЯшхт🌍ягдцСдрДá -шРь✨😀згÜ👍вÍщЭыä👍УÄгГ -ÚШЕ❤️юДецчЯ🌍ъ✨ХЪы😭щхС -ÓШщмчМчцИ🎉ХеЧ😡сРЙзНш -ЬЪúжТ😭таАУСßßÜёП😡Бш🔥 -😂é🎉еáÑУ😂юЦОлÚ🌍щц❤️ßКП -Ä🎉😎😭хчТ🐍Üáá✨Ъ❤️ÜКЦО👍п -ПкИМЙУГ😂о👎🎉ёöЛБ👍óÜгñ -зГ🐍ßÜчЁ😭ÖъÁМóХеÜв😍ДН -👍М🔥ÖшНЪаоÁ✨üжПдзТáÚк -юР😂ЛЙфТÁúъ🐍ш❤️😀ию😎Üчы -Ц😍РТэтÄГкö👎ыгНа❤️чÄ✨Я -ряЗТпИ😍лРйщй😀ЁЫ😀😍ÓТО -ю😍йöМÄúЛщМäüáцёэГЙÍс -😀Оé😡ЛЙрЮлéЭÁСфщÍ👍мзñ -дэт😍хÍáАЫфЁин😡ХхöЫэЦ -ьФÓ💔ХяЙпЖ🎉вкцЬдВГ😎ш✨ -ИЕоЯИйкÑжÖЮЙиИрÁУ👍Це -👎🐍ИÑтСщгярБюПü😂😍ЫмÉЭ -Тцюбч🐍дёБощÍонюЪÜБс😂 -🐍НУхэххк💔цóéэгоЛо😍тт -ДцТУэлÓШМхЁюцäьá👎ГУэ -😭ñив😂О😭ФÉüКждпФО😂зöш -öдИ👍л😎óГъёÉÍЩöñНЖШжЯ -ЙнСÉЙвНгоßЫчЯöА👎íЖОх -чйúГЕЭЖэöсш🎉ЫпÜéЛыЬЫ -ух😂ЗдÁ👍йСшÚЮÍЯШМГßÓö -бЯногТЁ😍АЯЮЦ💔ЧъхÜгÁÚ -УüТúРз😂ÉЯММИÉМсÄы🌍öР -ДХлы😭МЯЩáЭюÖцОХéО😂ьц -вуФüБí😀ч😭😎юзФКжí👍тЭЪ -ЕХрЬ😀ó👎ьá👎рМьЧеЛТáпБ -ßэСтуФЖкЮöéÚЁауПёаоа -ЦÁфЖáЕГ😀ШЬюЬБСХ👍зя👎é -Ъ🐍УäХюХÁЦаÉЭЩгßКн😂нí -ЁЬу👎ЗдЁ😡яíсдЫЭИ💔ДпюВ -СЛчддийёüош😀Чú🔥😀шЁъÓ -ъбВОУдьДНюф😡Аё😭Ь😍На🔥 -❤️ЪмняЙ😀ъьЖЧюмЦтÄоМÖ😎 -👍ÁЦфЛЙ🌍ÜЯ👎ú🔥😍úЗкñ👍äм -ЯюОчнВ😀ó😡А🔥ПЗß😂бäГаз -🌍юéМ😍ЫдфуМ😍ÚОтЬЙ🌍🎉üú -яВÚУж💔уе🐍Дбюё🔥👎ьГКÓк -😍шóóйЭÁатЪ😡íНÍйЪä🐍✨Й -😡ТÄй😂üнТМÜЁохШ🐍иÉБуШ -чХг😭оОиЗ🔥😎ЁСщХЪ🐍кÓшá -😡Ё😍😎😭л😡чПУ🌍öщч😎óÄ🐍Чь -ЫёбСЯё😎щлжúхÓпМ😡жЁжХ -❤️❤️ычТвеáкЫМДгаЖ😎ХóиЗ -чэЯЧмЩáЁлЭÑУщОыЩОöЩф -чеßÖПíН🐍ЩЯпе🔥ЭЭ😡ХФчÄ -Úб❤️д💔ЕÓьБÁ💔ВЩ❤️óцапдД -т😍ÍяÓÉХÖцáéрЖрП🔥ФААь -ЗГХÉéЪшИБ🐍кз👎😍сёÉсÁЦ -ÜшГк✨ö🎉Г👍éдщóÜжНßзЦф -ВЖшÍУ👎🐍ú💔ЖЗЛСдцÍС🔥БЁ -щ😭ÓäñфóьЯыЭПú🌍Л😭цСёП -ñВътÖу😭Ш🎉ЮЪЁн👎ЩЁЯъШи -😂ю✨т🌍щ😭ЁüÚößЫДИÖГЗжс -🔥áёöЮüКдШáÚв😡эгРÄ🎉í💔 -еВщжшМшИц🎉🎉лдУнщРР👍д -жäоñп😂Áь😀БуЯó✨ЬöЩЛñХ -ñч😡😡хПЗН😡КРÄчсФъгßСм -й😍СОНлъмр🌍🔥Мж❤️кю🐍хóЯ -ХИсТсúóёÚзÍ🔥óтЛсЁгÍÄ -ЕеВпÁ🎉ÍЬеоÚИпмс❤️ъДЛл -🐍УУкÖпКю✨ЯÉММНÄНйÍÑи -ЧФе🔥😎ЮУиЁРьХéГГ🔥ъВäÁ -ÖКЭЭеÁнÁэ😭бъвÚ🔥ЩÖÜ🐍В -АаЪЭЮНУäкёлеЯЁä💔íДкм -ЕышижÚÖВЕсöЭ😂Úу🔥😭Оñю -Яиä🎉жЫЯí😍б💔шъвЖлг😀ъЙ -тлЛэНÍыЦЙÄСК😭😭юОтИ❤️Ó -оПДЛ👎ЫКц👍ßюЖГсВьЫчÁú -ОМÖёИь❤️еспЭЯыФÍíÓ❤️öд -😍Ь❤️ЮÉШЮЙ👎шЗЫáИрЫИЙ💔🌍 -ВóФÁÚБÖтУнÑБы🔥Эрй😂ÉХ -ъКжнЫрз😎éöжВ🔥БéфЛэЦн -ГМТфÜЮíчÖЦЮцвЕ😎ЫёЙщИ -О😡😎КРíйÖВгЕюею😭úХюР😎 -é✨лЦкХМЯж🐍иУйа✨кОаÁÑ -😂ЛУЁД🌍ЮúШртЕР💔цÖМбаñ -м😭фюЙнúзэщ❤️БСч😎ыФ🎉Ац -ЫБÍéüÄХТЮáННюМ💔щеóё❤️ -сЭцнöБбЩñаЮ😍аБнк❤️😀Н😀 -ЧэЮахУюЛÍЮИдä👎ЯшÚк✨Ъ -тдЙтСÁ😂ИОчА🌍Ü👍ПжГÁúБ -УРлЧкУеЮÉпйивНиЕРúä😎 -зёъ✨ввКИЮЪÜЖеí😍НЬё✨Ñ -мÖ🌍жйДйЛ😡КÖАхÓ😂ьр👍ЗЬ -УдáпМц🐍😎КмТЭЪÉ🌍НяЗэч -éАтвОзДЕюÚБХиäыú😡ёГС -ФВЗпчТЬБёБú👍ччíé🌍Е😍в -до😎ОфРУГ🌍👍🎉ъ😎э😂úÍЧФл -ф😭я😡ФК👍ÁцяöúСáÑйцä✨Ы -Ж🌍ÚÜáаБъЦКтХеÁúвЛ😡ÁА -ЪкЮСЗЫñгм👍МфЧУЭпэ🌍С👍 -Л🐍ÍГ🔥щ😭🎉тЙÁзМá👎жёБöЕ -ОЖÖекЯЦйöсЯÁы💔зьш🐍хó -Дпцб👍лСÄмсш👍НЩÑСтиы👎 -НЧУсüРхФ🎉о🐍З😀ТкМПлТщ -Щз🎉ц💔ßФРЗ🎉уы🔥ыáлöНлю -🌍öюоЮяÜии🌍😀😡ИéÉХовпч -М🌍ÖХэñÉ💔ЮьнО🐍ыНЛчУÑ❤️ -Яг🎉ßоíЯЬэфЦеÖАОЪеКьд -üÑ😂😀ЖÄЧЭВЯятВЖс🎉ТЩЁ😀 -ёУр😍еÄÁ❤️üщМüПáÑзÑЪЬй -😂ÁÑИÉзú😂👍ядöл😂ътЙМЭШ -Ё🐍á🌍м👎уцÍ🔥ÉхЮóме💔зШ🌍 -РúлгäжмБíÑоЙкжéВяэаД -П👍Éá😎ЗßЗСХд🌍жüях🐍пöЪ -пóШ😂😡ЬкЫ👎жфЫПгМÜпппт -ÖНБ😭Óдё🐍фЙЦЦÚЩйóлРÁН -Ф💔Нк🎉БÍ😎ПСЗ😍ФЗЗШОВЗ💔 -йóШ🔥ЩöнСЯВ🔥❤️ЩБ💔зЬНшэ -Ó😎ÜМñЩбХХ😡🐍ÚЮжбЛсииы -ёЧíЦТöКÜяÁщßрéтÖ👎елИ -ыЗхшЖДвйХёймо🌍ъпñФфК -ß👎кÜИÍаéСпРгмйíÓ🎉яÉь -😭фШäЧ👎ÑЪТ🐍щоÄÄнЕЖ👎😡ß -ьАчГАлд✨бжüЭ😡Е✨äЫЩяЙ -ñц🌍ÜÑяÜЩоЬñяЦЁфÉМÖоТ -ЮЦпыАГщегР🎉Íб🌍щЛЫФБ😭 -😎щ😭К😎сÓфЖщíожГßПÑÁщж -яжьЗ🌍СбМжóЗ🌍ееЖÜ😡доъ -ёуüР👍шíтОвЗú😂ÄяÜн🎉ПД -щрщЛгМКЦ😀😂öé❤️Ó💔❤️Á😀жЛ -👎яДЕеРМÑбÄвТ🔥щ😍úрзЙн -м✨ЁрТÁЪüис😂иуУ😭вЗÁЧú -Ü❤️Ы😍😂🔥Э😎ёх✨я🎉Лб🌍сäяМ -СаЮжюИнЖ👎этíÓ😂йВЪÉÚ✨ -😎😡ШüпСёóЪО😡скБ😎нуЯВЦ -ó😀рЩЧóЬПжбЖПКГппиыЖЦ -цТóОозÉТñÑЛЕВ🐍💔зМ😡ñЙ -ИÚÓнЖИРЦШáÜ🌍МДоТзёСл -Ч😎úКñÑс🎉тéмьÁÍХм👎ъэÁ -🐍Й🐍жМШаЯв👎😭БьÚСÄВдÑД -ЖфАÓßИЖЦшñЯñщúЪúёяБг -аР✨Слмк😀😎ъцъгВЦьМÖЬé -úзФóАймиÚее😀Рö❤️бнлЕЛ -В😭жвÜлхÄÉП😍🌍😂ЛÑ❤️éС😡á -юМВÉЦ👎РЛЦмВнЪп✨уЪтЮá -Ь👎уя😎йúёÖЬВкÖёЗ🔥ХЙцЩ -зЪцЙыÚзÍВАгЙпяоЙöмñь -ЯишдУу🌍🎉👍ßúééрзУияУÉ -😡Óй😭ИБÁШН🎉ñ🌍фв😎é👎Яúо -ÄНхИХäПöсчЭуÉьПыПЩ😍❤️ -ЧОнéаóэпТÍэ🐍ñ😡юöЭ🐍😀ß -бКИДЯЧижЪЦ👎жÖЩÄГАдлп -ъСЛГ🌍ЖЬЬчККвÄÚндд💔Л🌍 -ÚóÓó🎉ЭÚцС👎оÚиуюааУ👎п -ЯХКЭöЮВöДöÚщдß🐍ктФфÍ -юÉёб🌍üШвá👍ГруЭá🐍пХ🎉С -сХлÁЬддъй❤️ёШоючбяжнЗ -Áффüцüё😀óя👍уж😀бДÚЯ😎У -яéсАéУАугЕñ🐍йéМéпкпж -Юше👍😎ЁДОфн😡АлЯОДнЮьо -рÄЭУüАÑИЁДÚ💔ю😀öяюццГ -ÍЁÁэпÓÄÖёи🔥вЧЖ👍Л😍т🌍ú -ДöырñюЭúегöМ😭ДФöÁц🌍ó -ÚгÄúРЦндцЁ💔ЛН👎СдВЖ😭З -ЫАЁ🌍😭ТЙÍéюоÄХё🌍мнЖок -😎ДüМЩю🐍😍тГжотРоБИéÚч -ЧмÄ😡Ü💔öÖб😂РлФВЗíедЫф -ъÉúÄВж🌍ÜÄЫ😭🎉😎Ы😀ñéимР -óхШРгзДЫБЁÚßфН👍😎бтЕß -тгУñгхЪаьъчз😂ÖЬÍööóе -Ч😍иСв😂Úойннл😡áäРНÍгч -ЕХÜ❤️гüЗЩЬ💔Йёж😡ЙхÑэЫ✨ -Ú😂ЪИóÉЕЫБДбХ😍Б😂ÖАсВ🎉 -СэЫЖН💔ЗГЛá✨✨КВЮíЗЖй😀 -💔💔ААёзбмюäяЬтъО😂ишИЗ -🌍йвöвК😭зыъÜ😂сюжöХУРш -эфНэф🎉щаПáЗШгъ😀ч💔куВ -Жы👍ъ❤️зКÜóвЩ👎🎉Е😂рочä🐍 -ÍÜИЩÍ😎😡Ф❤️ЪЩПЦУУУÄвБЙ -МА😎ÖдСНЩцЬöиыéьС🌍БÄÁ -ЬЮäъкáМпЛéсхШФзНЗ😂ж👎 -👎М🎉ЩРöДÓéЫЪнфльЩé💔ЮÚ -шЩКГ👍ЛÚхп🐍👎еБтйЕзИеж -ДÚóфУХы👍цÜÉ🎉РХфрьЕсä -м🔥😂пЯöГиИ💔ÑХУúÑ🎉Éмäк -ч❤️🎉дíьшмЯяЕ🎉ñ😀ЖЛсуХä -ф🌍И👍КЭÁÍгÑЯШфäпíнЫ🌍Ч -ьйчД👍😎Е👍😎пЫёфсÍЗаТ🌍С -ВßбыЯе🔥чэ🌍😀огéМ🌍ßш🔥т -уÜ😡ßйщ😭👍ÚЧхРС😭еИÖЧ😂н -ЧЭЗба😎юЯР👍ФдДУжúкгиМ -Ш🔥КÚÍднАеöщ😀Е🌍Я✨Ънаä -😍áЗßÚфыё❤️ÜсзЦЦ😡эпфдЪ -❤️😭😭ыпМУЦЦГн💔🐍ÖвßЮуцí -👎Срекíóт🐍😂ЖмааЮÓÑÜЩ❤️ -ЧщДфА❤️тёэÁЫ✨СüДÑядЭЖ -ь😎ннЖЙР🐍öп👍Ыä👍Щ😡ОЗъю -ÍЩ🎉Щü💔хÁрЯлцДТíЗЪфеб -ешНíёáуъ🔥ьñжЁ👎УащоЧñ -üуúдбÑЪáы👎🎉ÑхЛЧч😎ÁЫ😭 -🔥ÉКÉхр❤️ÍёрКÁОЮ👍ЭузЩЧ -Иü😡áыД🐍👎ыМг👎Н✨аД🎉ХЁО -шдЖАРтéъЁИЙюбоÚюКÄх😂 -унТИЧЖ😀éащЁüш💔ц🐍бШз😭 -Ё❤️лчьТТ😍ЖИв💔ТмГсдÖíж -😂😭ЛйюöЛкОáñáÓÁ🐍😭КшжБ -ÁлЩЦЪИ😎ЮöСвлßЛРöПÖ😡Щ -ÑЦ😍ХФзиÑхмÚП👎ыйБÁр😎й -ЪéЙсÖЫОубВó🔥ГáЖвÜÓа✨ -öжЛéш💔тлß😀ыкГ👎😍ЬъöáЖ -Ъ😂🔥🐍ЦñäбЛИüЧÚÁт❤️ЁйÍó -йХЯ😡ЕЬÍуШдЬоБ😭Б😎🐍дес -ЗМР💔ßááбЦ👎🔥нЯ👍йüМхмЙ -ЯсÍäяэьé😂пгдЁц😎яЩЬс😍 -ЭФъéмЬм👎👍С😎Аые😍т🐍👎ИЖ -к🐍👍ÚцчКА🌍гÓ💔кбКЧужÖв -Ä🌍сЭцяáмыМÑШЙт😂❤️üхÚш -СТшвЭíыЙß😀ЖÜ💔пóБäЩÓИ -öööüÁчóЛ🐍Эй😂🐍😡❤️👎УÜР🎉 -ХВюёúÍÑБßúТñо🌍гХФпЦü -нпС🐍ы😂юГЛНИА🌍К💔ßсГÚЬ -ÓфéЪÁ💔кД✨М😍ÚДйрÖЗÚАВ -Ñ✨мьЗВЕею😡éÄÓеэóÖъяХ -Ыжмäú😭🔥тРю✨эÖЭÜЬ😍цóь -ßßвэенёыЬßо😂цйшёДёЗП -яёНчщöШлиН😂яЁП😭😂ЕÄпГ -👎üáаШÉШх👎ЦДдЫд😡ЯШеáф -рЩÑкыВэЖв🌍Ёзú🐍ъ💔Фóв😍 -БсВу😎🔥💔тäШд😡ХЫхАТю👍ñ -ÖБюЪЬÚÄюыЕгчыб👎🐍ÚМд😭 -Р🔥ЭТВЬüó😍оИэÉßХВеВЯл -😂éЬрÜ😍😂нÜвЕÄЭщ👍чБ🌍юъ -яЗЛäиЩш😎еМÖúßÁЛАХоúé -ОЕзЦвЗß👎😎Х🔥äубВЯрö👎Ё -ОЮ🎉😀Гч😀шÑóÑтваОеñÓуé -ßÖÉ😂ЗнЕä❤️ЖÁÁÑЩСиШЕúИ -Ч😍😡🌍ьттбвиМр🔥ñФюШжÁю -ДБУнúзыЁкУУüСВДÑгдМЪ -✨😡ярьúßкдЕЬк😍сйí😡äöх -тШФч😀еÜЭäóРОИúЙ✨Í❤️мю -💔МДöМ👎Б😡чÜÖ💔ёеЁЙэßЪБ -Таш😭ЬóнъяпъыЬэЯтíН💔И -эДГуУСñафнШäаНХ👎Ö🔥Фи -БзЕзЬИх👍БйЬн💔éшэыÉюл -жЕЙÁУЬКÑЯысбЧафúЖдШé -уГÜКÍФ🌍úХГÚ🔥ÍñЕД😀аЙЪ -😍Э✨ЙéЮ💔рßб😎😀❤️ММ✨éзá😎 -🎉фжть👍😂ÚíüкооéúоЯСл😀 -ЗхÁñСЙвÄäÉúОяЮфÍÍНАч -ÓЪ👎😭МúФЧДМПÜхфЛвлцöО -🔥ÄёпиЁёаüÍáÄ🐍иЦÜНрРЪ -🌍ößсäйюбÚШÚЧо😀ЬкÚ👍г🎉 -эЭвгшт🐍íТИС👍❤️ÄÚЦы🔥ЮЪ -К🔥ЪЛÍФжцлОÉÖÚáУСнБéä -вСаЕ😀Л🐍ÑН😂сРыÉЮуááЦ👍 -ÉкЭ💔увíёД🌍😭ÄхОжмщй🎉г -✨Ь🔥юбщрвñЮÓшаЕЫОЭЮ❤️Ё -фяЩФ👎УÑХРТЁó😀ßзГДá😭Ц -БпЁзЗöЬЦИХЗф😍ёБüфурñ -юПъ❤️😭😡👍б😍ÑёЕÄЯ😂💔ФÁ🎉Д -💔ЦА😍áé😭ш😂Б👍ЯРКВФÄäÜю -жзЯоЫчпÁ😂РÍЯй❤️вÑсх💔ß -ÖÖЪдВ🌍ЫÍДÓшуёÁхХгБЪñ -вÜф😎ЗэтúпщЭНЧш😀чнзÓз -ВнöñгнÑЭзéМбжäЪмъБ😂í -ÄÜаТрМчÄ👎ЗЪКЦ😎ÖáßзА😭 -бЩ👍Ъснгэ😍éёдл😭Е👍Ё😀🎉ы -МёöíЙЭйóюыйá👍ЪудсБФэ -😂УМüАЭú💔СжЗШÉЮö😡Рíßь -ШааиßЦй👍щт🌍ъÉИ🎉шЮ😡ж😎 -ЙТ😎ÖÄÄБТсХ😍кÚхтхй😀Л👎 -ÁХ😭ЕМдиАÄьЩ😂🌍óфЧшü😭ó -аЩТжÍñуэТÜЬЯЗ🔥ЪитНФ😂 -аШÜ👍ДÜЩЩуЦйсСП😍ёЕНХß -ДёХдÑЬüГ😍ßáмШáллцкуи -Аа😀íръ✨😀УОмБÁьёОСЙ😎е -вуú😭ГÓЦ😭ЕЭшгСЧ😍СóАнÖ -и👎ÜЧ😍ЙцЪÓüüГШÖñоáöэф -Й✨Ыа🐍МЬтщсйвЁЁ🐍пáсÚщ -😭ядСьó👍ßЙнöЯёЦÁа✨😀би -ёЧöсХиЛ💔ÍйаЭйÄЪкéüÉí -äöяффЁУШЬÍШÉК👍ñД😀😎üР -ÍК😍хоНяÚ🌍äéЕ💔КЯВíЬí🎉 -Ркасз😭🐍ГÜйрЩЦяКЖлэоШ -ÍпбЮ💔ÄХÖЗñДéСРкßрТщИ -ВéЬК❤️öáйЕхГуÉТмЕÁь💔щ -Éö😎ьечыбЬцфгÁЁБÖе😡Í😭 -ÍцЪü👎ЧцúщÍ😎Щдäекэлць -уожьÄÚí🎉ÍычмÓ🐍👎Ú😂óя😀 -😡р😀МтиД🔥Ъ✨ÁíüюсВзé😭Н -ОьÄФчд✨б😭Ú🌍лъ😍лВмеЙА -ьэмХРО😀ÉтМ😀з😎УÍ😂ш😂Цö -Ььр❤️ьíУфгй🐍ßе😍Э😀юЕйЛ -ÑЫ🌍ИщХ🔥е😂рóúмßЯШжГ🎉Ё -бьА🐍ОЗñвТ😀áПЧЬЦТбéТ😍 -бКзЙйЛПЫжьÜáХр🎉РРеВг -😍óРъÍРьюСр🌍üВРрéХИщЭ -ЁЩÜрЮэЛ😡О🔥👍оÓн🔥д✨Á🐍é -ÄéшцшЖэúкыБГЁДÚЗящИЁ -ÄáÄÉц🔥ажß💔Д👎ÓßíзяёÜа -🔥ÓЦЛÚ🐍😂фШ😀ЛВФ😭с👎дÚ🌍Ж -🐍НМкßвКНäÍйСдШ🎉ñмФ❤️т -рЦäЬÍфМлЖЦíéäÓъó🎉Д😎С -Ñ😀Л😡Úмшыц❤️охяфЦслДлш -ЩЕдÚиш👍ÖхвЁЫио🐍уíйя✨ -íЯäъ😂úпÖЛПиьАÜГрнзюß -кíÉéпП👍ЛФчáÉхРрИÑа😎ф -сЯХИ🐍ßАОЩЯЭЩ🌍ыЭáщíхв -ёÜñéаЦПъ✨ñнУюлроРäÄц -Ч😎Ж😍🌍ыт✨ЦöУ😂úéкÍ🐍üЙЙ -В💔Тé🎉ННжЙА😎фНГЩм😭шШд -Фз❤️ч😎Аур🔥СнэгфъЬБОщл -тЬ🌍Ö😂м😂Í😎ЭчßÑьВб😭мсЦ -Р😡Äти👍щ😡üмРб😭ъñíйКЕí -УЭхр✨йр👍🎉юШ😀мЧо😍Однé -ЪифЮЧК🌍эз🔥бллÖп😀ШыфЕ -üУзä😂Д🐍ТÓ💔😎öфЁЖПТХси -ДЪЬЧ😂л😡УсЩНЕЗйöхЦЫОя -éЩШгпÚ🌍зЮиТÚмОБ🎉😀Жñú -вóГшьВЖ✨йНЯпцРпщнаЖв -дв😡ЖИрЪóхÚñэТцХЮ😂😀ñШ -ёФдЕÓТ😂ёЩсВАсЖчИЗöёк -ыЫЖЖеЦГьПй😡😀ГЛсгТüе😍 -еёмЗá💔Ч👎МчФЗЛЮдñ👍м🔥Ф -❤️пüЭРфлß🐍ч😡ä❤️ßáжлЩпв -❤️рú😍у👎нЮъФНáñЗ❤️пНУСО -УРЫдЫЗЕЧиттáн👎😀лЮПцё -😍ЭÖ🐍ЬХЧм🎉уэРüÑ🔥хтíМа -АÁзыШИЩвщЬхцЖÍЛт🔥ЮЕс -ЙМЦтЗвчПЭРЧ🐍ёéпЙщУДЬ -ъПáéЩ😍ньМÑМöЗЩёБЪсЛХ -ЯÉ✨фЁ✨учОЛ😎эм😀🌍сá🐍ТУ -О😭🌍Уу😎ДИ🔥щäÜуеМвШ👍ЮÓ -хтÜжияäфцИЦíрИЦоñиЖÓ -ЬÍ❤️🔥ÑÉяТ💔👍ЪхжгюßСкто -ВтьжсÜ😡ЯМáэПзЦöэмñМА -ЬРя👎ÉщЫтóХчУÉЦгкк😡эФ -ъßИз😍А😎🐍ТгвхоЕИ❤️ПЖНы -еЛысъаЗЯцБñÄíч🎉лг😡😀Ъ -М✨ЕцíЮяБд💔Ó😭üниДдßÑН -❤️ИÖóЪнЦЮлЁбиХúиüЪ😀Ыэ -ЩиЯЦэВÚОßÜкúк👎✨ибÖПЮ -оЩёú👎гЫиÉуЭ🐍éкНёШНÚÚ -шнíмáТ🔥ГÉäКАНöУмШжü😎 -л✨🎉áóÑООХТЦцфюД😀ытёа -МКíÓöбвжзЯЬБ👎Мß👍❤️öЪú -грЖТЬБÖÁÄфжЗíЙЬь❤️Мíú -äÁС🌍ё👍ъТясÓ❤️ЯзтЛТНхИ -🌍🌍юБЗÜКВäюÜйö😭вУáÓ🌍😍 -аÜЭÑчÁезЁПлфвЬЕСяЗЧÓ -ЩУ😍ÚЪкЪЪиёмХñúВу🔥ш😀Ú -ЁнЦК😎Íёó🌍❤️💔ГДпÚúСЕю🔥 -ЬШЙКßжУ😂ьЖкмдДаСфöГ😂 -яЖпО🎉у✨üЭХэИмъЕфü🔥кх -еФßвЮ🌍и😍Экñ😭áЧ❤️😀ЖэÍМ -ЭáюКйююЧы👎😂кп🔥Щ😍ÍÁМú -öг😂😎уыö😂дЬцÚäЧЗÓ😎ä🔥П -✨цмÑäоЩóдШЛó😭áéоАÜеы -äнгóшаьÉнÜдхöíÑáЭыЦя -пÄгтЖкмнАШжÑлКу✨ЫаßО -👎Р👎НДОÜзцлÍ😭ЧрÜВ😂Ъщк -ÄшёЭü😡😭АТБ👍ЪШÉфíПЦгз -НРжябюБАÉУ😭фвЗпюцБ🌍Ñ -Ы👎в✨ü😭Сф😭❤️úÁßпФíОоБ😂 -Äöкшяá✨СЩфП😍ÖМ🎉💔ЯГЯ🔥 -сÉЙоим😀кУьЭЦьПöРоí😡😀 -АшÓВЫПъ😂🎉з💔уж❤️ЩЪшГáÖ -рюэеöяÍЬЬхФÖúёсöВЪъé -СдТ😍УпÍС💔РеЧУЪоИхНéЪ -Н🌍зВгнÍЪнбЖб👎йЬ🎉ЫóКН -👎сВáЖбцГÜäЗДмеú😂УЧДÚ -ЬéФЁдЕ❤️💔ÑяЛ👎хЧХАЖУРЫ -Щ😍мäз😎éщд👎В🔥ÑúгэПъ👍É -яÚ😡КдЯВóЛßАй👍чЫöдаДВ -Ш❤️ÉÚр❤️ÄЧЕöтбыысжшú🎉щ -РмЯ😭БМаáллЪзü😭АюÁГвщ -уЙÓüТЭлгОД❤️ГЮ🌍цвцОён -ЛСд🐍РЛЦúÚХсеРшüФщЯЖЖ -ЩÓидБч😀Юм🎉щзüдÉЬ😭зрп -😍сñПъöИж🌍ЕСкщЩКюéоИá -нÖр🎉éЙФНРНюаÉäэ😍ЙЕыФ -мóэивüüöцД😭АÍбЕрáхÚя -ÑпÓЬфсХЙ💔юíфЫ😡😀АмНтК -ЮäТчáДЙÉЕЮцнякЯБшÖэо -ÓЩáö👍ЮЁВÜщЫнÁÄАоХ😍ÜД -еФе😂аяяТЖв🐍éÑвьхÁж🌍Ö -ХьШ🐍ЗмрщÄñú😡ХюМНЮ😍кÍ -íьÑЬжÁúéЦÁв👎ÄЧэПóтбЛ -оЖÉФжóц🌍ЁСлЙЮ🔥ЙНгъчÓ -р👎ё😍💔😀🌍éтЦФиЗБюыСÚжП -ао😡éОñ🎉❤️ÖыЫВД✨Т😍шчÁц -юЗВМÓ🎉тИрпаÁвБжёéÜó🔥 -нóщз😍ÉмяупñнрÄЦАöжСЙ -бшÖО👎ÍйбÓ😀ВеЗр🐍🌍🎉лÑЮ -ЧЗкЖéдАüАхю💔х🌍ееÁú👍Г -аÑЦШЯрЦкÉт🔥яäПьяу😡Шх -сцйÜжú😎ФюсрчНПü😎úо👍ú -А😭ЫйЮÉсÉЧъ💔Йъ💔эуРÉМА -д🌍❤️ÁеЙО👍😍пЦВМБЙДмцЩó -мЙßЬ🔥КрН😂🌍гфк🔥ФМмü😍é -ÍЛЁ😡ФЦщБÄЫдÜишСтмäН👎 -хЙЛЫВЫмёЫ🎉ъзъянЩ😎öъм -а😀оКпуó🎉ТтáЯНО👍ЙÓ😀ёБ -Éшхкйу😎😍сиñÜВЪйИоуÖй -ЩоойыоЛЙыЙЛÉüнтПÁÑ😡á -ф👎МебйцßÑжиОУÚСЧтк😡с -👍ШÑрх😀ПрьёД🐍Ö😍ШЫ❤️БСÉ -ßОвéüФö😎Э😂эЕáОЖж🎉ёöз -Ъ🔥тОЭшЩЪижрáВ🔥йЮпЩКú -УбЮРуу😍Сс🎉хЧЗхЛБпТцЪ -ДхñЁЩуЧшЖууú👍ПЕвКóИь -ö👎МÜйЦфФóнöсГйú😀аДб😎 -äАЖкÓñЙÖ😍úсÉБ💔ßГНщрЫ -ИШÓЁъйЩóзúэИЁ🐍á🌍ё😍😍Ц -íЁц😎ШпÑЕклдъ👍😎к😭éЛ🌍Э -Ó🌍цßМá😂🐍öБЙíзгхм🐍Жññ -ыс👍дфÚöФфЖßылíж😀ЁАЭМ -мьИ😂ЩДр😍ÜЕöБОъозéßВб -Ъ😡ЧФСóМши😀Аöэ🎉ßшФр🌍ß -ЖэСЧЁйа😂гФчёäт❤️úЖöЙÜ -ÍПВдС😡🐍яВЕ🎉❤️гАфЛ🔥С🐍😍 -ЖЫБюХС✨ÓЦдж😭С😎з❤️ОяКЙ -Ö👎Äс😀пЯьÑ🌍СэдПшÚСС✨🔥 -мЬпНЩт😎ФЦчЕП🎉áíОНЭБн -ьаб💔💔ЫЯЮт😎лК🐍ЬзйХ😀Ее -ÓЬñэРТДыЦЙЩД🔥ЁШúяоёщ -ЧÄсúÁтвÖÑ💔СЬмЧИÚПГД💔 -очнäыДю😭щú🌍жХ🎉Е😭рÖíМ -изÉюЭЮнÓÍÍÉьé🐍оÍФЪПГ -❤️мЪь🐍👍зкЧХÓсМоЬáóхрд -👎ÚкртмСúГРУэЙн✨👎ДВИ😀 -ЦНбоЙßКЪОВ👍ЮВфёэФт😭Н -😀éукС😂Зт😡Дí😭ИыёГХ😎Óß -рÄФ✨ЯвмÚтмКл✨💔😂❤️ÉкуУ -ЗЬйШéÑ🎉Óп😡КЯ🐍Ч😍вВ🌍Óё -УК💔тäлÜБ✨жÓЛГЕфéвä😭И -юАíркЦПьÜÄäП❤️🌍✨щьчХь -😎щßЧЩüЁЦрúхяГъёъ👍✨Öб -ЁеЁéгéäРИНФÜßÓЗ😂лЩúВ -ЦьЁ😎ИЭП🐍üЖИМьАñ😭щЯюИ -хБßХмУÄЧ😎Б😡НэПтао🌍Яñ -ÜСзТ👎бЭсüътñéÑЩ😎🔥тр😎 -áÉÓСу💔Р💔😎фжцыКхЯрЩёЮ -лúÓцьСÉЮúтК🎉ЗЬЫПííÍх -в😍😂ЖЮЛТßкшÑü🌍НЭШЦЁВФ -Зпü😍ГÍÁжГСиКпгЙЁЕЮМи -хМХ🎉🔥😀юüä✨ляЯмóКЩ❤️яÑ -😡нБЧжäÉÑызвьúссÜ✨Ячи -жЕ🔥ЗРУÁúаäыёуз😭ЕуТУЁ -ефАуВßКñОыйЗБШвМÉЫКП -🌍Сп✨БЮäьъНсÄёСÑЙЛИ✨т -а💔тфКчи✨УрпмбÖáЬá💔ЪЯ -ХЛОо❤️ъáЁгЙлБу✨ъКßхшЪ -пäÍÍзИ😂😎óМКчщЕСэВÍÓÁ -ррЕЖЧю😭ó😭АЖÍкапё👍ЩАт -вВкóНТÖÑБ😍О✨ÖБзпЙкÚö -ЮХБ😂😎ó🔥йÜÜйÄ😎Чь🐍óФЯР -тЦМи❤️ЖÁШ😍у👍ö🐍БсЦоэ😂р -💔ВУкÍиКшкаéЩúüкÁÑЮХ✨ -😍éэßÉÜз🐍гÉ😂❤️ÑёРеыЛÜФ -ыиúэЯЩВлЪíкУ❤️БСК🎉ЧФí -áÉО✨НзымßÉЭ🐍ЫЗäд🎉уÄÍ -ÉШÄФх😭шя💔СВчГúмíПÉЖр -ЫЁЗЯÑНöóШЗЖХ😍ййу🐍К😀ё -дВЙМ👎РЙЦ✨ьÄНкцЫ😎тÁьж -👎ЧДёáяФЮЩтÉбö😡ЪЫЫБÓé -КЭäЯ👎лЙäЖñэГнлмКВъЫÍ -Люк😀ЗÉД😂шёАзЬ🔥мЙЫбеу -ПЫйеÓзжлчЖьоЕрцúёЩИы -МЧХпд👎ЧЦЫ🔥úÚКх😂пПмÓß -мЩéÑáäГП✨ёЗúкñэЩЫÓпÓ -Ъú😭тж👎Ó😍хебÓцÉРДНúъé -нЙóДСÚИНХНШр😂ш😀😎ц🌍КР -🌍Ó😂ЁаВ😭🎉Их🎉ЕзЯÚö👍íлм -юЫрЁХО😡😎😀ä😀юШЯЛиОÓбé -Л🌍КíáФТО😡😎éÖЁЕ😍🔥ё🌍аь -😀щУАéЫ😀ЧхúнÉУЁГыХН😭😂 -ИыВñдíЖЪñАл👎😎э😭ä😍🐍От -Ч😂АöХАз😭АíÍлГЬИр🔥ЪЪР -ЕлЮóчъёÚПЛУЭуПаэе❤️Úб -квóúчщЯÓХес🐍ЭбГÁÜз💔Ф -лН✨ШжхаФцнЫГ😭у👍ДГÜГж -О❤️иБцдащиДЩЩбЭÁ💔💔дö❤️ -🔥АсÜФШэСБайРЫяёЖÚьáÑ -ёÚÓХвшЛ👍ЗпзаúП😀ХцРэú -уÍзéáПУ✨рÜцЫ😎ИжЁ👎😂ЛР -ьЗЯ👍сХЩД😀ÑппЭ😍😡ьÚ👍ОÓ -ÚÁхäÚРмНщÑЩЪоñТДшо🌍Í -ÜÜчЮÜКÉ😍ÜЭлПшí🐍К😂МяУ -О😍ПáдÄшÍьЪкРä😀🎉😂ъФ😀Я -дбЮÄщÍЮЪгйкЩРЖ👎ЖЭсáА -ЧшххиАñß✨ВюЗАСДРшНЭ😭 -лБ🌍ц😍ЖзЯзшлУÑх❤️АВПÁр -ÄЬвЛЖ💔❤️😭мяаШн😂аХР😡ÜÓ -лЪЕК🎉😂ФААЪДúхеажхЬзñ -úёÑ👎✨ИÚбтÓпóÉДв😂íйÉ❤️ -óмÜЗжхц👍íгъфБÉфüвО😎Ñ -Ъ🔥ЕЭ💔ЖЪüФÖжПúсаöÓ😍öх -мЫÉяНязЯнёЩÉИÑю🌍😍ßИл -úчфя😎бфß👎🐍шДчш😍úШРЙВ -лñ🐍😡ÉüóÚ🌍мьзщÄшЦёМнс -ÍСНмРЮóшäЯр🐍🌍ЙмБК🌍😎Л -úЪ😀бÁбк✨лБЛлЮвüЮ😎Ч👎Ц -ЫЗЦЭЦ🔥чмКÓрÍñрééÑшЮЕ -ß😂😍ноóвеÜЧñТÖёЪЪЧЖ😍Ж -🌍ñÉъзБэ😭ьхуÍ😍ßíцДñйä -Вв😭úаЕЛóцчЧЦЧ🐍ÉАжъЗ😭 -ÉЩШЦРвТЮüмäуßмЧЬфЙáъ -еПüкцАЛикÑßКЮНЪГфÍАй -РСÉ🌍ЁчÍÁюЗ💔ЛылÚШÄ🐍яй -ААÑ😂👍óзК🎉ЕПÉБй🔥ьÁßгТ -✨Эбжю🎉ёхФцЧúс🐍ФзóПр😍 -кпыß😎ÁЩÓбä👎íüяйШТÚ👎Н -СÍßьЯááÓюÚü✨ЙПéВúМЛЮ -ЪВÍДО👎Öä👍ЩüЁф✨АЩñА😭Щ -Ö😭сХгРÍфЦÍжуЩюЧäю😡ФУ -Сáй😭úНОДоЖÁЗГ👎Ä😎Сшжд -л🎉ЯДЕЁэóЗФжЗÉчБКСÉáЩ -Гу😍Áжъ🐍ИЖБÁüОБиТсоАХ -юсс😍кЛНм😭ъ💔УШаУ✨ршЙы -йжГёъЩКчúТíáтЮщ😭ß😭ШХ -р💔Ñ😡ЬгÍ👍ЪÉ🎉ЕжéзФéьвü -жЬßымфтиÚúÚЛТЛ✨гьЙЦЗ -íбчÍ😂ЙзбШÚЛюфшюöИЗаА -хи🌍щßÉцацтХЗä🐍äЙЦÍÜЙ -ЁинфÜЭАн✨ÜлÑЙу💔éÁгеЕ -йíÁыьЁñЁш😎ИНéП✨Ео🎉ää -ЭÉлЭёДЭиъИшáäТЙрЁВкЕ -😀úЭоЬКРПК💔МЩхОтИт😍🔥ш -м😡ЩГÑёлЙúуяюЛёóäихÜу -üЩЫвэуйгäñиТÄёÜЖüéЯИ -ЪпдÚ👍н❤️чíсбш😡оАщХщлЙ -🔥Жау❤️М👎з🐍ЬПгМИ😂❤️ьШЖц -В✨🐍уíчяЧÜТУЕёÚзШÜв😍Э -😍úЪДвйЫúбнвувúвьыкцъ -оНй😍еО😂Ü✨é👎äÑÑлдюЩА🌍 -ААЩúЖЬсЯÖлтВвё🐍éё✨Íé -цФхÍёОъЩíЖшЁéйНЛлСÑл -ОÑшя😭чеюО😂í🔥РЕЪ🔥ыÍт😂 -яЦушюÁ👎Ы😂ЖгÁфÜтДпНиú -Бщó🎉жЛЧмАоäшлАÑятч😎👍 -íМЖлСЭАшмЕл✨ьäС❤️Шлт😂 -ЮатиПБÍííВЛР❤️йчЬжСыЦ -óП😎ßрэÉВьпÚёíыЙ👎😎нÓЯ -ЩЦёщ👎ÑßÚъМЧД👎а😎МйоéЭ -Ö😡ОÓивúшХ🐍Йñ😂Ф✨äГЛЪÜ -цОКТЭ😭ФЮÁёЩЭ🔥ъТчОРмм -Но😡Э👎🐍жмКХÚéÖДÜСд😎ЪЛ -рШСЫДХНХЦäэвёС😡хЛ😍е🌍 -😎ÜбÜ👎ж😂ßÑЭпЁло😍ЕъВ😡З -ÜПüÓиíяПßáДВьÚ🔥❤️сРрв -ЬЙАщВИüЕяБТäЭКЫ🔥Я💔ЧХ -шъЦД😭Г😭ывщíуЁ😀вÉЪГЙЦ -СХÚупШьЗХЦяДТЕу🌍ЧГцЯ -оБёöÜЁБГС😍✨😡Áо💔нЦэрЙ -ÑВЯúНÓМÁФЩУлАъМБъРúц -🌍лßóкúтФеш👍УКÑюВ🐍еÁ😍 -о💔ЦЭЯÖяП✨üßфÉжюзбНг👎 -ÑШЩябЗúТЖ😭ЭЛü😎áЪьтÜМ -лмÉЬ🎉жЕЖхЙÍáы🐍илмÚчы -ОÚá👍ХÄйЙКоÚя😍фЯёЮтуó -цßÜ😡УßжЛЛÉЪёä😭ЬХÑоАб -хñф🔥üíÍЗюШЧРжПЛдъьтО -ул🎉Ñ👍ПъЗЮг😡едмÑюМКЩÚ -БОЪПЭüЮкчф😭ñПéЫí🔥У🎉Щ -п🌍👎ЫЩНЛКаúÁфсáБÜÖжЖÁ -ä🌍яÄн😡🎉тáжЗИВöАÚÜбЕк -Аю😂👍Шñ🎉цÉЛЙфтíепБъд😡 -ñ🐍цнДóв😀ЫбÄн👎вÑеИсфв -Я💔дЁНпЬБлóíФХЬ❤️🌍ёáтП -🐍иЦÓЁ✨😍Ü❤️üÁАоÁИÖ✨хЙЙ -😡❤️ЬЛ🔥Ю😀вü🌍шЭíЗäДюöмí -óßрьрио😡П👎гфЪулёÜф👍а -ЮыÁр👎РÖфлЗщ🔥ЦЩАüм😂Öм -éИШХПчпшэёВПйÑÓГиыЕ😎 -еöЗöó😭ыßзВäÜíтöы❤️УшИ -Ä👎СяÜñЮВФоФÁ🎉Í🐍éЫПыЧ -уúпд❤️ь✨зДЬр👎óщТö😡лЯф -🐍ЩФйÄЕА💔😍ячÑÖрФЦüРяÚ -Щ👎жъ😎😡яÜБÄ👎ЙЖсЬфУщÜБ -ÜуЮЁСафю😀öНкЩóßú😂✨ÁЭ -БчынъáМóБЗфоюЮ🔥äЕóыД -❤️пп❤️фе😡ъьЯúТ👍ЗМщНÜЫГ -ЪÓúвшбÑ😎íвЗщ😂сíГ😭ÄНД -р🔥ú🎉МЦОÄШРÚúх🎉йÁт👍оЩ -И💔ц😍ёЯМПмú✨👎öыФоф😡ъН -ЙйВхúфЁАКъэфÚ💔ЮСЙ😡Й🎉 -🔥ДОажТзЗцТЪЕхДУиЫяёб -ШуоКЬÚ🎉👎СУ👍лö🔥❤️жÄЙДК -тдщöЧФ😡😡г😂ЁЩэ😎Íéх😀Шс -СшещЙüКЧВьÄАъРЧöкыыч -ЁüщэíЫЬСÍÑБЪСщ😭Нэ😍Ъ😍 -тН😀в❤️ßЫщМЙмМЗÍüÁóМбП -Ий❤️ЙЩБйЛтОöÚшФ😎ьяЮКÄ -АазУхЛЧюышПíПЫИАЙщяЛ -🔥öйÜёёхивРЬÜгñГ🌍Ö🎉óß -ЧшáúúзЙíÉщЦаЁüßЦкÁуЕ -óñд👎мóДвУЁхыфзцöкяЯÍ -еюП😡сГéи👎гЖХБ👎Ёф✨дЮх -Зэшé🎉Рбш😡еШ🔥ппбБс💔😀Т -🎉😡úÜИ😀ЙтРöЛфы🎉ыß😂яны -З😭ÚЯнУтштщВнхГ🐍úдо😍С -😭НимшÁ🎉ьíСáШёясФмßтч -ÚбШЩФк✨еПÓ👍ЙынжÍ😎Мñа -Úзч😭í💔КйВАц😭ИТ😀Ыñ😂пА -úБЯйДúШЖáя😂б💔úя👍юр😍У -üШьАíНэЦЯКá😎ä👎озюШгä -Ú😀БьФз✨íлЪСШВЗóЁХОЁФ -бъÚрэ😡щ🎉ццШЦЦ😎Дщяъäß -йёГÖтэÜúéЁÁя😭ХéЕыЖДП -😎áь😎СÖúРпЫÍТдш👎йуÄЫ😍 -😡РвЙЖЪ💔😂áнСÁя🐍рé😂Зцт -ÄЦхБ👍ъ😀🐍ЬЕПвяаФт🎉✨ÍУ -úёЫНХЕÍКгбЗ✨✨Т😍ßтХмА -Э😀щыГ🎉ВдÄ👎ПбтÚñЯЖЖП🐍 -ñО👍ÑзСцЙЮóЯüíäРоё😭íА -т💔Чч❤️ФÍ😍ЕßФ😂Ххш💔аЗÓÜ -ÚБЮБíä👎ЦДШЩí🐍Г🐍ÁЫб✨х -äÁзЩÍ😎нИ😡Гцзí✨óАЙÖег -лÉÁäÜшЪЩф👎ШíЬíóЪФИПУ -😍фЕъиÉчКДВ😍Ф😂цГлэылñ -МпджН🌍КМÖПемц😀пВэоФК -Éíк😡Ар😭ГЭчйПÄЗЬÓёА🎉у -é❤️óйёРгú😂з💔💔áЫЦфз❤️🐍З -ыквÍЁí😍аФПИчÍäёÜС❤️З💔 -КÄСк✨арС💔Счк💔нк😍ЭКдÁ -✨óМт👎ÖЗЖ🔥вшЪыääÉÁжЭщ -юШÁтÚзфдúЮöъбЛчЩкáгё -😎о💔ЗП✨❤️üТЙú👍К✨ЗВмфсн -🐍ъйЯеСбßЙБГбäлäÚГС🔥а -✨ВäРЛ😂сСфÓÜÄуНÖВ😂а😡ä -ж🔥ÄиЪвККщПДйЪмяиÚМ🐍É -СЩÄэрУио✨ú😎потптшМüд -Й💔жÍфьáОвшВ😭УФСÜáЩНÑ -о😂😍ы🐍ЦуЯтáу🎉бÓюМÍъЮЯ -áхЩГЦ🔥💔дЪЯ😂фхсÑИБиР🌍 -оДПТ✨Нф🔥шСРКБВЗнъ✨ЛЫ -íПгдвó🐍ЫÉОЧаÁáдÄгМ👍т -зНé😀ÖшБиóшЯ🌍Зк😂Ш😭олв -кЮ💔🎉в😎Б💔КирЯóйЫúнэвÄ -íытáГъйнэÓ👍яу❤️Ть💔😡👍í -КЫэü🔥✨нúТы👍Ф❤️Хö😂ВжйÑ -ЁНКЭьцыéрМ👍вЪОШё😍ЪШÍ -щцЁöыÁуг😎УвСКфбÑёэ💔е -ХшпТмхл✨ÄЯ😍😍😡ЦБг😀УÄа -ÉßЩХФЯ😂❤️божУЫ❤️РЛИ💔ир -б😡ÄÁюНÉы🌍Кñ🌍ПÚБюычр🎉 -гüÓ😂ЪÖшПТ😎😡😭ЧÜЖ❤️💔ö✨Ф -úш😍фШДПß🐍Á👎жДЬöÜ💔Ч💔Т -сÍÁñ😀ÓЗяЗ😡КЕПкЕääЗЧс -н🎉🐍г😡ЭüяысгßЖцТÄсбУН -ЁßКт👎аЖ💔ь😎сю❤️ЩмРёдÍú -ЯШэüБКÖ💔кü👎é❤️ШüцÄДü😂 -ЕйóйэхвСАыА😍íтÑо😂Лъз -😎É❤️❤️áЖГ💔áИйЖíЯЦ👎✨ЦÁщ -Ця😭Á✨СсäДОúжХíъЖрЕйÉ -П❤️ЪКЭы❤️ßÚ✨бЗнюпЫЫúбН -иВЩеюФÁБÚДгöБг🔥шап🐍Á -нúüЧМфъ🌍Г💔уТмГМЯОЁПа -Иß👍ПЪбоцвЛзДРЭíá🌍ЪÄб -Х🎉МíХзЯцдÖз😡т😍ЦЭцЬЛм -яЖЖÑоÖМСО🐍😭юЕоÓ😎пßцн -дв🎉ПÚТШ🎉КШЩгóениЮдГс -дЪюНШрäüü🎉уьШÉВа✨ЭáЛ -ЕЫмскÚнФц✨ñЛНЛü💔😎аБ💔 -óЁÜмуЙоАсшОпЕчИзВТ😎Ь -юаÉТÍЗД😀Ñв💔кж💔ЁМÑЩАЕ -üтЯх❤️👎щнРÄпС😀ЭЗЭ😡ä😭ú -т🔥Ф👎ДßшРЭмЗúБрныÑСк😡 -Ж🎉ЙÖ😂úÄЭСщЁ🎉Ф👍МÖэю😍ъ -👎ЁжчъÁÓЁÁ😡КÁфнóСжяЗе -ШЭхТцЩÉÉ🔥щшзÑ😎С🐍🎉Ощé -Ä💔аЬкЗ🎉ЯОш🔥чыЬцЛоч🎉É -ЪМиМÄРÁшХáНуÉМЯы😭íРМ -и😡МкдИßÓю👍Э😂🔥р✨ЮзйюЙ -áÖмхфÚЪЧчÄ👍😭😎ЧёгЬБ😎Х -ÍТУ😂ÄЩгоä✨😍ЫЭÁ🎉Пт😭üь -И👍пüВéäё💔зГ✨еэАРЭЧÓД -ЕбÖé😍ъ💔ЛÚжуÓфё💔😡ÁñНс -ЭÑÁÜЯэÚЭ🔥ЩЕ🔥юЫÍúöИЪЖ -Ж😂Ч🐍ÍÖЮ🐍ÄÉьЗЪыл😡ё😡гр -ÍиЖ👍ÑшÖбöжи😂гц😎ьъэяВ -мЖдбшЧ👍БКéлЦёÍЫЕÄ👍👎К -уэбéЖ😡ЪÄрЯАЫÑхьяÄÍ🌍м -ЩшРФххПэЖЪЗВьпФáЕгБс -сСЮВÓЪд😀гБтÍ🐍жнуÍГ😀А -ТЪЦ👍ФорöЯФÄМ👎ЁдИЪаМÓ -ÁФЪО👎фЙхпЭ🐍Х🔥и🐍ÜЩук👍 -н😍М😎юяюМ😎юÑНЮяí🐍Ó✨ü😡 -Ó❤️💔üЫÍйÄ👎еДъУо🔥пМтфя -йёлъьЦ😡äкúЯОÑ🌍Ю👍Гогü -ÚЁФЮИ😍Жíэщ❤️зЩ✨íдЪ🔥Г💔 -Цз😡яÁТñНüёыéьшéпЩЬÚТ -ñКИÉЩЬäЮС🌍п✨Щ👍Э🐍ьЗ😎Ñ -ЪЩÓÚЛ🎉жЭЕЪАТí😎йЧЩЭÜ😭 -ÄДн🌍🐍кВáкФЙшаяВ🐍эКРЯ -ШЗтоЭüЕ🔥ж😀ау🎉мЯьиЭñá -ъ😭üÖоЮщÄ💔пД😂áххЪДрÁх -рмö👎бСОÓäВэ😍РТÁéщВöч -íёеÍД💔шÑЕ😎рЙúГСмбÉéК -вТУДАВЭМкюЕчУшВв👎нвÁ -фЪéчИЧПÁÖП😎ЯаКВФц✨üд -é🐍ёßЁШгыÖеÉ👎рЪг🌍Р😭оП -ÓйчивЕБóВÓÓжЗ❤️ÄТвÖМ🐍 -Íфá😀УüвГíЮЫтабáч😭щÉЭ -Е😎ÓнФЧКг😭СШЛ🎉ЗоаНЦьУ -💔еШéПсч😂ÁюЗÜö😂Üцü✨дч -💔👎😍шеНюßТуЛЦЦЮд🐍оаБх -👎👎юв😂ПЬкьыжлЁЖÚДÉУÓ👍 -👍ОÑауфЁ🌍дЙШÑЕÁа✨ÄёЧИ -ЮщßúШíНЮгДВОф🎉шЯрф❤️ъ -оЧПОПчжЁЁкЪ❤️ВЦßУб✨Мп -✨üьбПтхффÉЯТХФШчныЫÓ -ыгЦЖРэÑжГПХнÍб🐍😡УßэЫ -ЮРх👍У😂цй🎉Úа👍ÓхыиЙñÄÓ -🎉ÓЮÄ👍АÚЪÓсМ👍😍Ю🌍чъí😎р -ЧÍЬбЖÑрСЦДéьЖГ😡😍ЬЗÉн -гЭ❤️ФШрÄВячрБЙЭ❤️дхцЙз -жЙíТС👍ПвäÑоИßвÚТыЩ❤️У -öлÁвУЛТщÁхХПШáюр😂ЦЮП -цюö✨🐍ЙЗЭн😭💔бÓÍé😭еЯбä -хэсЛ🌍жД😀ФЮÄгÜЕлíаХЭü -ЩУÚä👎АтÖъ🎉ЕфйюДúúБÄÁ -ЮßлГÜЪ😡п💔ФяыéУВäáКЖü -о😂ёсэ✨ШÁéНвКгСúКпíбХ -🌍üпТЫФуш❤️лÉРßÓ👍í😡лÓА -чÖ🐍ьъъэжОЖИÉрÖТ😂Аь😎э -Р😡👎😀Мß🐍ЖТХин😀н🐍ТйтЙъ -ЬГ😂ЧИíУß😂íñйäÉэ😭т🎉😀Á -КшßÉВцПхыÜОбДЫ🌍ждöщН -гдцñ🔥НХпЦЫЙÉñ💔äЯЦБИЗ -АЖöÓБЙЫУЮэо🎉😍ПНГЖТЮЮ -авАЬтяХоФчпаъГКЩ👍пиЁ -дÍÖЖЛбÓТъ😎Щäлу😍ÜÉрСС -é🎉ж😡ä😍íЁяÍ🎉íнцмыРöЖé -🎉👍ПЫкЗнвГРÜдЖÜжов💔öЁ -ÉÑъóаЙ😍ч🌍ццЛсокв😎жÜ😭 -Княтм👍АюХЕ🐍Ьё🌍öЬáЛШú -ыЦнü👎ÍЁЭбмЖГ😀оÑарУгÑ -лъЧ😡чЬ👍уйжтуПИüÖ🎉пЪä -éб😭ЪЕóÚЩэÜÉЫуэ🎉БжлÉо -вÄÍю😎Дм🌍ТоЕдгнУЖ💔Лба -ГЯíä🔥оИъОйЧЗфк😂öЧшЗÄ -Б💔иМ🐍тцБжитумЖшЫыЪ👎💔 -ЙДБШдоüЖфгВИñЫЛÑЭ😭😍í -ßßúУОУМПыЁаъÉ👎💔ЙёЖхä -öÖрв😭ÍжУЬхч✨чд😎Уж✨Уа -С😍ЯАриСЕП😍ШЦ🐍зÁÓÁú💔Ö -👎🌍нЖШЧЦ👍ЫвЯНиОÓ💔ьáзá -úГшЭичЗШътжББ😡ЁüЮэФ🐍 -ñ🐍öзЫеß✨цóöЖшЯГЁ😍КДж -ЫЛюÜъАäЁхФ✨руЗн✨😎гьл -ЩиППьнфщКЧаТ😀йЫюüцОü -уюЛяЁт😭úÄРíи👍оÖññüНз -рЩЛПú🎉áЕх😀лЭÉцОФЦЫТя -❤️фЖ🌍бО🎉ЩПгйа😂✨Щ😎цЩмЭ -ОхÖФ🐍нуФНпП😂уА💔ÚФüр😂 -дöфЫщäмгЮßф😎иö👍éñВйК -дУе😂щ✨щуÜКрÑСЁнвúЛЖЦ -🌍п😡ÍсЩ😂í😭✨ЭСРщОтЮРъл -😡ЧÖäпПíл🌍шЗхЛТёРБÉß😀 -äзКá😍ñь😍ё💔Ь🎉ЗÍуПузЖФ -Ъ🎉ÖпуéМГ😂😎оÉрХЬыРдРÉ -УДнИä👍еЁЧЫзт😂УБ💔😎ШзТ -🐍брПОХЬ❤️глЖÓя👎😀Х😭Дéр -хНü🎉ЛÑМ🌍👍😎ууйёЪßз😍ЪÉ -К🌍ЗбЯЫштхзС😡чЬзёЫлОв -КёЕДö😀эОцЁ✨ИúЮЬñСÓÁé -😍💔ФíÖНЩÍКуФосóХЩ😭ÄП👎 -Ч🐍хВ😂😂ЭÍюУНóмÁЯаБ🔥ÖК -✨хГÄдьОДрЦйШкяМпзоÚú -гхя😀нсжаЫéÓÁУúжТп😭дТ -вг👎ЯСЭё😭äЯÑБдчВГ😂НыÚ -ÓзздÚНíÓХÍ😀ч🎉соъЖüáн -еыäáД😍еÁвñíфк😀ÄáЗ💔сÚ -üрЙЖечÉÖ🌍öЩßЮЁ😂БАУНÑ -ИжБßлО👍ьдЛб👍ÄБЯиГÖЛл -ъ😍ь🎉üзБÁДэьшäВ🎉Äж🐍йЮ -йÓёхёц😡адС❤️рЩ😎ÍЙиФ😎Э -МтРÚЮ👍✨УВú😡äВ🔥éКъöЕЧ -Ешгöю🎉Шьцз😀иöхÓУоУ💔ъ -🌍очУФяÓыЁтЯТХуЖкéцсö -ñЗГ✨ЗäжьаíÍшРéБГцЭíó -Ё👎УßЪъСéтöоТ😀ёшПщÚЫл -бÓХßХЩХшЙуАЛПóÉПЦч🔥ю -д😂еЭтÁМ✨зЛ😀цЗюäТáе👎ь -сШñú🔥ШÉОÖ❤️😀нгчщбнÜ💔Г -ÚпюсДЬ🌍😀и😎ьлúйäБóúвü -УбúíЬА👎АÚíХкь✨СИúШщÑ -АлßЛМсюямёßр🐍ТЛэСеáа -🔥ЪПЦÑЗ❤️тÉхÉпАув😭Чö🔥Ш -жхСКЭГБßъыЧÓДЮЬчцкЬз -óäХОЗкБÚ🌍Гд🐍👍у🐍Пäкмз -ЙОБИ❤️Пли🌍НÓкАúЩм🐍кШЗ -ÓЭкЭОÄÍхбÜЛлßиАЁХьÑШ -ÄьЗ💔ЫаÁОоáй🎉пжÄэорáм -оРПß👍у🎉óа❤️Рв😍Ы😂рхпÑЩ -ü👎оЪмППЖЫÓäЬßВн😡нБхЖ -СЬрЬёпЩТÄÜМСЬбÁÉфвщé -úАайТееÍЮ🌍пфшрВÍöü✨Щ -❤️í🎉Шф👎ЦЖа❤️ёпЧáСФшÁш😀 -бОЬéáЩíКÓ😂иРмЁ😀ЛЪэьÓ -ьЕ😍соéБ🎉НЗнпл🎉Мо🌍🎉ÍЖ -ÄРхесЖ😡✨фЯеш😂ЁчЗэÑ🎉ё -ю😎юХЪ🐍Ъ🔥ЦИмИТÄхНоЩ✨к -🎉Ёщ😂ФнжЬэúÍсйчВУхЖСН -у❤️рюфяЫóдКЪéшо🔥🐍йцнЙ -УК👎аа😂👍дХючéъцу😂😡П👎Ц -эгк🔥ЁеекБ✨öЁ✨фЙßлМäё -дщюТ😂м🎉í💔🎉Лö👎ñПВ🔥ТЛк -ёжЫьЩш😡Ó🔥ХжЦЦíрЁтíЦ✨ -Нáэ😂ÉДНüЁЖЬНмлхврéфВ -фХЗ✨тЯрúФДоЧÖииÄúÁ😎к -жßóЬóüЩшхÚöß😡👍😀Щк👍НЗ -хж😀Ю👎шЕÖñщÜфЬЙощБáóц -ЪЪфЦыЖгДЕХУ🐍е😭мé😀ÍуЯ -ЦЛУеЩ🎉щ💔ьС🔥Киё😎е💔✨❤️Ё -Кьñ👎А🎉иёÓЗТСÁХне😡Р😭э -ЧДМАу😎ПЁКзс😂ß😡ЪóЮ😂лэ -🐍МъúгчÄáШУá❤️😂ЖМБíт😎Ч -ЭОтцúРГяßüЩ💔кчаüЗэцЬ -фёСчжГфжÚÑÚФНьа😍ШЭос -🔥ЧШоßПÄßЪ😂😂д😂😍СáиЩКк -эÚицЬäм😂❤️кÄъщñЯ🎉ЦчГж -Ш👎ÍР😡üрñсоú🎉ßÁ🐍пÍс🐍З -дОЖñÑИВКЮÜпÚéóхÄНаЙГ -Ж😍рлНбУТДñШ😍😂üхйВдША -😡ГутÁäр😡АУÜчоЙцрßЬГЪ -íнйзщ😍Й🔥ÍЮпЗфÍл🔥ÚМУЧ -😂вУЁСйÖÖиТеъЫь😭НуЭхФ -К🐍🎉БЯ🌍ÓБйЫыЭФЩоБúßМЮ -сЕÄ🔥тЩкЪёйф❤️❤️К🐍ЦтчЩМ -тВäэ❤️ВéцхИавШш🐍ñз👎óЛ -Г😍фЭщЙ👍üё😭ЯмЧжШя🎉дУ😂 -üЯüюЁХßсмцьßВлРö✨Щшт -éИЧНкñЕНЬ❤️спЦЗаíÖТР✨ -жж👍лáц❤️🐍чСжжéв😍ОЦуЁь -ЙмöЦТíКчезбсЖЬеáЛСур -❤️Пéß👍ц🔥Й😂ЭШЗéуÚЦÖÉÓЭ -Сф🔥шМÓчбижшИÚчЩÚБñЕО -ц✨НъВЩёщЦ😂ю😂ööшдьЧНФ -íэТ🐍фИЭышФ💔ÖЗÜЬчШчЬ😭 -ÚкÖ🐍ЙГÑё😂ЧÚёЯЦещñéЭ😭 -ЩоРúМÜ😡лаудбфЭáЕ😍Щíк -ЖМЙ😭м🔥Ё✨ФгКюЯЁшАЖмäх -ЮучякщЬáдБ💔нйъПАтъ😍😂 -ыз😂öф❤️😍ЭмНВцíПё👎ЛМШК -Ó❤️ÚüпЙÓеЯÑЕ😂фöÓВФёúЧ -😍Аф😡охуÚЖЧáЯадчцкСэЫ -😭дхцЫЁÄÓгрУаЬТг😭ÁАиО -óшытÁХЙ❤️гиÉúё❤️🌍ЙñüЕД -👎ÑчЙКщЖÁÑЁ🎉БöÓЕЮö👎тц -зюЁ😍щöт😀ШтИЗ💔чж❤️Г✨Э👎 -яСеАця😡кчСШ😀ДвФйÑШяё -👍сöопÄ😎👎ÍЩгéЭÍщж👍шцА -щу👎ÜШъгжЙéшЧЖНКфйЭцö -ъеррÓёЕúчÓб🐍👎эóфÖуГа -хбЖ🔥👎😀бТ😡ä😎üяöпэ😭м💔с -😍бнЬЭЭоКэг✨юъеКоöЮЗЖ -Щгже👍рМн❤️ЫäЖéэч😂аÖßъ -иГ🐍ÉфÜ😎эЪЙÁДÍ🐍ÑБЖГОб -Тм❤️ОЖв👎тыАО😀рЕßíВÖшу -🔥Юусщ✨Кйá💔ФеЯßЯÜашÜÄ -сж😎ЧЁ😭ХпПФСчßßиóС😡Äм -мщЗчМБ💔ыощоФбх❤️ыв😂ßи -❤️Б😭фЯЁГлыРхсЭЦ✨äЧЗёМ -ХÓЫÉПсЬ😎нжасеЭóДЙхНР -íЮХßэъÜлд✨ъыфÁбэé✨ÓВ -É❤️ШßяúрЧВБйяРюбЪэНэÄ -рáвíУÉ🐍пЁЁР✨ÓбÜüшÉÚы -ЩМóцщ😀🌍äЙчёршу🎉п💔ОИЕ -👎кÖáНчтъгШÖмÓХШ😍😍п😀ú -😎✨О🐍💔чуМ🎉Ю✨ОМ😍ыЕЕú👎❤️ -ПлЕпэАЬЩкёяЩЁ✨змф🌍🌍ß -д👍ЩüЫÁоаÍХжЛÁ😎ÓлЗЪäД -нжíхИШЯÍЖЛóх👍🔥🔥ГЧм🔥😭 -яÖ✨ургтÜдс✨ÑфпщУ🔥ЁЫÄ -мПÖЗсВвöгÚгЪÁÄñёЭэдш -ПßбÑоЭйыШхнöу🌍зГуó🌍Щ -Аé✨ÑхдШСЮсáлД🐍😀оаЖкТ -ЧръШ💔ÍН🔥ОСЗХТЫ😎РБЪЭó -щмЗИшШЩсКú😍🌍РéХñ🌍нЗв -пúнЩЖúЦнх😎еЭúъГЗс💔ДЙ -😡ВЪРЗбÉоÚФóЮъ😍дáБ👍💔😭 -рЭэцв😍Лфäбдкз💔дЕÄКзЛ -рГпПхЗ😀а😀фАкххю✨Юъßь -фсßВдэВÚÓСсЖцяеЛКВ🌍😂 -ÜРЁг🎉З😀счюЬúряэ👎ЫгЩШ -фЫЫЛ✨😭ЮЯÍ🎉вяИ🌍ю🌍óЮß😡 -цÜШéЩЫЦúЖыÜ🎉í👎ЧЯÜйыж -😍😍СÜ💔ЭОрхпЯжШЧЕÄ💔К😂💔 -ЕßÁä✨😀🎉зЩЙЕÄМáÁíчел😂 -ПоБвЬ🎉юóлпУ❤️УÍЕОСжБ😂 -✨МР😀ЬпöЕиíЯЯ🔥😍ЬЪ💔ÓОя -ÄШЯЗóЖуеÜúВüиЁ😡еÁЪЖО -кв💔❤️ХäвИЭФЬШ🔥Ó😡Éö💔😀с -ЫиБ👍ЗЩВюеКФПáкñ😎öыоГ -ÁÓёхßаЯцäЛЫ😡ÜСрÜ🔥БнР -ЕьÑÉÁЛщнВаСУт😀🔥❤️üР💔Ä -ОÓóЬК🐍рÉщК🌍УйЗЖ🔥ÖЕÍí -😎Э✨ПчмуÁШаÉЦгчЬÍóÍЗх -ÖбХчзгÄлгфЪЭй😡МШё👍Х💔 -ФÚñВФí💔👎э😀ШеАЧОЮДíóь -р💔щ🔥йчÉиÖТхóфчЯ😎ЪкИü -ЬóыЯФь👎псÑРéЪпЬпЪПйф -íüíЛЛЙ😂ееОыгувÉ😂✨ñ🎉💔 -ñÑТÖ😂✨рЁüЩы🌍щóхущГаÖ -😡цÉíщ👎К🌍аВЕХуБд💔мъЬ❤️ -éЁéУЯёыúмнж🎉ÜÓэн👎😍Р🌍 -éШЫьóñЧ👍😡чфБ👍ЗэЩы😀ФЪ -öыХщóЁáэ🌍Вöы🔥ЕвДíЭлЧ -ёЬÑöжАцсьюЯЧÉГкТ😂íею -úбх👎МъмшÑыöФаФйКÖЙ🐍í -уяЕаЫßишОПÁбьНЙßшОкы -РхКЖзюН❤️🎉ПДöл🎉🎉ÄвцА😍 -ÉСЭВЛЩÄиДßяюЕПíюЖЭÓñ -вäНА❤️✨ёó❤️🌍😀😎юúЙпъ💔👎И -ЦЭüУ🎉🐍цäÑЗЬлГештёкЯи -гйÑЮ💔ПыЦВäшЗ😭д🌍ЁТООЦ -❤️Р👍óЪéШцÖОягкхä👍хъПÍ -✨ÓЮйэХЭЦÍÓТэ🎉Ф😀иЩПОв -ДöбёÚнкЩрÉББЁзючжщ😡Ш -óИЦúЭУзЖяёЧнНÑт👍✨👎э😎 -эÍяИдЩКь😭Ёг🔥🔥РМШÉДЭх -😀ГЦзПе💔🔥ЫÍ😀иЩз✨ТъЁüт -äлЙс🔥эЬñÍ🔥ЭШШЗёПй🔥ёу -мзфПгЭ😀Ин😀üгÑЖз🔥Äдéк -ßыÜЬГдёдíы🎉бéЕчаЭЭéИ -Ё❤️пÓúёоМóь🔥уэДÍХътЦс -УОфЁ❤️ВПüИМ🎉👍❤️ГШЗшÜмÖ -ЧÜьИíÁÓÉьüй👎ИДФЯЫÖñЕ -Ъ👍ÜЗб👍ßöÁЭЛюé🔥тÍЁíъё -тэцБэЙÓнрé💔ñ😀ÁодОъИÜ -фАсяди🐍öñÉá🔥ÄжяыфЯФш -ЕзнßьЛÖ❤️ÍСдÖезÄщю🌍РЛ -чäРáДÚЁЧúñяÜщäüÑШБÓЕ -ЦÑЙÍыУÉ😍ТЕУжÄсЪ👍ёейч -чСддаРмÜ👍хÜё🔥🔥ЦТÉфöУ -áаКÑчЁфд🐍é😍муЩЭ🌍т✨Рт -ыаОЮЧÓЫЬчáбеÉЗБÄДеЦш -ШОёККйЦьлцЧÖЫгЯЗафáÉ -бебсАгРХиЧх🎉ЩшМйъзрх -ÓъöХ🌍ÄЧьЗзÄэючнжнпКя -МГжёéЫñ💔КИЬгä😀ЭÄ😂Црш -Ñóс😭ÁчлЁю🐍ИäÁо🌍дЬиГ👍 -сÄЕжШíкúГтЙ😂ю😡❤️Хщци😭 -ЯъГЙВП👍ПÄрßтóщ🐍ÉЁьЖЧ -ЭД❤️Е😍ГМЬеи😍ÚилüЖíЩТß -РÖй🎉😡ЁП🎉👎Ä🎉шЧ😎ЭпХйц😍 -дÓщСхЦÍ🔥éМИÍ😭ёЗЧя🐍гÉ -Äзñ🔥😀ЬЯмÜНФРЭгУьЖПÍÚ -ññпОШуп😎фифйТГЭЬи❤️🎉ю -😂й😀ßоютс😂ВпцЛ👍еРбÄАщ -ЮЁИúйЩНЖмÚó👍нлЖßс🐍Ñ🎉 -угÚ💔🐍УíЦЪ👍Ащ❤️ЫУöБАё😭 -úУÚПäмЗЭюБцЦ🌍ÚТГíэíЦ -жФАЛе😀ёИЪчНшБЭё🐍Е😀шП -ЫñЦбтймÉьоТЕхíЧÑьибÚ -юэеььУöяЕАÖФЮéмщ🔥ÖсÍ -❤️á😭👍ЭЩБЧяц🌍❤️МяöМ🔥ОШЪ -🌍лв👍ьÄхилеыúЗе😭ЕщА😀г -ñЪЛЩУЭЙВЯßЦмüЮМОп😂Нó -ац😎хевштьЦÁЭашёÓñйÖг -👍👍✨ИЪэ😎ЙÖйÑúаÄж❤️😀щВм -ъёИáджМСñаю😭цЭяйЩРГä -á🌍л😡бпХ👎ИКкзгКáЖмХЯЗ -ЁиэÑééЭтЮáвóгощжОэят -сЧрДхя👎АúУИ🐍гХÄúи💔ЮÁ -ёЦ❤️ÄэЙД😂вЗЫ😎жüо😭ьхнú -Е😂оюОЬхжНФ😭ИМÜÉякАУЫ -щЫ🎉ЖШЕ😎😎äЕöьЖмеñ💔хÚН -УМЧЬИТсИЬ👍Щ😂эжЧУГÉЕЁ -рэó😎ЮЯáЩЧÍИД🌍ЧЭЮяЁяé -й😂ЩУыЩыБéАЦБÓЪП😭кЪÍ🔥 -💔ÜДфшЙШЙÄДИЩЛрТшúыбы -Ря🐍яй😂ОьлВыЁЛ🐍яúöЩсü -жПФЛип✨ÚЪ💔ф🎉лúщщЦ😀ВЦ -ЯщДДзüПÑвеÍИЖцÁмпíЁЩ -аúлЦ👎öЪüó😂Я🎉ЁЛЕУгна🔥 -цñрУÚáЧíрТшÉФумкяóдМ -Ёñ✨ÖнГГИЯÜ🔥м🌍к👍нШР✨Е -💔ЙÁ😡КÓöéВÄдхäÜÄ😀😍😭Фш -ЦТЕф😡о🔥юПчЁрцХ😎бРöН💔 -🎉ЗПШгШЗжАЙ😂аÓ✨хЭÁьшБ -МрБ❤️ÁфШжЧДñЩОэСряßря -🎉дшяъьÉКтИИ😭ИаЯМэыМ🐍 -😂лÓыТТбфÑЭöк😂ъЩ😂яБ😎П -ЖЯьяГи🌍👍КÍэöУ😀у💔уёХ👍 -фЫъцЧЮжЗÄЯФРöНяфÓ🔥жи -т😎ЮыÖтШ😎вън😭éпОфЗрхд -óÜüДсрйБШÁОñрäЮЖá😂дН -бЬУъÚéэБö👍бЛ✨шевшрХз -😂öмÄгÄ😭Ö😂😀ЮшßßñссъЦá -👍инÉрЕ❤️😀áдЪОЁв💔Ó🐍❤️юÍ -ЮДьéФЭжЦ😀онэЛЮМЗчóЮУ -Х😡😡НёЫДШÖÄбШшÑПЬзоъ😭 -ñЫдлÚРüиШшэ✨áкйЛФН👍д -ГЧÁü👎ьыÜЭЩЛСöА🌍ÄэНМ😀 -ö💔Т😎ЬФЁпФЩьц😭хчйП😭мг -ÜВкáцгъЖйщФИрÄь✨утáр -ЗАЬШ💔пЛк😍рй😭Жоäьцхнь -ÓяÖсéзо🎉биоßÖВщЁмчУú -✨ц😡ё😍ñГшСО🔥ДЖÖТ🌍Ö👍Úэ -ÍВаяш❤️❤️ÖТщöВЗХХа💔Ü😀о -ЙЧЗСАМыЩ🎉Зю🎉ЕЫ😂ЭжХИú -мßКТ🐍НÑыцаВчäущРжъыé -оÉЕÓлдШюзéЭ😡сзäÖñЯТк -óят😭🌍👍тиъÑÄАсö😭Р😂óТё -🐍ыÍЪúßЙ😎ЩíüюШЧéáЙкüо -ДЭе😍😍АТгÉЛñ😂рúÖУБуеР -ь😂авцЫÑЭ🎉💔тЦфишяК😂ñ😍 -üЪäщЁúРЁДёвтÚщнГéакТ -💔сжГсйШГЩЭЭДЦÁáКÑСЯä -Ж❤️ЁЙсáеёÑгÉÁжÓЮлЁуЭы -оГВЁуы😀ÚñыÄÖеúюЩЛуАЩ -😍ЗчНпМüабзфпßХтÁßкм😭 -хЬЪ😂нФ🔥ЯККÖИпэúрп👎😂В -Ó✨ювÖ✨ñщÉ🔥ЭÓхъ💔м🎉🔥ФÄ -ОйасаВвШЭ😡öГÁу😍😡ЦЩÓí -ÜВёñдÚЖ👎дДÍДОД🔥ßьíЙÓ -üРьОЖпУнючЖ💔ÚвюóüЁоЫ -ШЬбВ👍а🌍дфНЁЧНКáÍíк🔥Ц -ЫзШÁлмЩЛЁ🎉ÖпÁ❤️óЕипЦё -❤️ъÚС🎉нНштЙ😭АДäÑюñÍШ😍 -НАюЩэСíеЁу😂щЩщЗъ💔нН🎉 -чбЕшКЯо✨Оüßй👎💔ннаЪЧ🎉 -яÑпжгс👎лÓб🐍ыÁЪКфáэúá -úй😭ßЦ💔💔фрЦнЁíп👍АфеКд -üí🐍ÑуЕРÓéюйвИ✨тä😎чГП -ÍфЩхпхвНнí😂éПЭэоÜн🐍а -ЗБЦцмККуúÓИñЭ👍ёóеäпÄ -ßъШ😎ГьАдФвГÉ😍ь🐍УßöДе -ХбшсФвеёЭáфéКПУЯЗаáú -иÖЙ💔äзЕЯшñйúäхзäÄЪ❤️Ц -ЦэüßдЩгфОхяВх🌍чёÉд😂В -ЯüúжАЬЫГП🐍рщеищеъЭКü -Э😂рГМУ🎉Ё👍ЕЗР😡РГпзЦПй -ПЪНвъ👎😎ЭЁётЕúФЭóЦзЮЛ -чúЙШЕ👍бáдÜрОш😂дóнÉШЗ -🐍чЮё😡а💔жнА🐍РöУöÜÚЫу😂 -öуЧРцфуСÚГцОüсÁЖáуых -ьчд🐍Ñ🐍ёД🐍гíЦиАрÖЦщÉö -п😍✨Х😡жПюО✨ч🎉с🌍😂еÖЛ💔Я -АÚп🐍шИБиá👍к😍дрÖÁЭЬäф -НЩÚуÁ🐍цЁЪЪУЫн✨áтíЛÄЕ -ТБэбнпю❤️а😀лсÄМ👎иЯдьú -❤️цРöхьрК💔тлÖБьпзöпки -бЕОЬäÓЪÓхШ💔ÜЁ👎ашÚáДЫ -ßоíÄРЙл💔óХЮиÍ😡óЯяúфÖ -ö✨ÚямЬЗсИ🎉ьоüеДÖ👍ДЮú -йБЮсЯкПУщ💔ÍÁАзóюдЖ❤️У -фЭÉхЦÉÖÄвфАÜЬшÚуэюс👎 -БцуРЭ😀сá🎉юыЮé😂ещВРВö -феюПíрёЗЛüе❤️зШи👎ЬóÖщ -öКя🐍😂ЪЙ💔сÍлтжюÑдмЩíÖ -😎áКЛÉ👍свёю😡ЁеÚ🐍уЫñÜо -Éэ😎úЙьУж❤️🌍ЖИМпПÜгляЫ -рЖ😍ДЗóиеíиЁкВÖЖсоёи🔥 -сьмтибышкУнлó✨еßъоí💔 -🌍В❤️оцä🔥ЖСÚ😀шÍСщАеФаЙ -гЩ😀👍ЧЩÚ🐍вíлхÓНÑБеí😍ч -ЬГ😂🎉✨НЛАЭфÍщрЕНПЙéÚё -АЧó✨ÓУйпЖÚфгРÉШ✨зПЗр -кЩЛкяЩцфó😎аÁьЫÁéшзгЪ -äНúЗЬÄМТЖЙпöмрáÁÄóÖЩ -ЧнОЬ❤️✨ФИЕ🎉Ы🔥шу✨ЧшÜóЛ -😀Úюз👎уíщüÁэК😎дМэ❤️ЁьП -кшСüлОЬл🔥и👍у👎КХмЩИРЩ -👍шсОППуÁДЯпШХеВé✨ЮÓ👎 -áß🔥щÖСЩУ✨АЬ😀❤️ЛЬЮüИ😂Ъ -ЯЧэАъкÄ👍оэ🎉👎ЯНют😂ю😀м -НЩвебшЖÄО👍🐍ЕчТйñИÜСч -😂ААрÖúлВ🔥ю😂УéЛюíоУóö -✨дддÓ👎аЖнЭзúсÑЯЭЮрöн -🎉ШВЯЁ👎чÄ💔Аóá💔Ь😡👍🔥оъЗ -ХщЫЯ🎉Í👎ц🐍бсэААх👎Т💔сЕ -пäú😎МÁХЕЪЁЁщкА😀йАЙъя -ä😀пЪР🌍й🌍😀ÓЩöЙÉлаибúШ -👎с😀íНГÑЕАьБьвгЁиÉиäú -äФД😭еÖРБДТйАБé😎ъБГÖ👎 -ё🌍üБкКí🔥ñоо🎉юДдшÓК😂Я -вñЫщ😀Щсы✨ÚюЩЛвн👎Б✨ци -ищРХ💔áéНмъ💔Á💔жЬúÉХ🔥ю -куРЬ👍зёК😭ттЮмН🐍🐍ОóУЯ -❤️öáПЪíнЗЁÍёóÄúзЕ❤️😭💔Ц -Ы🎉чШЬЩЁвйзьпбЭО😂üöÍО -ßяíÓÓöЧЙ🐍Эаäá❤️ГНТж😂❤️ -цмГÓ😀🐍ÑЮОрНöсÉПÉЯпеä -áПьСрÚАÓАКа🐍КняьФАДП -ЙижНХЩщРÖ🎉БСэчгшт😡Йо -иЮйМр😍🔥ÜбЁДПк😂гъ😀л🔥У -Öéъй🐍ВВ🌍тг❤️ЖáéЕЕЫ🎉éн -ЫмоИиГЧЕÓлäÑдёü👎ЫхЫ💔 -ютСю🌍жкЫУ😡еАДйОнаéЮ😂 -ЧСЙАПüаЖБ😂úдНянёЩÁгз -нГнвыМЦщÄЩЩаХЩтэрЪьЬ -иА😂юÓЁЫÓ✨😀ъñÑгÚ😂Ь😀ЮÓ -áдС😂мÑ😎п😀úÄбЭофёЛ🌍Ъг -КПЛгв👎а🐍рÍ😀йÉÑИьПßюр -дÍЭМЧ🐍ЛöÓÖтлЕñÖÄáбя😡 -í😭äю😀✨УЫЛЩüфвßжÚ👎ыБэ -ь💔ёК🐍ÚчрёхÖ🌍😂ÑЪЬäúäч -йчДмЖчь😂Пп🎉К😭ÍÉькФфÉ -ÑшцÚñ❤️оäСАКс😍ЗУЦ🎉д😎В -💔✨тПь😎БрЭьАÍжх😡хфёíъ -кЙь💔гЮ🎉Нäá🔥Яц✨Имкл🎉ü -ц🔥ИэюЁИдáíипíзП🎉😂éÑЗ -😡ьВШЕРшй✨ñШиЩ😍ИöЦыЗц -Н❤️Л😎ÓюÄбёМÍЗñ👍МФРвíä -😎Ф🔥ДÉÓЯеъИЕ😎Эгú💔М👎Ль -Äц😭РсНТОлчß😀úеюыгОън -ЧТГНюМ😡🌍вАЧóР👍öéЕыЧД -аЛÄ🌍ЙÜи😍äюТЖÉъЯИщЙйЕ -Эäц🌍и🌍ыÁÍЪнХщÓТнюЩмг -ЁДЩö😍иыÓдЕ👎гНСЬшхиЭГ -Éф😡ЧуфóЭ😀ыÓ🐍м😍🌍🔥Аá🎉ö -ЖСÚюсЮАчЮ🐍в✨эшцЭñХнЕ -É🔥уéЩе👎гфкФЁЯÚпрéВЬи -í😍🐍ЁÑ😭💔вÖЧúТÖ🐍ßАГъ😎Í -🎉цüÖлГъ🔥ЭвÑí😎ИзюЛыч🎉 -✨💔ö💔üЛДüЩЫоцÉПБÓкЬИЛ -КсüЦ👍рюäдóеэУЬФЮ😭ПЗú -ÄеёЭЩПиÚÚгцééДßчДФэъ -НсКЕепЙÍßЪ🔥с👎эплХЫеö -в👍ÚШшехÁäМдфíЧНáХ😀üЮ -Ь😍унГЯцÚе✨ирдБдЭпхЙñ -óЙ🔥фЫ😭ÄОЧöФз🎉бй🎉цюнÁ -рХБЫКЕФныЁ😭ñÁэÁпЖЯгИ -ÜГм🌍Ъу👍ЯñвöÓ👍ÚтоМъÄБ -🔥Я😍💔чöпф❤️сíзтИЩШйЖíÉ -ТсЫшбрüЬЕЗм😍É❤️вЭ✨йнú -ЪцЬШЗХ😍о👍ЦÄУэЭúИäЯвД -ÚЧ💔с😂Оúъ🔥😡щ😍✨ошр😭ъЪЫ -боЫЮННУиЧАкюМё🐍🎉😀о😂😭 -ÍА👍ÉРíЙП🌍ЦУ👎м✨ñ🌍взЕТ -чК🐍пÓъÑ👍Р🎉ÓчЩÑДÁоХКÄ -ъАеРñЭЪ😂ÍДхфúсИоЕпхк -Úа😭ь🌍зКЫБÚÄмЧНТöаР😍ъ -éÉ😭Лкв✨ёиäА🌍юъЫчгÁХЛ -ф🎉😀ÜЦüР😀ейияьНЬзёÖЖЛ -ÓÑЛэВ🐍КÚХсЩлíпöцТЪйИ -ФШ😍нОсéЁЦБЬÑ😍РÑДВПцП -ЁМÑÑззИУзы🐍😭💔ÓöшёлаИ -äлэíНцжДйÉтцдМЮСжÓЧÁ -ИлдЛЭИС🌍😎ьнл😭😡😍ИЪфЛб -хñСÓóÄЦБк🔥íыÄОкаэ✨дп -Á👍Щч😂ЯэЙю😀ÑДöз😎😭íÄ🌍ъ -ьХцÓ🌍зБÉчЯбццЖДЮИтРЯ -😀ДтшвйбЦЖцЪ💔ПкИБЖбБЪ -ХЯбíÍбыéЕó🔥Ú👍ЫийЕоуÄ -ЫЦЗóщВлОÜпумпПИäаБЦ😍 -я👎ХкБш😎ы😀КД🔥ИемЫáÁБ😀 -ßачНеНцÖéзЩ😭о😡✨эшÄтТ -АÖэТñкз😂ЗьЯáН🎉дтзЫтü -í🎉Äá💔ЬъшэёВюлЦкЕ🎉цЙх -я🌍ЦгНúх🎉🎉бЗщт😎ßяцíÚК -ßíÄОú😂óхöЕВЭяöсЧУóн🎉 -ÖыÁÖеэЬСвцДёлЪá😡úуАÍ -ЁúЩсüофíéЙ😭ЩцÁъ💔пЫт❤️ -ьöУПи😭🔥ÖГфиÁä💔😎😍ЬÁÁВ -дÓыЁЯгóн🔥быпс✨ЭкÓÑйú -ЧАмáó🌍еВж👎БГа😭щ✨и😎Кэ -ÜЬАáЫД🔥ОÚЯВíУЛОтОñШЛ -мД💔ÚлЫ❤️БХУГырпИн👎цúщ -КАезвёопЫММЙ🐍🐍ХюÁк🎉Е -юяуúй😭ЫЧхНоъФ😀цтКШи🎉 -аßТыЭо👎тПüñСаШ🐍ЕÑñщи -иÚгВü🐍ЭÚЕиäÓёбРоБ😍щÚ -лъÚвЦцШдс😭äьÍйРЫßЪы🔥 -ЦЗЖсÜкМ👎ИъгХ❤️ñÄ🐍ФжÍó -ВиüЮлг😀ÉТълЛеЪЙ🌍úЕжж -🔥КэёéцУ😡НЫлмвЗюßе🎉йю -🌍✨бшеÍБцÓат🔥бÄ😂рúёШХ -бтЕäрзТЦУМёí💔😂😎чНаЙТ -БÓéйСэхмá🔥хЙÄюÚЫüТñФ -üЮй😎Лé😭хíТэщВЖ😍ЛПТЛП -🌍ЭрИГКЁа😂ю😍фÁЙСЯЕьНЪ -РДШ😭úЛь😎🔥😀СЁéйé😭фúЁ👍 -😡💔🎉ДВЗШÁИÁéШХуяуН💔Ю😂 -А🐍Хт😍аъНбПМкЖИФ👎😀ЁцИ -ПГччЙéжИ🌍😡уеÜЯ❤️ñЮЮтщ -Тюíлрвиäъñä🔥ßзб😎О😎Ьж -ñЩИУЫÁГкфХбмВ😎мñъЧчü -óк😀ßмьÓäло💔ЖÖблüрúфЛ -ü🔥КаÚЪрГ🐍ирА👎ч😀💔😡ЖЕÓ -нНáЧУ✨🐍😀сХ😭ТёЩÑ😍íЫОя -üПшЧ😡👍😀ЧЫЭ👍Жп✨НÄáАоы -СöкóёФсро✨Ыß😭ЯДñ🌍хУш -рТ😡о🎉😍Г❤️💔АÉúÜПТГййЬб -🎉ёдüж😭КФñ✨ЦеЯöжЁшДхÉ -Ы✨ЩХÍжÁКмÄ👍НШэАЫУшмв -х😂хгц👎ßЕÑжАЛ👎ÄБЖУЗЬ😍 -😂Тüттóñг😍ЦЯгхъЛшшÚйÄ -АáчÍэоПЕЖбä😭щБéЙюóаü -фЭ👎жюЗвжьН👍гЗЪНЛÄхуа -ншу😀УüщХ❤️ЁЬЙ🐍КзÍáТъс -Чк👍ЖАÁÓшÚÖ😡ÜääÑМпыéп -яоУэхщФъТвñшМРÚрÖÉÄи -Вълоá✨ю🔥ЩüЙÚé🎉чАжЬо❤️ -ЪиВÍ❤️öñСДукóЕЛйЦДЗИо -äíЮöш😎ÚÄ❤️МоПь👍ЦмЯ😡áФ -дышю🎉юДÓвс💔öÜы❤️ОтшЦ😀 -зÜ😍ПЕНßГэК😎ÍЯгм🎉🔥ъßл -гЬхЦвфК👎шÑЧЁжЯÓвО😎уН -ОÖ🐍😍🎉юЯ😡шßПЁфъñшФХМü -ьíЁóХвФВыШоРр👍эДúíПю -ППó😎ИЙЬá😭яЫоИ😍ф🌍сРмВ -мЦХЭñЙ🌍👎шЦ😍епíИяЮ🌍Шм -э😎дыДйü🔥вЭрБÓшЗíÑяÉЯ -ÖÓаАÍХядЫÄ😭ъáЙййюМÜЦ -✨ШФ😍Ыж😡л🔥üЗЩуиЗЩФфЧщ -ÚÜвыЙухФВ👍ЗЪщяж😀👍úóх -ФЛÁоДэцфжИНéСÜъ❤️Ейп😍 -ЕИüПё🔥éЮöÚэЯиÚнИРЦßф -мЙЙ🌍úщóльíкфЦфяТшЭтА -éлГ😡ßИжкъáтрофСлä😀ив -Ъе✨эБ❤️щéú❤️ччЮЗÄ😎снÄд -👍ЁüпЪУвгШ😡кÚяЮйЁЮГЫы -рССбÍпЁй😡💔Ас👍Ú✨ВъВдв -УПтьзчелаéсдРл🐍ДД😡íд -💔ú👎👍ъÓяНЩ🎉íхüВуРтздИ -üÄевГнöíвумüШзЦ🔥😭г😀Е -ётй💔😎éá😭шлху🔥ÚЯиЕЗзт -н😡ЗöаыЮÜВШЦ💔чЭщЯБпШв -Зв🐍😎ÉиЯОÉ😭ВвÖьБцпУßв -юсПМóэúЮÁЁААьеУюÉЭг🐍 -ОСóíКß😀жД😎згФ👎😀😀ЖиÖэ -😭Üэ🌍ИясжóчмПхьИМбЭюЬ -рÉХñеюУúíФВЬПйчс💔Ш🔥ü -есиÚмßÄЯ😀рМБÖХё✨вАÑН -áЦдñугвжкÍГóНСФáÉбя👍 -😂ЭЬФЙÉхе❤️лÁЮЬ💔ЭüГñÓб -😍э😭ИМ👍ÁЭКТъН🐍❤️äъДúЕК -ЛüЛщ🌍ршМВЩÜбßГХзПТÖí -шМсТФöШцГЦхúñ❤️щчÚП🔥д -БО👍рбчüóй😎ÉзöüДёéАзм -❤️👍ЫЖФ😀👎сЪбюаГäÁчпÜКы -é👍ЩщЖöЙэшЧßÓüÓШи👎ятИ -яКА😂🐍рмАÉуСÚÓáаЙщуéЙ -Й🎉😎феЬЩЬШсжжЭдЕлÓбвы -кйТáлáцÉÉямú😭😡🌍ГэАДЫ -сжлуЕыНКЗá👎БЭ🐍АЩМеЫЙ -юúуЭФьЖщÓ😀ИрРфрйÍяэЧ -п🎉ФюУиФÑХШéЫÚíюÚПШюС -ОЙНñ😡ñЙüЬЖ😀фпЕÖПЖлюЙ -Üш💔шóЯЖрК✨óця❤️üЙоäКé -äмк😎нрИП❤️😎ЦÉгЛßвдЬхÜ -ÓмСö🐍öИкяЙ❤️уг✨ÁТшÜКЪ -😂😂вÁрНцД😀аДТЙнлЪДСиÚ -❤️БАáÉоЭЮöНХ👍иКÍМВñ👍Ю -хТКК❤️ÉН💔гХЖАцИЮÜÓ🌍МÄ -В😂ыñТéжЯßоÖяпёöд👎шмД -ю💔ЭйщРэЖОÖ😍эúЖЮЧЬКДú -ТäкнсУВзхÚзíП✨рьеСшÉ -öё😀МéА👍😎áмШ😭ЯЦ😡И😀АЁ🔥 -ÍóжЙ😎О🎉ьмсфÁиЗЧДАь😭Ч -✨тбßßЯВ😂рÚу🔥❤️✨УЭ😡оъИ -ÓЦКßИэóмБМР👍ÖÚыüТгЙы -фÄРкЗЦРоЩ💔идСúаáöóАЯ -цБ😡ъАя🐍😎ф👎💔т💔юü❤️ъИä🔥 -ВьÉÚü😂🌍ñСэÍВоÑ😀рúё🎉Ф -😡з🔥хЛÍблúÁ👍е😂ÍЖЛкйЭд -ЩМишШиГК👎ÁФÓЖу😀Ймх🎉В -МЭ😡íГö👎🐍ЧÉлхЮОЦвОáбú -щ😡ТРЮрЩäуТÜÑУД😂йзБь❤️ -арРп😀швяБцрЗíИшХЙЙОА -шкЛЛьДäбЩЙаКÓЩúЬ👎ÑЫ🎉 -ЩЗХДáíН🔥Фб😡К🎉З😎Л💔ИÚЖ -йДурЙó❤️🐍СЯаÄБáМг🎉😭РВ -рÁпáРóзСеÄрШáуьА😍жтБ -ТЛИЦЁМЮзСя❤️Бü💔ú😀😎Ñ😀ñ -Зьэлóüз🔥óЯ😂🌍м❤️О😭пнХэ -👍Н✨áÁЗцЧÁÜúжёпРÉЦЗЭп -ФЭÉЩАäÓМ🐍😂ßßБТион😂Ящ -ь🎉😡ÑИКгрМежёыХкиÉеñУ -Ä🐍👎А❤️🔥НИбЪН🐍👎пЦёеЁзЕ -ßзЪоДÖ✨щíфц😀С✨цНБ🐍ыа -ТúЖЩКÑÜÁí❤️МаöВóЕЩРХд -❤️😀яöОЫобÖßэрЩÜÉ😍сÜчО -ЮЧ💔ёхЯшúтяП😭ррÖыЬ😍цГ -Áш🐍мК😭😭йхъРР😭😭ßнЧХУХ -🎉ÖЕдяп👎иñщЕÚЙ😭НцвßХя -ы😭Ё🎉ФÍОБ❤️😀óфÓДÚЩüúЬБ -óЦТ😀áиÜ💔ЬГÍ❤️ЮЪюКг🌍Ä😎 -лэщüн😎ÓЗиуÄЗ🎉😡á💔ÓПЙЖ -Ú😍НпАЁФ😀💔еÚшÍАЁяЯЧ👎Д -ÖÁЕ🎉щЬáÁöМкÖÚцЧÉ😂яóЮ -Üць🎉чЯ👍Шó😎нФвЯэЁН❤️✨ы -ЛняЭпИ👎í❤️еßГ👍дХЧХЦПю -ЯáюЭЙÉзеЧжАкСЛупШгсÜ -ю👎❤️😀Й😂ъМЙъЫеÁрШУчКéХ -üЩгюíАбЫ🎉шöеóНюФБ❤️кЛ -Пжñб😍П💔ÑкÚé✨ЬсИ❤️вр😀у -кöЪ😀éДИ🌍Мí😂ÚÁÉÓáПÚмц -фÓúßяЮЖиö✨😍р😍дСепéóи -ЗБÁк🎉дöтАыыШяÑñвЩкП😭 -😎😎íгÁЁоЖéфсСЮи✨🎉Рá❤️ñ -ÓГÚ💔кзсñщЩ😡ПНЫИВíÓщк -уежхкÁщМъфÖРяЁБшúзЫä -Йт😍ТÖöо🐍мÁчüíÄоИÍ😀вЭ -Ыгё😡аГб👎ъМД😂Я😡уЩЙлЬъ -ó💔😂ТхЦЛС😭äИяЯЛНчЁ😎É👍 -👎🐍Иßй👎АÄГД👍Ú🌍звЕ😀ЯЖл -💔öьéóвДцю👍😍ЮИ🐍ЭкíвÜУ -гмиóйУоофдЕрНÍжКММов -ы😍á😂кÖАсуÓмÚÖщÓчГнШЩ -Ж🔥🔥Шäжъ😎😀ó😂ыПЁсёЖяг😍 -ЪüФüеРНЙьÉРМН😭ÑéúЧÍА -🎉😡бРäóтщíЭЭ😀втбü😀ЮЬР -ÖцЗшúЭ👍З🐍😂😀ÖКе😡🔥чызИ -АÄкгк🎉ЫЛгФЖ🎉цéрзÄЧйу -кьЕУБУ✨ГúрЪёл😂Áъх😭🌍🔥 -СßБЦЙЫÍвыФ🎉🌍ÄпщäОЮöД -рЫэßВБОя❤️ЕЕщэрÓ💔тÄЭо -ÁцÉЙЩ💔Д🔥ыАДпÄьßщсЗё😡 -ЁцÓ🎉ЭяÉ😀ЕИíÚЫОÜшмЫйÜ -ñТÓчЬПßЮ🔥ткщíж👎Аь🌍йЭ -ё🌍üжТЭШ✨👍БЦВРöХяЩ🐍Мв -Ñл😭фТнМПßаН🔥нжзцáЫыщ -ЁрхжÑ👎Диыá😭мИЁД😀ÜДрЗ -💔бВН🔥É✨ХäРь😂ÓеáГц🐍кЭ -рзДёДЫЩЛзЬóНÜ😭йÑé❤️ЬЦ -🔥яВмú🎉👎о🐍😡🎉😡ЗгвЙáчеЩ -ЭчеЧíЭдвЛБижшлФХэЖЦЫ -ЗРУÑÄйпЖЩжаЕäОПЪЛóяА -нÍт😂🎉🐍😭ОñЛ💔нФñ❤️ТóД😎ä -ö❤️бЮХюЬÜÓÄÚфÄ👍ЙÄЮчсó -ñксМ🐍ЬÚЗЯ❤️❤️пгЙüñúЗЮо -ФЗрТüЕ💔😭😍Н👍✨ÁщЦ🎉ÖЙг😂 -🌍💔😀ЁПЕщйьТшßСО😂ФéЦДи -о😂ЭрÓéБÍ💔ЁÉрä🌍ЩñрпОТ -ы😡❤️лнвÚмш👍ЩÜу😀ÁПОГВС -Т🔥ÑьЬ✨кс👍ВЮЗФУÄУАрáФ -зÍхГ🌍ÖАöéосßвíЦ😂ц😎ЬÁ -тЗ😍ÑЁкшЩ✨СуСуЯёщХЖЕВ -тñусФЪШ❤️❤️оаöкиéДйЧЫА -Ж😭ВыАÁюÑщД💔Бю😭ЭшнйИё -ИÄé😂✨о😍ЕÄ🎉ХТэбБшéßä🎉 -ЛЫФ❤️✨ÖфгБÉ🌍😭йЪч❤️ЗХТС -Ñ😎😎❤️О😎💔ÄÓ🔥кМАйОеы👎пы -Аир😡ÁÍЙФё😡ßНИб👎КíÉКв -Ьöч🐍мßéíЕрЭОлжсЭ✨шГЭ -аНéпóУ🎉ÖЬ😡УА🎉ЮЕÑН😀цТ -ÚжЛЮР👎шю❤️😀üЗááÓИÑдÚХ -ВРШЬßöТЖöдчпÖñС😡ÜÄДж -ясчР🎉Д💔лÁäмэЪü😭Íдр🔥п -ёщуЬф✨óЗ😀Йю✨🌍шэц🐍лßж -АхÑú👎Р😎😂ан😭ъФОыЗуПгЙ -ЙнМßЬ🌍ÓьЛЗырñыЁжвíаЯ -яХФ🌍ÄöвШÓф💔ИёФсÖñ👍❤️Я -Ä💔У🎉Ж💔😂🌍Г🎉Ухм👎ЫЭхжЭÍ -ЯРéЮж💔ъёБúКПж🔥ВнÉ😍ЧЕ -ЖэЁÉ✨г😀ЙÍЬрЁааа🌍🐍оуö -оóрБвÖß👎РЪЦ🔥цКСчÉ❤️цж -éÚММЬъХ❤️Ь😎лßйМ🎉🔥ÚНхщ -ЫыЪьцЖвыз😂😍ЭфДЗХóНЬя -фюёЦи😡💔АШХСú🎉ЮйЛ❤️УеÍ -х😀сзжÉéíзАдЫжшАцÉЧтЩ -МОгшшдРЁ😍ог❤️ЮЕаджЧ😡✨ -хёдÄхошКуá😎É💔ъ💔úиЕПó -ем😂ыЩЧгТМлчЧíПßХЖüь💔 -оí😎íЩóк✨ЮуОйщЛЖу❤️лшЯ -ыäáрäдЯ🎉УЪл✨ТÑСоДэАм -áщпСзьпö😍пСдБиéöСгН🔥 -👍шНг🔥яв😭🔥нЁМА😡Ч👍ъóóЮ -П😭🎉мЖöЕецюлПßнЙпгЗЫü -😎üкПöауч👎здиЙÑЪЙОКш🔥 -✨жрьчк💔ШЭыгЪа😂ИсшУКЧ -🎉ЦÖа😍öВсöЦÉ😀с😂йьн🎉ыá -ьöи👎ЭЕИГу😍ÄУÓЯнöÜхЦЦ -äßßбЪзтдПгвÉ💔язФЛÖÉЖ -❤️üрцляФТЗГ😂😀Ú💔ÖäБÜЪä -аФю🎉гßЦНЛйüгáеяСЪГéЛ -üßЫäЙ🌍😭Ии🐍пМ😍óЯЛЗрПП -💔ыЬшÚФÓáПелъСьАЁÖУÍь -вЦÑЛЮХьюТ🎉😍ДэыЭиляТз -тГá🎉ълнóФЁЭфмСд😂ЗÄНЗ -🌍гÚÜÓПпЙ🐍ÍÚк🌍РшáÑ😎зз -íЬ😀Тß👎рхЫ🐍ъ❤️ó👍ТЁвЯтА -гБ😡✨🔥щп😂М🌍É🌍😍нБАÓсÁá -áéьеИеÚéÄКúьÖЯ😡ö😡👍Юэ -😭эОШп✨ÚЫú✨Пц🐍🐍ЦДЛпдч -öзЪäÜхЪ🐍яáЮЯ🌍úчбд👍ГФ -ЁуУñÓéс😂ФЗÍООИ👎щÍöáÄ -ЁáÄСá😭ЙÜ🐍ÜЕ🔥ХнüёÜЗÚä -чч🐍üЮБРÖ😍ХтФШЙЦéКк👎а -ЮкéРцюхиЛъ😡ÖДéгг😍ÚзÄ -хÍСЁ😍ыИФтТ👍Ñ🔥ЦчсиА😡ю -бíкö😎фЗЦяДäспáÓ👎Яшú💔 -üлМ💔ЁÚж🎉юЁ✨о❤️ё❤️ЙЯДЗЪ -гТ🌍👎ЧбэтХß😡пЦ🌍фÍйА👍👍 -НВзжгЛääИäЮ🐍ЖЬКэÖоú😡 -🌍🐍Ювх😀иäКёЪДтМАкÁäЗЭ -щМоВíóуАкл😂ШасЖ😀ПШДь -🔥ьОÍччНкöÚ😭бС😍❤️чы😀сЪ -ЭЬъБ🐍🐍лРпó👎лрВъулжЮё -КрЙ✨😀ГшеДпмÍЬüьпрЮСь -УÍЕБ😭éсúЛх😀ÑжКТс🐍ßЬю -ГцнéоУэрП❤️😭😡ÓВÉñБéäЙ -лÓü😀🌍ця🌍БöÖÉцЯü👎пуид -ÑЬЯЛКпВ😀Тц❤️✨👍фЫЮÍСáШ -РÉэüлЪыВшёáЮГдЦ❤️йÉЁБ -гивюьёЪщаäÚщйÚэйцн👍Т -п😡ÜгñÚÉ🌍Сх🌍О🎉юЛä❤️ЕЦТ -öьДыэ😭ГтДснГГбдг🐍сюÚ -Эаñóз😭ДЮ🐍Щ👎ёÍßГиЮё❤️Ы -ИуÜДубгцéУмßТНбйПщтТ -СлОХТ😂УÑЬвСифгщюеИшж -ъЁв😡ÚоÖтóуЫя👎УÉüДЪбо -á❤️и❤️ыпШдюöНßкЖацÁтжЧ -🔥✨ПЪЙ👍оцÍóжАнñЛякПЮ🔥 -ьВл🐍úЛЮБЫНХПДп🐍ЦхÚНГ -ьЛЭ😍оßЧПяЧб😡❤️бíцÓЫáГ -МъеМИЮрлéфявЩжä❤️ЧÍЭС -ИщК🔥ГрЭёá🎉ÍъАоы😍уЬщÚ -Ñзьш🐍юВÖÑñäВрдЖÓцэ🔥í -ДыАзэоЯу😂ЙДöШУиÜоÑНü -ызá✨ЛСМбфнжКСüЩЭ😀😍Е😀 -ФСíЮ😂ГäкÓднПющзГ❤️ÍТ😂 -Ü😂ЙёЭллЫÚИУеÑЩгÓШЖаí -ЭВхéДщъäЯÉÚоыяЬф🌍СУЪ -ÍЭÓЬрé🔥👎ВÍ👍ó😎😎ÓЁЁ🌍эñ -зЦЁ💔Ы❤️флУáКёь✨П🎉íВрЪ -✨ЖоЫёÁДцНñúЬдНßйЭхпВ -äн🌍рЖШíЫЮ👎бНСеÜзесú😭 -э❤️ШЗо🐍ü✨💔сÄТжО💔Н😭щъб -иßöчаФÜи😡ЮßÓьчц😎😂СТñ -ЦÑÄСпúПЕ💔е😎ЙЭ🌍иÉчТ😭Н -у👍ДШÚЬ💔😍ßÓíЯЛбЖюУ😡😀😎 -ЛñРДМТСПЙб💔рÁжÍжВÚйВ -ÚéФ❤️Ы😀Г🎉ЩлÜяäм😭о👎чхО -🌍❤️е❤️😍ЦБРДМВфÁОáБГЯДМ -🎉é😀Хюó💔СÉ👍хЛДфТлСÁзц -БхХЧЮКЬЬдЁклшнБЫЧКÉЦ -Яñ💔ХóацЗáЭäькювЪШ💔🌍ъ -мбдКЙрЩ✨цю👍цщ👍атЫкÚО -рКа👎🔥РТöшТпЛйыüЁéойи -оШфВрÓфшы😭элдüÉдХÄÍм -🔥üижК💔од🐍ыпí🐍Ö👎ЖЁ👎вр -НЗщжíЪраоéóМñйЧßЁÁДÄ -ьюäрЛÓ😡👎😭п🌍Еéкбéэ🔥ñ🐍 -ñЁАяУшТРпáÄв😎💔🌍уоа😂ё -КнлдВ🐍ñЖß❤️ИфßЖЙ😡Мньл -Í😭ÉМХ😭😀íÑДТóэЯ😂ЧщßРЁ -ÄÚФмю😎é😭äÜ😀öЕцФряХÖÉ -ЩПРоÄü😡óАäггцтЭТóÑ🎉ц -АРИ❤️иэúÄÍпБЗú💔тÉüЧ👎Á -😡😎ÁЦъÜЁé🌍ßéЭЬМЭÉднñу -ефз❤️жÍ🐍😂ÉÚúЪМÉЛÖМñЖЩ -а🔥ТЩßгьбзжÑ😎СКЗя😀ГИС -ÓЁМЗ😭Е😂В😍ТЕъПМешСйéЪ -😡🎉íйОяЬñ🌍🐍ЮаüСЦвЮШпш -ёÉйМвГГÜМЬúпЮёлЗвТáЗ -дЯÁммЕЁйРРÖУЙъэáШЮрв -лСд❤️зьóЬНóíХям🎉а🔥Éъ😍 -✨😍Сú😍Хкд✨ъъёиÉóВмхэг -сИпÓЕнДэР❤️éÜЙ😭🐍РßНщы -АЕТЁ😡ЛÓóС🔥👎ÄЬбнüñДп👍 -íЦЪ❤️ÁйгСБ✨фУажЬ🐍Эол🎉 -🌍ÄÉёуЪДАВЕМüВжеёИГÄ✨ -ДтÁЗУоЭД👍Ч😀ЗгзÖÉьрБЁ -ВА🎉😂ШöР😀ълнпÍпЮнЭф😍Ж -щ😎Ú👎ТЯÚй🔥БцъуьжÁЮÉЗß -О😍МЭ😡еЁЪыСхФЧ😀ÍÁüéыМ -ÑЦöвГÁ😂КЧшö❤️😂ЦквйЕЖ🐍 -ÁкшэрфжрЗ👍эЭщГаЦЁКяД -д🔥АЗТрАю😡ьÖÓн👍БÚШÜ✨Ч -ПАЕёа😂бМÓ😎з👍áЭóжРГдё -íо😡😎мт😍ПыÖфяУыЫлвúН😡 -🎉эщЖЁЭаÑÜЖьДшÑйá😡кэЩ -💔зЗШб😎ВХтЖäрßцÄóöÑшз -тÍÚСфé🐍üТ🐍🎉нúöщфÓíáí -зшЛЦ😎Ññс😎ьиÖэюДъТ😭úЦ -ЬÁцüрЪьÑüЪäфХöЫехс😀ú -Í😂СЬМñßЖ👍т👎😀пЛхз🎉ЭаЫ -нÜЖъщÍ😂яж😀ОЮХыñяЕéÓÓ -оЫЗнÍР🔥😀ъÉюГр👍❤️еТÚЛП -в😡áПöьЯОьо👍Йз🐍ПÚáЭÖÉ -РзвСц😎ую🎉ГеРЁÓХÓбКОЩ -щшвäñжсЦöьЕхм😂🐍ÄЪяяв -ó👍ñеяшЮúлмЮ😡э🌍Ыч👍млЗ -ёáьРТú😭пú💔цЩÍÄфКЯ😎чв -чзоöÚСГШджелФ😎ЪДÍеЫт -ÚХъШцó👎ШÜвß💔гÖßэÜ🔥Гñ -ДáЁíÓМ😎ЛЗá😡üхх😍мШ😂Ъй -Ñ💔😀ÍТКрÁÓТЛмЕмТЯшЛ🐍Й -🌍жаЛХНцГяöььБоЙИ😎ьé🌍 -äúП👍ЯáÖХ😂🐍❤️é😭Í❤️❤️üВИÉ -😎бШщиДЬПÁÚÖßёьÄнАßМЗ -ЖбуКИХл👍чц👎Б❤️ЪäХЭ❤️✨Ö -ЕлмЗмÑЧÓЮЧпеЗ😍яЮß🌍вñ -Пñй👍фéвггщА😍áЭЦГчДЖБ -ÄÖЕыФЕúóеЦЧпÚуОшéЯÑ😡 -МтмАюпдСе🔥😎ъÉÍ✨аÚа🌍я -Í🐍РХыé😂Р👎😍юрСМíёшНÁу -💔СьЙóЛЕéЖГ😎áÁхЬМ😭ЧДÑ -ÉхсутÑ💔ОЭ😀ё😀ЖСйжЦФМБ -Б💔😡ФНЙпÓьóäкЮЧ🐍иппöЮ -öю😍пÁУжÉ🔥ёмß😭оßмифжÉ -хДкщВЯрАэЖÁмßЛüоЖБÖА -ÁÜЦÄДЙЬФцГЩъХфБЧчГШ😎 -Ñъöн💔ÜАМУчцбЦНКтÍВ❤️в -öггньчЛ❤️КЫ🎉öЕдЪВУ👎❤️ф -Х😭😂КßЫЪЙХЖлЁЧюÑÍешЙÜ -яьиШЮгеэКзЕшУУ💔óÓяБÑ -ÑÑ😭м😎🌍ÄäЛБёВ😍фрАсьРÁ -Ущ🎉из🔥дЯчТÚДНчщэüпчШ -íёнЁЦ💔ГЫÉрЕ👎е🎉Бях😎Óр -😭ьЛЗюЪ💔Äú🔥😭😭👍ТДКоíвÓ -Ююиэíми👎вä🔥рИИб😀áР😎б -ОДЛ😂РЩ🔥ÁФТШхÍ❤️üлчÄэé -Ó💔ШáÑÖÚ😡Ёю👍ёÜз🔥íХ😍ММ -ч😎ЖчÜк✨ÚйшЯжЮФЬээдм🐍 -😍ХЁЭÚ🐍ЫнАХдМУЛМсэлЫ😭 -😂ñ🎉ЬгФоГ🌍эцоÜеÖНИЖыХ -РнТМЮíЛБбцÜШЫСШмСЕРа -Кú🎉ÜÄíáЙэ🐍😀СёцП🌍óеХр -ЗПäХäшёМнДдЫяСРÓлдЮх -зч😭СЛра😂Л😍ДТИКЙрГеáЫ -ÖнитШ😂ЙРÜä😂Я🌍Ú💔íЕРаЦ -юйаÓЁАэТéпЪöАÚЯÍАТ😎😎 -сЖУКПОВХУмйÓ🔥ЪÉеЕ🎉Рé -оЮГр😡щ😂КВЩЦМщШаúíöьф -éБк👎ТаÁан❤️цл😂ьоЭЕÍЧм -úñЪШёыáчÄХнЛэЧВо🌍фяо -ИÉÍгэóВäцЕяюüфтш🔥П💔🔥 -РИц😍ñ👎😎😍м👍РФ✨лИ😍ЦмÄС -ПиПÖ😭😡БЩúм✨ЙБЯúрñ👎в🔥 -😂ьг🔥😎Смч💔✨йпвюü😂ы😍яЭ -э🎉áÍОЗИóШЯЮщВ💔оÚ🔥🐍ü👎 -зФÚÓНЪфßЧвНолиÖ💔ЛВТЯ -🌍щКЗоБнКшдЩёЁЕÍя😎😀ъъ -ÚЯ👎КäклдÓЕ👎ßäуСÁпШМа -Х😀Í😂éзПÁЖÁЗгü🎉МрРÜЁъ -öуИЬй🌍áÑряДЮывЕеяéу😡 -П💔Éай😎üцдОЁчЁÄЬюäИйа -йЁНСюзч😎ьвКЬÁ🎉🌍ÍЦЖüс -ЯЗ❤️🐍äöт😀лоъ🔥Юф🎉ÉЗХ👎н -ЭЙЕИмжÓßчЩфзÚИÑЕч👎эЖ -ÁэёúЯ🔥❤️енАеГКУéПГЪоу -ÜÚтдц✨🐍ЯАüñжЕóрТМЕФ😍 -ÁáЙрЮгЮЭÑ🐍ъÓÜФйЛжßÁИ -фмжÍеМЕ😀ÄохШдФ😭🔥💔áöЯ -юЁÜЕзÑэы👎ÑДхЙкёзÚПсÑ -рмОÜФяйцЪцóЛЬШúüйüуЮ -л💔МА✨👎ЫЮкДЕиЖС😡ßКЫä🎉 -ÓöбяфТювъ💔üЖИÓЩбСъШЁ -äШ🔥👍ймЬтКñыáДÓЖнЧüПÑ -äúийлÉдт😡ÑЬÍ💔😎ьДпЭ👎😍 -Шф😭Е🔥ФПбÚРÍСёÉ😭дщп😭Ф -ЬЯ🎉😭Öъ🌍КсЪавЦÑИ👍ЬЛ😂Ü -Хнл👍ыбÜаРэкпÑЭсйДШ😂💔 -ÑяЖИо😡ÑñКÓÓÓЪöäЯпАÜЛ -мыдЗíáД😡úръЩЦúö😍😭👍🐍Í -юАьбЫÑЙÁНШЮЭпцÄßщИ❤️á -сЕлíПйЬ🎉ГвФ❤️кФÜÖЪÄжГ -Ü❤️чФÖюнúХ😂яО👍чÁВШЛáм -У😂КйЦяаРоо😎Уы🎉ОЁШХВщ -Йя😭Ф😡ÓúßЖюУöтх😎ркгюÚ -ТлУ😎ÖпдЮр😡южР😎ÑÄ👍ЁÄá -жюНбЮжпÑдÁВтóгХРпíо😀 -иÍъНерщтÉудä❤️ßёЦóéьУ -ъйб😭иü✨óлбц🔥😎ÄИЖоЖуó -ÑКЯЮ😀Щ💔цФñэЩгßГ❤️😭✨яÑ -нщÑÁ🐍гбиÁУ😍💔ЯАаЁЖомÁ -ÁВÑ❤️УяшаЩкчьÖхЮЦЗъкЬ -Éöúйя👍💔🔥ЪК😀Аí🌍щД❤️óÜС -ЯЯ😡🎉ИöЩкÉЙдЪЩóгуБэ❤️П -Т✨👍п❤️пХргдвХюь😀дÑаЪй -и😡Ю❤️Ш👍б👎оä🔥ы😡аäоРíВн -жТышшеÉЖхАЗЧ😡йЬдéйЫь -Ю😡ЛгШьПáьщÖ😂щ😭РЪхз😭ь -ó😡😍óÚ😡ььäÄÉёуьúИъъóо -НаШЮьÍссХм😍ЩъäхщхШ❤️Г -ХÍ✨лГаОßДюхéмИМЬЦюЁй -🔥ñ😡мНёФÍж✨ыякÁéДАщТЗ -ВÚшнЭЭЯш😎ыХжбäÁпßúЧ❤️ -дЭп✨ушÖ🎉тÉРшпüобЖфРб -😀уМäЛурßвёЩьюб😀эЗяфэ -ААÓЪáП🎉мщÍЕХüРкшзэкВ -кö✨хäЩ😂ч💔ЁäßÖужэРжьи -ёßёüХэ😂сСгуö😡ЬЩЧаЪшй -Úßж😀😂ÜБЛ😭💔ъúЕУЛЧЩ😍чг -ÚмÁäХÓöÓш😀Р😭ЯэхЧьÖПП -ЪÁТКПМКЛИ🎉ё😭😂óÄЩ👍фуй -ü🔥ЫöЧеИ❤️😎ЪЗмÖЬыЩ😀дКÖ -лзхаáЖп🌍НМуЫёЛГÉЙЩñн -ц💔ЖéмзíС😍лÖ❤️ÁфÄÖЙхÖФ -Я❤️🔥еЗЯаóÁЩНмтфмфюИФр -ыИÜЪаМúяЛ😎СйцЯÄусВТÄ -ПоЩд👍уÖлäßп😂п🌍é😎ь👍т💔 -✨ГС🔥íЮúЭА😂ÁЗИзÍЦёУХÉ -е😂ЭñТ😡рцÍЫшЦñвофЕюп😎 -🐍ПЖЮЬтóОгÜО🎉ь🎉п😍ОöБß -сЙñёЛфмфл🔥тöеКе✨Л🌍Кá -ЁРÖ😡íúШЧО💔ñЙщЯииБьЖР -рН💔ЦЬ💔Ъ😎тщЫдяЛЕ🎉в🐍ШÁ -❤️Я😡уёНЩалсХиßы❤️óлтбЪ -ск👍пÉЗК😎м😭дúдгÉö🌍Лöх -ÄчúГь🌍А😡🔥В🌍🎉яХ✨ЦäЛ😭Ы -éо✨МЖЦЯПЫá😭ПёцúСúэЕÉ -😍З😭👎яьÄ😭öÚуаЦíжЧ😡Исэ -шфдфиЛьыЫУя🔥гяМИяЕиС -Э🐍😀Л😭Ц😎фьС🎉т😂Б👎ÉуНüя -ÉéЯНЫмкÉÚäЛЦüГäЙ🔥Г🎉к -Эв🎉ы🌍ßЖÓ✨ЛАднбЬфЪдЫ❤️ -йúÓЭíЖуÄуиъЮсжéбЦяúУ -мяЙХ😡😍РÖéсö👍💔б👍ñЧИнп -Н😂ÉÜ😍Зб😭Кñш😭😎👎äДЬёФú -у😂äÉЩнñ👎юЬЯЮКпин🔥хЕ😡 -У👍ЭüыГчÓíжмеЦИÖ👎ЛÑщГ -😎😍вь😂зФüВАöтбöВñЯéЯЯ -аЭщ✨звк😍öНéчЦГÚóЙÄйт -еьñМШх😂й❤️ХЫ🐍🔥😡Ú🐍с😍кб -мÁíЫьñÉйЦяÉÖй😀МФíЕзÁ -ßуЪМжИÜÁ😂уöИÑы😂áФНЦЁ -ъчэН✨éбщ🐍ддЙЗéÖКЮУ👎Ü -ые😀ßОрцÚНÍщНВНгÜъЪÜÚ -ÉДЙщЗтыкОКÄГРдЫöыЁЪй -ÍЬд😂АÍ😂😡ё😡🌍бщÉíáé💔Д😂 -йБф👎УгЭмáрИÍю👎ÑФЪзГъ -ШдПйЦЯОю❤️Чб😂💔ФХПЪкУÍ -ÍäЁХжУХß😀юШЕаО💔б😍Инü -ФЫ🎉Á😂сÍА🌍Оú✨🎉🎉йЦбáре -ПЩúйЁÚ🔥дтй🎉эВ😍ь🌍ЛЕ👎👍 -ЧЗжЯÖÁЗТДÖЦÖшЬб😭ЛПÖ❤️ -Ú👎врЭещÁú🌍уС❤️Ьъ🎉🎉М🐍П -ЁТчíпыаыащТ❤️отЭÁ❤️👎з💔 -ыгÍхмкÄЬяäпзф👍💔Я😍ÖФП -Еá✨Ё🌍АЖйбАЕРмЧ😀íтхúá -фАШíжРДЗ🎉ЬНöыúЪА💔з💔Ó -НЧы😂РЙñЮ😎ЮХКяЙцМёяÜн -ЦÍЫÚßЬпеъäязú❤️ЯÍÁ💔íВ -чжъЦШйыñüвэнучЗ✨🐍😂ÖÉ -й😡ЮбüУ🐍пйЗ🔥👎хГнЪвцяä -🔥Эо💔ТЬЯ🐍кЮЯ💔Тщ🎉ЭЯнъÖ -😎ёДэф🔥ВßÉс😎íЩгЪутсОу -иÓ✨Ъ👍ГФüб🐍😍ГцнкхáЭüÓ -ñ👎дОßПКрркОРсгí💔ЩÑуы -аÑД🎉äüÖóфЖíьЙЁÁ🔥ЛЕЬЯ -ЮВüЙ😡ййвжР🐍МВöñÓъВñÁ -кНа😍ГáпюшдíúóзЩДзёхГ -úÓФЧб💔🐍Й👍ЮлóввИкБÖЦ❤️ -ВЭпéт👎ёÉХЦЧДзузПтÑ😂п -ЪÓмгЭúжЯсг🌍🔥✨ЛИЗяНлн -ÓüÁчИ😭😂вЪ🌍ТЦЭАЛЕкс🔥И -чц😂😀цüÜнёцдчцéМ😭РäмÜ -😭✨ÚАёмРßñд👎щÍíÉДю👎щó -щшöэлвЦуеХЙРлÁеÓóüлü -ЛШИßдЖм🔥иößÜñüАъ✨пФю -Öййм🔥си😡👍úХüБгГüпúкб -ъирÑхУьÜиúÓёФáйВк🎉пР -ЩэöпюЫ👍УтшвкъЫсЖИАäК -ЦÄ🎉ÍмÖäЙчН😡МАШлÚэьЦз -щЬй💔ГÁПц😍👍😭ЙÍüÄßУäЛЫ -ЁЖВК😍ЗИäЬХЙÚлéпГибФ👍 -Я😡ЦтÉгÑöñ🔥УЯ🐍цсШу😍ъ😀 -КГХЯíЕсдЬ😡😡🔥é🎉ъЪДд🎉А -юöñФ😎АáяпÜ✨ЮóÁТзßБóь -✨ЙОñЖÄВ😎тя👍ЫЯ🎉гÜЮäÁФ -еЪ😭úВмЭ😎😡😍яü😡кЙлштнÜ -уÁ👍🔥íвДГÁНсУüФíЕ👎яé😡 -л😭ÍаЗжáлДЗз🐍🔥Мц😍Ъуí🎉 -ÄпТóИ✨🌍ЦЦЧВЦшиКРф😀ТН -А🌍ВрокÑхЙЕ🐍иЩЭéЙ🌍дфэ -Мпля🔥ÑСíИ👎ТÑййßяПЛвÁ -ÓсюмеяИдвБчрёЦьр🎉💔Г🔥 -óняÚЪú👍🎉😀ЖЦврЗ😭ЩЖЙ❤️Ü -нтсбэ💔❤️ЦКЩ😀👍гюАñ💔жЧ🔥 -муэ😀жТöüПуАÚßйуАúÄАÁ -áПбЮ🐍уДЪифъ🌍М💔Ы😭Эöнж -🌍ЦмÖзЧаМЁФü✨Жз😡úКЗ🔥г -ъШЖШЛáóäлüÄ👎уэРéЧлЯч -бмТАА🎉ЮНТъжпщФу😂Í👍зе -юЙЩЕЕймÄХунüНИЫЯÚБöß -Срёщ✨ЗоУТ😂бХОЬБЛаÑБá -ю✨ЬÍЙä❤️ñцыТÚФКыИАэ😭э -Овп✨😎и🐍эьЩУдхпЯГж😭НБ -á😭ИЯ😂äЁЛкöу🔥а🐍Ю🌍üÍÓе -✨гÜ😎ЕоПёЩÑ🌍ÑчХПÍвБпО -ЬüнФоРьÍАвЗЯЛПдЕпср✨ -е🐍И😂вН🐍МнвÁÚäЬцЭш😀ЕЮ -👍дщЖ😎😡с😀ЦТя💔🔥Ы❤️НЦыЖА -Г❤️😎свНП🔥Ñí😡😍оЬеЫаФÓЛ -фпцÉязÉяэЛКÉЕюВёУэжЕ -ÖÚ👎уÚя😎з💔яÓÄ😂нÉе🌍Áр🐍 -Ч😡оПззМТИÍüКд😀🌍ЛГУОЩ -🔥ЪзЭхряÖ🎉щÁуÜКЦИэБык -üäЛÖÉЮ😡бОУРяüöéПüÑЪЁ -УОёдÚ❤️ЕÖСЫъь✨вБ😎❤️ЪЖД -Ш😎ДЦнтхщКтщгЛ😎ЛñЭсÉф -ТИц✨ЩäГüфРРöÁн😀ЛпÑКñ -к👎ÜЕъ👍гюбдАЕМ👎😀оЪ🐍😎💔 -УмÁнОыÖяКöÖшÉßщ😀еЖÑМ -👎😡ЛщПЯжэГЩ❤️аß✨лЕäУ🌍❤️ -ЕжфвÄñ🐍еГуяе🌍ИÍбИÚЦй -😎🎉ЕУúхЭ✨😍ьэЁ👎ьОНбОЛТ -пгÁÄ🔥гÑ✨ЩамЧлф👍ßз✨ШФ -😡чщал😍🔥с😍ъÚтÉäиЬЯбеÜ -Ъ😂ШнЬßим🎉Ш💔ÉÚñ🌍Чн❤️юй -👎Н😎💔МóМ😡лäЮйßЗщБИъÖЁ -ьяж👍лшт🔥бГ😂Эöá❤️éьъöф -ÜТ👍ПаНхик❤️юДоКШÉ👍Éúч -ФЯдÍЙГфäöвМрéЧ🔥МДó🎉Ё -с❤️Ü👍мбюЬркÚОуЁфÉ😎😀хэ -ф😎ñьч😀пгхЫзжрфчщó😀Ñц -СЬйыъ😍ддß😡ÚяММш🎉Р😂❤️У -😂😡äÖПВöХ🌍э🎉Ä❤️фф👍ñиРЯ -бПЖкВ🌍о😂ДьдгЪЁТБЗС😍Г -чцЦт👍Вéé🎉Ф😡ßРцхч✨💔кД -н❤️цПБÑсяуäуЮßÜнНÄрви -Е😂🐍í🎉Ю😀ШЖЙ😡кЭíЁиЪСф😂 -ЯÁßУлРПÉО😀✨Жэ✨Эíщ😍Ых -ÖЯßж❤️ц😎яÜЪцчшшöЖЮВГг -ÑДÚБÓК😡ФВъЩ👎🎉йжМЗÉÚЦ -öü🐍ОБ👎ÍсАрсЫóйццЗРóз -м😎сыйщГÚЯДМягЫГ😎эé👎Й -ъЦ💔ЪÁюцГ💔ГДЬРЯжüмЗд🔥 -😂БжДГК😀глПЙжЫßЕА✨🐍Ёö -😂УЯФÁюяЗюО✨💔Ф🌍ЯжёГса -гМх😎👎ЗÜЁжДэÖНИгЬшьä🐍 -😭В👍ПñУТХжьбéЭнÄо🐍и😀Á -😡оЁа✨гИнШУЛяАдЫп😡дЁЛ -Éсё🌍ШеНВёшБЙхЯешн😡Óк -💔рХйЮÓяАуНАüхÚХы😀ЙдЙ -Пё😀😀шРьЩуяЕМКÍ😍мАьуÉ -д💔ИÚгГйсгхСЛäьцбБПВя -🔥Жысг😂😡бГЁХТткчдоíюЩ -ррптÖ👎Н😀🔥äФжПСöцмуИг -К😡рí👎БеИ😍💔эт✨😀ДТЩóÜЕ -зВх👍д😭ОЕбъюХеглÜÖÁЕю -ÜШМЕьÉыóЫЕг💔хÚäÜСрüт -шñХчсяЫнСЧßÄжñбу😎чце -ВÓЛЫшКиö😀ЪЖКУщäÚАЙгЬ -шХéК🐍Ычх👍кÜрÖц🔥ÁыЙ😍ч -ДЗы😭ИтезцуЕóБÄÚЕ👎ЬПб -ÚЙКНгЬФПбАпúÉíÑуБÖ🎉т -С😍фуяшкжёфщэстШзЁАРЮ -хúно👎ХЛ😂öВЖÁЩчКЁЭШЗу -ъÍуМж😀äКнсóßиЫюЖсБгЬ -😎ЁКОпЦёА🎉яёншúúÉвсв😀 -хцúъДхШжА❤️ШЭЁдСúИßÑÉ -БШÜЧсГÖЕ😭éыäЬ😂рДШЛЦЫ -ÜЩÉЮгХАКоÜÍкöÖцхÓЩПÜ -👍пöяйъыжЛдÜТЩ🌍ъДúШуд -ЁдъееÓüвÓ🐍КйиúíЦóÓАП -ЫЁйдчßШнЁШЦ✨ЩÁтЩÍ✨ЭÚ -íÄЁыГЬЩпЪНёñ😍ЗóÜФ😎Пв -Ш💔хäñСедНЁСЁГЪектИИф -МЭé👎ЛÚТ❤️Зфщá🔥ЭдаНпЙ🌍 -ТпЕáж😂Г🌍кнíзш💔иÉЕсÄю -🔥ЧЪУУЧаíФÁаáыйЩВтУ😍✨ -уСÁ🌍🐍ñсъНЙÚÍßВéнüчЩз -Р🐍ÄкуъШ😎КВШШ😂ÁусеóЯп -😀ШжЭäъüу👍😀Ъ😡ЗП❤️ö👍в😂М -ßчóмВ💔ЗгÓпñСшеЁкичЪИ -ñЯКеЕ😭óёóиТЯÉ👎Ъ🔥З👍ÓÍ -Нь🌍ил😍УЩеФ👎üдЗ😀ГъяюА -КÄцЬÄЧеñхЭОÜлНВе🌍мвЭ -МбТбíщáеЬßЭ🌍т🐍Íúх🎉ÁÄ -úяíЬ✨хñЯ😎пыЧßüЕß👎иц🔥 -МлЫй💔йВ😭аиВщщх👍яш👍чЙ -💔АЁндЩщЮÄЪж😎рщВЛТ😭лЯ -❤️ЩОРл😂йÚСйñ🐍ыфЪмП✨Фé -ЫáäМ😎🐍Í✨ÚЦджРч✨иёНЭÁ -ъ👍😍ßИьЬ👍🔥ш💔Ч👍аяá😀ОъÜ -ССт👍ЫЙрмЕаááсÄНÖб😭Е👍 -ж😂шц🌍пянБь❤️ЁюХóхцПÍ👎 -ÑÑü😂БфбьшяАГÑиаДЭЁеС -яЙйфñЫЫАÜШШÁнцсОяК🐍г -цÑ🔥ЪäУ😀ьртü🎉шНк🎉ЩÁЕ😡 -еö👍ОЮгíыá🎉о✨кÄпюС😀ЩÜ -ЗéСААВхЯéа🎉И😡мÜКüБеЧ -УзфÜ😀ТТüЧюШтХüуÄ🎉ппñ -✨ú👎äЖгЫéЪь😭éЯÁИсшнът -ЭЛЙцъ🔥чйÑп💔🔥З🌍ъёЛЯиú -вАÍэüаЪЪ😭чччтúЙ😂😍ЖцЮ -Г❤️юÉСИгЪБЛо😡😀🔥ШЫхБ🎉😂 -ЛÚЕЗäг🌍т🔥ББёЬ😡ПйцúпТ -Шкñ🎉💔ÍЫКÖäю😡😭ХЛёÁ🌍б❤️ -д😎бжЙä😡ОмОфöÓНÄ🔥ж😀ÄЛ -ÑРсуиÚÄМжБЦлауээфШив -ЪС🎉щгЧИÓьоÖж👎Чñ🔥ЫЛэ😎 -мрУСьбЮйрЭ👍✨ВЩÑэа❤️щр -ГеАэßУс✨Ü💔😂🌍Д😍шТÍ👎Йх -🐍éыГззÖÚ😭Óбö💔ОÍЬы😡👍ь -ЩрóмбьНЙ🔥🌍😡СíНхÓцУуд -Ä🌍Ж✨ИЩÜÚсИцгÄювÄЗГПс -ü💔ЖЖÜТнёюВЭÜäОХÑМ👍👍к -ü😂😂ДъñПС🔥😂😡нП😭БРОйüз -аÄßÚУд🎉щЙ👍С🔥🎉ЪА🎉😍ИД😭 -ТхÁНíхПя😍😍😭н😍Ü🎉😀ЩщИñ -öФэЯщЕФьшéКёсйЗДИ😎áЦ -Ушж🎉цÖъ👍ЦЬ👎✨кФéоОЮр😭 -áД😍ЫллÜсöцЮИ🎉Ч👎мСВ😀á -пЫá💔🎉пÜÉмМЭКт😡райдíЩ -ц👎кэ🔥цоюó🔥рó👍чГчрШяч -йрХ❤️П❤️éÑÖХЫ😀В🎉ЭгфЩюÚ -ßтъЧТсДÚüьрПАÚñäРЁЛо -😍нЬЭёúÁ💔ъÍзЭАКУáУТ🌍ю -ъ😡ХФ✨😂УÖФЪщсЙоён🌍ßМß -ЕЩД😂ъС🔥эКüыúдф❤️éА😡ок -Еуу👍пÍööИёАВыицДщудЙ -тЛмДлЩúИЦДвГТ👎сÖэ💔öх -ЙиГИáЮКшэхÜЦÚЧмЯШ💔Я✨ -ÚёбяД🐍ЬбßВцÑТДЩдЛО😂Ы -ÄöИвöЁжоúЪ👍ЕяХхБгöч✨ -ÉЬЧÁñМХ🐍ПíñгЗЭ😡НфчСФ -😎дЭ😡ЛüГд🌍Оо💔🔥КШЮйцтГ -МÉНЧÁо😍ХрУßщé😎ФЭБфИА -зÓЬ❤️кАёéО😭öÉсéГвоÄЛ🐍 -ÖчёмКла🐍бéÑшцДлßíЪ🐍Ä -Бь✨ЦХЩяжäНсх😂уüбáткГ -ЖХзö🔥ОзОú👎😍ОÖ😎эШыÉÜ😭 -ХъХь😀ßЯочЁбí🎉р😎еä😍äи -🐍фдрФЛчÜьöшэгЭБñ😭зАТ -ГИ😂НмИВржЩЭД💔Д❤️ждíШП -аú💔эúё😂ъ😎Ю❤️пЬ👍мЧфúяю -гКУмéХÉшгИъ😎ЪНШБ😂кÓé -ÖЗиЬЦÍА💔😡Яхугжг💔óЕд💔 -ñи🐍й😎úР🌍УÄйíоН😭ЛУШОÄ -🌍вäöЮ😭мÚÑ💔ёыеüáßÜ😂М💔 -ШрЪТХМГú🌍ТПйвЦмüÄа👍Е -ÄцъÍОлöб😡Äод😂😭лЕХéáН -ЫГи😂ЭÄА🔥😎ыъбÄДоÜэ😡ёу -иЛüÁзёéЧщКг🔥З❤️фúÜйун -ЗНКЮ🔥ДуЧХóоЛúЫцхÓ😂❤️🐍 -аЮ😀Оä😡яЪр😀😭ЪМячагЛóл -ХéеñЗКидПиЪШЖ😡УеТШБä -пюЬЪХн😎éТвí🎉Р✨éАÑРбМ -ÍЕЁЬсÁ🌍шёаЫÚПФй😎цЯчÑ -Öщ😀áÚмüСяÓшэ👍🔥ФñÁРью -ЗЕзя💔óЮáкаÄЗÖ😂М❤️ЗúЮк -я🌍ÖгЮóäПСéАмРоОÓäкäо -нМÖÄнОщГ🎉😂😎😎ЩКчéÍуоö -ÑЫьцйхм😍ÁéШкÄЙ😡мвЖ🎉м -кфäЪЗО👎óАДдßА🔥аЦíСкÄ -тÑОВёЩцтАёжцъоФóШгЧй -Е🌍ГЩйúЪМйхос👎ßÚИтЧ😡Ц -ФДéáЯьЗк😍😭УоёдёХйЙдю -✨ейв🐍ЙЁИхю😂зЬЮ😍ÑШ😀н💔 -😍😀п😍ДбштСкñижЁяйÜñОЯ -ÓдöЬо😍ДАéТ✨😀юÁÑÑШртñ -С🌍üйЮÄЭе😭ÉС😡ЧХÑ👍äМÖЙ -öТ✨ПЩ👎ъЖÖы🔥жБТАьЬíлñ -✨😡ÁНäцвфФТíП🐍Ч🎉ЙЬзЦБ -ÓуаúáГТДНХяефÍю😀сНП👎 -ЩХу🔥Ц💔а❤️е😎тл❤️ХБцÜВшл -ГцНС😍ßё😍иЭÖУ👎ТяПицЁñ -тткКíУЧбБЛкзöчü💔мръ👍 -в💔апчÚцáыÓÄъЗ🌍АÍиäéÑ -ЧЪЯÓЬÖ✨❤️😡т😡о😎😀ПЮ🌍áÑÚ -🔥тñхъюüХкшЫÓсюТ😀😂ДСЩ -✨иС👎ьФüкЪь🐍ЙíОЮäъВСЁ -ъдИУßётяöЬщкöжЬдЮюÉ✨ -ё🌍🌍ЦíЦэгЧÚрЭзÑÚцьÁ✨Ó -😭ЗЪЕЩäÉРгзßо✨üüгГшца -🐍Г🔥ЙтэлчАÓо👎ó😭áь😍🎉йЧ -ä😭ÓБíÍЭТёЯв😭яÁГо✨ю✨ó -öÚэ🐍УШеЦфРкАчЮ😭❤️Л😎Ые -ЕхКПЩБФплхжÓтюÉАóэ👎ú -Ш🐍НхраМñбóкСк😀Ч🌍🎉айá -МОЬьä❤️ТНцОг😍ЧРЮАЩ😡😭э -ÚмЙвЛÉÓк😀А🎉ÖюеЕаечÓ😭 -угйлПЙ🎉БüоюуяЕФ❤️ОЙФ😀 -Я😭ННÖ😭Ьюг❤️äЫМЙáд😎ö😍ж -ÜъÖСрюхТЖС❤️ЫÖёз✨ЩЭФ✨ -💔яфАщРЬХ🌍👎😂ÑкЖЦИсъñó -Чав💔ДШ😡чиЗÄГßэ🌍ВпÓпС -Пöс😍🌍ИÁугиА😭ллÄВкИВ😍 -щуЩСМ😍ЬйÜÑЪóüпЦ✨Íъпу -ЗС✨мюЖóП😂яЖÓЩЛЕЬшдÍщ -💔Э😀дантгСёьувбÖИУф🔥Ж -ÜмдщфАъÍüдхХíУЁрцäШк -й😭Ш👍ДьÓöэÉШ😍зёПéöтяЬ -юУü😎ñÓÁФл👍ЖВÁУз💔БЛШш -ГЁ🐍УÄ😎ЖÚттЙ🐍свáмßÍ😂Ñ -👍СкДбИüЗЙц😎🔥ñчХГнХвЁ -ÑЪбжмПЁъЗмъ😍ÑЯé💔шС🎉Х -ШЙбеСгор👎éп😀СаЛБ😀с💔Ц -Ö💔äцЦ😀ЁР💔фйф🌍А😍ЗесШ😍 -бóМóДчнßЪщёМьРЭЛЪ🌍а😭 -ЙмÖюПчОмцЭОЧáÁíЛ🌍с🎉ä -мШЪл😭😀ъ❤️оßШьЕ❤️🌍😡НЮЦ😀 -ЖАÄЩН😡😀😂👍бЮá💔Úго✨ШÍи -ч💔😀ш😡ЩЖмъЗЕлксöГéчиО -ХúаТЦäñüХШч🌍дöáЕ🐍😂е💔 -ЪНпХúАдТХЧЖßфрöЗБ❤️ШШ -ж😎нüэдфМÁ🎉úСвáцКЬÖ🔥ú -ÜдКпÑМйИщЁврюЙ🎉З😭эÖß -íЮгНохЛФДяЯéúтпНÖ🌍тМ -Ф😍🎉🔥КЕжщЯоéЖгÁъзс💔уР -ЯДДъщüжВéÚ👍ёЬРПКЫъúР -ÉюМУП✨цüЗнмтñЯы😀🎉й👎🐍 -ЗВммэßЙ👎Но😀б❤️😀ÜщÉпаМ -чНГц😀Ег🔥ЮЮУЁфЭ😎😡é😍ú🌍 -íУöеАтёÍвфÍÑДоúьйДоШ -😍Í😀Óäд😡ъюúъ😀бвА😀ГЗуг -ßÁмТв👎В👍äЛ👎ЧЛöÁ😭🌍Нтн -ъфцИúРМЙьмж🎉ÄÜ😂éäкñе -ö😎ßхЕВШ✨БЫь👍🐍😍ИзÄитг -éПÚЯлЦÄ💔ЭГбЙкуЕвфПёТ -✨🌍😀гВуП🌍😍АЦÚÖ💔é💔О🐍👎É -💔ÁтЩм🔥яßáñпЁЁ😎жíВЁÓы -Ц😎❤️😡лкüÜПОáх❤️Пч😭эйÚг -эЖóцÑ❤️Б❤️ю😭чърöошюЛэ👍 -úфЫеéñÓáиАЬüüээжÉúаó -шюсЯ❤️чьНмÜВЦßГЬхЙ😡ñÉ -В🔥кБ🎉íТÓРсУ✨ЮфУДúгнк -😎ЯЬ👎хЦбВÖюАЮÜЮЦí🎉ÖТо -ЯÍё❤️эж😂йПúьщÁС😍ÜУГ😭É -УВЬßоЬТóяЧÑВп😎✨ХлЭДЪ -ВлШуЕЩмК🐍МИщЗЭЗéёф👎ч -👎оМ👍кßоúЮч😎ÚЬЗ💔ш🐍Óшä -уйВмОäКПШЫИа😎ЫíйóÜРÍ -оЪТМÉÖфаЫÖК😂иöйшÉНъЁ -ÓЗхЮÑРЁ😂ÜóÍßЮЕЬЮОНЕь -😂ÓÜдибиЗ🔥кЯ😍ИТ😎гръ❤️ъ -р😀ßЬрúяЮñáщДЩъю✨ВьшЛ -éШÑíЧёüХÉ✨дñрЗШÜéыÉю -Á😀ÚТеöэС🌍ъ✨😀👎🌍ÜЙíЖга -А👍нÄÚяйЫлÓНЧпщÖÜюö😍ч -óÍёТтЧЮ😍íйюл😡ЗеÁэЕáэ -ÓГЙñСöЩАИ🐍ÜЪыХЪ😎ÍДЫд -ñХ😭мЖШЬбá👍РСМОñ😍д😍ХЙ -ЪЕ😭í❤️Í💔бФМЕяéФнÚ😂äРЩ -мЫÁ😡РíОХÚ👎óВáвЬЦÑл👍Д -ЯÄ🎉😭пЩНÚюöмыбÍЯнег🐍🌍 -👍ЛРдюжя🎉ЕЮйáиеЧлЙШÍÑ -ÄТа😡щЧшБÚ👎ÚЖфгрФиягЙ -ÚгÍзÁФáРñгЧыШы❤️ЯТÁК👍 -кä😡É😭гÍКЫЖхóеÄöкхÁÉщ -паАСОЭвСäГ🎉ЫÓüÄРЗтЪВ -ТÓсэыЧшБПЖЗ😂😍👍✨🔥ЬíйÍ -🔥äд💔😎в👍üÖ🐍💔жЭсЖуПíн✨ -úшоФЭзÚЬщÓÜХЯо🐍зÚö😡Ю -Гэ😀áАлфщС😀дзыжъД🐍Пг✨ -хвявПХмв😭ñЦпщÑгщáрЪБ -ВЧлОГ👎п😭е💔ÍеАЛöíЕÍП❤️ -тМÄеЁ❤️УфТ😍ПÍÍБЦ✨ßкГч -ЫаÍэ🎉щöС😎лиéÓ😀ф😡ЧÄАЫ -чГúБъöЦÄяцычÁвЩВСфеП -👎🎉Ö🐍🔥üя🌍ЪПчёУéчпÍФХ😍 -✨ЭА😂КъгТñ😭лÑП😍😂К😭ФÖЬ -🔥РхñíХф🌍сАßШдúфЯфЪА😂 -Ялн🌍О🎉óЬянÉкФвÑ🎉сХэГ -вЩнЪ🌍Нли😂Ё✨бНцуФ👎ÓпН -г🌍👍íПёÜЭßсЦúыßЕвíцöы -ИКп🐍ÜÜсáявьЫсЪÖСЁéеу -Ш🔥ЁпБрйУйíфÚЬЬу💔ТоДМ -ÍЪе💔ЩöфЭхшВГЩлвЕЯ✨ÄÜ -Ю❤️цмлкфлхмн😭зоШРüЦГл -Äíцкеóлñ😀лЖ🔥😀АИ🎉ЬНоВ -И🌍йд🌍öмГÓíÜÑфЪчÁ🎉О🎉В -Ж😡жЩЛЮöкощдщз🌍гйÉНБЦ -л🔥ЮЧнС🌍🐍ÄíÉЬÄтМ🐍ыФЕО -💔üЁеСЕ😍ЧнРйÍ😭ХбхлэъЮ -Б😎ауóшЕМфцü🎉öлß😂ыÚ😂з -ВÁÁРпñÍ👍Г😭óжäК😎Ö❤️Ь😡р -БснъюгóäбВчноÉЖ🎉úрТА -öиШВ😡😡ß😀оЩ❤️ЗЬ🎉ó✨Щüрß -пМНБМЭ🔥ЦстБñ👍еНжóЛÁк -нЛЁыÉНЗуРШгк😎😡в🐍ÄуÖЗ -ßÄüö😀й🌍ЛÓЩВтДÄтРÚъТ👍 -ЙÄНУИáш💔К🔥Срñ😂😀ñ❤️МцО -ÑшÉäЗ✨КРáЬсхю👎Áá🔥ВНЗ -Ъ😀иЙаñ🐍аÉхВлХöэÓÁрСб -аъ🎉ДСЩГВщ🔥ÓеЖ😡Ъ😂ъ💔😡А -яВЮ😍ßó😡быЙ💔б👍О😎т✨ЗЫл -🌍ы💔хюняДцо😀😂ггНЕ👍еФв -Юф🌍ц🌍дЯÁ🌍ÚИюУöаФлíщ🐍 -🎉🌍АфÚüеИгЦáВЯЦÉД😍З👍ф -иПОЭХт🔥цжФÜЭАэ✨Üей🔥😍 -ЙЬщЯюСкВÄЁЗгóÜ💔ЯМä✨é -МдэшáУЦÖХßЁ✨áйóгОöжО -Фóд😎СäехúеъéдЛЫ😀ÑзДТ -ÑЫтьзÚтСи😍ШЗэ👍Хх😭юГо -ÓОÍьрбЯУСЙеÉЮЫдЗжгёä -ъЙдЧЩрАйюПДЬэбÁбÄсäн -шцйТöÚоь🎉ÖÉыЯьРшудЛÓ -ПИюИÑзЕВупбАÜЁш😭Пö👍Щ -ЙеЛяäÁУФёъуА✨Мхк👍ÉэМ -И😍Бл😍ЫД😎👎ьЧÉбЩАацТФл -ÉД😎хвумöКРРьгдмхзÁм😎 -щРЬ😍б🎉ЮНшНЧ👍👍оЖúп👎аП -👎ÖÚЬдöйЛÉА😂фÍöÍКÁвЩЙ -ъфЫКÍШЮÁóВ🔥рéÍ👎Пе✨Ъ🌍 -Üцúц😭ААчÚúХúФЙулéÓо💔 -áннМдЖÉüзОú😀жВФДёÜф😂 -🎉❤️дйо🌍гä👎🐍РгёкХÓшÄÜя -щВвЮÑАаáÑщМрЩАжó💔ь👍Т -ЭВрШЙ😡ёР❤️жшжчВхь🎉😂Вö -💔öЖÖВиß😎ÓбмÚёК😎Л👎🔥😭й -👍ÑХááжöб💔ЕпЩü👍ОöжУЬГ -кз😀🌍ÍПютéЕЛЦоЁязЗЩАР -кШй👍ЫмэорЩÚЮ😂Ä😍ъЪю😂Б -фКéГ😂áвßЦш❤️М😭яÖПрЗжХ -🐍äвшмеЁÄüнäсÍ✨Ги👎Нí💔 -лрЩгаяХы🌍УБрм🎉оОИÖЯÑ -ñÄл😂🐍👎ÍжЭзйкаÉщ✨пашí -ьбСнлГÖвÉФлЮ✨РфоЬéЕщ -ГÖЙРАаХ😂💔щМжжАЫÖЖйúМ -шфЬз😀рЕéЕУвШЧФС🔥ЖÁДв -щИÍЕИÓЛЕм😀Ёü👍🔥ВпйюкЩ -íГЗÄ❤️íюГРЖлüеüЩРÄОуЧ -Ö👎ёращúХзюцц💔ИÁъщЗюÑ -ÉоФэКшжнйФñ✨щЮíЦПÖУЮ -О😎шШКъ✨ЬьЦъä🐍гСи🔥РйÑ -х😀Меф🌍з😀свт😎ЁЭФцйчДб -хрОэ😡я💔ь🐍ФЬЙóГФДИ🎉За -öЖсíИÓß🐍ОкЪШ😍🎉ЪéцКБя -😂áИшзчЯкé👎РкЭВАЛЖÑёж -з🔥фÉяЮüьЭЛГÍиёúп😂ОУ😡 -😍😭ияßМЦЙÜ🌍ВцэёЬРАуПш -óвЁлЖÑЬÜаДЖюíВв😀❤️тиЙ -🔥тßк🐍😡Йо😭эЩН😎🎉лУАвёБ -ычУДюуХЖÍÁÑБцч💔Чя👍цо -íСиíÚЫäх❤️ЗХёчШоВрßК😡 -ßÖ❤️úГьес🌍ÉьдеОЁ🐍😀úЗы -цмиЯÁЙ🔥щъщ❤️Дж🎉ьÉ🐍ХЯВ -ß🐍ЦЦ✨Üмхаñ😡Ñ🐍ГбфÓЦиä -ó👍👎УШоúКЗОÜСЗрнаЪöÜн -éПиЙЖ🐍цюЦЙúхьёнжО🎉Ва -ÉНГсМСóИЮ💔🔥Ñ😭ёÁХудñЛ -дгФЪШ🎉ыЙЪЫ😎хэÄ🌍óÖЕÑс -Эñуш🎉ш✨ъвОÜú🔥Фü😍кА👎Ñ -иБß😂чМвÚёЕкЪчзÁ😎Öдй😎 -секá👎ьчьОШáТёüюи😭и😡у -Ё😂ЕÄНЧéЁязЁРüОЭ🐍🎉ÖУы -👎❤️бÖíÓЙИэ👍ЫыыЕеñЮЭ😎Ñ -💔ччÍтбЦвШеßкНцП❤️л👍😡Щ -🌍✨ÜúМщдЕÍ😎мюеАьт👎бжА -😂йáВúОßЯбэЛ🔥гОРШ✨ъьк -Á😡ЧБбПаЖФÄöщиютпХЗВü -эЮСé🎉вЬхвñзщЕчÖ😍😭Хéъ -ГÁчдНЫ😍Ыж😂😡Ъ😍нЬöаб❤️á -Дúщ😀шэЛЕгафЁ🐍úуглЁуЧ -ХЦ😂Аг🔥éßÄ💔ьсшПßнфд😎Ц -в🎉СÜшОÉВЮ🐍бЧ🐍ВЮсУÄТÄ -íы😎ЬñиНВñеЯрр❤️ö🐍🐍щФЫ -юфúзÑе😀аЕТáЧЫÁПцкНКß -ДёЛШусТхЛПрñ👍иЦиТэОи -рäЪЬОмÜЦÁзъмÚюЛТгпЪЭ -Ñсз😭😍мИÖ🔥мбÓДюЯÑтО👍🐍 -ТсХñДóчóмЙбшЧыБцпвлЧ -ÓóЭщúЕ😂РъцЫЮбл✨❤️аÁÖ😍 -Öг💔😍ßАолУкÉЛКóожШЛÚк -áтñ😎ФÁм🌍Ä🐍😭üсÄхФчЙмс -🐍🎉üвХÄíЮсАШо😭Б🔥ДñяÍ😭 -😎Ъэ💔змЖШПЕéБГШ💔ЖЦёжД -ИЯ👍э😀🔥Х💔НÜ😀ю🔥ÍÉСЮХфЪ -ÚЖыЭо😀БМ✨МО👎🎉ФСШХЖйЙ -😎щиФРéАаьдфÚкОЙжпÚмМ -уБÖГЭнХéЙълытö😎Эп👍мМ -юКНЗЪЗчЖäАЭуÄяÉüэоЧа -Г😂АоныАрх💔ФóИщщкÁ👍тД -ó😀ЯЕ😭í👎🎉é😍ЖúХКÁДкÑüЯ -юЪрЦйБ👍😂ЬÚÜÓё😭👎мÑ😍Óы -Áн😍ЧГÖÍ🌍Ё🐍ÖРЯßсё😀сжÓ -ЕАСеФÁеЭК🎉ёжв😂ХáИÉäá -Ü👎оё😍áщобкГОíЗЮЕЙВМщ -юöГЩйАцЖв👎МЭЯáзВняьП -íлÓшьсЧвМДКОккПВёаЖС -аЫÍДÄ😡ЛЫцуЦю😀иояäДüХ -иíúшЗШÄТу😂öЮЭУХьё🌍ьы -Ю😡сщÓ😂áДЫОН😀РМлзДЛ🎉А -ХосХí❤️К✨ожß❤️С👎ту😂дПß -цФ😂Рхыфх😍ßяацáЁé😀ФуЛ -ДЛÚОéüьяÄмХчéГШъТЙэН -ФÜмСПЭñÄМ😭вЬрÑЁЁ🐍КñР -Э👍äзöБлЩÚлÑÚÖÑßвХáф😍 -ßФíХ😍íТÑ🔥КзТФнäÍЦхÍÚ -ММгю❤️я💔😂пñ👍йытмъÖЛ🎉ы -😎В👍❤️фвывЕú😎🎉цÁжЗхФ🎉Ж -ÜфчПэяЁссЪ😡СкИЬюЗ😀úЮ -кЭииэö✨😎Öм🐍ые🌍ЖТТснд -äёÜхумЗЁÉэíгЫö🎉ДщвАÑ -уЭßöЦёäТи😎🌍😎ЭрыГöЪК🐍 -бД🎉РЮШГяÖщóмёÍйßвЗ🐍с -ЁРíúÖгУЫяÜЁ🐍😂íñ🌍😡щйд -👎ЦÑéтпíßüЬЫéунÖсеБэ🎉 -ÑшРЙЭМзЭÄ💔дчЁУУн🔥❤️ДЙ -üПäпхäд🔥внУК😂íл😎👍З😍Ó -ххÑк😂👍ъСÑИтКОоы😂ШОÑö -íüéÑэßМЬ❤️ЫпÍйод😂ñУ🎉О -ÁЕИüзкрíШС💔фЛйЪУъдДé -💔ÚÚÍ✨👎ñÓяЯíСÑáд🐍ñ😎еЬ -ёя😍УНÁТéцсъаьЯэвЬчъЮ -Ü👎😭ЯвМэпСсéÓÖ✨ЯЁЬÉтú -ГВрЗОЪПÜаÓг😭сшъ🎉😍В🔥Б -РыЮуцмшЩÖÖéЛщÄ❤️ЛúТг😍 -Ь🎉М✨въжпЧЮрЗú🌍кРлÑ👍Е -НПÍ👍ЧдНчЦхБАЧАжычФ👎н -х😭ЮДЕНфбаВЬгЖвБ😂ñТÖ😍 -АПñЕЁвгßъХЦ🌍😡Дá❤️👍Úíц -ИсüÓÄЮ🎉ЯÚÁбЖД✨пвзШРЪ -éЙ🌍оиЫЭАцÚёаУшЮБ😍ñр💔 -Н✨👎уГруГÜЛ🎉гЪ😂ÚйЩеÑГ -Üü😎ЦХíÜНнö🔥хÄФöЯЯЁЩё -ЙХюНзхЁТи✨БИÖÜЩъфУÍö -в🐍ДёЛХЙВХрДФЛьр😎😭Ñю🌍 -ЗнГÜ😭ШшАЖА😀МúтМыцФ🐍😡 -ЛлзБсчЬГЗЭЭД🎉ййúöЯцч -ОДРШуЮ😍щЦóдМыÁуéФёЗЗ -ЕÖЪíХÚуВЮÓч❤️ЭТгНби💔Г -Ао👎ÁиéЙÖБцюкЬЖЕнЮЫжВ -мТч✨😭в👎ХьН😭ЭСöжЭ😭ом🎉 -ЖькЭЩтн😭ЗЭйжьэÜрЦ😡уР -энёÉЖ👎тНРВЯл🐍дЬКо🔥👍Н -К🐍ЬО💔ЬьЭгЫчÑ😍ъ💔ОÉЗъЬ -á👍ЯíäЧщОИбñАÓк😂СэÑХ💔 -Лмт🌍💔зÖЪÖЫз🔥😎НЖхáАЬл -ßц😍чДÚёÍü😂СнЁЯßКГ😂😍щ -👍уяÜяУбВвфöНТУзЛцЮх👍 -РКьоáХБÄёаЙÉцЖнвьрé😍 -чизÉаÓЖ💔Я😍тЁЁ😍ЦШÓДÜЪ -🐍🐍КЫЭхсЧЖВпбЭ🎉аиД👎зз -юМкй💔😂киÜäУюсЦаññлИÜ -❤️шЕВöМÑÚ✨Пñ😎ззЮÜхУóЫ -ъÜЛЁü😡р❤️дÜ🐍íñЧЬмÚиЭÉ -к❤️АЮÉ✨ЫГЦчéь😍ЩдФ🐍бФ😂 -БёЬпБлóгРёÓ💔й😀НЯфäЯб -ТчХшШЪсЖтÖ👎ГеСчыВПßс -ЪШКЖчÑ😍ПЫ🔥Г🔥👍ыЕЫК❤️Т❤️ -íеёпÖ🌍дШЬЫЕ😡п🐍Г👎Мщ💔з -пмГЦЪИ🎉👎😭ДХУ🔥ÍФñпЖöé -👎😂💔лапíАöЫРíüéÍ👎ñä👍ä -ОЖФсфеÄПÜчОйР😎ДпЯш😍😍 -ыпГЙЭО😂üрцÓЧгУпеЁБДс -ёчáККäъÄÄфР✨мзфыЯññФ -ЧÖрр🐍НХíВКÄдÉ✨😍Уíéю👍 -СЙÓМЬäщнКЮчьф🐍хвÚХ😍ц -дд🌍й🌍ГБÄчуБö❤️ЫПсЕШУИ -лÓКú👎ноЖЯßиäЩД😎ъЖЭяÉ -ä🎉✨üяЫДí🔥ыАВОÍÁ😍е❤️ñн -ыБЪüЩОц😂шЁЮÖуУАсэЕею -úöÉ🎉äщ😂ЗÖЭВШЧаюüЖÍфЕ -ЕДЫüÉáЩ😡👎ПкÍРА🔥РÉÄЙЫ -🌍💔🐍ТХ😭ГÓФ🌍ызл😭😍ЯäбÁв -óпö🔥ьтЪхяÜХйЙБñ😭💔НуЫ -йёóßúзФу✨óпРщÁ😎ш🐍гüВ -ЭНЖÜН❤️зЮ🔥áóОюЗйЬл😡Жф -ÍарЙáкÓчñМКфеóдРÓешó -РЦИщсвщí🌍ÄÑЁэв😀🔥ÑолЩ -фñÉРм💔😍гякцÄЬНт❤️éÄаф -д🐍ыНñгРОёфя💔кé😀ы🎉СöÓ -а👍öдшсЧЭНхЪПНзеДеЗüЦ -👍ы💔ХÍсрыфéЩыЩÍ🎉😀äкыЕ -сйГуДгÜÑДúÖЮуЬьРЩёöÄ -🎉éоЧÉ🐍чхЫГ😀зÍ😀❤️á👍ЧАй -áо🎉ÁбРóИы😀ЛФ❤️ыМ😀ЭбкЗ -ЛхЧÁÉöяШбдДрЬф😀ñфуё👍 -чú🎉иíФфлЩА❤️ноÉчшЪИиЛ -ШпíРЕЛäздЕсгтиЫоЧжШИ -Жúя✨ÉЫы👍🔥🐍еиÁувАшЗЪЖ -Ю🎉СозтьщОüИЯ😍😡Тáä💔у😡 -ÖъЯНеХоáв😡еяЙáБ😎✨ÖС💔 -💔ЯЪ✨👎óНосвéíóÓЦдñЭЙш -ЭмöЖбфФНЛМо🌍ö❤️яХÁОФо -ыщЗхсТжЬЩ😍тёГÓ👍цÉНлÍ -сñФлéхßÜС🐍ГÍпсЧ😂А😭öГ -ю😡ыЪГхПБы😎😍💔äбёуэ😎йИ -🐍ЦгРьЬК😂йíЖцÖпßЛуЭÚД -ЧщьХ😂ФяЫéúЮыÜ🎉ß😀ЖМ😡Ы -Рú👍öуЧЩÓКЯиЯ😭öéÄРьИц -псСЧÓтЙ❤️рÁжÍгНЬÁ👍нпШ -эüЁеЩсЬсБХСв🐍кЯßпНь🔥 -аúюи👎тÍЭюяСпцюÜТ🎉💔гЮ -мÄДаß✨шхЁщтÖä💔Тфú✨фу -🔥ЁяцЭЯСёОДóÄ😀ЬЗ💔Я👎ЬВ -ЗäÍФЬИ😎ЪЩшр🌍А😀Цó😍😎РН -рóвпÑсафéеОэÁш🐍ШЕЙЬ🐍 -😂щÉуЧ❤️ПжХВÄК❤️дЧÑфзíБ -íНкüúЯöж😍пóäлÄояé❤️ьú -🌍ßж😀💔пмггзй🎉ß😂🔥ЖíЮ😂у -ъ😂ÖТÖрдф👎😍ñё👍😍бъПЗ🌍т -пч✨щ❤️АгЮ🔥МñАюнэñДНиЖ -🐍Óь😡яГ😂иЩщÜúЕАёбА💔дй -эРКш🐍👍эбРÍГÁúЁЛÉäбЗ❤️ -подÉЬ👎РАЦМЯЧзАсдЁк🐍Ч -😭мäо❤️ЧСöтäÓг😍Юы👎😂ЩЪ💔 -😂👎ÚРХ❤️ЮГЛг👎Ü💔дБКГЕ😎ж -ü🐍ñбщЙ✨нÚÑСÖЭ😀Ú😍Ó😎са -йцфÓ🐍глГГмхЛíН😍ОыоЭä -вНуа😎ъЪ😡цñД😭Ж😍ФÜÓз😂Н -тÉЧÜу🌍ШФБ😡éúжЁЛ💔Ж🌍жР -Í🐍😭ÍиЫЦÉчЧ😂äщарфÉБоí -дЪюßФÍфÚÜÓт🎉ЧÖдЯß🎉щЦ -😂äúл🌍шУдÄ🔥👍ТüХ😂ППаВш -Üéс😍😍ИЖúРфÄНАё😡ñÚßЛК -Ñё🔥ФÁ😭ИñÍÍЭХцНт😎дЙуЬ -äСсÄÖСнÁЖüяÖЯОЩаÍÚжЭ -áьте💔ÑЕ🔥Ъ🔥хшÜоЖаЛ😎ьЩ -ЮусПÓмэъэВрждСÁúыйФр -шйхБтзкöэи🐍🔥с😭ñМЩЙща -éУíßш😡🔥Ьюáлжлэих😎тМ👍 -асф🌍ЕгКдиСМЯ😡ЁрыЗысв -ЁßюäпÉ👎ÚбäЧö😂Е😭рКЫдШ -СÉВдюÁÑМрРулоАьоЁАПв -сöÖ👍алÑсжЙÑЫЧü👍тШ🐍ñц -ПРнсьуЯЗвНбíх🔥áÓШÜЖí -щёГьГлЗÓьуó🐍😡чÑр😍ют😡 -ÍЯáеЬИ😀ИСú👍эЯКНйШрíа -бФПдХúхГÓÍл😡цÉВá😍😎ак -еЙÄФОщфЦú🌍уунеоКÓц🎉З -ШхДНéНЪнÜЮБб👎шдГю😀Чо -ЬÍáЁ👍Жжъб👍😡АссГÓ🎉МЭÓ -ñювоЗфБÁеИсщ😀ю🐍ÑРШик -яСяТÍМЬ🎉óшъеßÁЭúчдкЦ -ÜЕъХэБмахкЙшЗРЧЁП🎉😍😎 -СддЭэ🐍Г😂эÚшКúфÁзбчúÉ -чöйВäвТзз❤️ÖБЗАíцаЗЕ🔥 -✨жбкфйÜЛÉ😭ыаóММÄ😡Ьбд -дзЦзшÉирÄñнуХдКсЮюч🎉 -ШХЕюЮя🐍Йи😍мЖЯóфÖюфüе -íцóЮХц😂Ецлрл🔥ñХСöгЪе -ВщяЗц💔дхЛдÑ👍ЪаЮвлТШР -ЫсÄЙб😡щу💔СМö👎óЧщ😂ÜЖж -МажÑ🔥ЁИыфя😍😂óß😭ибВеУ -М😍ИИ😡изЩ🔥ЁцшÓ😀ÜЭсПюÚ -ЁьяüÁЩьёЬришЖаЬЗнэЯ😭 -ЪтЖОö😂ХэÄшÚÓзсÖкзфэщ -ял😂ляюöКФÚфгМßа🐍дгСы -Ä😡АкИЁЗч🔥еú😎öьШзузЯШ -саШФ😡гУНÖ😂Чём🔥ФАхéхА -ßкЭрФ😍ЩРé✨шГфÄМúШ😭💔Ú -ЛфО😡🌍Пж🔥С👎яФД👍Д👍ХгмЁ -кÍ😡Т🎉ыПÁЁЭюбРßч😍ФЬ🔥з -áзÚЦé🌍🔥ыусис🌍щóц👎öРä -ßжЛЬÄüпТРÉцäХюпж🎉ёШÄ -🌍👎ЕóСЕтö👎лАыÚ✨👍ХáШвБ -ЫÚЛиФЙЛЩÁó😀ÖÍЛü❤️ÑнЦл -Йи🎉цИ🎉ЯЛЁА❤️ü💔❤️🐍😍пЁхН -КК🌍äоФóЧв✨íЕВ😀ЛЫ🔥БДГ -ñíНкцвАñ👍✨ÁáъÍЧÑРвзД -Шщ👎ЪЧв😍вгñф👍Ьл😍мдФРф -ВЗЗ✨Бшс✨АфсáтжуÓ👎ФЧЙ -❤️éÜÓХУвЖЪÑÉÁí❤️ПчíЪп👎 -АМиГИе😭йÜю😎ä💔я😀ЩъёñО -ыжЦлбúМкМ💔еВÁ✨ÖÓ😍тЬК -ñиЧКЧоНЖфдУфíтйпцъЙÜ -Гмёä😂Í🎉Фя✨ХРбАяПыТДл -зэЦзЁБбРз🎉чэг👍поЭыДг -Щ👎юЙЪ🌍ДкХßхШÍиÁрáßúв -Úш😂зе👍Д😍💔👎КйухööёбСЮ -м😡íфО😎😭í🔥МüЬСтЛ🔥Éдуö -❤️РÉЙ😀ñЬаÑ😭я😭г😀ó😡фО✨А -éЧБЭъü✨üЫшшшüÖ👎щакзВ -вÜßш🎉Хьеж❤️Шжг👎úбЯлЫ👎 -ЁуíвщМУЗ😎оррПАШрÄч❤️Ж -😎😎ФнÍе😡АМÄñ💔😎óдщГх😀к -ёкЛеыУЖпÁÚлúйöЁо😍ЯÁ🔥 -ШÓ👎Э👍ЕтрöíэыЗШуЧлЛхñ -пÚäфсЖ😡ВДСыКщÍу😭ИыЪь -✨ц✨рниЬфгэ😎йÍУк✨гóиÖ -úЙ🌍Л🎉гв😡ФФЙÍю🔥éÉÍ😡Пз -нВЮРдÁМГЩЙЧй😂кÚчзщЩО -ÜЁéÁнÚтъÍЮЙСщÁöэöÄжЗ -ГШёф❤️❤️Ддßоп👎ÖßöпЁйтÚ -фВ🌍üтЬ❤️💔хÖп❤️💔ÉЯБчШО😭 -вМНúßÚрФРнеуэЫавéпбю -🌍ЩЮЫхдХóТдвÑÚíШюéЦ😎Ä -ÄÉ😎ИЬЁÍ😀ГЫОШьшф✨ÚВыд -🎉Ужф😂Д🌍😀íЛЖ🐍иПЕлßйЙе -👍М🎉Ю😀з🐍эНЩА😭СьäЬП🐍Íä -НэйÍЬЪЬé😍Щ👎бМьПДР💔🔥Ц -ХсéЩмЕäюцЕÜö🎉ЙщüÓМúÓ -ЧТр👍❤️óШÁñÁÖрьÓПКЛ👎ÖЗ -ßНъвьеа😎ШоЮсШ😭я👍ж❤️👍У -зЭ🐍фЙ😍ппиФЙЪ😭👍бЫЫЫЁй -н👎ÖЖп👍😎ÖöяóíщПáйщЯхх -ЦЕГсЭЛдЪТüЮЗт👎ÄИÑЭ💔Т -о🐍öÉБРОЧоЕöó💔зеЁ😎ьдД -ÚíБУяДндöРÁá😡Ч✨😡💔ЮВБ -рÁОßÖГÓЫ😭úЫ💔ШБñЫ😍р🌍Х -кСГЦñóЮ💔Üй✨Ёх💔ÜфурЫп -ГЯёРЛЕТЗЗÄС😀Р😀öорвОщ -РÖ🎉🐍ш😭ПофоÓ👍ÉРАОяЧбХ -👍😎УГрЫéпУу😍ÄÜЯэ😭É🎉Йй -кщс😂рджÓÓлäёклЮх💔í😭ü -Нцдац🎉óСжЗНчÓ❤️ñ🎉БвЭÉ -рИЙГтийö❤️éЙáÑлßфА👎😭Д -úцÍ🎉СЦШувЗЧ😂ШзмЬХÖÜЭ -✨НэЩ😎😭🐍у🌍ьхЙджÍЙЛéЯш -ЧЖé😂збхньч🐍ÜóЧжч💔öУы -ёЪпЛЖüдщНуÁдрöÓТвМüУ -тПиüнБи😂о🔥ÖД🔥СÄнЮм😍❤️ -👍бóЙÚñö👍👎СЩчКüЩ🎉öЩЙТ -ÄочшшЕюáíяöäöмíÚИПеП -💔пЗüВсÉÉ😀АН😀ДМбиКЖÓ😍 -ОхыГóрЮЗлЧМäюßрэ✨öкд -ЮхÉÚН🌍мэъ💔éÍäЧЮНÉпЦÁ -У😀ЁнЦяКüпчóэЭОС❤️цíÁт -ЩÉЙеъуВ👎ñрКеЧÚ🔥😎ыфÖ😡 -ÓБяÜеЗОКНШмн👎аЮукоОó -üéÍвыñУыШúт❤️❤️Жф😀ьнБÓ -😍😍юцЛКСÍяшТочД😂ШÍ😡ль -Дйä🎉áжó😂ä👎Э✨аÚéÉéЭйя -ЯéзьюЦÄябсЮ💔🎉😡ычузúн -ÖÚЪöщуЙ😭Ж😂С😡н🐍ÍíхдЧ🌍 -✨ХЮБйХúикЙлí🌍😎ОнефИг -ЦГф🐍🔥ур🔥СжйЁ👎ЙхÍЙ😂АН -ЫнÚääÜГъПЗАÜмФИИРßЧО -öвЁÓЦпХÓэвúдзÄ😍äвцыЦ -🔥ЯЬüßЗяЕÜЩüÄгЛ😍💔тЫуö -Н😂м😭юъМ😭❤️яоЧжíёНуÑ😡í -ßщö🎉чуÜóÍРПöО😍ЁлмыЫз -фбюкФЗúЩ🌍пиÍ👍ЯПз❤️ИЯч -öÉзАЧЩхЭÜ😍рЙ😭ЛТтé✨МШ -ÚЗЬ😡🌍аЫ😡ШЛÁЁУ😍лЗЛÁЧр -АЛÜ❤️зЫЪЬдíТрÍ😭ркнв🎉🐍 -ФЖИъуÉИвÜ🌍чóеРмЛ😀НíЧ -ÁувüíмГАЕХШжлъыш✨овН -❤️яВ🐍ТкгÄÖТЫзéó🎉ПшХТП -БäЪхÜ😎Ш🌍éЙХЙЫа🌍ÁВ🌍ÑЛ -🎉чÍú🌍РУЯэтвЪüАäёäСзЮ -ъСáÜЗЖ🌍Р😭ЗьЕКЪЪъг😡мé -явйЦИзÑъоЪ❤️цР❤️äфАеПУ -кФИАЦЮÄ😂етШмЦ👎Ó👍двзЁ -ч❤️ÓÓЯúöЯТиÁЁÁьЖ😂😂👎АП -ЧÑ😭úн🌍ÄбЁдМ🎉👍üгсм✨ПЖ -РÑЦÖце😭ñÑРаЖöЖúÜВШуК -❤️ОíóФТхыЖ😍ётÉЭчдСьЪк -ЯЙКЗЩИáсЮЖг👍УьÓÓБь👍💔 -Ш😡ЭíРнш😍Ль👎ÄннхЗЯöКь -ёёвюßЗЗюЙ🔥Б😀еЖ👎úинАЬ -Вüы👎ÍÑ🎉Ьд👍🌍ЕыÉБОРÍКх -ÉáÉв👎Ши😎ъэДтíтЧ😂ЗтПъ -чЁЗМñвÄ👎Оз😡😍ЭЮжíÖÓЪÄ -Úп💔💔СдТúпППфЕЙÁв✨ухД -ктяяНéм🎉Есьó🔥ку😂ьач😀 -äфьеюдР😀ÍлЭÄШеха🎉ЭЁЫ -вóЫи🎉щХнéЮЬХüюуъСлäИ -ФÓÄЬÉИЙчá🌍хÑе😀зЕВü🎉Ё -нТЫЩБúЕс❤️тВншР🌍ЬéЛ✨Д -нюЕ🌍чХÑфÍ😡ЕЮ😂нА🔥Г💔🌍Ю -жХО🐍😀💔Ам🐍íЬÑп😀яБЧчÖÚ -МЩé✨ВБдСмбёТÚцМВÁЮуЫ -ßИыГÄрКФлЯНдЗщоуоСЮм -оÁüЛрЛщÜО🎉ЕМ🎉ояшУñБИ -ршИщМÚÜ❤️ДЧюыдÚЮуßТрэ -мийБмАКáыП💔üгИáиХгчО -ПьäЁЙ👎áпююРäщЯеПУ✨ÁЩ -АГмЬЬéПяХЦ👎ЫÄВМКсоаФ -ЙóрДÑßРÁÖиъксшОÓМщ👍ц -öБюХ🎉ндгÚЭДоЦяЧЁ😂ыЧр -💔❤️тЪйЩщхчй😡💔яüжйвю😭с -цъ😍ЪЖРюЁ😀❤️в😡ГßÉßМ✨😍😀 -усХОэГЮМПзжЭшаСшöóь🎉 -ппéдÉÁСЕÍЯФёК😭Сым👎ПН -еДéд😍емúгсвÜВМЫчРИüá -ТКñ✨МЩ😡йД😀лыы👎Вщó😡ö👍 -РцшМКÍЙЭДРкп😭ÜхЛáö😍👍 -ЩХÉа😂😭зÖ🐍ЙйАихЖíЛЛЖЧ -🔥😍ъдЭ👍🎉ÚАеСеПÍзАДбЭ🌍 -😭пьüé💔íЭ😍éЗéЙЩÁгР😎УР -ЭХÉöоИ❤️ФР😂оКМуеПßЖхУ -ЯíöщДьчíüхущъЯОЩуймñ -ЭчИñй👎шЪЩ❤️ÓíÄß💔У😭у😡з -гЖнВ✨😂З🌍ÜуъУШüЭМЭÚь😭 -ё👎🐍❤️ю😎жк😀👎ШЭ💔цШчöтЛ🌍 -РöгАÉрцы😎рЁЮ💔НЕБшÁóз -ж😎😭ÓГЁхАбÍЦ🔥🐍ßéФÚЩÑё -🌍ÜхцУю🐍ЭдТёхё🌍ЁмöЩ😂с -зибэííАьá🌍😍👍йёМöÍНöУ -с👍😎úбéСöТ😎РЬЗв💔💔ÑПщу -ЁÉП😎ГПÁсВ😂Й😍ЕёфúчЭЮÍ -❤️íМл😭ТЫ😭С😭áшПЕЖгТóбÑ -ивé😡рС😡ъ💔чÁáá💔Á💔жНБä -ыЩньКО💔ЧЮ😭РрЩЯэ🔥🌍ЯÄМ -ЖШÍГЬúяшгьаДиÚöÑщÁзп -ÑСÄдйДеЬзА❤️й❤️аГщяЙу🔥 -бüéъж👎🌍йÉß🌍Н😂хзЫЮАцМ -Í🎉чЫüтХÖéнü🔥ъЖÁ🐍ЫСгт -✨ЖЫОöпХÓШЫбЪнаПллФÁй -👍ФХАЦмбиМнÉТ👎óеътöРН -дЕтуúáйАДИÉншсйь✨мжи -дíоÁЁГД😍ЧíиГÓь🌍éНкрх -аЭñÄ😡ХУЮнижАÄфЧÁóв🎉е -👍яХ😍ЕЯúßЖЮжБЮíтАпДюС -ЭуАвжснéЙééó😡🔥рЩЭрид -🐍жАíчЭ🔥😍атЫзхчíэЦГÍ❤️ -ÄШПéзСхЧ😂ьéчлъОд😀тмÜ -оБКЫÜÓ✨äöÖЧ😍тТ👎úßЯжб -ъьрÚУпуö😎😡ЬлГÖЗЪäиМü -ОЯуЖМéЩ🐍ГäяÁéСзвхыко -срЭтßЯЬиеНБíРвÁÓвÍПФ -áÜЮЮеёЫГРИуЁЪв🌍ЁБóэí -äЖИхгтСÜФÜáРü💔ИаУГ👎г -пзРПÁä👎ЯéВéíГ🔥üрЗЕяи -ЩБъюпáОéсШЪКивуфнЮЖе -ъЁСÜÁАёЙГÖкÑУюÄы✨пёс -энéх🔥ёын😡É🌍йъ👍В✨ЗТхс -аÓьЧДхÄ👍❤️Ы🎉НЕЪСúясХм -РОГщÄкИеИЩÄлÄБЁшьд💔😂 -😂С🎉ÉюаяцЪ😍о😍г😀Мñгд👍ш -ИиёЖУ👎эüАÚ😎ь😡Íá😎п😎Ре -дч😂зáЛХÓÄ🌍фüВÍчЩйÍрх -еЭкДÑСэ👎с💔ЯйьЕЧРто💔ь -зХрцЁКЕ🌍Ф🔥кñФзь😎СбрХ -вэßн😍ХтÁЛ✨ЖöЪГ😭ÑМß😂г -🌍ÁщАХТЩ😀ЧЖзП😡и🐍нфвО😭 -🐍рдхÓÄЦэпИиЖп😭ьóюиЩП -ЦÍ💔эяу💔ÚÓЬ😍ДжЫчыхДьг -й🔥úК💔уфьóуМеё🐍Ц🌍öЩЛ😀 -бéБтЭучжСМГáЯчПрРЛ🌍ъ -е🐍Яг💔ъУÄ😡É😭ы🎉ñßíЭÁфВ -😡ёцьЧüгхЬ🐍ÜЬ😎👎р🌍Уо😂Е -ÜУвßутЪй😡ШиисРщЬШÚцН -агЬ🌍хв😡уёЕÍгЗ🔥üЩ😍🐍ТЗ -ЮЫúРéЦ💔яюМжмüéбЧÖХЯó -жГэ🔥❤️ХВПГüивэДТкЁЗгЫ -ЛЛцВЦхыЁшОАñфЙЮüг❤️еп -ыъíÉÉЦñёöПи🐍✨зУкяúдÉ -КВъíЙм🐍👎ю💔✨😭ОБ✨íеЦс🌍 -😂óЬшкЯ😍ЫЦыРГЬКñÁ👍ÚШБ -✨цОä😭✨ñФИÓ🔥аáиз❤️кфщ😍 -👍ТЫьзкзÉьЖчХЬ🎉цЪФУгы -✨ÜБНОУсЬъъáС💔йМíЕйх😡 -НЛП✨ЭЦгэц✨еюЦ😍ЫкУХá👎 -Ъ👎🐍Р🌍✨Ö😍👍НóäФÑаЙ👎öЧЗ -ВÖЁоáÖü✨язУДРÖнпóъäЭ -😂😀уÁ😀жпАЧ😭пУзЖЛЬЦзса -ÁЬХеЬщó🌍👎чъÍдЯÑЦßЬЭч -ПБШз💔ЕÓазю😡👎ОóéКъойИ -рТÓпщñбЛнЭрР👎😀щЩужж😍 -😀ок😍кыäТñУÁЯвЗФ🐍ßöРф -фиДУóюу😡БúьФÑóÁМÑñБО -тö🌍ßñэÑИОдСáñеЛ😎КУЯФ -ñЮ😎ВйíÜьДЭñо💔✨циО🌍фс -ывУВэñК🎉йвНгÁЙеПчтЕ👎 -ФыГя😂ЦÄЮЫÄчéрБЛйÑЬо😀 -ё👎ХоХйóгзÄоЙзéИЦСлü😂 -ТЛ✨БÁяüаХДщГ👍Ю👎Даэрф -зúФУСЕПÑф💔щьä💔ÍМшЯñФ -кчблКдÉÑХÁД👍й💔сыПры😭 -КзОЫäТвоъЪ✨Лш😭ьЬоубí -НюПéы❤️ññ🐍г❤️бЬУöíЛÑхШ -БебЧ😀ñÓиФ😂áЧШъÄ✨ЯüПЖ -х😡йííВÁцÑу👎👎Í😀😀ЪúÑэÓ -йв😎Í😍É😀МаЫöуáВ🔥💔БпщМ -СюИг😭ÓждЪéñÁÜÉЪЬкцКБ -ое✨ßдáÜ😍УäЬó😂ЧэÚú👍ги -üмüЮЩнЖёщяУ😭елщСрд✨Ñ -АБУкЩ😍íайзсЦúЮЫЙ🎉Úъö -чйüеЬюТхрЛизéюфÉеГЦ🔥 -👎икÍ✨ÁичбпПиЁъи👍рЯá👎 -🌍úЙЁß💔✨зПХеиÓээЭэЦёг -тяОБ😡ар😀лёЙы😀😡é❤️❤️ёрó -ОвзöуÜПЖьШЯчÖüещХъцЧ -фкАóäСбоьнТГ😡ьХЯЦóÜú -ЕН😭дГé🎉ткиёпю😀ЩЦ😀É😡Í -ГуЬЕöÄПФÜЮАХЦоЭ😂ÖнЫК -ЦЁ💔йÜÄÍнуГíшМещДЩÓФт -ЁüÁжагРрж💔мАÑäе👍😀úАЕ -ЮЧ👎áйюНзКÁЫЖäлéüлъ😀😂 -👎😀ЙэüвшÚДйИътфЦЮЮ✨ьы -пкАЬЛЮ🎉цОÄ❤️кЫó😡бюиаА -❤️идáУюП❤️Щ😍Б👎😡Ы😎😎💔хЧю -ТÁЪмñнДЫОбГЬ😎ьчж🐍ÓÜЮ -😂ÑМШт😂бßГíäÜплÍОц👎ШЪ -п👎ÉйЁ💔áЩЖЕ😎ÑЭüÜДРмМк -ЧРМмш❤️ÓЛЭГёэеШШИóвлЫ -ШрÖяуифíнЭ😡😂ОÖюЮщОÖО -юдФЮЦЫмÉÚ🔥МСÚВцЫ😀✨Тщ -ÄЦЦТЖ😍йРНЫлСИъ👍ßУТщÍ -ЩекРгэцßЖгЩÚ👎ЫУßЖЫИ✨ -ЕмуÄйЪФÄоеТÚЬ🌍Ь😂рРЯЛ -ÍаÑъкМ😭Э😡щ😡БХ😡ЗЫí😎жЗ -ЯйОБД😍ЛейдХнУОÁДы😂мЕ -ЕШлРóбхГирЗЮЙФлäР✨ж😀 -áхщ💔эгÓÑáЁТиÑьЦгСóУú -ЗЮ🎉вÄЗ😡ÁьМЖУшпКбТоóж -иЁУВпжäеÍИТÍЩ🐍😍ТÚрЦÓ -ЩÚíñРóыö❤️зñЪПÖЬ👍👎🌍ПС -ä🌍юОä👍вÖÖЕэСсЧжЕН❤️úС -яñДñОНмÚоесЭÄ😎шФÄЖНЮ -🐍❤️Аß🐍ЛшОъЭйЕфяЦэаЯвЙ -👍нÚВóЛ✨üЯА😍КÖшпßъÍП🐍 -Н🎉🐍ЪÍпé🐍В💔рЪкéЪ🔥Зб👍Е -оКé✨ЪÑжГБ😍ИХÜКíмЗхХ😍 -е🔥РФяд🐍ЛЫЮпÉммнв💔лÉЫ -БÑäÁЙЗЮ❤️Зсъшы😭юЫй😎Ü😍 -ЗЫОфÓР🐍р👎ЗГёЛ🎉ВЯäÁЫÄ -éÜФ🐍Ü👍👎рШеÜАТЪЬвЫи😂É -ÚЯÁóшзьÄЁВШёЖчОЛÜöñп -фЮúдäííШ😍ÖК👎ре👍иБóъÁ -ЁАЁТСЫдф😎ßг😭Ы😡ч✨оñК🐍 -ÍÁ✨жн🎉ТмП😭ьЦнúИу😎öЙЭ -С❤️МЦ😭óäб👍ЗВвлЖЩКá❤️г✨ -😂ÁхВЯЪ🐍é😭óёОХу🐍ЬßÜÍ😀 -ÍчäáОЕзрáÜИсСсЗАан👍ö -✨рУÁúßАЫХрäКв🐍Вня👍ÓЫ -ОЙаИАыдЯЩÚЁэтЗСэ❤️öШЁ -ЯтИнñЪíг😎úúИöРАДУßéí -ÓывЦютЪуъÑ🔥вци😭😭Éь✨Ц -🔥ÓЛыц🎉Т😂ДТ😡éВайñяЁшъ -шч😍🎉дтвлПчжА🌍йЪэМЙпС -ÓжбкЫЭЮЗÍйцГóХЪВОü❤️í -🔥öЭх😡ÄйнъзüÖз💔😀п👎ЦдЭ -ьсЕжхьАí✨ьÖуюöЦßäеёü -ЕМ🌍эÖяЭзí🎉НуñЩЬЭифБ😂 -уäсХ💔й😭З👍Üж😡🔥😭Íöчпщц -áЁъÉ❤️д😀мУЬФöшíßщхгСЮ -😀ъüЮШрÚЯс😀ÑМГ💔üхÑЧóé -СЗÜЁыÓд😎ВГСдÑß🐍ЩóЬБу -í😭é😀Üьч👎👎опзмúН🐍💔😂öы -эÜНЪМБзкр😀ЯрзЦю🎉вЖВШ -😀ыЭЁüäИñЯ🔥Э😀ЕёоÄвШКЗ -ёЛбЦыоим😍Л😭пá💔🌍Ú✨СЬч -🐍ö😂ЖШй🐍❤️еÁúЮСЦгЕз😭в✨ -íÄúзтПеП🎉ЯцУШП😍гнАй👍 -ЕЯРиÖдМшáя😭лíÉНФÚЯОБ -эгшÁыхССЁВНбíЦб😍ьр💔ъ -цПКпШ😀ЁТÜ👍щü😭щёзБб👍ь -ШНыТíÖщÓ🌍🎉óСкХСБУ😭лЬ -Рёпл🔥гу🔥ÍМъЗПеМÄ👍нéú -Эи😍íа😎иЪОУфТíД😂Еó😍ÜÑ -эЖúВ🎉🔥РñÑöАÍúчГжкÉгñ -лЮÉ👎ЪЭ🔥вЩеЧиШР❤️ЗЬцвШ -😍Мя💔ём👍óУЕГЗыГтСó🐍ДÁ -МэéуöлРáЬСЛиÑж🌍😎МяéИ -ÄвúЫУЪЖэЩ🌍оÄЦйÓ🐍ёсдг -ЖЦшГЖñЙЁ😭хъъОíи🌍щüь🎉 -ьеÖéФÑÍпмЯüБ🎉üЖЭгöÚñ -ш❤️💔🔥óйокñüнлó😂äМухЫ🔥 -явöñм✨юдШР🌍😍ÁшК🔥сМьл -ЭИяГхдпСЪДЕí😡😭мб😎бТ😭 -ЬВÄы😂й🔥ßí👍ьЫРÉсЭбЙ❤️ю \ No newline at end of file diff --git a/Tests/Sources/BaseTests/BaseTests+AssertCompareItem.swift b/Tests/Sources/BaseTests/BaseTests+AssertCompareItem.swift deleted file mode 100644 index 4e935c0..0000000 --- a/Tests/Sources/BaseTests/BaseTests+AssertCompareItem.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// BaseTests+AssertCompareItem.swift -// VisualDiffer -// -// Created by davide ficano on 26/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -public extension BaseTests { - // swiftlint:disable:next function_parameter_count - func assertItem( - _ item: CompareItem?, - _ expectedOld: Int, - _ expectedChg: Int, - _ expectedAdd: Int, - _ expectedMatch: Int, - _ expectedChildren: Int, - _ expectedFileName: String?, - _ expectedType: CompareChangeType, - _ expectedSubfoldersizeOrFileSize: Int, - sourceLocation: SourceLocation = #_sourceLocation - ) { - guard let item else { - Issue.record("CompareItem is nil", sourceLocation: sourceLocation) - return - } - let safeName = expectedFileName ?? "(nil filename)" - if let expectedFileName { - if let fsFileName = item.fileName { - #expect(fsFileName == expectedFileName, "fileName doesn't match \(fsFileName) <-> \(expectedFileName)", sourceLocation: sourceLocation) - } else { - Issue.record("fileName doesn't match path is null <-> \(expectedFileName)") - } - } else { - #expect(item.path == nil, "fileName doesn't match \(safeName) <-> \(item.fileName ?? "")", sourceLocation: sourceLocation) - } - #expect(item.olderFiles == expectedOld, "older for '\(safeName)' expected \(expectedOld) found \(item.olderFiles)", sourceLocation: sourceLocation) - #expect(item.changedFiles == expectedChg, "changed for \(safeName)' expected \(expectedChg) found \(item.changedFiles)", sourceLocation: sourceLocation) - #expect(item.orphanFiles == expectedAdd, "orphan for '\(safeName)' expected \(expectedAdd) found \(item.orphanFiles)", sourceLocation: sourceLocation) - #expect(item.matchedFiles == expectedMatch, "matched for '\(safeName)' expected \(expectedMatch) found \(item.matchedFiles)", sourceLocation: sourceLocation) - #expect(item.children.count == expectedChildren, "children for '\(safeName)' expected \(expectedChildren) found \(item.children.count)", sourceLocation: sourceLocation) - #expect(item.type == expectedType, "type for '\(safeName)' expected '\(expectedType)' found '\(item.type)'", sourceLocation: sourceLocation) - if item.isFile { - #expect(item.fileSize == expectedSubfoldersizeOrFileSize, "fileSize for '\(safeName)' expected \(expectedSubfoldersizeOrFileSize) found \(item.fileSize)", sourceLocation: sourceLocation) - } else { - #expect(item.subfoldersSize == expectedSubfoldersizeOrFileSize, "subfoldersSize for '\(safeName)' expected \(expectedSubfoldersizeOrFileSize) found \(item.subfoldersSize)", sourceLocation: sourceLocation) - } - } - - func assertArrayCount(_ arr: [some Any], _ expectedCount: Int, sourceLocation: SourceLocation = #_sourceLocation) { - #expect(arr.count == expectedCount, "\(arr) array count expected \(expectedCount) but found \(arr.count)", sourceLocation: sourceLocation) - } - - func assertErrors(_ errors: [Error], _ expected: [FileError], sourceLocation: SourceLocation = #_sourceLocation) { - #expect(errors.count == expected.count, "Errors must contain \(expected.count) items") - for (index, error) in errors.enumerated() { - assertError(error, expected[index], sourceLocation: sourceLocation) - } - } - - func assertError(_ error: Error, _ expected: FileError, sourceLocation: SourceLocation = #_sourceLocation) { - guard let fileError = error as? FileError else { - Issue.record("Error is not a FileError: \(error)", sourceLocation: sourceLocation) - return - } - #expect( - fileError == expected, - "Error doesn't match: expected '\(expected) found '\(error)'", - sourceLocation: sourceLocation - ) - } - - /** - Create the test setup and then stop execution - */ - func assertOnlySetup( - sourceLocation _: SourceLocation = #_sourceLocation - ) throws { - #if TEST_ONLY_SETUP - throw TestError.onlySetup - #endif - } -} diff --git a/Tests/Sources/BaseTests/BaseTests.swift b/Tests/Sources/BaseTests/BaseTests.swift deleted file mode 100644 index 5cd5061..0000000 --- a/Tests/Sources/BaseTests/BaseTests.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// BaseTests.swift -// VisualDiffer -// -// Created by davide ficano on 26/02/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -enum TestError: Error { - case onlySetup -} - -open class BaseTests { - public var rootDir: URL - public var dateBuilder = DateBuilder() - public var className: String - // swiftlint:disable:next line_length - public let defaultPredicate = NSPredicate(format: "fileName == \".DS_Store\" OR fileName LIKE \"CVS\" OR fileName LIKE \".svn\" OR fileName LIKE \".git\" OR fileName LIKE \".hg\" OR fileName LIKE \".bzr\" OR fileName LIKE \"*~\" OR fileName ENDSWITH \".zip\" OR fileName ENDSWITH \".gz\" OR fileName ENDSWITH \".tgz\" OR fileName ENDSWITH \".tar\"") - public let fm = FileManager.default - - public init(rootDir: URL) { - className = String(describing: Self.self) - self.rootDir = rootDir.appending(path: className, directoryHint: .isDirectory) - } - - public convenience init() { - self.init(rootDir: URL - .desktopDirectory - .appending(path: "visualdiffer/test_suite_swift/", directoryHint: .isDirectory)) - } - - public func buildDate(_ strDate: String) throws -> Date { - try dateBuilder.isoDate(strDate) - } -} diff --git a/Tests/Sources/BaseTests/DateBuilder.swift b/Tests/Sources/BaseTests/DateBuilder.swift deleted file mode 100644 index e90af61..0000000 --- a/Tests/Sources/BaseTests/DateBuilder.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// DateBuilder.swift -// VisualDiffer -// -// Created by davide ficano on 25/02/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -public struct DateBuilder { - let isoDateFormatter: DateFormatter - - public init() { - isoDateFormatter = DateFormatter() - isoDateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss z" - isoDateFormatter.locale = Locale.current - isoDateFormatter.timeZone = TimeZone.current - isoDateFormatter.formatterBehavior = .default - } - - public func isoDate(_ strDate: String) throws -> Date { - guard let date = isoDateFormatter.date(from: strDate) else { - throw NSError(domain: "Unable to parse date", code: 0, userInfo: nil) - } - return date - } -} diff --git a/Tests/Sources/FileCompare/DiffResult/DiffResultBaseTests.swift b/Tests/Sources/FileCompare/DiffResult/DiffResultBaseTests.swift deleted file mode 100644 index 01bde25..0000000 --- a/Tests/Sources/FileCompare/DiffResult/DiffResultBaseTests.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// DiffResultBaseTests.swift -// VisualDiffer -// -// Created by davide ficano on 10/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -class DiffResultBaseTests: BaseTests { - func assert( - sectionSeparators lines: [DiffLine], - separatorIndexes: [Int], - sourceLocation: SourceLocation = #_sourceLocation - ) { - for (index, line) in lines.enumerated() { - #expect(line.isSectionSeparator == separatorIndexes.contains(index), sourceLocation: sourceLocation) - } - } - - func assert( - sectionSeparators lines: [DiffLine], - isSeparator: Bool, - sourceLocation: SourceLocation = #_sourceLocation - ) { - for line in lines { - #expect(line.isSectionSeparator == isSeparator, sourceLocation: sourceLocation) - } - } - - func assert( - lines: [DiffLine], - expectedValue: [DiffChangeType], - sourceLocation: SourceLocation = #_sourceLocation - ) { - #expect(lines.count == expectedValue.count, sourceLocation: sourceLocation) - - for (index, line) in lines.enumerated() { - #expect(line.type == expectedValue[index], sourceLocation: sourceLocation) - } - } - - func assert( - linesMode lines: [DiffLine], - modes: [DiffLine.DisplayMode], - sourceLocation: SourceLocation = #_sourceLocation - ) { - #expect(lines.count == modes.count, sourceLocation: sourceLocation) - - for (index, line) in lines.enumerated() { - #expect(line.mode == modes[index], sourceLocation: sourceLocation) - } - } -} diff --git a/Tests/Sources/FileCompare/DiffResult/Tests/DiffResultTests.swift b/Tests/Sources/FileCompare/DiffResult/Tests/DiffResultTests.swift deleted file mode 100644 index d25a507..0000000 --- a/Tests/Sources/FileCompare/DiffResult/Tests/DiffResultTests.swift +++ /dev/null @@ -1,564 +0,0 @@ -// -// DiffResultTests.swift -// VisualDiffer -// -// Created by davide ficano on 08/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length -final class DiffResultTests: DiffResultBaseTests { - @Test func diffLine() throws { - let leftText = "line1\n\nline2\nline3" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 1, changed: 1)) - - assertArrayCount(diffResult.sections, 2) - #expect(diffResult.sections[0] == DiffSection(start: 1, end: 1)) - #expect(diffResult.sections[1] == DiffSection(start: 3, end: 3)) - } - - @Test func findNextSection() throws { - let leftText = "line1\n\nline2\nline3" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - var didWrap = false - // first move - if let section = diffResult.findNextSection(by: -1, wrapAround: true, didWrap: &didWrap) { - #expect(section === diffResult.sections[0]) - #expect(didWrap == false) - } else { - Issue.record("No section found") - } - - // second move - if let section = diffResult.findNextSection(by: 2, wrapAround: true, didWrap: &didWrap) { - #expect(section === diffResult.sections[1]) - #expect(didWrap == false) - } else { - Issue.record("No section found") - } - - // third move - if let section = diffResult.findNextSection(by: 3, wrapAround: true, didWrap: &didWrap) { - #expect(section === diffResult.sections[0]) - #expect(didWrap == true) - } else { - Issue.record("No section found") - } - } - - @Test func findPrevSection() throws { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - var didWrap = false - // first move - if let section = diffResult.findPrevSection(by: 5, wrapAround: true, didWrap: &didWrap) { - #expect(section === diffResult.sections[1]) - #expect(didWrap == false) - } else { - Issue.record("No section found") - } - - // second move - if let section = diffResult.findPrevSection(by: 3, wrapAround: true, didWrap: &didWrap) { - #expect(section === diffResult.sections[0]) - #expect(didWrap == false) - } else { - Issue.record("No section found") - } - - // third move - if let section = diffResult.findPrevSection(by: 1, wrapAround: true, didWrap: &didWrap) { - #expect(section === diffResult.sections[2]) - #expect(didWrap == true) - } else { - Issue.record("No section found") - } - } - - @Test func findSectionRange() throws { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - guard let indexes = diffResult.findSectionIndexSet(with: 6) else { - Issue.record("No indexes found") - return - } - #expect(indexes == IndexSet(integersIn: 4 ..< 8)) - } - - @Test func findAdjacentSections() throws { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - guard let indexes = diffResult.findAdjacentSections(from: 5) else { - Issue.record("No indexes found") - return - } - - #expect(indexes == IndexSet(integersIn: 3 ..< 8)) - } - - @Test func jumpToLine() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.leftSide.findLineIndex(by: 5) == 4) - #expect(diffResult.rightSide.findLineIndex(by: 5) == nil) - } - - @Test func justDifferentLines() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - let filtered = DiffResult.justDifferentLines(diffResult) - - #expect(filtered.summary == DiffSummary()) - - assertArrayCount(filtered.leftSide.lines, 6) - assertArrayCount(filtered.rightSide.lines, 6) - - assert(sectionSeparators: filtered.leftSide.lines, separatorIndexes: [0, 1, 5]) - assert(sectionSeparators: filtered.rightSide.lines, separatorIndexes: [0, 1]) - - assertArrayCount(filtered.sections, 3) - #expect(filtered.sections[0] == DiffSection(start: 0, end: 0)) - #expect(filtered.sections[1] == DiffSection(start: 1, end: 1)) - #expect(filtered.sections[2] == DiffSection(start: 2, end: 5)) - } - - @Test func justMatchingLines() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - let filtered = DiffResult.justMatchingLines(diffResult) - - #expect(filtered.summary == DiffSummary()) - - assertArrayCount(filtered.leftSide.lines, 2) - assertArrayCount(filtered.rightSide.lines, 2) - - assert(sectionSeparators: filtered.leftSide.lines, separatorIndexes: [0, 1]) - assert(sectionSeparators: filtered.rightSide.lines, separatorIndexes: [0, 1]) - } - - @Test func copyLines() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - let rowsToCopy = IndexSet([3, 6]) - - DiffResult.copyLines( - all: diffResult, - current: diffResult, - rows: rowsToCopy, - source: .left, - visibility: .all - ) - - let leftChangeTypeCopied: [DiffChangeType] = [.matching, .deleted, .matching, .matching, .deleted, .deleted, .matching, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeTypeCopied) - let rightChangeTypeCopied: [DiffChangeType] = [.matching, .missing, .matching, .matching, .missing, .missing, .matching, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeTypeCopied) - - let copiedModes: [DiffLine.DisplayMode] = [.normal, .normal, .normal, .merged, .normal, .normal, .merged, .normal] - assert(linesMode: diffResult.rightSide.lines, modes: copiedModes) - - let linesStatus = diffResult.rightSide.lines - let lineNumbers = [1, -1, 2, 3, -1, -1, 4, -1] - - for (index, line) in linesStatus.enumerated() { - #expect(line.number == lineNumbers[index]) - } - } - - @Test func copyLinesDifferences() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - let rowsToCopy = IndexSet([1, 4]) - - let filteredDiffResult = DiffResult.justDifferentLines(diffResult) - - DiffResult.copyLines( - all: diffResult, - current: filteredDiffResult, - rows: rowsToCopy, - source: .right, - visibility: .differences - ) - - let leftChangeTypeCopied: [DiffChangeType] = [.deleted, .deleted, .deleted, .deleted] - assert(lines: filteredDiffResult.leftSide.lines, expectedValue: leftChangeTypeCopied) - let rightChangeTypeCopied: [DiffChangeType] = [.missing, .missing, .missing, .missing] - assert(lines: filteredDiffResult.rightSide.lines, expectedValue: rightChangeTypeCopied) - - let copiedMode: [DiffLine.DisplayMode] = Array(repeating: .normal, count: 7) - assert(linesMode: diffResult.rightSide.lines, modes: copiedMode) - - let linesStatus = filteredDiffResult.leftSide.lines - let lineNumbers = [2, 5, 6, 7] - - for (index, line) in linesStatus.enumerated() { - #expect(line.number == lineNumbers[index]) - } - } - - @Test func deleteLines() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - let rowsToDelete = IndexSet([3, 5, 6, 7]) - - DiffResult.deleteLines( - all: diffResult, - current: diffResult, - rows: rowsToDelete, - side: .left, - visibility: .all - ) - - let leftChangeTypeDeleted: [DiffChangeType] = [.matching, .deleted, .matching, .missing, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeTypeDeleted) - let rightChangeTypeDeleted: [DiffChangeType] = [.matching, .missing, .matching, .added, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeTypeDeleted) - - let linesStatus = diffResult.leftSide.lines - let lineNumbers = [1, 2, 3, -1, 4] - - for (index, line) in linesStatus.enumerated() { - #expect(line.number == lineNumbers[index]) - } - } - - @Test func deleteLinesDifferences() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - let rowsToDelete = IndexSet([2, 3, 5]) - - let filteredDiffResult = DiffResult.justDifferentLines(diffResult) - - DiffResult.deleteLines( - all: diffResult, - current: filteredDiffResult, - rows: rowsToDelete, - side: .right, - visibility: .differences - ) - - let leftChangeTypeDeleted: [DiffChangeType] = [.deleted, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: filteredDiffResult.leftSide.lines, expectedValue: leftChangeTypeDeleted) - let rightChangeTypeDeleted: [DiffChangeType] = [.missing, .changed, .missing, .missing, .missing, .missing] - assert(lines: filteredDiffResult.rightSide.lines, expectedValue: rightChangeTypeDeleted) - - let linesStatus = filteredDiffResult.rightSide.lines - let lineNumbers = [-1, 3, -1, -1, -1, -1] - - for (index, line) in linesStatus.enumerated() { - #expect(line.number == lineNumbers[index]) - } - } - - @Test func deleteLinesMatches() { - let leftText = "line1\n\nline2\nline3\nline5\nline7\nline8\nline9" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .changed, .deleted, .deleted, .deleted, .deleted] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .changed, .missing, .missing, .missing, .missing] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 5, changed: 1)) - - let rowsToDelete = IndexSet([0]) - - let filteredDiffResult = DiffResult.justMatchingLines(diffResult) - - DiffResult.deleteLines( - all: diffResult, - current: filteredDiffResult, - rows: rowsToDelete, - side: .right, - visibility: .matches - ) - - let leftChangeTypeDeleted: [DiffChangeType] = [.matching] - assert(lines: filteredDiffResult.leftSide.lines, expectedValue: leftChangeTypeDeleted) - let rightChangeTypeDeleted: [DiffChangeType] = [.matching] - assert(lines: filteredDiffResult.rightSide.lines, expectedValue: rightChangeTypeDeleted) - - let linesStatus = filteredDiffResult.rightSide.lines - let lineNumbers = [1] - - for (index, line) in linesStatus.enumerated() { - #expect(line.number == lineNumbers[index]) - } - } - - @Test func insertLinesLeftDestination() throws { - let leftText = "line1\n\nline2\nline3" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let newLines = "line2.1\nline2.2\n" - diffResult.insert(text: newLines, at: 3, side: .left) - - let leftChangeType: [DiffChangeType] = [.matching, .deleted, .matching, .deleted, .deleted, .changed] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .missing, .matching, .missing, .missing, .changed] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 1, changed: 1)) - - assertArrayCount(diffResult.sections, 2) - #expect(diffResult.sections[0] == DiffSection(start: 1, end: 1)) - #expect(diffResult.sections[1] == DiffSection(start: 3, end: 3)) - - let expectedLeftLines = [ - (1, "line1"), - (2, ""), - (3, "line2"), - (4, "line2.1"), - (5, "line2.2"), - (6, "line3"), - ] - - let expectedRightLines = [ - (1, "line1"), - (-1, ""), - (2, "line2"), - (-1, ""), - (-1, ""), - (3, "line4"), - ] - - for (index, (leftNumber, leftLine)) in expectedLeftLines.enumerated() { - let (rightNumber, rightLine) = expectedRightLines[index] - let diffLeftLine = diffResult.leftSide.lines[index] - let diffRightLine = diffResult.rightSide.lines[index] - - #expect(leftNumber == diffLeftLine.number) - #expect(leftLine == diffLeftLine.text) - - #expect(rightNumber == diffRightLine.number) - #expect(rightLine == diffRightLine.text) - } - } - - @Test func insertLinesRightDestination() throws { - let leftText = "line1\n\nline2\nline3" - let rightText = "line1\nline2\nline4" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText) - - let newLines = "line1.1\nline1.2\n" - diffResult.insert(text: newLines, at: 1, side: .right) - - let leftChangeType: [DiffChangeType] = [.matching, .changed, .missing, .matching, .changed] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .changed, .added, .matching, .changed] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 1, changed: 1)) - - assertArrayCount(diffResult.sections, 2) - #expect(diffResult.sections[0] == DiffSection(start: 1, end: 1)) - #expect(diffResult.sections[1] == DiffSection(start: 3, end: 3)) - - let expectedLeftLines = [ - (1, "line1"), - (2, ""), - (-1, ""), - (3, "line2"), - (4, "line3"), - ] - - let expectedRightLines = [ - (1, "line1"), - (2, "line1.1"), - (3, "line1.2"), - (4, "line2"), - (5, "line4"), - ] - - for (index, (leftNumber, leftLine)) in expectedLeftLines.enumerated() { - let (rightNumber, rightLine) = expectedRightLines[index] - let diffLeftLine = diffResult.leftSide.lines[index] - let diffRightLine = diffResult.rightSide.lines[index] - - #expect(leftNumber == diffLeftLine.number) - #expect(leftLine == diffLeftLine.text) - - #expect(rightNumber == diffRightLine.number) - #expect(rightLine == diffRightLine.text) - } - } -} - -// swiftlint:enable file_length diff --git a/Tests/Sources/FileCompare/DiffResult/Tests/EndOfLineTests.swift b/Tests/Sources/FileCompare/DiffResult/Tests/EndOfLineTests.swift deleted file mode 100644 index 23bfecb..0000000 --- a/Tests/Sources/FileCompare/DiffResult/Tests/EndOfLineTests.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// EndOfLineTests.swift -// VisualDiffer -// -// Created by davide ficano on 11/10/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -final class EndOfLineTests: DiffResultBaseTests { - @Test func endOfLine() { - let eolCarriageMixed = DiffLineComponent.splitLines("line1\rline2\nline3").detectEOL() - let eolUnix = DiffLineComponent.splitLines("line1\nline2\nline3").detectEOL() - let eolCRLFMixed = DiffLineComponent.splitLines("line1\r\nline2\nline3").detectEOL() - let eolCR = DiffLineComponent.splitLines("line1\rline2\rline3").detectEOL() - let eolCRLF = DiffLineComponent.splitLines("line1\r\nline2\r\nline3").detectEOL() - let eolLineWithoutEOL = DiffLineComponent.splitLines("long line").detectEOL() - - #expect(eolCarriageMixed == .mixed) - #expect(eolUnix == .unix) - #expect(eolCRLFMixed == .mixed) - #expect(eolCRLF == .pcCRLF) - #expect(eolCR == .pcCR) - #expect(eolLineWithoutEOL == .missing) - } -} diff --git a/Tests/Sources/FileCompare/DiffResult/Tests/WhitespacesTests.swift b/Tests/Sources/FileCompare/DiffResult/Tests/WhitespacesTests.swift deleted file mode 100644 index f0b3d44..0000000 --- a/Tests/Sources/FileCompare/DiffResult/Tests/WhitespacesTests.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// WhitespacesTests.swift -// VisualDiffer -// -// Created by davide ficano on 01/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -final class WhitespacesTests: DiffResultBaseTests { - @Test func ignoreLeadingWhitespaces() { - let leftText = - " leading spaces\n" + - " another line" - let rightText = - " leading spaces\n" + - "another line" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText, options: .ignoreLeadingWhitespaces) - - let leftChangeType: [DiffChangeType] = [.matching, .matching] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .matching] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 0, changed: 0)) - - assertArrayCount(diffResult.sections, 0) - } - - @Test func ignoreInternalWhitespaces() { - let leftText = - " line1\n" + - "another text\t\ttabs and spaces" - let rightText = - "line1\n" + - "another text tabs and spaces" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText, options: .ignoreInternalWhitespaces) - - let leftChangeType: [DiffChangeType] = [.changed, .matching] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.changed, .matching] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 1, added: 0, deleted: 0, changed: 1)) - - assertArrayCount(diffResult.sections, 1) - } - - @Test func ignoreAllWhitespaces() { - let leftText = - " line1\n" + - "another text\t\ttabs and spaces\r\n" + - "last line " - let rightText = - "line1\n" + - "another text tabs and spaces\n" + - " last line" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText, options: [.allIgnoreWhitespaces, .ignoreLineEndings]) - - let leftChangeType: [DiffChangeType] = [.matching, .matching, .matching] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .matching, .matching] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 3, added: 0, deleted: 0, changed: 0)) - - assertArrayCount(diffResult.sections, 0) - } - - @Test("Ignore all whitespaces differences but compare EOLs") func ignoreAllWhitespacesThenEol() { - let leftText = - " line1\n" + - "another text\t\ttabs and spaces\r\n" + - "last line " - let rightText = - "line1\n" + - "another text tabs and spaces\n" + - " last line" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText, options: [.allIgnoreWhitespaces]) - - let leftChangeType: [DiffChangeType] = [.matching, .changed, .matching] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .changed, .matching] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 0, changed: 1)) - - assertArrayCount(diffResult.sections, 1) - } - - @Test func ignoreCharacterCase() { - let leftText = - "LINE ONE\n" + - "Another Line" - let rightText = - "line One\n" + - "another line" - - let diffResult = DiffResult() - diffResult.diff(leftText: leftText, rightText: rightText, options: .ignoreCharacterCase) - - let leftChangeType: [DiffChangeType] = [.matching, .matching] - assert(lines: diffResult.leftSide.lines, expectedValue: leftChangeType) - let rightChangeType: [DiffChangeType] = [.matching, .matching] - assert(lines: diffResult.rightSide.lines, expectedValue: rightChangeType) - - assert(sectionSeparators: diffResult.leftSide.lines, isSeparator: false) - assert(sectionSeparators: diffResult.rightSide.lines, isSeparator: false) - - #expect(diffResult.summary == DiffSummary(matching: 2, added: 0, deleted: 0, changed: 0)) - - assertArrayCount(diffResult.sections, 0) - } -} diff --git a/Tests/Sources/FileSystem/BaseTests+AssertFileSystem.swift b/Tests/Sources/FileSystem/BaseTests+AssertFileSystem.swift deleted file mode 100644 index c5ebf21..0000000 --- a/Tests/Sources/FileSystem/BaseTests+AssertFileSystem.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// BaseTests+AssertFileSystem.swift -// VisualDiffer -// -// Created by davide ficano on 28/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping -public extension BaseTests { - func assertSymlink( - _ item: CompareItem, - _ destPath: String, - _ isFolder: Bool, - functionName: String = #function, - sourceLocation: SourceLocation = #_sourceLocation - ) throws { - guard let url = item.toUrl() else { - try #require(item.path != nil, "Unable to find path for \(item)", sourceLocation: sourceLocation) - return - } - do { - let resolved = try FileManager.default.destinationOfSymbolicLink(atPath: url.osPath) - let real = URL(filePath: resolved, directoryHint: item.isFolder ? .isDirectory : .notDirectory) - let destUrl = appendFolder(destPath, functionName: functionName) - #expect(destUrl == real, "symlink dest doesn't match: expected \(destUrl) found \(real)", sourceLocation: sourceLocation) - #expect(item.isSymbolicLink, "\(url) must be a symlink", sourceLocation: sourceLocation) - if isFolder { - #expect(item.isFolder, "\(url) must be a folder", sourceLocation: sourceLocation) - } else { - #expect(item.isFile, "\(url) must be a file", sourceLocation: sourceLocation) - } - } catch { - Issue.record(error, sourceLocation: sourceLocation) - } - } - - func assertTimestamps( - _ item: CompareItem?, - _ strCreateDate: String?, - _ strModDate: String?, - sourceLocation: SourceLocation = #_sourceLocation - ) throws { - guard let item else { - try #require(item != nil, "fs is nil", sourceLocation: sourceLocation) - return - } - guard let fsPath = item.path else { - try #require(item.path != nil, "Unable to find path for \(item)", sourceLocation: sourceLocation) - return - } - do { - let attrs = try FileManager.default.attributesOfItem(atPath: fsPath) - - if let strCreateDate { - if let creationDate = attrs[.creationDate] as? Date { - let isDateEqual = try buildDate(strCreateDate) == creationDate - #expect(isDateEqual, "Expected creation date for \(item.fileName!) is \(strCreateDate) but found \(creationDate)", sourceLocation: sourceLocation) - } else { - Issue.record("Unable to get file creation date for \(fsPath)", sourceLocation: sourceLocation) - } - } - - if let strModDate { - if let modificationDate = attrs[.modificationDate] as? Date { - let isDateEqual = try buildDate(strModDate) == modificationDate - #expect(isDateEqual, "Expected modification date for \(item.fileName!) is \(strModDate) but found \(modificationDate)", sourceLocation: sourceLocation) - } else { - Issue.record("Unable to get file modification date for \(fsPath)", sourceLocation: sourceLocation) - } - } - } catch { - Issue.record(error) - } - } - - func assertMismatchingTags(_ item: CompareItem?, _ oldValue: Int, _ fileName: String, sourceLocation: SourceLocation = #_sourceLocation) { - guard let item else { - Issue.record("CompareItem is nil", sourceLocation: sourceLocation) - return - } - #expect(item.mismatchingTags == oldValue, "Tags for '\(fileName)' expected \(oldValue) found \(item.mismatchingTags)", sourceLocation: sourceLocation) - } - - func assertFolderTags(_ item: CompareItem?, _ value: Bool, _ fileName: String, sourceLocation: SourceLocation = #_sourceLocation) { - guard let item else { - Issue.record("CompareItem is nil", sourceLocation: sourceLocation) - return - } - #expect(item.summary.hasMetadataTags == value, "Folder '\(fileName)' tags must be \(value)", sourceLocation: sourceLocation) - } - - func assertMismatchingLabels(_ item: CompareItem?, _ oldValue: Int, _ fileName: String? = "", sourceLocation: SourceLocation = #_sourceLocation) { - guard let item else { - Issue.record("CompareItem is nil", sourceLocation: sourceLocation) - return - } - #expect(item.mismatchingLabels == oldValue, "Labels for '\(fileName!)' expected \(oldValue) found \(item.mismatchingLabels)", sourceLocation: sourceLocation) - } - - func assertFolderLabels(_ item: CompareItem?, _ value: Bool, _ fileName: String? = "", sourceLocation: SourceLocation = #_sourceLocation) { - guard let item else { - Issue.record("CompareItem is nil", sourceLocation: sourceLocation) - return - } - #expect(item.summary.hasMetadataLabels == value, "Folder '\(fileName!)' labels must be \(value)", sourceLocation: sourceLocation) - } - - func assertResourceFileLabels(_ item: CompareItem?, _ expectedValue: Int, _ path: URL, sourceLocation: SourceLocation = #_sourceLocation) { - guard let item else { - Issue.record("CompareItem is nil", sourceLocation: sourceLocation) - return - } - do { - let foundValue = try getLabelNumber(item.toUrl()!) - #expect(foundValue == expectedValue, "Label for '\(path)' expected \(expectedValue) found \(foundValue)", sourceLocation: sourceLocation) - } catch { - Issue.record("Found error \(error)", sourceLocation: sourceLocation) - } - } -} - -// swiftlint:enable force_unwrapping diff --git a/Tests/Sources/FileSystem/BaseTests+FileSystemOperations.swift b/Tests/Sources/FileSystem/BaseTests+FileSystemOperations.swift deleted file mode 100644 index d342626..0000000 --- a/Tests/Sources/FileSystem/BaseTests+FileSystemOperations.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// BaseTests+FileSystemOperations.swift -// VisualDiffer -// -// Created by davide ficano on 25/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -public extension BaseTests { - func appendFolder(_ path: String, _ isFolder: Bool = true, functionName: String = #function) -> URL { - rootDir - .appending(path: functionName.trimmingCharacters(in: CharacterSet(charactersIn: "()")), directoryHint: .isDirectory) - .appending(path: path, directoryHint: isFolder ? .isDirectory : .notDirectory) - } - - internal func createFolder(_ path: String, functionName: String = #function) throws { - try fm.createDirectory( - at: appendFolder(path, functionName: functionName), - withIntermediateDirectories: true, - attributes: nil - ) - } - - internal func createFile(_ path: String, _ content: String, functionName: String = #function) throws { - try content.write( - to: appendFolder(path, functionName: functionName), - atomically: true, - encoding: .utf8 - ) - } - - internal func removeItem(_ path: String, ignoreError: Bool = true, functionName: String = #function) throws { - if ignoreError { - try? fm.removeItem(at: appendFolder(path, functionName: functionName)) - } else { - try fm.removeItem(at: appendFolder(path, functionName: functionName)) - } - } - - func setFileTimestamp(_ path: String, _ dateString: String, isFolder: Bool = true, functionName: String = #function) throws { - try fm.setAttributes( - [.modificationDate: dateBuilder.isoDate(dateString)], - ofItemAtPath: appendFolder(path, isFolder, functionName: functionName).osPath - ) - } - - func createDataFile(_ path: String, _ bytes: [UInt8], functionName: String = #function) throws { - try Data(bytes: bytes, count: bytes.count).write(to: appendFolder(path, functionName: functionName)) - } - - func setFileCreationTime(_ path: String, _ dateString: String, isFolder: Bool = true, functionName: String = #function) throws { - try fm.setAttributes( - [.creationDate: dateBuilder.isoDate(dateString)], - ofItemAtPath: appendFolder(path, isFolder, functionName: functionName).osPath - ) - } - - func createSymlink(_ path: String, _ destPath: String, functionName: String = #function) throws { - if destPath.starts(with: "..") { - // URL create absolute paths so we use the string version for relative paths - try fm.createSymbolicLink( - atPath: appendFolder(path, functionName: functionName).osPath, - withDestinationPath: destPath - ) - } else { - try fm.createSymbolicLink( - at: appendFolder(path, functionName: functionName), - withDestinationURL: appendFolder(destPath, functionName: functionName) - ) - } - } - - func add( - tags: [String], - fullPath url: URL - ) throws { - // Apple removed from Swift the ability to change tags/labels - // The workaround consists to continue to use the legacy NSURL - // https://developer.apple.com/forums/thread/703028 - try (url as NSURL).setResourceValue(tags, forKey: .tagNamesKey) - } - - func add( - labelNumber: Int, - fullPath url: URL - ) throws { - // Apple removed from Swift the ability to change tags/labels - // The workaround consists to continue to use the legacy NSURL - // https://developer.apple.com/forums/thread/703028 - try (url as NSURL).setResourceValue(NSNumber(value: labelNumber), forKey: .labelNumberKey) - } - - func getLabelNumber(_ url: URL) throws -> Int { - let resources = try url.resourceValues(forKeys: [.labelNumberKey]) - if let labelNumber = resources.labelNumber { - return labelNumber - } - return 0 - } -} diff --git a/Tests/Sources/FileSystem/CaseSensitiveBaseTest.swift b/Tests/Sources/FileSystem/CaseSensitiveBaseTest.swift deleted file mode 100644 index d184b07..0000000 --- a/Tests/Sources/FileSystem/CaseSensitiveBaseTest.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// CaseSensitiveBaseTest.swift -// VisualDiffer -// -// Created by davide ficano on 17/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation -import Testing - -open class CaseSensitiveBaseTest: BaseTests { - static let serialQueue = DispatchQueue(label: "com.visualdiffer.volume.testQueue") - - let volumeName = "VDTestsMatchCase_Swift" - let volumePath: URL - - override public init(rootDir _: URL) { - volumePath = URL(filePath: "/Volumes/\(volumeName)/") - super.init(rootDir: volumePath) - - mountVolume() - } - - public func mountVolume() { - Self.serialQueue.sync { - // create the case sensitive ram disk if necessary - if !FileManager.default.fileExists(atPath: volumePath.osPath) { - let process = Process() - process.executableURL = URL(filePath: "/bin/sh") - process.arguments = ["-c", "diskutil erasevolume 'Case-sensitive HFS+' '\(volumeName)' `hdiutil attach -nomount ram://1048576`"] - do { - try process.run() - } catch { - Issue.record("Failed to create volume \(volumeName) \(error)") - } - process.waitUntilExit() - } - } - } -} - -extension CaseSensitiveBaseTest { - func assertVolumeMounted(sourceLocation: SourceLocation = #_sourceLocation) throws { - try #require( - fm.fileExists(atPath: rootDir.deletingLastPathComponent().osPath), - "Unable to find the case sensitive disk, test can't be executed", - sourceLocation: sourceLocation - ) - } -} diff --git a/Tests/Sources/FileSystem/Mock/MockFileOperationManagerDelegate.swift b/Tests/Sources/FileSystem/Mock/MockFileOperationManagerDelegate.swift deleted file mode 100644 index f2a28d2..0000000 --- a/Tests/Sources/FileSystem/Mock/MockFileOperationManagerDelegate.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// MockFileOperationManagerDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 27/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -@testable import VisualDiffer - -class MockFileOperationManagerDelegate: FileOperationManagerDelegate { - let confirmReplace: ConfirmReplace - var isRunning: Bool - var errors = [any Error]() - - init( - replaceAll: Bool = true, - isRunning: Bool = true - ) { - confirmReplace = ConfirmReplace( - yesToAll: replaceAll, - noToAll: false - ) { _, _ in false } - self.isRunning = isRunning - } - - func waitPause(for _: FileOperationManager) {} - - func isRunning(_: FileOperationManager) -> Bool { - isRunning - } - - func fileManager( - _: FileOperationManager, - canReplaceFromPath fromPath: String, - fromAttrs: [FileAttributeKey: Any]?, - toPath: String, - toAttrs: [FileAttributeKey: Any]? - ) -> Bool { - confirmReplace.canReplace( - fromPath: fromPath, - fromAttrs: fromAttrs, - toPath: toPath, - toAttrs: toAttrs - ) - } - - func fileManager(_: FileOperationManager, initForItem _: CompareItem) {} - - func fileManager(_: FileOperationManager, updateForItem _: CompareItem) {} - - func fileManager(_: FileOperationManager, addError error: any Error, forItem _: CompareItem) { - errors.append(error) - } - - func fileManager(_: FileOperationManager, startBigFileOperationForItem _: CompareItem) {} - - func isBigFileOperationCancelled(_: FileOperationManager) -> Bool { - false - } - - func isBigFileOperationCompleted(_: FileOperationManager) -> Bool { - false - } - - func fileManager(_: FileOperationManager, setCancelled _: Bool) {} - - func fileManager(_: FileOperationManager, setCompleted _: Bool) {} - - func fileManager(_: FileOperationManager, updateBytesCompleted _: Double, totalBytes _: Double, throughput _: Double) {} -} diff --git a/Tests/Sources/FileSystem/Mock/MockFolderReaderDelegate.swift b/Tests/Sources/FileSystem/Mock/MockFolderReaderDelegate.swift deleted file mode 100644 index 41b3d13..0000000 --- a/Tests/Sources/FileSystem/Mock/MockFolderReaderDelegate.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// MockFolderReaderDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 26/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -@testable import VisualDiffer - -class MockFolderReaderDelegate: FolderReaderDelegate { - var isRunning = false - var errors = [Error]() - - init(isRunning: Bool) { - self.isRunning = isRunning - } - - func isRunning(_: VisualDiffer.FolderReader) -> Bool { - isRunning - } - - func progress(_: VisualDiffer.FolderReader, status _: VisualDiffer.FolderReaderStatus) {} - - func folderReader(_: VisualDiffer.FolderReader, handleError error: any Error, forPath _: URL) -> Bool { - errors.append(error) - return true - } -} diff --git a/Tests/Sources/FileSystem/Mock/MockItemComparatorDelegate.swift b/Tests/Sources/FileSystem/Mock/MockItemComparatorDelegate.swift deleted file mode 100644 index 508f973..0000000 --- a/Tests/Sources/FileSystem/Mock/MockItemComparatorDelegate.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// MockItemComparatorDelegate.swift -// VisualDiffer -// -// Created by davide ficano on 26/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -@testable import VisualDiffer - -class MockItemComparatorDelegate: ItemComparatorDelegate { - var isRunning: Bool - - init(isRunning: Bool = true) { - self.isRunning = isRunning - } - - func isRunning(_: ItemComparator) -> Bool { - isRunning - } -} diff --git a/Tests/Sources/FileSystem/Tests/AlignmentTests.swift b/Tests/Sources/FileSystem/Tests/AlignmentTests.swift deleted file mode 100644 index 450e6c8..0000000 --- a/Tests/Sources/FileSystem/Tests/AlignmentTests.swift +++ /dev/null @@ -1,1605 +0,0 @@ -// -// AlignmentTests.swift -// VisualDiffer -// -// Created by davide ficano on 24/07/13. -// Copyright (c) 2013 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class AlignmentTests: CaseSensitiveBaseTest { - @Test func leftMatchCaseRightIgnoreCase() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .timestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/1.txt", "") - try createFile("l/d.TXT", "") - try createFile("r/m.txt", "") - try createFile("l/m.tXt", "") - try createFile("r/m.tXt", "") - try createFile("r/m.Txt", "") - try createFile("l/m.TXT", "") - try createFile("r/m.TXT", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 2, 2, 6, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 2, 6, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "m.Txt", .orphan, 0) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - } - - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 6) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 2, 2, 6, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 2, 6, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "m.Txt", .orphan, 0) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - } - } - - @Test func leftMatchCaseRightIgnoreCase2() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: true, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/0.txt", "") - try createFile("r/1.txt", "") - try createFile("r/d.TXT", "") - try createFile("l/m.txt", "") - try createFile("l/m.tXt", "") - try createFile("r/m.tXt", "") - try createFile("l/m.Txt", "") - try createFile("r/m.TXT", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 2, 2, 6, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 2, 6, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "0.txt", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 6) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 2, 2, 6, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 2, 6, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "0.txt", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - } - } - - @Test func leftMatchCaseRightIgnoreCase3() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: true, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("r/1.txt", "") - try createFile("r/d.TXT", "") - try createFile("l/m.txt", "") - try createFile("l/m.tXt", "") - try createFile("l/m.Txt", "") - try createFile("l/m.TXT", "") - try createFile("r/m.TXT", "") - try createFile("r/n.tXt", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 3, 1, 7, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 3, 1, 7, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 1, 0, 0, "m.tXt", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 0, 1, 0, 0, "m.Txt", .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - - let child8 = child1.children[6] // l <-> r - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "n.tXt", .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 7) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 3, 1, 7, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 3, 1, 7, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 1, 0, 0, "m.tXt", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 0, 1, 0, 0, "m.Txt", .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - - let childVI8 = childVI1.children[6] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "n.tXt", .orphan, 0) - } - } - - @Test func bothIgnoreCase() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/10.jpg", "") - try createFile("r/10.Jpg", "") - try createFile("l/20.jpg", "") - try createFile("r/20.JPG", "") - try createFile("l/debug.jpg", "") - try createFile("l/Help.jpg", "") - try createFile("r/help.jpg", "") - try createFile("l/sea.txt", "") - try createFile("r/sea.TXT", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 1, 4, 5, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 4, 5, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 0, "10.jpg", .same, 0) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "10.Jpg", .same, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 1, 0, "20.jpg", .same, 0) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "20.JPG", .same, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "debug.jpg", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 0, 1, 0, "Help.jpg", .same, 0) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "help.jpg", .same, 0) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "sea.txt", .same, 0) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "sea.TXT", .same, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 5) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 1, 4, 5, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 4, 5, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 0, "10.jpg", .same, 0) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "10.Jpg", .same, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 1, 0, "20.jpg", .same, 0) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "20.JPG", .same, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "debug.jpg", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 0, 1, 0, "Help.jpg", .same, 0) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "help.jpg", .same, 0) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "sea.txt", .same, 0) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "sea.TXT", .same, 0) - } - } - - @Test func onlyOneOnLeft() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: true - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("r/m.txt", "") - try createFile("l/m.txT", "") - try createFile("r/m.tXt", "") - try createFile("r/m.Txt", "") - try createFile("r/m.TXT", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 1, 4, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 3, 1, 4, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 1, 0, "m.txT", .same, 0) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "m.Txt", .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "m.TXT", .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 4) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 1, 4, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 3, 1, 4, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 1, 0, "m.txT", .same, 0) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "m.Txt", .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "m.TXT", .orphan, 0) - } - } - - @Test func createLeftOrphans() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: true - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/1.txt", "") - try createFile("l/d.TXT", "") - try createFile("l/m.txt", "") - try createFile("l/m.tXt", "") - try createFile("l/m.Txt", "") - try createFile("r/m.Txt", "") - try createFile("l/m.TXT", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 5, 1, 6, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 1, 6, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 1, 0, 0, "m.tXt", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 1, 0, 0, "m.TXT", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 6) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 5, 1, 6, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 1, 6, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 1, 0, 0, "m.tXt", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 1, 0, 0, "m.TXT", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func folders() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: true - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/m.TxT") - try createFolder("r/m.TxT") - try createFolder("l/M.TxT") - try createFolder("r/M.TxT") - - // create files - try createFile("l/m.TxT/hello.txt", "") - try setFileTimestamp("l/m.TxT/hello.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/m.TxT/hello.txt", "1") - try createFile("r/m.TxT/Hello.txt", "12") - try createFile("l/M.TxT/hello.txt", "") - try createFile("l/M.TxT/Hello.txt", "123") - try setFileTimestamp("l/M.TxT/Hello.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/M.TxT/Hello.txt", "1234") - try createFile("l/0.txt", "") - try createFile("r/1.txt", "") - try createFile("r/d.TXT", "") - try createFile("l/m.txt", "") - try createFile("l/m.tXt", "") - try createFile("r/m.tXt", "") - try createFile("l/m.Txt", "") - try createFile("r/m.TXT", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 2, 0, 3, 2, 8, "l", .orphan, 3) - assertItem(child1.linkedItem, 0, 2, 3, 2, 8, "r", .orphan, 7) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 1, 0, 0, 0, 2, "m.TxT", .orphan, 0) - assertItem(child2.linkedItem, 0, 1, 1, 0, 2, "m.TxT", .orphan, 3) - - let child3 = child2.children[0] // m.TxT <-> m.TxT - assertItem(child3, 1, 0, 0, 0, 0, "hello.txt", .old, 0) - assertItem(child3.linkedItem, 0, 1, 0, 0, 0, "hello.txt", .changed, 1) - - let child4 = child2.children[1] // m.TxT <-> m.TxT - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "Hello.txt", .orphan, 2) - - let child5 = child1.children[1] // l <-> r - assertItem(child5, 1, 0, 1, 0, 2, "M.TxT", .orphan, 3) - assertItem(child5.linkedItem, 0, 1, 0, 0, 2, "M.TxT", .orphan, 4) - - let child6 = child5.children[0] // M.TxT <-> M.TxT - assertItem(child6, 0, 0, 1, 0, 0, "hello.txt", .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child5.children[1] // M.TxT <-> M.TxT - assertItem(child7, 1, 0, 0, 0, 0, "Hello.txt", .old, 3) - assertItem(child7.linkedItem, 0, 1, 0, 0, 0, "Hello.txt", .changed, 4) - - let child8 = child1.children[2] // l <-> r - assertItem(child8, 0, 0, 1, 0, 0, "0.txt", .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child9 = child1.children[3] // l <-> r - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let child10 = child1.children[4] // l <-> r - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let child11 = child1.children[5] // l <-> r - assertItem(child11, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child12 = child1.children[6] // l <-> r - assertItem(child12, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - assertItem(child12.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let child13 = child1.children[7] // l <-> r - assertItem(child13, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child13.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 8) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 2, 0, 3, 2, 8, "l", .orphan, 3) - assertItem(child1.linkedItem, 0, 2, 3, 2, 8, "r", .orphan, 7) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 1, 0, 0, 0, 2, "m.TxT", .orphan, 0) - assertItem(child2.linkedItem, 0, 1, 1, 0, 2, "m.TxT", .orphan, 3) - - let childVI3 = childVI2.children[0] // m.TxT <--> m.TxT - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // m.TxT <-> m.TxT - assertItem(child3, 1, 0, 0, 0, 0, "hello.txt", .old, 0) - assertItem(child3.linkedItem, 0, 1, 0, 0, 0, "hello.txt", .changed, 1) - - let childVI4 = childVI2.children[1] // m.TxT <--> m.TxT - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // m.TxT <-> m.TxT - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "Hello.txt", .orphan, 2) - - let childVI5 = childVI1.children[1] // l <--> r - assertArrayCount(childVI5.children, 2) - let child5 = childVI5.item // l <-> r - assertItem(child5, 1, 0, 1, 0, 2, "M.TxT", .orphan, 3) - assertItem(child5.linkedItem, 0, 1, 0, 0, 2, "M.TxT", .orphan, 4) - - let childVI6 = childVI5.children[0] // M.TxT <--> M.TxT - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // M.TxT <-> M.TxT - assertItem(child6, 0, 0, 1, 0, 0, "hello.txt", .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI7 = childVI5.children[1] // M.TxT <--> M.TxT - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // M.TxT <-> M.TxT - assertItem(child7, 1, 0, 0, 0, 0, "Hello.txt", .old, 3) - assertItem(child7.linkedItem, 0, 1, 0, 0, 0, "Hello.txt", .changed, 4) - - let childVI8 = childVI1.children[2] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 0, 1, 0, 0, "0.txt", .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI9 = childVI1.children[3] // l <--> r - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // l <-> r - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let childVI10 = childVI1.children[4] // l <--> r - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // l <-> r - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let childVI11 = childVI1.children[5] // l <--> r - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // l <-> r - assertItem(child11, 0, 0, 1, 0, 0, "m.txt", .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI12 = childVI1.children[6] // l <--> r - assertArrayCount(childVI12.children, 0) - let child12 = childVI12.item // l <-> r - assertItem(child12, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - assertItem(child12.linkedItem, 0, 0, 0, 1, 0, "m.tXt", .same, 0) - - let childVI13 = childVI1.children[7] // l <--> r - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // l <-> r - assertItem(child13, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child13.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 0) - } - } - - @Test func closestMatch() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: true - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("r/1.txt", "") - try createFile("r/d.TXT", "") - try createFile("l/m.Txt", "") - try createFile("r/m.txt", "") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 1, 3, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 1, 3, "r", .orphan, 0) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "m.txt", .same, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 3) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 1, 3, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 1, 3, "r", .orphan, 0) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "d.TXT", .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 0, 1, 0, "m.Txt", .same, 0) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "m.txt", .same, 0) - } - } - - @Test func copyFolder() throws { - try assertVolumeMounted() - - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: true - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/0.txt", "12") - try createFile("r/1.txt", "") - try createFile("l/m.txt", "123") - try createFile("l/m.tXt", "") - try setFileTimestamp("l/m.tXt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/m.tXt", "1234") - try createFile("l/m.Txt", "12345678901234") - try setFileTimestamp("l/m.Txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/m.TXT", "12345") - try createFile("r/next.txt", "") - - let copyRoot: CompareItem - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 2, 0, 2, 0, 6, "l", .orphan, 19) - assertItem(child1.linkedItem, 0, 2, 2, 0, 6, "r", .orphan, 9) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "0.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 3) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 1, 0, 0, 0, 0, "m.tXt", .old, 0) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "m.tXt", .changed, 4) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 1, 0, 0, 0, 0, "m.Txt", .old, 14) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "m.TXT", .changed, 5) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "next.txt", .orphan, 0) - - copyRoot = child6 - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 6) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 2, 0, 2, 0, 6, "l", .orphan, 19) - assertItem(child1.linkedItem, 0, 2, 2, 0, 6, "r", .orphan, 9) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "0.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 3) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 1, 0, 0, 0, 0, "m.tXt", .old, 0) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "m.tXt", .changed, 4) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 1, 0, 0, 0, 0, "m.Txt", .old, 14) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "m.TXT", .changed, 5) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "next.txt", .orphan, 0) - } - - let fileOperaionDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperaionDelegate - ) - let copyCompareItem = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - copyCompareItem.copy( - srcRoot: copyRoot, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - do { - let child1 = rootL // l <-> r - assertItem(child1, 1, 0, 2, 1, 6, "l", .orphan, 19) - assertItem(child1.linkedItem, 0, 1, 2, 1, 6, "r", .orphan, 18) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "0.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 3) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 1, 0, 0, 0, 0, "m.tXt", .old, 0) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "m.tXt", .changed, 4) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "m.Txt", .same, 14) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 14) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "next.txt", .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 6) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 1, 0, 2, 1, 6, "l", .orphan, 19) - assertItem(child1.linkedItem, 0, 1, 2, 1, 6, "r", .orphan, 18) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 1, 0, 0, "0.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "1.txt", .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 0, 1, 0, 0, "m.txt", .orphan, 3) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 1, 0, 0, 0, 0, "m.tXt", .old, 0) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "m.tXt", .changed, 4) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 0, 0, 1, 0, "m.Txt", .same, 14) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "m.TXT", .same, 14) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "next.txt", .orphan, 0) - } - } - - @Test func regExpr() throws { - try assertVolumeMounted() - - // align both .raw and .png to .jpg - let fileNameAlignments: [AlignRule] = [ - AlignRule( - regExp: AlignRegExp(pattern: "(.*)\\.", options: []), - template: AlignTemplate(pattern: "$1", options: []) - ), - ] - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: true, - isRightCaseSensitive: true, - fileNameAlignments: fileNameAlignments - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/001.jpg", "1") - try setFileTimestamp("l/001.jpg", "2001-03-24 10: 45: 32 +0600") - try createFile("r/001.raw", "12") - try createFile("l/002.jpg", "123") - try createFile("l/003.jpg", "12345") - try createFile("r/003.raw", "1234") - try createFile("l/004.jpg", "1234567") - try createFile("r/004.raw", "1234567") - try createFile("l/005.jpg", "123456789") - try createFile("r/005.raw", "12345678") - try setFileTimestamp("r/005.raw", "2001-03-24 10: 45: 32 +0600") - try createFile("l/006.jpg", "12345678901") - try createFile("l/007.jpg", "1234567890123") - try createFile("r/007.png", "1234") - try setFileTimestamp("r/007.png", "2001-03-24 10: 45: 32 +0600") - try createFile("l/008.jpg", "123456789012345") - try createFile("r/008.raw", "1234567890") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 1, 4, 2, 1, 8, "l", .orphan, 64) - assertItem(child1.linkedItem, 2, 3, 0, 1, 8, "r", .orphan, 35) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 1, 0, 0, 0, 0, "001.jpg", .old, 1) - assertItem(child2.linkedItem, 0, 1, 0, 0, 0, "001.raw", .changed, 2) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 1, 0, 0, "002.jpg", .orphan, 3) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child4 = child1.children[2] // l <-> r - assertItem(child4, 0, 1, 0, 0, 0, "003.jpg", .changed, 5) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "003.raw", .changed, 4) - - let child5 = child1.children[3] // l <-> r - assertItem(child5, 0, 0, 0, 1, 0, "004.jpg", .same, 7) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "004.raw", .same, 7) - - let child6 = child1.children[4] // l <-> r - assertItem(child6, 0, 1, 0, 0, 0, "005.jpg", .changed, 9) - assertItem(child6.linkedItem, 1, 0, 0, 0, 0, "005.raw", .old, 8) - - let child7 = child1.children[5] // l <-> r - assertItem(child7, 0, 0, 1, 0, 0, "006.jpg", .orphan, 11) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child8 = child1.children[6] // l <-> r - assertItem(child8, 0, 1, 0, 0, 0, "007.jpg", .changed, 13) - assertItem(child8.linkedItem, 1, 0, 0, 0, 0, "007.png", .old, 4) - - let child9 = child1.children[7] // l <-> r - assertItem(child9, 0, 1, 0, 0, 0, "008.jpg", .changed, 15) - assertItem(child9.linkedItem, 0, 1, 0, 0, 0, "008.raw", .changed, 10) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 8) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 1, 4, 2, 1, 8, "l", .orphan, 64) - assertItem(child1.linkedItem, 2, 3, 0, 1, 8, "r", .orphan, 35) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 1, 0, 0, 0, 0, "001.jpg", .old, 1) - assertItem(child2.linkedItem, 0, 1, 0, 0, 0, "001.raw", .changed, 2) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 1, 0, 0, "002.jpg", .orphan, 3) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI1.children[2] // l <--> r - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // l <-> r - assertItem(child4, 0, 1, 0, 0, 0, "003.jpg", .changed, 5) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "003.raw", .changed, 4) - - let childVI5 = childVI1.children[3] // l <--> r - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // l <-> r - assertItem(child5, 0, 0, 0, 1, 0, "004.jpg", .same, 7) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "004.raw", .same, 7) - - let childVI6 = childVI1.children[4] // l <--> r - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // l <-> r - assertItem(child6, 0, 1, 0, 0, 0, "005.jpg", .changed, 9) - assertItem(child6.linkedItem, 1, 0, 0, 0, 0, "005.raw", .old, 8) - - let childVI7 = childVI1.children[5] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 1, 0, 0, "006.jpg", .orphan, 11) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI8 = childVI1.children[6] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 1, 0, 0, 0, "007.jpg", .changed, 13) - assertItem(child8.linkedItem, 1, 0, 0, 0, 0, "007.png", .old, 4) - - let childVI9 = childVI1.children[7] // l <--> r - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // l <-> r - assertItem(child9, 0, 1, 0, 0, 0, "008.jpg", .changed, 15) - assertItem(child9.linkedItem, 0, 1, 0, 0, 0, "008.raw", .changed, 10) - } - } - - @Test func regNoMatchButIgnoreCaseMatch() throws { - try assertVolumeMounted() - - // No file matches this rule but they must be aligned by case - let fileNameAlignments: [AlignRule] = [ - AlignRule( - regExp: AlignRegExp(pattern: "(.*)\\.jpg", options: []), - template: AlignTemplate(pattern: "$1.raw", options: []) - ), - ] - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.alignIgnoreCase, .contentTimestamp, .size], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: false, - fileNameAlignments: fileNameAlignments - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/New York.jpg", "123") - try createFile("r/New York.jpg", "123") - try createFile("l/SanDiego.jpg", "1") - try createFile("r/sandiego.jpg", "1") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 4) - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 4) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 0, "New York.jpg", .same, 3) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "New York.jpg", .same, 3) - - let child3 = child1.children[1] // l <-> r - assertItem(child3, 0, 0, 0, 1, 0, "SanDiego.jpg", .same, 1) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "sandiego.jpg", .same, 1) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 4) - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 4) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 0, "New York.jpg", .same, 3) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "New York.jpg", .same, 3) - - let childVI3 = childVI1.children[1] // l <--> r - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // l <-> r - assertItem(child3, 0, 0, 0, 1, 0, "SanDiego.jpg", .same, 1) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "sandiego.jpg", .same, 1) - } - } - - @Test func bigFolderLeftIgnoreRightMatch() throws { - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - srandom(1000) - for _ in 0 ..< 10 { - let len = Int.random(in: 0 ..< 34) + 6 - var str = String(repeating: " ", count: Int(len)) - for l in 0 ..< len { - let index = str.index(str.startIndex, offsetBy: l) - str.replaceSubrange(index ... index, with: String(generateAsciiChar())) - } - for _ in 0 ..< 10 { - let index = Int.random(in: 0 ..< len) - invertCase(&str, index: index) - let direction = Bool.random() ? "l" : "r" - let path = "\(direction)/\(str).txt" - - try createFile(path, "12") - } - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/BufferedInputStreamTests.swift b/Tests/Sources/FileSystem/Tests/BufferedInputStreamTests.swift deleted file mode 100644 index ab35365..0000000 --- a/Tests/Sources/FileSystem/Tests/BufferedInputStreamTests.swift +++ /dev/null @@ -1,204 +0,0 @@ -// -// BufferedInputStreamTests.swift -// VisualDiffer -// -// Created by davide ficano on 17/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping -final class BufferedInputStreamTests { - @Test func minBufferSize() throws { - let lines = [ - "Ciao 👋", - "🍕+🍺=❤️", - "🚀 Go!", - ] - try testLines( - lines: lines, - bufferSize: 1 - ) - try testBisLines( - lines: lines, - bufferSize: 1 - ) - } - - @Test func bigBufferSize() throws { - let lines = [ - "Ciao 👋", - "🍕+🍺=❤️", - "🚀 Go!", - ] - try testLines( - lines: lines, - bufferSize: 100_000 - ) - try testBisLines( - lines: lines, - bufferSize: 100_000 - ) - } - - func testLines( - lines: [String], - bufferSize: Int, - encoding: String.Encoding = .utf8, - sourceLocation: SourceLocation = #_sourceLocation - ) throws { - let inputStrem = InputStream(data: lines.joined(separator: "\n").data(using: encoding)!) - defer { - inputStrem.close() - } - let bis = BufferedInputStream( - stream: inputStrem, - encoding: encoding, - bufferSize: bufferSize - ) - defer { - bis.close() - } - - bis.open() - - var i = 0 - while let line = bis.readLine() { - #expect(line == lines[i], sourceLocation: sourceLocation) - i += 1 - } - } - - func testBisLines( - lines: [String], - bufferSize: Int, - encoding: String.Encoding = .utf8, - sourceLocation: SourceLocation = #_sourceLocation - ) throws { - let inputStrem = InputStream(data: lines.joined(separator: "\n").data(using: encoding)!) - defer { - inputStrem.close() - } - let bis = BufferedInputStream( - stream: inputStrem, - encoding: encoding, - bufferSize: bufferSize - ) - defer { - bis.close() - } - - bis.open() - - var i = 0 - while let line = bis.readLine() { - #expect(line == lines[i], sourceLocation: sourceLocation) - i += 1 - } - } - - @Test func bigFile() throws { - let testBundle = Bundle(for: type(of: self)) - - guard let filePath = testBundle.url(forResource: "big_file_5000_lines", withExtension: "txt") else { - Issue.record("Unable to find resource big_file_5000_lines.txt") - return - } - - let bis = try BufferedInputStream( - url: filePath, - encoding: .utf8, - bufferSize: 500 - ) - defer { - bis.close() - } - - bis.open() - - var lineCount = 0 - while bis.readLine() != nil { - lineCount += 1 - } - - #expect(lineCount == 5000) - } - - #if OLD_OBJC_BUFFERED_INPUT_STREAM - @Test("Test any regression with the original Objc code") func bigFile2() throws { - let testBundle = Bundle(for: type(of: self)) - - guard let filePath = testBundle.url(forResource: "big_file_5000_lines", withExtension: "txt") else { - Issue.record("Unable to find resource big_file_5000_lines.txt") - return - } - - let bisNew = try BufferedInputStream( - url: filePath, - encoding: .utf8, - bufferSize: 500 - ) - let bisOld = TOPBufferedInputStream( - fileAtPath: filePath.osPath, - encoding: String.Encoding.utf8.rawValue, - bufferSize: 500 - ) - defer { - bisNew.close() - bisOld.close() - } - - bisNew.open() - bisOld.open() - - var lineCount = 0 - - while true { - guard let bisNewLine = bisNew.readLine(), - let bisOldLine = bisOld.readLine() else { - break - } - lineCount += 1 - #expect(bisNewLine == bisOldLine) - } - - #expect(lineCount == 5000) - } - #endif - - @Test("Native Swift Data Encoding differs from Objc") func nativeSwiftAsciiEncoding() { - let data = Data( - [ - 0x62, - 0x70, - 0x6C, - 0x69, - 0x73, - 0x74, - 0x30, - 0x30, - 0xD4, - 0x00, - 0x01, - 0x00, - 0x02, - 0x00, - 0x03, - 0x00, - 0x04, - 0x00, - 0x05, - ] - ) - // we can't use the native Swift String(data:encoding) because the results are different - let swiftString = String(data: data, encoding: .ascii) - let nsString = NSString(data: data, encoding: String.Encoding.ascii.rawValue) - - #expect(swiftString == nil) - #expect(nsString != nil) - } -} - -// swiftlint:enable force_unwrapping diff --git a/Tests/Sources/FileSystem/Tests/ComparatorTests.swift b/Tests/Sources/FileSystem/Tests/ComparatorTests.swift deleted file mode 100644 index 07f4979..0000000 --- a/Tests/Sources/FileSystem/Tests/ComparatorTests.swift +++ /dev/null @@ -1,336 +0,0 @@ -// -// ComparatorTests.swift -// VisualDiffer -// -// Created by davide ficano on 06/03/13. -// Copyright (c) 2013 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping -final class ComparatorTests: BaseTests { - @Test() func timestampTolerance() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .timestamp, - delegate: comparatorDelegate, - bufferSize: 8192, - timestampToleranceSeconds: 5 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/sample01.txt", "content") - try createFile("l/sample02.txt", "content") - try createFile("l/sample03.txt", "content") - try createFile("l/sample04.txt", "content") - try createFile("l/sample05.txt", "content") - - try createFile("r/sample01.txt", "content") - try createFile("r/sample02.txt", "content") - try createFile("r/sample03.txt", "content") - try createFile("r/sample04.txt", "content") - try createFile("r/sample05.txt", "content") - - try setFileTimestamp("l/sample01.txt", "2001-03-24 10: 45: 00 +0600") - try setFileTimestamp("l/sample02.txt", "2001-03-24 10: 45: 14 +0600") - try setFileTimestamp("l/sample03.txt", "2001-03-24 10: 45: 30 +0600") - try setFileTimestamp("l/sample04.txt", "2001-03-24 10: 45: 40 +0600") - try setFileTimestamp("l/sample05.txt", "2001-03-24 10: 45: 20 +0600") - - try setFileTimestamp("r/sample01.txt", "2001-03-24 10: 45: 05 +0600") - try setFileTimestamp("r/sample02.txt", "2001-03-24 10: 45: 10 +0600") - try setFileTimestamp("r/sample03.txt", "2001-03-24 10: 45: 36 +0600") - try setFileTimestamp("r/sample04.txt", "2001-03-24 10: 45: 20 +0600") - try setFileTimestamp("r/sample05.txt", "2001-03-24 10: 45: 15 +0600") - - let rootL = folderReader.readFolder( - atPath: appendFolder("l"), - parent: nil, - recursive: false - )! - - let rootR = folderReader.readFolder( - atPath: appendFolder("r"), - parent: nil, - recursive: false - )! - - let expectedResults: [ComparisonResult] = [ - .orderedSame, - .orderedSame, - .orderedAscending, - .orderedDescending, - .orderedSame, - ] - let count = expectedResults.count - - #expect(count == rootL.children.count, "Expected count \(count) found \(rootL.children.count)") - - for i in 0 ..< rootL.children.count { - let l = rootL.children[i] - let r = rootR.children[i] - let result = comparator.compare(l, r) - #expect(result == expectedResults[i], "Result \(result) : Dates tolerance (\(comparator.timestampToleranceSeconds) secs) error \(String(describing: l.fileModificationDate)), \(String(describing: r.fileModificationDate))") - } - } - - @Test func compareAsText() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .asText, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/sample01.txt", "a\nb") - try createFile("r/sample01.txt", "a\r\nb") - - try createFile("l/sample02.txt", "a\nb") - try createFile("r/sample02.txt", "a\n\rb") - - try createFile("l/sample03.txt", "a\n\n\nb") - try createFile("r/sample03.txt", "a\r\n\r\n\r\nb") - - let rootL = folderReader.readFolder( - atPath: appendFolder("l"), - parent: nil, - recursive: false - )! - - let rootR = folderReader.readFolder( - atPath: appendFolder("r"), - parent: nil, - recursive: false - )! - - let expectedResults: [ComparisonResult] = [ - .orderedSame, - .orderedDescending, - .orderedSame, - ] - let count = expectedResults.count - - #expect(count == rootL.children.count, "Expected count \(count) found \(rootL.children.count)") - - for i in 0 ..< count { - let l = rootL.children[i] - let r = rootR.children[i] - let result = comparator.compareContent(l, r, ignoreLineEndingDiff: true) - #expect(result == expectedResults[i], "Result (index \(i)) expected \(expectedResults[i]) found \(result)") - } - } - - @Test func binaryContent() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .content, - delegate: comparatorDelegate, - bufferSize: 13 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l") - try createFolder("r") - - // a and b are identical - let leftBytes: [UInt8] = [ - 0x96, 0xBD, 0x8D, 0xC7, 0xE9, 0xE8, 0x75, 0x18, 0x99, 0xF1, - 0x15, 0x2F, 0x58, 0xCC, 0x8B, 0xB1, 0x50, 0x3F, 0xD1, 0xEF, - 0xC9, 0xF8, 0xCD, 0xE1, 0x90, 0x18, 0x1D, 0x0B, 0x02, 0x8A, - 0x71, 0x0E, 0x49, 0xB9, 0x1B, 0xE3, 0x78, 0x68, 0x9D, 0x97, - ] - var rightBytes: [UInt8] = [ - 0x96, 0xBD, 0x8D, 0xC7, 0xE9, 0xE8, 0x75, 0x18, 0x99, 0xF1, - 0x15, 0x2F, 0x58, 0xCC, 0x8B, 0xB1, 0x50, 0x3F, 0xD1, 0xEF, - 0xC9, 0xF8, 0xCD, 0xE1, 0x90, 0x18, 0x1D, 0x0B, 0x02, 0x8A, - 0x71, 0x0E, 0x49, 0xB9, 0x1B, 0xE3, 0x78, 0x68, 0x9D, 0x97, - ] - - // create files - try createDataFile("l/sample01.txt", leftBytes) - try createDataFile("l/sample02.txt", leftBytes) - try createDataFile("l/sample03.txt", leftBytes) - - // increment last byte - rightBytes[rightBytes.count - 1] = leftBytes[leftBytes.count - 1] - rightBytes[rightBytes.count - 1] += 0x11 - try createDataFile("r/sample01.txt", rightBytes) - - try createDataFile("r/sample02.txt", leftBytes) - - // decrement last byte - rightBytes[rightBytes.count - 1] = leftBytes[leftBytes.count - 1] - rightBytes[rightBytes.count - 1] -= 0x11 - try createDataFile("r/sample03.txt", rightBytes) - - let rootL = folderReader.readFolder( - atPath: appendFolder("l"), - parent: nil, - recursive: false - )! - - let rootR = folderReader.readFolder( - atPath: appendFolder("r"), - parent: nil, - recursive: false - )! - - let expectedResults: [ComparisonResult] = [ - .orderedAscending, - .orderedSame, - .orderedDescending, - ] - let count = expectedResults.count - - #expect(count == rootL.children.count, "Expected count \(count) found \(rootL.children.count)") - - for i in 0 ..< rootL.children.count { - let l = rootL.children[i] - let r = rootR.children[i] - let result = comparator.compare(l, r) - #expect(result == expectedResults[i], "Result \(result) found \(expectedResults[i]): Content \(l.path!) \(r.path!)") - } - } - - @Test func size() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .size, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/sample01.txt", "1") - try createFile("l/sample02.txt", "123456") - try createFile("l/sample03.txt", "123456") - try createFile("l/sample04.txt", "21") - - try createFile("r/sample01.txt", "1234") - try createFile("r/sample02.txt", "1") - try createFile("r/sample03.txt", "123") - try createFile("r/sample04.txt", "21") - - let rootL = folderReader.readFolder( - atPath: appendFolder("l"), - parent: nil, - recursive: false - )! - - let rootR = folderReader.readFolder( - atPath: appendFolder("r"), - parent: nil, - recursive: false - )! - - let expectedResults: [ComparisonResult] = [ - .orderedAscending, - .orderedDescending, - .orderedDescending, - .orderedSame, - ] - let count = expectedResults.count - - #expect(count == rootL.children.count, "Expected count \(count) found \(rootL.children.count)") - - for i in 0 ..< rootL.children.count { - let l = rootL.children[i] - let r = rootR.children[i] - let result = comparator.compare(l, r) - #expect(result == expectedResults[i], "Result \(result) : Size error \(l.fileSize), \(r.fileSize)") - } - } -} - -// swiftlint:enable force_unwrapping diff --git a/Tests/Sources/FileSystem/Tests/CopyFilesTests.swift b/Tests/Sources/FileSystem/Tests/CopyFilesTests.swift deleted file mode 100644 index 52a10e3..0000000 --- a/Tests/Sources/FileSystem/Tests/CopyFilesTests.swift +++ /dev/null @@ -1,1633 +0,0 @@ -// -// CopyFilesTests.swift -// VisualDiffer -// -// Created by davide ficano on 11/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class CopyFilesTests: BaseTests { - @Test func copyFilesOnlyMatches() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/a/bb/ccc") - try createFolder("r/a/bb/ccc") - - try createFile("l/a/bb/ccc/second.txt", "123456789012") - - try createFile("r/a/bb/ccc/file.txt", "123456789012") - try createFile("r/a/bb/ccc/second.txt", "123456789") - - try setFileTimestamp("l/a/bb/ccc/second.txt", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 1, 0, 0, 0, 1, "a", .orphan, 12) - assertItem(child1.linkedItem, 0, 1, 1, 0, 1, "a", .orphan, 21) - - var child2 = child1.children[0] - assertItem(child2, 1, 0, 0, 0, 1, "bb", .orphan, 12) - assertItem(child2.linkedItem, 0, 1, 1, 0, 1, "bb", .orphan, 21) - - var child3 = child2.children[0] - assertItem(child3, 1, 0, 0, 0, 2, "ccc", .orphan, 12) - assertItem(child3.linkedItem, 0, 1, 1, 0, 2, "ccc", .orphan, 21) - - var child4 = child3.children[0] - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file.txt", .orphan, 12) - - var child5 = child3.children[1] - assertItem(child5, 1, 0, 0, 0, 0, "second.txt", .old, 12) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "second.txt", .changed, 9) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 0, 0, 0, 1, "a", .orphan, 12) - assertItem(child1.linkedItem, 0, 1, 1, 0, 1, "a", .orphan, 21) - - let childVI2 = childVI1.children[0] // a <--> a - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // a <-> a - assertItem(child2, 1, 0, 0, 0, 1, "bb", .orphan, 12) - assertItem(child2.linkedItem, 0, 1, 1, 0, 1, "bb", .orphan, 21) - - let childVI3 = childVI2.children[0] // bb <--> bb - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // bb <-> bb - assertItem(child3, 1, 0, 0, 0, 2, "ccc", .orphan, 12) - assertItem(child3.linkedItem, 0, 1, 1, 0, 2, "ccc", .orphan, 21) - - let childVI4 = childVI3.children[0] // ccc <--> ccc - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // ccc <-> ccc - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file.txt", .orphan, 12) - - let childVI5 = childVI3.children[1] // ccc <--> ccc - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // ccc <-> ccc - assertItem(child5, 1, 0, 0, 0, 0, "second.txt", .old, 12) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "second.txt", .changed, 9) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child2, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 1, 1, "a", .orphan, 12) - assertItem(child1.linkedItem, 0, 0, 1, 1, 1, "a", .orphan, 24) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 1, 1, "bb", .orphan, 12) - assertItem(child2.linkedItem, 0, 0, 1, 1, 1, "bb", .orphan, 24) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 1, 2, "ccc", .orphan, 12) - assertItem(child3.linkedItem, 0, 0, 1, 1, 2, "ccc", .orphan, 24) - - child4 = child3.children[0] - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file.txt", .orphan, 12) - - child5 = child3.children[1] - assertItem(child5, 0, 0, 0, 1, 0, "second.txt", .same, 12) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "second.txt", .same, 12) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 1, 1, "a", .orphan, 12) - assertItem(child1.linkedItem, 0, 0, 1, 1, 1, "a", .orphan, 24) - - let childVI2 = childVI1.children[0] // a <--> a - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // a <-> a - assertItem(child2, 0, 0, 0, 1, 1, "bb", .orphan, 12) - assertItem(child2.linkedItem, 0, 0, 1, 1, 1, "bb", .orphan, 24) - - let childVI3 = childVI2.children[0] // bb <--> bb - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // bb <-> bb - assertItem(child3, 0, 0, 0, 1, 2, "ccc", .orphan, 12) - assertItem(child3.linkedItem, 0, 0, 1, 1, 2, "ccc", .orphan, 24) - - let childVI4 = childVI3.children[0] // ccc <--> ccc - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // ccc <-> ccc - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file.txt", .orphan, 12) - - let childVI5 = childVI3.children[1] // ccc <--> ccc - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // ccc <-> ccc - assertItem(child5, 0, 0, 0, 1, 0, "second.txt", .same, 12) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "second.txt", .same, 12) - } - } - - @Test func copyFilesReplaceNoToAll() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder_0/folder_1") - try createFolder("r/folder_0/folder_1") - - // create files - try createFile("l/folder_0/folder_1/file_1.txt", "1234567890") - try setFileTimestamp("l/folder_0/folder_1/file_1.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/folder_0/folder_1/file_1.txt", "1234") - - try createFile("l/folder_0/file_2.txt", "1234567890") - try createFile("r/folder_0/file_2.txt", "1234567") - try setFileTimestamp("r/folder_0/file_2.txt", "2001-03-24 10: 45: 32 +0600") - - try createFile("r/folder_0/file_3.h", "123") - try createFile("r/folder_0/file_3.m", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 1, 1, 0, 0, 4, "folder_0", .orphan, 20) - assertItem(child1.linkedItem, 1, 1, 2, 0, 4, "folder_0", .orphan, 16) - - var child2 = child1.children[0] - assertItem(child2, 1, 0, 0, 0, 1, "folder_1", .orphan, 10) - assertItem(child2.linkedItem, 0, 1, 0, 0, 1, "folder_1", .orphan, 4) - - var child3 = child2.children[0] - assertItem(child3, 1, 0, 0, 0, 0, "file_1.txt", .old, 10) - assertItem(child3.linkedItem, 0, 1, 0, 0, 0, "file_1.txt", .changed, 4) - - var child4 = child1.children[1] - assertItem(child4, 0, 1, 0, 0, 0, "file_2.txt", .changed, 10) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_2.txt", .old, 7) - - var child5 = child1.children[2] - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_3.h", .orphan, 3) - - var child6 = child1.children[3] - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file_3.m", .orphan, 2) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 4) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 1, 0, 0, 4, "folder_0", .orphan, 20) - assertItem(child1.linkedItem, 1, 1, 2, 0, 4, "folder_0", .orphan, 16) - - let childVI2 = childVI1.children[0] // folder_0 <--> folder_0 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_0 <-> folder_0 - assertItem(child2, 1, 0, 0, 0, 1, "folder_1", .orphan, 10) - assertItem(child2.linkedItem, 0, 1, 0, 0, 1, "folder_1", .orphan, 4) - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 1, 0, 0, 0, 0, "file_1.txt", .old, 10) - assertItem(child3.linkedItem, 0, 1, 0, 0, 0, "file_1.txt", .changed, 4) - - let childVI4 = childVI1.children[1] // folder_0 <--> folder_0 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder_0 <-> folder_0 - assertItem(child4, 0, 1, 0, 0, 0, "file_2.txt", .changed, 10) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_2.txt", .old, 7) - - let childVI5 = childVI1.children[2] // folder_0 <--> folder_0 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_0 <-> folder_0 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_3.h", .orphan, 3) - - let childVI6 = childVI1.children[3] // folder_0 <--> folder_0 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_0 <-> folder_0 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file_3.m", .orphan, 2) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - // src is on right so folders are inverted - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child1.linkedItem!, - srcBaseDir: appendFolder("r"), - destBaseDir: appendFolder("l") - ) - - child1 = rootL.children[0] - assertItem(child1, 0, 1, 0, 3, 4, "folder_0", .orphan, 19) - assertItem(child1.linkedItem, 1, 0, 0, 3, 4, "folder_0", .orphan, 16) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 1, 1, "folder_1", .orphan, 4) - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "folder_1", .orphan, 4) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 1, 0, "file_1.txt", .same, 4) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "file_1.txt", .same, 4) - - child4 = child1.children[1] - assertItem(child4, 0, 1, 0, 0, 0, "file_2.txt", .changed, 10) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_2.txt", .old, 7) - - child5 = child1.children[2] - assertItem(child5, 0, 0, 0, 1, 0, "file_3.h", .same, 3) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file_3.h", .same, 3) - - child6 = child1.children[3] - assertItem(child6, 0, 0, 0, 1, 0, "file_3.m", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file_3.m", .same, 2) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 4) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 1, 0, 3, 4, "folder_0", .orphan, 19) - assertItem(child1.linkedItem, 1, 0, 0, 3, 4, "folder_0", .orphan, 16) - - let childVI2 = childVI1.children[0] // folder_0 <--> folder_0 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_0 <-> folder_0 - assertItem(child2, 0, 0, 0, 1, 1, "folder_1", .orphan, 4) - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "folder_1", .orphan, 4) - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 0, 0, 0, 1, 0, "file_1.txt", .same, 4) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "file_1.txt", .same, 4) - - let childVI4 = childVI1.children[1] // folder_0 <--> folder_0 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder_0 <-> folder_0 - assertItem(child4, 0, 1, 0, 0, 0, "file_2.txt", .changed, 10) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_2.txt", .old, 7) - - let childVI5 = childVI1.children[2] // folder_0 <--> folder_0 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_0 <-> folder_0 - assertItem(child5, 0, 0, 0, 1, 0, "file_3.h", .same, 3) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file_3.h", .same, 3) - - let childVI6 = childVI1.children[3] // folder_0 <--> folder_0 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_0 <-> folder_0 - assertItem(child6, 0, 0, 0, 1, 0, "file_3.m", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file_3.m", .same, 2) - } - } - - @Test func copyOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/only_on_left") - try createFolder("l/only_on_left/second_folder") - try createFolder("l/only_on_left/second_folder/cartella senza titolo") - try createFolder("l/only_on_left/second_folder/cartella senza titolo 2") - - try createFolder("r") - - // create files - try createFile("l/only_on_left/second_folder/symlinks copia.zip", "12345") - try createFile("l/only_on_left/symlinks.zip", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - var child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - var child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child5 = child2.children[2] - assertItem(child5, 0, 0, 0, 0, 0, "symlinks copia.zip", .orphan, 5) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child6 = child1.children[1] - assertItem(child6, 0, 0, 0, 0, 0, "symlinks.zip", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> (null) - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // only_on_left <-> (null) - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // second_folder <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // second_folder <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child2, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, "only_on_left", .orphan, 0) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "second_folder", .orphan, 0) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - - child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - - child5 = child2.children[2] - assertItem(child5, 0, 0, 0, 0, 0, "symlinks copia.zip", .orphan, 5) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child6 = child1.children[1] - assertItem(child6, 0, 0, 0, 0, 0, "symlinks.zip", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, "only_on_left", .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> only_on_left - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // only_on_left <-> only_on_left - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "second_folder", .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> second_folder - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> second_folder - assertItem(child3, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - - let childVI4 = childVI2.children[1] // second_folder <--> second_folder - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // second_folder <-> second_folder - assertItem(child4, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - } - } - - @Test func copyFilesPresentOnBothSidesWithFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder_1/folder_1_1/folder_2_1") - try createFolder("l/folder_1/folder_1_2") - - try createFolder("r/folder_1/folder_1_1/folder_2_1") - try createFolder("r/folder_1/folder_1_2") - - // create files - try createFile("l/folder_1/folder_1_1/folder_2_1/file_changed.m", "12345") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_matched.txt", "1234") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "12345") - try createFile("l/folder_1/folder_1_2/match_2_1.m", "12") - try createFile("l/folder_1/file.txt", "1") - - try createFile("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "1") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_matched.txt", "1234") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_older.txt", "1") - try createFile("r/folder_1/folder_1_2/match_2_1.m", "12") - try createFile("r/folder_1/file.txt", "1") - try createFile("r/folder_1/right_orphan.txt", "1234567") - - try setFileTimestamp("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "2001-03-24 10: 45: 32 +0600") - try setFileTimestamp("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let l = rootL.children[0] - assertItem(l, 1, 1, 0, 3, 4, "folder_1", .orphan, 17) - assertItem(l.linkedItem, 1, 1, 1, 3, 4, "folder_1", .orphan, 16) - - var child1 = l.children[0] - assertItem(child1, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 14) - assertItem(child1.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 6) - - var child2 = child1.children[0] - assertItem(child2, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 14) - assertItem(child2.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 6) - - var child3 = child2.children[0] - assertItem(child3, 0, 1, 0, 0, 0, "file_changed.m", .changed, 5) - assertItem(child3.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 1) - - var child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 1, 0, "file_matched.txt", .same, 4) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 4) - - var child5 = child2.children[2] - assertItem(child5, 1, 0, 0, 0, 0, "file_older.txt", .old, 5) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 1) - - var child6 = l.children[1] - assertItem(child6, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - - var child7 = child6.children[0] - assertItem(child7, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - - var child8 = l.children[2] - assertItem(child8, 0, 0, 0, 1, 0, "file.txt", .same, 1) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 1) - - var child9 = l.children[3] - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 1, 1, 0, 3, 1, "l", .orphan, 17) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 1, 1, 3, 1, "r", .orphan, 16) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 1, 1, 0, 3, 4, "folder_1", .orphan, 17) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 1, 1, 3, 4, "folder_1", .orphan, 16) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 14) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 6) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // folder_1_1 <-> folder_1_1 - assertItem(child4, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 14) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 6) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 1, 0, 0, 0, "file_changed.m", .changed, 5) - assertItem(child5.linkedItem!, 1, 0, 0, 0, 0, "file_changed.m", .old, 1) - - let childVI6 = childVI4.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_2_1 <-> folder_2_1 - assertItem(child6, 1, 0, 0, 0, 0, "file_older.txt", .old, 5) - assertItem(child6.linkedItem!, 0, 1, 0, 0, 0, "file_older.txt", .changed, 1) - - let childVI7 = childVI2.children[1] // folder_1 <--> folder_1 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // folder_1 <-> folder_1 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child1, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 5, 4, "folder_1", .orphan, 17) - assertItem(child1.linkedItem, 0, 0, 1, 5, 4, "folder_1", .orphan, 24) - - child2 = child1.children[0] // folder_1 - assertItem(child2, 0, 0, 0, 3, 1, "folder_1_1", .orphan, 14) - assertItem(child2.linkedItem, 0, 0, 0, 3, 1, "folder_1_1", .orphan, 14) - - child3 = child2.children[0] // folder_1_1 - assertItem(child3, 0, 0, 0, 3, 3, "folder_2_1", .orphan, 14) - assertItem(child3.linkedItem, 0, 0, 0, 3, 3, "folder_2_1", .orphan, 14) - - child4 = child3.children[0] // folder_2_1 - assertItem(child4, 0, 0, 0, 1, 0, "file_changed.m", .same, 5) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_changed.m", .same, 5) - - child5 = child3.children[1] // folder_2_1 - assertItem(child5, 0, 0, 0, 1, 0, "file_matched.txt", .same, 4) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 4) - - child6 = child3.children[2] // folder_2_1 - assertItem(child6, 0, 0, 0, 1, 0, "file_older.txt", .same, 5) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file_older.txt", .same, 5) - - child7 = child1.children[1] // folder_1 - assertItem(child7, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - assertItem(child7.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - - child8 = child7.children[0] // folder_1_2 - assertItem(child8, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - - child9 = child1.children[2] // folder_1 - assertItem(child9, 0, 0, 0, 1, 0, "file.txt", .same, 1) - assertItem(child9.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 1) - - let child10 = child1.children[3] // folder_1 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 5, 1, "l", .orphan, 17) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 1, 5, 1, "r", .orphan, 24) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 5, 4, "folder_1", .orphan, 17) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 1, 5, 4, "folder_1", .orphan, 24) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - } - - @Test func copyDontFollowSymLink() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder1") - try createFolder("l/folder1/folder2") - try createFolder("l/folder1/folder2/folder3") - try createSymlink("l/folder1/folder2/symlink1", "symlink_test1") - - try createFolder("r/folder1") - try createFolder("r/folder1/folder2") - - // folders out of comparison but used to create symlinks to them - try createFolder("symlink_test1") - try createFolder("symlink_test2") - - // create files - try createSymlink("r/folder1/folder2/folder3", "symlink_test1") - try createSymlink("r/folder1/folder2/symlink1", "symlink_test2") - try createSymlink("r/folder1/folder2/orphan_symlink", "symlink_test2") - - try createFile("symlink_test1/file1.txt", "12345") - try createFile("symlink_test2/file2.txt", "123") - try createFile("r/folder1/folder2/sample.txt", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - - var child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 0, 5, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 5, "folder2", .orphan, 2) - - var child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child4 = child2.children[1] // folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - - var child5 = child2.children[2] // folder2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - - var child6 = child2.children[3] // folder2 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - - var child7 = child2.children[4] // folder2 - assertItem(child7, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 4) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 0, 5, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 5, "folder2", .orphan, 2) - - let childVI3 = childVI2.children[0] // folder2 <--> folder2 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder2 <-> folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // folder2 <--> folder2 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder2 <-> folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - - let childVI5 = childVI2.children[2] // folder2 <--> folder2 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder2 <-> folder2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - - let childVI6 = childVI2.children[3] // folder2 <--> folder2 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder2 <-> folder2 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: true - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child2.linkedItem!, - srcBaseDir: appendFolder("r"), - destBaseDir: appendFolder("l") - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - - child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 1, 5, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 1, 5, "folder2", .orphan, 2) - - child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child4 = child2.children[1] // folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - try assertSymlink(child4.linkedItem!, "symlink_test1", true) - - child5 = child2.children[2] // folder2 - assertItem(child5, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - try assertSymlink(child5, "symlink_test2", true) - try assertSymlink(child5.linkedItem!, "symlink_test2", true) - - child6 = child2.children[3] // folder2 - assertItem(child6, 0, 0, 0, 1, 0, "sample.txt", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "sample.txt", .same, 2) - - child7 = child2.children[4] // folder2 - assertItem(child7, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - try assertSymlink(child7, "symlink_test2", true) - try assertSymlink(child7.linkedItem!, "symlink_test2", true) - - assertErrors(fileOperationDelegate.errors, [ - FileError.createSymLink(path: child3.path!), - ]) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 1, 5, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 1, 5, "folder2", .orphan, 2) - - let childVI3 = childVI2.children[0] // folder2 <--> folder2 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder2 <-> folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // folder2 <--> folder2 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder2 <-> folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - } - } - - @Test func copyFollowSymLink() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder1") - try createFolder("l/folder1/folder2") - try createFolder("l/folder1/folder2/folder3") - try createSymlink("l/folder1/folder2/symlink1", "symlink_test1") - - try createFolder("r/folder1") - try createFolder("r/folder1/folder2") - - // folders out of comparison but used to create symlinks to them - try createFolder("symlink_test1") - try createFolder("symlink_test2") - - // create files - try createSymlink("r/folder1/folder2/folder3", "symlink_test1") - try createSymlink("r/folder1/folder2/symlink1", "symlink_test2") - try createSymlink("r/folder1/folder2/orphan_symlink", "symlink_test2") - - try createFile("symlink_test1/file1.txt", "12345") - try createFile("symlink_test1/file2.txt", "123") - try createFile("symlink_test2/file2.txt", "123") - try createFile("r/folder1/folder2/sample.txt", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 1, 1, "folder1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 4, 1, 1, "folder1", .orphan, 16) - - var child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 1, 1, 4, "folder2", .orphan, 8) - assertItem(child2.linkedItem, 0, 0, 4, 1, 4, "folder2", .orphan, 16) - - var child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 2, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 2, 0, 2, "folder3", .orphan, 8) - - var child4 = child3.children[0] // folder3 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 5) - - var child5 = child3.children[1] // folder3 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 3) - - var child6 = child2.children[1] // folder2 - assertItem(child6, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 1, "orphan_symlink", .orphan, 3) - - var child7 = child6.children[0] // (null) - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 3) - - var child8 = child2.children[2] // folder2 - assertItem(child8, 0, 0, 1, 1, 2, "symlink1", .orphan, 8) - assertItem(child8.linkedItem, 0, 0, 0, 1, 2, "symlink1", .orphan, 3) - - var child9 = child8.children[0] // symlink1 - assertItem(child9, 0, 0, 1, 0, 0, "file1.txt", .orphan, 5) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child10 = child8.children[1] // symlink1 - assertItem(child10, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - assertItem(child10.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - - var child11 = child2.children[3] // folder2 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 1, 1, "folder1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 4, 1, 1, "folder1", .orphan, 16) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 4) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 1, 1, 4, "folder2", .orphan, 8) - assertItem(child2.linkedItem, 0, 0, 4, 1, 4, "folder2", .orphan, 16) - - let childVI3 = childVI2.children[0] // folder2 <--> folder2 - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // folder2 <-> folder2 - assertItem(child3, 0, 0, 0, 0, 2, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 2, 0, 2, "folder3", .orphan, 8) - - let childVI4 = childVI3.children[0] // folder3 <--> folder3 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder3 <-> folder3 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 5) - - let childVI5 = childVI3.children[1] // folder3 <--> folder3 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder3 <-> folder3 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 3) - - let childVI6 = childVI2.children[1] // folder2 <--> folder2 - assertArrayCount(childVI6.children, 1) - let child6 = childVI6.item // folder2 <-> folder2 - assertItem(child6, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 1, "orphan_symlink", .orphan, 3) - - let childVI7 = childVI6.children[0] // (null) <--> orphan_symlink - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // (null) <-> orphan_symlink - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 3) - - let childVI8 = childVI2.children[2] // folder2 <--> folder2 - assertArrayCount(childVI8.children, 1) - let child8 = childVI8.item // folder2 <-> folder2 - assertItem(child8, 0, 0, 1, 1, 2, "symlink1", .orphan, 8) - assertItem(child8.linkedItem, 0, 0, 0, 1, 2, "symlink1", .orphan, 3) - - let childVI9 = childVI8.children[0] // symlink1 <--> symlink1 - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // symlink1 <-> symlink1 - assertItem(child9, 0, 0, 1, 0, 0, "file1.txt", .orphan, 5) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI10 = childVI2.children[3] // folder2 <--> folder2 - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // folder2 <-> folder2 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child2.linkedItem!, - srcBaseDir: appendFolder("r"), - destBaseDir: appendFolder("l") - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 5, 1, "folder1", .orphan, 21) - assertItem(child1.linkedItem, 0, 0, 0, 5, 1, "folder1", .orphan, 16) - - child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 1, 5, 4, "folder2", .orphan, 21) - assertItem(child2.linkedItem, 0, 0, 0, 5, 4, "folder2", .orphan, 16) - - child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 2, 2, "folder3", .orphan, 8) - assertItem(child3.linkedItem, 0, 0, 0, 2, 2, "folder3", .orphan, 8) - - child4 = child3.children[0] // folder3 - assertItem(child4, 0, 0, 0, 1, 0, "file1.txt", .same, 5) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 5) - - child5 = child3.children[1] // folder3 - assertItem(child5, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - - child6 = child2.children[1] // folder2 - assertItem(child6, 0, 0, 0, 1, 1, "orphan_symlink", .orphan, 3) - assertItem(child6.linkedItem, 0, 0, 0, 1, 1, "orphan_symlink", .orphan, 3) - - child7 = child6.children[0] // orphan_symlink - assertItem(child7, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - - child8 = child2.children[2] // folder2 - assertItem(child8, 0, 0, 1, 1, 2, "symlink1", .orphan, 8) - assertItem(child8.linkedItem, 0, 0, 0, 1, 2, "symlink1", .orphan, 3) - - child9 = child8.children[0] // symlink1 - assertItem(child9, 0, 0, 1, 0, 0, "file1.txt", .orphan, 5) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child10 = child8.children[1] // symlink1 - assertItem(child10, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - assertItem(child10.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 3) - - child11 = child2.children[3] // folder2 - assertItem(child11, 0, 0, 0, 1, 0, "sample.txt", .same, 2) - assertItem(child11.linkedItem, 0, 0, 0, 1, 0, "sample.txt", .same, 2) - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 1, 5, 1, "l", .orphan, 21) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 5, 1, "r", .orphan, 16) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 1, 5, 1, "folder1", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 5, 1, "folder1", .orphan, 16) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // folder1 <--> folder1 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder1 <-> folder1 - assertItem(child3, 0, 0, 1, 5, 4, "folder2", .orphan, 21) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 5, 4, "folder2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // folder2 <--> folder2 - assertArrayCount(childVI4.children, 1) - let child4 = childVI4.item // folder2 <-> folder2 - assertItem(child4, 0, 0, 1, 1, 2, "symlink1", .orphan, 8) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 0, 1, 2, "symlink1", .orphan, 3) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // symlink1 <--> symlink1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // symlink1 <-> symlink1 - assertItem(child5, 0, 0, 1, 0, 0, "file1.txt", .orphan, 5) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func copyFailure() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("l/dir1/dir2/dir3") - - // create files - try createFile("l/dir1/dir2/dir3/file1.txt", "1234567") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // dir3 - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - // simulate a copy error deleting the file to copy - do { - try fm.removeItem(atPath: child4.path!) - } catch { - Issue.record("Found error \(error)") - } - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // dir2 <--> (null) - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // dir2 <-> (null) - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // dir3 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir3 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child4, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] // l - do { - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // dir3 - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // dir2 <--> (null) - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // dir2 <-> (null) - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // dir3 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir3 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func copyPreserveFolderTimestamp() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/folder1/folder2/folder3") - try createFolder("r/folder1") - - try createFile("l/folder1/folder2/folder3/file1.txt", "12") - - try setFileCreationTime("l/folder1", "2010-01-01 00: 00: 00 +0000") - try setFileTimestamp("l/folder1", "2010-02-02 02: 02: 00 +0000") - - try setFileCreationTime("l/folder1/folder2", "2010-04-04 04: 04: 00 +0000") - try setFileTimestamp("l/folder1/folder2", "2010-05-05 05: 05: 00 +0000") - - try setFileCreationTime("l/folder1/folder2/folder3", "2011-05-05 05: 00: 30 +0000") - try setFileTimestamp("l/folder1/folder2/folder3", "2011-06-06 06: 00: 30 +0000") - - try setFileCreationTime("l/folder1/folder2/folder3/file1.txt", "2012-07-07 09: 09: 00 +0000") - try setFileTimestamp("l/folder1/folder2/folder3/file1.txt", "2012-08-08 10: 10: 10 +0000") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 1, 0, 1, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 1, 0, 1, "folder3", .orphan, 2) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // folder3 - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 1, 0, 1, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // folder2 <--> (null) - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder2 <-> (null) - assertItem(child3, 0, 0, 1, 0, 1, "folder3", .orphan, 2) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // folder3 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder3 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: child1, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - try assertTimestamps(child1.linkedItem, "2010-01-01 00: 00: 00 +0000", "2010-02-02 02: 02: 00 +0000") - - let child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 1, 1, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "folder2", .orphan, 2) - try assertTimestamps(child2.linkedItem, "2010-04-04 04: 04: 00 +0000", "2010-05-05 05: 05: 00 +0000") - - let child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 1, 1, "folder3", .orphan, 2) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "folder3", .orphan, 2) - try assertTimestamps(child3.linkedItem, "2011-05-05 05: 00: 30 +0000", "2011-06-06 06: 00: 30 +0000") - - let child4 = child3.children[0] // folder3 - assertItem(child4, 0, 0, 0, 1, 0, "file1.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 2) - try assertTimestamps(child4.linkedItem, "2012-07-07 09: 09: 00 +0000", "2012-08-08 10: 10: 10 +0000") - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "folder1", .orphan, 2) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 1, 1, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "folder2", .orphan, 2) - - let childVI3 = childVI2.children[0] // folder2 <--> folder2 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder2 <-> folder2 - assertItem(child3, 0, 0, 0, 1, 1, "folder3", .orphan, 2) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "folder3", .orphan, 2) - - let childVI4 = childVI3.children[0] // folder3 <--> folder3 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder3 <-> folder3 - assertItem(child4, 0, 0, 0, 1, 0, "file1.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 2) - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/DeleteFileTests.swift b/Tests/Sources/FileSystem/Tests/DeleteFileTests.swift deleted file mode 100644 index c432e3f..0000000 --- a/Tests/Sources/FileSystem/Tests/DeleteFileTests.swift +++ /dev/null @@ -1,1069 +0,0 @@ -// -// DeleteFileTests.swift -// VisualDiffer -// -// Created by davide ficano on 11/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class DeleteFileTests: BaseTests { - /** - * Delete folder with files on right: there is an orphan file and a newer file - */ - @Test func deleteCreatingOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/a/bb/ccc") - try createFolder("r/a/bb/ccc") - - try createFile("l/a/bb/ccc/second.txt", "123456789012") - - try createFile("r/a/bb/ccc/file.txt", "123") - try createFile("r/a/bb/ccc/second.txt", "12345678") - - try setFileTimestamp("l/a/bb/ccc/second.txt", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - let vi = rootL.visibleItem! - - var l = rootL.children[0] - assertItem(l, 1, 0, 0, 0, 1, "a", .orphan, 12) - assertItem(l.linkedItem, 0, 1, 1, 0, 1, "a", .orphan, 11) - - var child1 = l.children[0] - assertItem(child1, 1, 0, 0, 0, 1, "bb", .orphan, 12) - assertItem(child1.linkedItem, 0, 1, 1, 0, 1, "bb", .orphan, 11) - - var child2 = child1.children[0] - assertItem(child2, 1, 0, 0, 0, 2, "ccc", .orphan, 12) - assertItem(child2.linkedItem, 0, 1, 1, 0, 2, "ccc", .orphan, 11) - - var child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file.txt", .orphan, 3) - - let child4 = child2.children[1] - assertItem(child4, 1, 0, 0, 0, 0, "second.txt", .old, 12) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "second.txt", .changed, 8) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 0, 0, 0, 1, "a", .orphan, 12) - assertItem(child1.linkedItem, 0, 1, 1, 0, 1, "a", .orphan, 11) - - let childVI2 = childVI1.children[0] // a <--> a - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // a <-> a - assertItem(child2, 1, 0, 0, 0, 1, "bb", .orphan, 12) - assertItem(child2.linkedItem, 0, 1, 1, 0, 1, "bb", .orphan, 11) - - let childVI3 = childVI2.children[0] // bb <--> bb - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // bb <-> bb - assertItem(child3, 1, 0, 0, 0, 2, "ccc", .orphan, 12) - assertItem(child3.linkedItem, 0, 1, 1, 0, 2, "ccc", .orphan, 11) - - let childVI4 = childVI3.children[0] // ccc <--> ccc - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // ccc <-> ccc - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file.txt", .orphan, 3) - - let childVI5 = childVI3.children[1] // ccc <--> ccc - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // ccc <-> ccc - assertItem(child5, 1, 0, 0, 0, 0, "second.txt", .old, 12) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "second.txt", .changed, 8) - } - - try assertOnlySetup() - - let fileOperaionDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperaionDelegate - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - child2.linkedItem!, - baseDir: appendFolder("r") - ) - - l = rootL.children[0] - assertItem(l, 0, 0, 1, 0, 1, "a", .orphan, 12) - assertItem(l.linkedItem, 0, 0, 0, 0, 1, "a", .orphan, 0) - - child1 = l.children[0] - assertItem(child1, 0, 0, 1, 0, 1, "bb", .orphan, 12) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "bb", .orphan, 0) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 1, 0, 1, "ccc", .orphan, 12) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 1, 0, 0, "second.txt", .orphan, 12) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "a", .orphan, 12) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "a", .orphan, 0) - - let childVI2 = childVI1.children[0] // a <--> a - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // a <-> a - assertItem(child2, 0, 0, 1, 0, 1, "bb", .orphan, 12) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "bb", .orphan, 0) - - let childVI3 = childVI2.children[0] // bb <--> bb - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // bb <-> bb - assertItem(child3, 0, 0, 1, 0, 1, "ccc", .orphan, 12) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // ccc <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // ccc <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "second.txt", .orphan, 12) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - /** - * Delete the folder on left including filtered files - * Folder is orphan - */ - @Test func deleteOrphanWithFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l") - try createFolder("r") - - try createFolder("l/only_on_left/second_folder/subfolder1") - try createFolder("l/only_on_left/second_folder/subfolder2") - - try createFile("l/only_on_left/second_folder/test1.zip", "123456789") - try createFile("l/only_on_left/test2.zip", "123456789") - try createFile("l/.DS_Store", "1234567890123") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var l = rootL.children[0] - assertItem(l, 0, 0, 0, 0, 2, "only_on_left", .orphan, 18) - assertItem(l.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let child1 = l.children[0] - assertItem(child1, 0, 0, 0, 0, 3, "second_folder", .orphan, 9) - assertItem(child1.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 0, "subfolder1", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] - assertItem(child3, 0, 0, 0, 0, 0, "subfolder2", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child4 = child1.children[2] - assertItem(child4, 0, 0, 0, 0, 0, "test1.zip", .orphan, 9) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child5 = l.children[1] - assertItem(child5, 0, 0, 0, 0, 0, "test2.zip", .orphan, 9) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child6 = rootL.children[1] - assertItem(child6, 0, 0, 0, 0, 0, ".DS_Store", .orphan, 13) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 18) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> (null) - assertArrayCount(childVI2.children, 3) - let child2 = childVI2.item // only_on_left <-> (null) - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 9) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "subfolder1", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // second_folder <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // second_folder <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "subfolder2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI2.children[2] // second_folder <--> (null) - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // second_folder <-> (null) - assertItem(child5, 0, 0, 0, 0, 0, "test1.zip", .orphan, 9) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI1.children[1] // only_on_left <--> (null) - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // only_on_left <-> (null) - assertItem(child6, 0, 0, 0, 0, 0, "test2.zip", .orphan, 9) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - child1, - baseDir: appendFolder("l/only_on_left/second_folder") - ) - - l = rootL.children[0] - assertItem(l, 0, 0, 0, 0, 1, "only_on_left", .orphan, 9) - assertItem(l.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - child5 = l.children[0] - assertItem(child5, 0, 0, 0, 0, 0, "test2.zip", .orphan, 9) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child6 = rootL.children[1] - assertItem(child6, 0, 0, 0, 0, 0, ".DS_Store", .orphan, 13) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "only_on_left", .orphan, 9) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> (null) - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // only_on_left <-> (null) - assertItem(child2, 0, 0, 0, 0, 0, "test2.zip", .orphan, 9) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - /** - * Delete the folder on left, filtered files remain - * Folder is orphan - */ - @Test func deleteOrphanLeaveFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l") - try createFolder("r") - - try createFolder("l/only_on_left/second_folder/subfolder1") - try createFolder("l/only_on_left/second_folder/subfolder2") - try createFile("l/only_on_left/second_folder/test1.zip", "12345") - try createFile("l/only_on_left/test2.zip", "12") - try createFile("l/.DS_Store", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var l = rootL.children[0] - assertItem(l, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(l.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - var child1 = l.children[0] - assertItem(child1, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child1.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - var child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 0, "subfolder1", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child3 = child1.children[1] - assertItem(child3, 0, 0, 0, 0, 0, "subfolder2", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child4 = child1.children[2] - assertItem(child4, 0, 0, 0, 0, 0, "test1.zip", .orphan, 5) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child5 = l.children[1] - assertItem(child5, 0, 0, 0, 0, 0, "test2.zip", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child6 = rootL.children[1] - assertItem(child6, 0, 0, 0, 0, 0, ".DS_Store", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> (null) - assertArrayCount(childVI2.children, 3) - let child2 = childVI2.item // only_on_left <-> (null) - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "subfolder1", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // second_folder <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // second_folder <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "subfolder2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI2.children[2] // second_folder <--> (null) - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // second_folder <-> (null) - assertItem(child5, 0, 0, 0, 0, 0, "test1.zip", .orphan, 5) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI1.children[1] // only_on_left <--> (null) - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // only_on_left <-> (null) - assertItem(child6, 0, 0, 0, 0, 0, "test2.zip", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - child1, - baseDir: appendFolder("l/only_on_left/second_folder") - ) - - l = rootL.children[0] - assertItem(l, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(l.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - child1 = l.children[0] - assertItem(child1, 0, 0, 0, 0, 1, "second_folder", .orphan, 5) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 0, "test1.zip", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child5 = l.children[1] - assertItem(child5, 0, 0, 0, 0, 0, "test2.zip", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child6 = rootL.children[1] - assertItem(child6, 0, 0, 0, 0, 0, ".DS_Store", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> (null) - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // only_on_left <-> (null) - assertItem(child2, 0, 0, 0, 0, 1, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "test1.zip", .orphan, 5) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI1.children[1] // only_on_left <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // only_on_left <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "test2.zip", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func deleteFilesPresentOnBothSides() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder_1/folder_1_1/folder_2_1") - try createFolder("l/folder_1/folder_1_2") - - try createFolder("r/folder_1/folder_1_1/folder_2_1") - try createFolder("r/folder_1/folder_1_2") - - // create files - try createFile("l/folder_1/folder_1_1/folder_2_1/file_changed.m", "123456") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_matched.txt", "12") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "123") - try createFile("l/folder_1/folder_1_2/match_2_1.m", "1234") - try createFile("l/folder_1/file.txt", "1") - - try createFile("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "1234") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_matched.txt", "12") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_older.txt", "1") - try createFile("r/folder_1/folder_1_2/match_2_1.m", "1234") - try createFile("r/folder_1/file.txt", "1") - try createFile("r/folder_1/right_orphan.txt", "1234567") - - try setFileTimestamp("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "2001-03-24 10: 45: 32 +0600") - try setFileTimestamp("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let l = rootL.children[0] - assertItem(l, 1, 1, 0, 3, 4, "folder_1", .orphan, 16) - assertItem(l.linkedItem, 1, 1, 1, 3, 4, "folder_1", .orphan, 19) - - let child1 = l.children[0] - assertItem(child1, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 11) - assertItem(child1.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 7) - - let child2 = child1.children[0] - assertItem(child2, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 11) - assertItem(child2.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 7) - - let child3 = child2.children[0] - assertItem(child3, 0, 1, 0, 0, 0, "file_changed.m", .changed, 6) - assertItem(child3.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 4) - - let child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - - let child5 = child2.children[2] - assertItem(child5, 1, 0, 0, 0, 0, "file_older.txt", .old, 3) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 1) - - let child6 = l.children[1] - assertItem(child6, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 4) - assertItem(child6.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 4) - - let child7 = child6.children[0] - assertItem(child7, 0, 0, 0, 1, 0, "match_2_1.m", .same, 4) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 4) - - let child8 = l.children[2] - assertItem(child8, 0, 0, 0, 1, 0, "file.txt", .same, 1) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 1) - - let child9 = l.children[3] - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 1, 1, 0, 3, 1, "l", .orphan, 16) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 1, 1, 3, 1, "r", .orphan, 19) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 1, 1, 0, 3, 4, "folder_1", .orphan, 16) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 1, 1, 3, 4, "folder_1", .orphan, 19) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 11) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 7) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // folder_1_1 <-> folder_1_1 - assertItem(child4, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 11) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 7) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 1, 0, 0, 0, "file_changed.m", .changed, 6) - assertItem(child5.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 4) - - let childVI6 = childVI4.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_2_1 <-> folder_2_1 - assertItem(child6, 1, 0, 0, 0, 0, "file_older.txt", .old, 3) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 1) - - let childVI7 = childVI2.children[1] // folder_1 <--> folder_1 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // folder_1 <-> folder_1 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - child1, - baseDir: appendFolder("l") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 3, 1, "l", .orphan, 7) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 3, 1, "r", .orphan, 19) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 3, 4, "folder_1", .orphan, 7) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 3, 4, "folder_1", .orphan, 19) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // folder_1 <-> folder_1 - assertItem(child3, 0, 0, 0, 1, 1, "folder_1_1", .orphan, 2) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 2, 1, 1, "folder_1_1", .orphan, 7) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // folder_1_1 <-> folder_1_1 - assertItem(child4, 0, 0, 0, 1, 3, "folder_2_1", .orphan, 2) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 2, 1, 3, "folder_2_1", .orphan, 7) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let child5 = child4.children[0] // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 4) - - let child6 = child4.children[1] // folder_2_1 <-> folder_2_1 - assertItem(child6, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - - let child7 = child4.children[2] // folder_2_1 <-> folder_2_1 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 1) - - let child8 = child2.children[1] // folder_1 <-> folder_1 - assertItem(child8, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 4) - #expect(child8.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 4) - #expect(child8.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.linkedItem!.orphanFolders)") - - let child9 = child8.children[0] // folder_1_2 <-> folder_1_2 - assertItem(child9, 0, 0, 0, 1, 0, "match_2_1.m", .same, 4) - assertItem(child9.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 4) - - let child10 = child2.children[2] // folder_1 <-> folder_1 - assertItem(child10, 0, 0, 0, 1, 0, "file.txt", .same, 1) - assertItem(child10.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 1) - - let child11 = child2.children[3] // folder_1 <-> folder_1 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 3, 1, "l", .orphan, 7) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 3, 1, "r", .orphan, 19) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 3, 4, "folder_1", .orphan, 7) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 3, 4, "folder_1", .orphan, 19) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 0, 0, 0, 1, 1, "folder_1_1", .orphan, 2) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 2, 1, 1, "folder_1_1", .orphan, 7) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // folder_1_1 <-> folder_1_1 - assertItem(child4, 0, 0, 0, 1, 3, "folder_2_1", .orphan, 2) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 2, 1, 3, "folder_2_1", .orphan, 7) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 4) - - let childVI6 = childVI4.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_2_1 <-> folder_2_1 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 1) - - let childVI7 = childVI2.children[1] // folder_1 <--> folder_1 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // folder_1 <-> folder_1 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - } - - @Test func deleteDontFollowSymLink() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder1") - try createFolder("l/folder1/folder2") - - try createFolder("r/folder1") - - // folders out of comparison but used to create symlinks to them - try createFolder("symlink_test1") - try createFolder("symlink_test2") - - // create files - try createSymlink("l/folder1/folder2/folder3", "symlink_test2") - try createSymlink("l/folder1/folder2/symlink1", "symlink_test1") - - try createFile("symlink_test1/file1.txt", "12345") - try createFile("symlink_test1/file1_1.txt", "12") - try createFile("symlink_test2/file2.txt", "123") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 0, 2, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - try assertSymlink(child3, "symlink_test2", true) - - let child4 = child2.children[1] // folder2 - assertItem(child4, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - try assertSymlink(child4, "symlink_test1", true) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 0, 2, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // folder2 <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder2 <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // folder2 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder2 <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - child2, - baseDir: appendFolder("l") - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 0, 0, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 0, "folder1", .orphan, 0) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 0) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 0, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 0, "folder1", .orphan, 0) - } - } - - @Test func deleteFolderCreatingOnlyOrphans() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("r/dir1/dir2") - try createFolder("l/dir1/dir2/dir3") - try createFolder("r/dir1/dir2/dir3") - - // create files - try createFile("l/dir1/dir2/dir3/file_2.txt", "1234567890") - try createFile("r/dir1/dir2/dir3/file_2.txt", "1234567890") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 1, 1, "dir1", .orphan, 10) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "dir1", .orphan, 10) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 0, 1, 1, "dir2", .orphan, 10) - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "dir2", .orphan, 10) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 1, 1, "dir3", .orphan, 10) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "dir3", .orphan, 10) - - let child4 = child3.children[0] // dir3 - assertItem(child4, 0, 0, 0, 1, 0, "file_2.txt", .same, 10) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_2.txt", .same, 10) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - child2.linkedItem!, - baseDir: appendFolder("r") - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 10) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 10) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 10) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // dir3 - assertItem(child4, 0, 0, 1, 0, 0, "file_2.txt", .orphan, 10) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 10) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 10) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // dir2 <--> (null) - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // dir2 <-> (null) - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 10) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // dir3 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir3 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file_2.txt", .orphan, 10) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/DisplayTests.swift b/Tests/Sources/FileSystem/Tests/DisplayTests.swift deleted file mode 100644 index bcc1752..0000000 --- a/Tests/Sources/FileSystem/Tests/DisplayTests.swift +++ /dev/null @@ -1,322 +0,0 @@ -// -// DisplayTests.swift -// VisualDiffer -// -// Created by davide ficano on 11/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping function_body_length -final class DisplayTests: BaseTests { - @Test("Bug 0000170: Display filter set to 'No Orphan' hides not matching files") func displayFilterNoOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .noOrphan - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - // create folders - try createFolder("l/dir050") - try createFolder("r/dir050") - try createFolder("l/dir050/dir100") - try createFolder("r/dir050/dir100") - try createFolder("r/dir050/dir100/dir110") - try createFolder("l/dir050/dir100/dir120") - try createFolder("r/dir050/dir100/dir120") - - // create files - try createFile("r/dir050/dir100/dir110/file101.txt", "12") - try createFile("l/dir050/dir100/dir120/file101.txt", "12") - try createFile("r/dir050/dir100/dir120/file101.txt", "12") - try createFile("l/dir050/dir100/dir120/file102.txt", "123456") - try setFileTimestamp("l/dir050/dir100/dir120/file102.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/dir050/dir100/dir120/file102.txt", "123") - try createFile("l/dir050/dir100/dir120/file103.txt", "12") - try createFile("r/dir050/dir100/dir120/file103.txt", "12") - try createFile("l/dir050/dir100/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 1, 0, 1, 2, 1, "dir050", .orphan, 16) - assertItem(child1.linkedItem, 0, 1, 1, 2, 1, "dir050", .orphan, 9) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 1, 0, 1, 2, 3, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 1, 1, 2, 3, "dir100", .orphan, 9) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 1, "dir110", .orphan, 2) - - let child4 = child3.children[0] // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let child5 = child2.children[1] // dir100 <-> dir100 - assertItem(child5, 1, 0, 0, 2, 3, "dir120", .orphan, 10) - assertItem(child5.linkedItem, 0, 1, 0, 2, 3, "dir120", .orphan, 7) - - let child6 = child5.children[0] // dir120 <-> dir120 - assertItem(child6, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - - let child7 = child5.children[1] // dir120 <-> dir120 - assertItem(child7, 1, 0, 0, 0, 0, "file102.txt", .old, 6) - assertItem(child7.linkedItem, 0, 1, 0, 0, 0, "file102.txt", .changed, 3) - - let child8 = child5.children[2] // dir120 <-> dir120 - assertItem(child8, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - - let child9 = child2.children[2] // dir100 <-> dir100 - assertItem(child9, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 0, 1, 2, 1, "dir050", .orphan, 16) - assertItem(child1.linkedItem, 0, 1, 1, 2, 1, "dir050", .orphan, 9) - - let childVI2 = childVI1.children[0] // dir050 <--> dir050 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir050 <-> dir050 - assertItem(child2, 1, 0, 1, 2, 3, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 1, 1, 2, 3, "dir100", .orphan, 9) - - let childVI3 = childVI2.children[0] // dir100 <--> dir100 - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // dir100 <-> dir100 - assertItem(child3, 1, 0, 0, 2, 3, "dir120", .orphan, 10) - assertItem(child3.linkedItem, 0, 1, 0, 2, 3, "dir120", .orphan, 7) - - let childVI4 = childVI3.children[0] // dir120 <--> dir120 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir120 <-> dir120 - assertItem(child4, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - - let childVI5 = childVI3.children[1] // dir120 <--> dir120 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // dir120 <-> dir120 - assertItem(child5, 1, 0, 0, 0, 0, "file102.txt", .old, 6) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file102.txt", .changed, 3) - - let childVI6 = childVI3.children[2] // dir120 <--> dir120 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // dir120 <-> dir120 - assertItem(child6, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - } - } - - @Test func displayNoOrphanShowEmptyFolders() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyOrphans - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/folder_1") - try createFolder("r/folder_1") - try createFolder("l/folder_1/folder_1_1") - try createFolder("r/folder_1/folder_1_1") - try createFolder("l/folder_1/folder_1_1/folder_2_1") - try createFolder("r/folder_1/folder_1_1/folder_2_1") - try createFolder("l/folder_1/folder_1_2") - try createFolder("r/folder_1/folder_1_2") - - // create files - try createFile("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "1234") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_matched.txt", "12") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_matched.txt", "12") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_older.txt", "1") - try createFile("l/folder_1/folder_1_2/match_2_1.m", "1234") - try createFile("r/folder_1/folder_1_2/match_2_1.m", "1234") - try createFile("l/folder_1/file.txt", "1") - try createFile("r/folder_1/file.txt", "1") - try createFile("r/folder_1/right_orphan.txt", "1234567") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 3, 1, "l", .orphan, 7) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 3, 1, "r", .orphan, 19) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 3, 4, "folder_1", .orphan, 7) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 3, 4, "folder_1", .orphan, 19) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // folder_1 <-> folder_1 - assertItem(child3, 0, 0, 0, 1, 1, "folder_1_1", .orphan, 2) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 2, 1, 1, "folder_1_1", .orphan, 7) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // folder_1_1 <-> folder_1_1 - assertItem(child4, 0, 0, 0, 1, 3, "folder_2_1", .orphan, 2) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 2, 1, 3, "folder_2_1", .orphan, 7) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let child5 = child4.children[0] // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 4) - - let child6 = child4.children[1] // folder_2_1 <-> folder_2_1 - assertItem(child6, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - - let child7 = child4.children[2] // folder_2_1 <-> folder_2_1 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 1) - - let child8 = child2.children[1] // folder_1 <-> folder_1 - assertItem(child8, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 4) - #expect(child8.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 4) - #expect(child8.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.linkedItem!.orphanFolders)") - - let child9 = child8.children[0] // folder_1_2 <-> folder_1_2 - assertItem(child9, 0, 0, 0, 1, 0, "match_2_1.m", .same, 4) - assertItem(child9.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 4) - - let child10 = child2.children[2] // folder_1 <-> folder_1 - assertItem(child10, 0, 0, 0, 1, 0, "file.txt", .same, 1) - assertItem(child10.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 1) - - let child11 = child2.children[3] // folder_1 <-> folder_1 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 3, 1, "l", .orphan, 7) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 3, 1, "r", .orphan, 19) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 3, 4, "folder_1", .orphan, 7) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 3, 4, "folder_1", .orphan, 19) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder_1 <-> folder_1 - assertItem(child3, 0, 0, 0, 1, 1, "folder_1_1", .orphan, 2) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 2, 1, 1, "folder_1_1", .orphan, 7) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // folder_1_1 <-> folder_1_1 - assertItem(child4, 0, 0, 0, 1, 3, "folder_2_1", .orphan, 2) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 2, 1, 3, "folder_2_1", .orphan, 7) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 4) - - let childVI6 = childVI4.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_2_1 <-> folder_2_1 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 1) - - let childVI7 = childVI2.children[1] // folder_1 <--> folder_1 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // folder_1 <-> folder_1 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 7) - } - } -} - -// swiftlint:enable force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/EmptyFolderColorTests.swift b/Tests/Sources/FileSystem/Tests/EmptyFolderColorTests.swift deleted file mode 100644 index 3e98d89..0000000 --- a/Tests/Sources/FileSystem/Tests/EmptyFolderColorTests.swift +++ /dev/null @@ -1,1075 +0,0 @@ -// -// EmptyFolderColorTests.swift -// VisualDiffer -// -// Created by davide ficano on 02/11/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class EmptyFolderColorTests: BaseTests { - @Test func initialColors() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/empty") - try createFolder("r/empty") - try createFolder("r/empty/empty1") - try createFolder("r/empty/empty1/empty2") - try createFolder("r/empty/empty1/empty2/empty3") - try createFolder("r/empty/empty2") - try createFolder("r/empty/empty3") - try createFolder("l/utils") - try createFolder("r/utils") - try createFolder("l/utils/cell") - try createFolder("r/utils/cell") - try createFolder("l/utils/cell/popup") - try createFolder("r/utils/cell/popup") - try createFolder("l/utils/view") - try createFolder("l/utils/view/splitview") - - // create files - try createFile("l/utils/cell/popup/file", "12345") - try createFile("r/utils/cell/popup/file", "123456") - try setFileTimestamp("r/utils/cell/popup/file", "2001-03-24 10: 45: 32 +0600") - try createFile("r/file", "1234") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 1, 0, 0, 3, "l", .orphan, 5) - #expect(child1.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 1, 0, 3, "r", .orphan, 10) - #expect(child1.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // empty <-> empty - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "empty1", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // (null) <-> empty1 - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 1, "empty2", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child4.linkedItem!.orphanFolders)") - - let child5 = child4.children[0] // (null) <-> empty2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child5.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child5.linkedItem!.orphanFolders)") - - let child6 = child2.children[1] // empty <-> empty - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "empty2", .orphan, 0) - #expect(child6.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child6.linkedItem!.orphanFolders)") - - let child7 = child2.children[2] // empty <-> empty - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child7.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child7.linkedItem!.orphanFolders)") - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 1, 0, 0, 2, "utils", .orphan, 5) - #expect(child8.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 1, 0, 0, 0, 2, "utils", .orphan, 6) - #expect(child8.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.linkedItem!.orphanFolders)") - - let child9 = child8.children[0] // utils <-> utils - assertItem(child9, 0, 1, 0, 0, 1, "cell", .orphan, 5) - #expect(child9.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.orphanFolders)") - assertItem(child9.linkedItem, 1, 0, 0, 0, 1, "cell", .orphan, 6) - #expect(child9.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.linkedItem!.orphanFolders)") - - let child10 = child9.children[0] // cell <-> cell - assertItem(child10, 0, 1, 0, 0, 1, "popup", .orphan, 5) - #expect(child10.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.orphanFolders)") - assertItem(child10.linkedItem, 1, 0, 0, 0, 1, "popup", .orphan, 6) - #expect(child10.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.linkedItem!.orphanFolders)") - - let child11 = child10.children[0] // popup <-> popup - assertItem(child11, 0, 1, 0, 0, 0, "file", .changed, 5) - assertItem(child11.linkedItem, 1, 0, 0, 0, 0, "file", .old, 6) - - let child12 = child8.children[1] // utils <-> utils - assertItem(child12, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child12.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child12.orphanFolders)") - assertItem(child12.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child13 = child12.children[0] // view <-> (null) - assertItem(child13, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child13.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child13.orphanFolders)") - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child14 = child1.children[2] // l <-> r - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file", .orphan, 4) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 3) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 1, 0, 0, 3, "l", .orphan, 5) - #expect(child1.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 1, 0, 3, "r", .orphan, 10) - #expect(child1.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 3) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // empty <--> empty - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // empty <-> empty - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "empty1", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // (null) <--> empty1 - assertArrayCount(childVI4.children, 1) - let child4 = childVI4.item // (null) <-> empty1 - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 1, "empty2", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // (null) <--> empty2 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> empty2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child5.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child5.linkedItem!.orphanFolders)") - - let childVI6 = childVI2.children[1] // empty <--> empty - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // empty <-> empty - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "empty2", .orphan, 0) - #expect(child6.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child6.linkedItem!.orphanFolders)") - - let childVI7 = childVI2.children[2] // empty <--> empty - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // empty <-> empty - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child7.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child7.linkedItem!.orphanFolders)") - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 2) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 1, 0, 0, 2, "utils", .orphan, 5) - #expect(child8.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 1, 0, 0, 0, 2, "utils", .orphan, 6) - #expect(child8.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.linkedItem!.orphanFolders)") - - let childVI9 = childVI8.children[0] // utils <--> utils - assertArrayCount(childVI9.children, 1) - let child9 = childVI9.item // utils <-> utils - assertItem(child9, 0, 1, 0, 0, 1, "cell", .orphan, 5) - #expect(child9.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.orphanFolders)") - assertItem(child9.linkedItem, 1, 0, 0, 0, 1, "cell", .orphan, 6) - #expect(child9.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.linkedItem!.orphanFolders)") - - let childVI10 = childVI9.children[0] // cell <--> cell - assertArrayCount(childVI10.children, 1) - let child10 = childVI10.item // cell <-> cell - assertItem(child10, 0, 1, 0, 0, 1, "popup", .orphan, 5) - #expect(child10.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.orphanFolders)") - assertItem(child10.linkedItem, 1, 0, 0, 0, 1, "popup", .orphan, 6) - #expect(child10.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.linkedItem!.orphanFolders)") - - let childVI11 = childVI10.children[0] // popup <--> popup - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // popup <-> popup - assertItem(child11, 0, 1, 0, 0, 0, "file", .changed, 5) - assertItem(child11.linkedItem, 1, 0, 0, 0, 0, "file", .old, 6) - - let childVI12 = childVI8.children[1] // utils <--> utils - assertArrayCount(childVI12.children, 1) - let child12 = childVI12.item // utils <-> utils - assertItem(child12, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child12.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child12.orphanFolders)") - assertItem(child12.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI13 = childVI12.children[0] // view <--> (null) - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // view <-> (null) - assertItem(child13, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child13.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child13.orphanFolders)") - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI14 = childVI1.children[2] // l <--> r - assertArrayCount(childVI14.children, 0) - let child14 = childVI14.item // l <-> r - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file", .orphan, 4) - } - } - - @Test func moveFolderWithChild() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/empty") - try createFolder("r/empty") - try createFolder("r/empty/empty1") - try createFolder("r/empty/empty1/empty2") - try createFolder("r/empty/empty1/empty2/empty3") - try createFolder("r/empty/empty2") - try createFolder("r/empty/empty3") - try createFolder("l/utils") - try createFolder("r/utils") - try createFolder("l/utils/cell") - try createFolder("r/utils/cell") - try createFolder("l/utils/cell/popup") - try createFolder("r/utils/cell/popup") - try createFolder("l/utils/view") - try createFolder("l/utils/view/splitview") - - // create files - try createFile("l/utils/cell/popup/file", "12345") - try createFile("r/utils/cell/popup/file", "123456") - try setFileTimestamp("r/utils/cell/popup/file", "2001-03-24 10: 45: 32 +0600") - try createFile("r/file", "1234") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 1, 0, 0, 3, "l", .orphan, 5) - #expect(child1.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 1, 0, 3, "r", .orphan, 10) - #expect(child1.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // empty <-> empty - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "empty1", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // (null) <-> empty1 - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 1, "empty2", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child4.linkedItem!.orphanFolders)") - - let child5 = child4.children[0] // (null) <-> empty2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child5.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child5.linkedItem!.orphanFolders)") - - let child6 = child2.children[1] // empty <-> empty - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "empty2", .orphan, 0) - #expect(child6.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child6.linkedItem!.orphanFolders)") - - let child7 = child2.children[2] // empty <-> empty - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child7.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child7.linkedItem!.orphanFolders)") - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 1, 0, 0, 2, "utils", .orphan, 5) - #expect(child8.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 1, 0, 0, 0, 2, "utils", .orphan, 6) - #expect(child8.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.linkedItem!.orphanFolders)") - - let child9 = child8.children[0] // utils <-> utils - assertItem(child9, 0, 1, 0, 0, 1, "cell", .orphan, 5) - #expect(child9.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.orphanFolders)") - assertItem(child9.linkedItem, 1, 0, 0, 0, 1, "cell", .orphan, 6) - #expect(child9.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.linkedItem!.orphanFolders)") - - let child10 = child9.children[0] // cell <-> cell - assertItem(child10, 0, 1, 0, 0, 1, "popup", .orphan, 5) - #expect(child10.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.orphanFolders)") - assertItem(child10.linkedItem, 1, 0, 0, 0, 1, "popup", .orphan, 6) - #expect(child10.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.linkedItem!.orphanFolders)") - - let child11 = child10.children[0] // popup <-> popup - assertItem(child11, 0, 1, 0, 0, 0, "file", .changed, 5) - assertItem(child11.linkedItem, 1, 0, 0, 0, 0, "file", .old, 6) - - let child12 = child8.children[1] // utils <-> utils - operationElement = child12 - assertItem(child12, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child12.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child12.orphanFolders)") - assertItem(child12.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child13 = child12.children[0] // view <-> (null) - assertItem(child13, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child13.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child13.orphanFolders)") - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child14 = child1.children[2] // l <-> r - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file", .orphan, 4) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 3) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 1, 0, 0, 3, "l", .orphan, 5) - #expect(child1.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 1, 0, 3, "r", .orphan, 10) - #expect(child1.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 3) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // empty <--> empty - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // empty <-> empty - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "empty1", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // (null) <--> empty1 - assertArrayCount(childVI4.children, 1) - let child4 = childVI4.item // (null) <-> empty1 - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 1, "empty2", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // (null) <--> empty2 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> empty2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child5.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child5.linkedItem!.orphanFolders)") - - let childVI6 = childVI2.children[1] // empty <--> empty - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // empty <-> empty - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "empty2", .orphan, 0) - #expect(child6.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child6.linkedItem!.orphanFolders)") - - let childVI7 = childVI2.children[2] // empty <--> empty - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // empty <-> empty - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child7.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child7.linkedItem!.orphanFolders)") - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 2) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 1, 0, 0, 2, "utils", .orphan, 5) - #expect(child8.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 1, 0, 0, 0, 2, "utils", .orphan, 6) - #expect(child8.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.linkedItem!.orphanFolders)") - - let childVI9 = childVI8.children[0] // utils <--> utils - assertArrayCount(childVI9.children, 1) - let child9 = childVI9.item // utils <-> utils - assertItem(child9, 0, 1, 0, 0, 1, "cell", .orphan, 5) - #expect(child9.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.orphanFolders)") - assertItem(child9.linkedItem, 1, 0, 0, 0, 1, "cell", .orphan, 6) - #expect(child9.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.linkedItem!.orphanFolders)") - - let childVI10 = childVI9.children[0] // cell <--> cell - assertArrayCount(childVI10.children, 1) - let child10 = childVI10.item // cell <-> cell - assertItem(child10, 0, 1, 0, 0, 1, "popup", .orphan, 5) - #expect(child10.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.orphanFolders)") - assertItem(child10.linkedItem, 1, 0, 0, 0, 1, "popup", .orphan, 6) - #expect(child10.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.linkedItem!.orphanFolders)") - - let childVI11 = childVI10.children[0] // popup <--> popup - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // popup <-> popup - assertItem(child11, 0, 1, 0, 0, 0, "file", .changed, 5) - assertItem(child11.linkedItem, 1, 0, 0, 0, 0, "file", .old, 6) - - let childVI12 = childVI8.children[1] // utils <--> utils - assertArrayCount(childVI12.children, 1) - let child12 = childVI12.item // utils <-> utils - assertItem(child12, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child12.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child12.orphanFolders)") - assertItem(child12.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI13 = childVI12.children[0] // view <--> (null) - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // view <-> (null) - assertItem(child13, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child13.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child13.orphanFolders)") - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI14 = childVI1.children[2] // l <--> r - assertArrayCount(childVI14.children, 0) - let child14 = childVI14.item // l <-> r - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file", .orphan, 4) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - fileOperation.move( - srcRoot: operationElement, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 1, 0, 0, 3, "l", .orphan, 5) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 1, 0, 3, "r", .orphan, 10) - #expect(child1.linkedItem!.orphanFolders == 7, "OrphanFolder: Expected count \(7) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // empty <-> empty - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "empty1", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // (null) <-> empty1 - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 1, "empty2", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child4.linkedItem!.orphanFolders)") - - let child5 = child4.children[0] // (null) <-> empty2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child5.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child5.linkedItem!.orphanFolders)") - - let child6 = child2.children[1] // empty <-> empty - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "empty2", .orphan, 0) - #expect(child6.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child6.linkedItem!.orphanFolders)") - - let child7 = child2.children[2] // empty <-> empty - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child7.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child7.linkedItem!.orphanFolders)") - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 1, 0, 0, 2, "utils", .orphan, 5) - #expect(child8.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 1, 0, 0, 0, 2, "utils", .orphan, 6) - #expect(child8.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child8.linkedItem!.orphanFolders)") - - let child9 = child8.children[0] // utils <-> utils - assertItem(child9, 0, 1, 0, 0, 1, "cell", .orphan, 5) - #expect(child9.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.orphanFolders)") - assertItem(child9.linkedItem, 1, 0, 0, 0, 1, "cell", .orphan, 6) - #expect(child9.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.linkedItem!.orphanFolders)") - - let child10 = child9.children[0] // cell <-> cell - assertItem(child10, 0, 1, 0, 0, 1, "popup", .orphan, 5) - #expect(child10.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.orphanFolders)") - assertItem(child10.linkedItem, 1, 0, 0, 0, 1, "popup", .orphan, 6) - #expect(child10.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.linkedItem!.orphanFolders)") - - let child11 = child10.children[0] // popup <-> popup - assertItem(child11, 0, 1, 0, 0, 0, "file", .changed, 5) - assertItem(child11.linkedItem, 1, 0, 0, 0, 0, "file", .old, 6) - - let child12 = child8.children[1] // utils <-> utils - assertItem(child12, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child12.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child12.linkedItem!.orphanFolders)") - - let child13 = child12.children[0] // (null) <-> view - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child13.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child13.linkedItem!.orphanFolders)") - - let child14 = child1.children[2] // l <-> r - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file", .orphan, 4) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 3) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 1, 0, 0, 3, "l", .orphan, 5) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 1, 0, 3, "r", .orphan, 10) - #expect(child1.linkedItem!.orphanFolders == 7, "OrphanFolder: Expected count \(7) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 3) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "empty", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 5, "OrphanFolder: Expected count \(5) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // empty <--> empty - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // empty <-> empty - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "empty1", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // (null) <--> empty1 - assertArrayCount(childVI4.children, 1) - let child4 = childVI4.item // (null) <-> empty1 - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 1, "empty2", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child4.linkedItem!.orphanFolders)") - - let childVI5 = childVI4.children[0] // (null) <--> empty2 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> empty2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child5.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child5.linkedItem!.orphanFolders)") - - let childVI6 = childVI2.children[1] // empty <--> empty - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // empty <-> empty - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "empty2", .orphan, 0) - #expect(child6.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child6.linkedItem!.orphanFolders)") - - let childVI7 = childVI2.children[2] // empty <--> empty - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // empty <-> empty - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "empty3", .orphan, 0) - #expect(child7.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child7.linkedItem!.orphanFolders)") - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 2) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 1, 0, 0, 2, "utils", .orphan, 5) - #expect(child8.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child8.orphanFolders)") - assertItem(child8.linkedItem, 1, 0, 0, 0, 2, "utils", .orphan, 6) - #expect(child8.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child8.linkedItem!.orphanFolders)") - - let childVI9 = childVI8.children[0] // utils <--> utils - assertArrayCount(childVI9.children, 1) - let child9 = childVI9.item // utils <-> utils - assertItem(child9, 0, 1, 0, 0, 1, "cell", .orphan, 5) - #expect(child9.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.orphanFolders)") - assertItem(child9.linkedItem, 1, 0, 0, 0, 1, "cell", .orphan, 6) - #expect(child9.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child9.linkedItem!.orphanFolders)") - - let childVI10 = childVI9.children[0] // cell <--> cell - assertArrayCount(childVI10.children, 1) - let child10 = childVI10.item // cell <-> cell - assertItem(child10, 0, 1, 0, 0, 1, "popup", .orphan, 5) - #expect(child10.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.orphanFolders)") - assertItem(child10.linkedItem, 1, 0, 0, 0, 1, "popup", .orphan, 6) - #expect(child10.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child10.linkedItem!.orphanFolders)") - - let childVI11 = childVI10.children[0] // popup <--> popup - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // popup <-> popup - assertItem(child11, 0, 1, 0, 0, 0, "file", .changed, 5) - assertItem(child11.linkedItem, 1, 0, 0, 0, 0, "file", .old, 6) - - let childVI12 = childVI8.children[1] // utils <--> utils - assertArrayCount(childVI12.children, 1) - let child12 = childVI12.item // utils <-> utils - assertItem(child12, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child12.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child12.linkedItem!.orphanFolders)") - - let childVI13 = childVI12.children[0] // (null) <--> view - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // (null) <-> view - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child13.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child13.linkedItem!.orphanFolders)") - - let childVI14 = childVI1.children[2] // l <--> r - assertArrayCount(childVI14.children, 0) - let child14 = childVI14.item // l <-> r - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file", .orphan, 4) - } - } - - @Test func copy() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/utils") - try createFolder("r/utils") - try createFolder("l/utils/view") - try createFolder("l/utils/view/splitview") - - // create files - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // view <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // utils <--> utils - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // view <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // view <-> (null) - operationElement = child4 - assertItem(child4, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - fileOperation.copy( - srcRoot: operationElement, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // view <-> view - assertItem(child4, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // utils <--> utils - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - - let childVI4 = childVI3.children[0] // view <--> view - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // view <-> view - assertItem(child4, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "splitview", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - } - } - - @Test func deleteFolder() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/utils") - try createFolder("r/utils") - try createFolder("l/utils/view") - try createFolder("r/utils/view") - try createFolder("r/utils/view/splitView") - - // create files - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child3.linkedItem!.orphanFolders)") - - let child4 = child3.children[0] // view <-> view - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "splitView", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // utils <--> utils - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, "view", .orphan, 0) - #expect(child3.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child3.linkedItem!.orphanFolders)") - operationElement = child3.linkedItem! - - let childVI4 = childVI3.children[0] // view <--> view - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // view <-> view - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "splitView", .orphan, 0) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - fileOperation.delete( - operationElement, - baseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let child3 = child2.children[0] // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 0, "view", .orphan, 0) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - #expect(child1.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "r", .orphan, 0) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "utils", .orphan, 0) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - - let childVI3 = childVI2.children[0] // utils <--> utils - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // utils <-> utils - assertItem(child3, 0, 0, 0, 0, 0, "view", .orphan, 0) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/FiltersTests.swift b/Tests/Sources/FileSystem/Tests/FiltersTests.swift deleted file mode 100644 index 5e9eddb..0000000 --- a/Tests/Sources/FileSystem/Tests/FiltersTests.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// FiltersTests.swift -// VisualDiffer -// -// Created by davide ficano on 01/09/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -final class FiltersTests: BaseTests { - @Test func filterFileNameIgnoreCase() { - let root = CompareItem( - path: "/fakePath", - attrs: [.type: FileAttributeType.typeDirectory], - fileExtraOptions: [], - parent: nil - ) - let item = CompareItem( - path: "/fakePath/all_lower_case.txt", - attrs: [.type: FileAttributeType.typeRegular], - fileExtraOptions: [], - parent: root - ) - - let filter = #"fileName ==[c] "ALL_LOWER_CASE.txt""# - #expect(item.evaluate(filter: NSPredicate(format: filter))) - } - - @Test() func filterFileNameCaseSensitive() { - let root = CompareItem( - path: "/fakePath", - attrs: [.type: FileAttributeType.typeDirectory], - fileExtraOptions: [], - parent: nil - ) - let item = CompareItem( - path: "/fakePath/all_lower_case.txt", - attrs: [.type: FileAttributeType.typeRegular], - fileExtraOptions: [], - parent: root - ) - - let filter = #"fileName CONTAINS "ALL_LOWER_CASE.txt""# - #expect(item.evaluate(filter: NSPredicate(format: filter)) == false) - } - - @Test() func filterPathIgnoreCase() { - let root = CompareItem( - path: "/fakePath", - attrs: [.type: FileAttributeType.typeDirectory], - fileExtraOptions: [], - parent: nil - ) - let dirName1 = CompareItem( - path: "/fakePath/dirName1", - attrs: [.type: FileAttributeType.typeDirectory], - fileExtraOptions: [], - parent: root - ) - let item = CompareItem( - path: "/fakePath/dirName1/all_lower_case.txt", - attrs: [.type: FileAttributeType.typeRegular], - fileExtraOptions: [], - parent: dirName1 - ) - - let filter = #"pathRelativeToRoot CONTAINS[c] "DIRNAME1""# - #expect(item.evaluate(filter: NSPredicate(format: filter))) - } - - @Test() func filterPathCaseSensitive() { - let root = CompareItem( - path: "/fakePath", - attrs: [.type: FileAttributeType.typeDirectory], - fileExtraOptions: [], - parent: nil - ) - let dirName1 = CompareItem( - path: "/fakePath/dirName1", - attrs: [.type: FileAttributeType.typeDirectory], - fileExtraOptions: [], - parent: root - ) - let item = CompareItem( - path: "/fakePath/dirName1/all_lower_case.txt", - attrs: [.type: FileAttributeType.typeRegular], - fileExtraOptions: [], - parent: dirName1 - ) - - let filter = #"pathRelativeToRoot CONTAINS "DIRNAME1""# - #expect(item.evaluate(filter: NSPredicate(format: filter)) == false) - } - - @Test func filterFileSize() { - let attributes: [FileAttributeKey: Any] = [ - .size: NSNumber(value: 5), - ] - let item = CompareItem( - path: nil, - attrs: attributes, - fileExtraOptions: [], - parent: nil - ) - - let filter = "fileSize == 5" - #expect(item.evaluate(filter: NSPredicate(format: filter))) - } - - @Test func filterModificationDate() throws { - let attributes: [FileAttributeKey: Any] = try [ - .modificationDate: buildDate("2012-05-05 11: 00: 11 +0000"), - ] - let item = CompareItem( - path: nil, - attrs: attributes, - fileExtraOptions: [], - parent: nil - ) - - // after 2012-06-01 06:00 - let filter = #"fileObjectModificationDate > CAST(347518800.000000, "NSDate")"# - #expect(item.evaluate(filter: NSPredicate(format: filter))) - } -} diff --git a/Tests/Sources/FileSystem/Tests/MetaData/LabelsCopyTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/LabelsCopyTests.swift deleted file mode 100644 index afec523..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/LabelsCopyTests.swift +++ /dev/null @@ -1,501 +0,0 @@ -// -// LabelsCopyTests.swift -// VisualDiffer -// -// Created by davide ficano on 17/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class LabelsCopyTests: BaseTests { - @Test func copyFolderWithLabels() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase, .finderLabel], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/Level2") - try createFolder("r/Parent/Level2") - try createFolder("l/Parent/Level2/FolderWithLabels") - try createFolder("r/Parent/Level2/FolderWithLabels") - - // create files - try add(labelNumber: 2, fullPath: appendFolder("r/Parent/Level2")) - try add(labelNumber: 3, fullPath: appendFolder("r/Parent/Level2/FolderWithLabels")) - try createFile("l/Parent/Level2/FolderWithLabels/file1.txt", "123456") - try createFile("r/Parent/Level2/FolderWithLabels/file1.txt", "123456") - try add(labelNumber: 7, fullPath: appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - try createFile("l/Parent/Level2/FolderWithLabels/file2.txt", "1234567890") - try add(labelNumber: 3, fullPath: appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - try createFile("r/Parent/Level2/FolderWithLabels/file2.txt", "1234567890") - try add(labelNumber: 3, fullPath: appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - try createFile("l/Parent/line2.txt", "12345") - try createFile("r/Parent/line2.txt", "12345") - try add(labelNumber: 2, fullPath: appendFolder("r/Parent/line2.txt")) - try createFile("l/line1.txt", "12345") - try createFile("r/line1.txt", "12345") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 26) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 4, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 4, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 4, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 4, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let child3 = child2.children[0] // Parent <-> Parent - operationElement = child3 - assertItem(child3, 0, 0, 0, 1, 1, "Level2", .mismatchingLabels, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertFolderLabels(child3, true, "Level2") - assertMismatchingLabels(child3, 2, "Level2") - assertResourceFileLabels(child3, 0, appendFolder("l/Parent/Level2")) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "Level2", .mismatchingLabels, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, true, "Level2") - assertMismatchingLabels(child3.linkedItem, 2, "Level2") - assertResourceFileLabels(child3.linkedItem, 2, appendFolder("r/Parent/Level2")) - - let child4 = child3.children[0] // Level2 <-> Level2 - assertItem(child4, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertFolderLabels(child4, true, "FolderWithLabels") - assertMismatchingLabels(child4, 1, "FolderWithLabels") - assertResourceFileLabels(child4, 0, appendFolder("l/Parent/Level2/FolderWithLabels")) - assertItem(child4.linkedItem, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, true, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 1, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let child5 = child4.children[0] // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 1, "file1.txt") - assertResourceFileLabels(child5, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file1.txt")) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 1, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 7, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let child6 = child4.children[1] // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertResourceFileLabels(child6, 3, appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 26) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 4, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 4, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 4, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 4, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 1, "Level2", .mismatchingLabels, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertFolderLabels(child3, true, "Level2") - assertMismatchingLabels(child3, 2, "Level2") - assertResourceFileLabels(child3, 0, appendFolder("l/Parent/Level2")) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "Level2", .mismatchingLabels, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, true, "Level2") - assertMismatchingLabels(child3.linkedItem, 2, "Level2") - assertResourceFileLabels(child3.linkedItem, 2, appendFolder("r/Parent/Level2")) - - let childVI4 = childVI3.children[0] // Level2 <--> Level2 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // Level2 <-> Level2 - assertItem(child4, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertFolderLabels(child4, true, "FolderWithLabels") - assertMismatchingLabels(child4, 1, "FolderWithLabels") - assertResourceFileLabels(child4, 0, appendFolder("l/Parent/Level2/FolderWithLabels")) - assertItem(child4.linkedItem, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, true, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 1, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let childVI5 = childVI4.children[0] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 1, "file1.txt") - assertResourceFileLabels(child5, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file1.txt")) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 1, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 7, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let childVI6 = childVI4.children[1] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertResourceFileLabels(child6, 3, appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: operationElement, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 3, 2, "l", .orphan, 26) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 1, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 0, 3, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 1, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 2, 2, "Parent", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 1, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 0, 2, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 1, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 2, 1, "Level2", .orphan, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertFolderLabels(child3, false, "Level2") - assertMismatchingLabels(child3, 0, "Level2") - assertResourceFileLabels(child3, 0, appendFolder("l/Parent/Level2")) - assertItem(child3.linkedItem, 0, 0, 0, 2, 1, "Level2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, false, "Level2") - assertMismatchingLabels(child3.linkedItem, 0, "Level2") - assertResourceFileLabels(child3.linkedItem, 0, appendFolder("r/Parent/Level2")) - - let child4 = child3.children[0] // Level2 <-> Level2 - assertItem(child4, 0, 0, 0, 2, 2, "FolderWithLabels", .orphan, 16) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertFolderLabels(child4, false, "FolderWithLabels") - assertMismatchingLabels(child4, 0, "FolderWithLabels") - assertResourceFileLabels(child4, 0, appendFolder("l/Parent/Level2/FolderWithLabels")) - assertItem(child4.linkedItem, 0, 0, 0, 2, 2, "FolderWithLabels", .orphan, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, false, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 0, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let child5 = child4.children[0] // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 0, "file1.txt") - assertResourceFileLabels(child5, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file1.txt")) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 0, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let child6 = child4.children[1] // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertResourceFileLabels(child6, 3, appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 3, 2, "l", .orphan, 26) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 1, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 0, 3, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 1, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 2, 2, "Parent", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 1, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 0, 2, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 1, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 2, 1, "Level2", .orphan, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertFolderLabels(child3, false, "Level2") - assertMismatchingLabels(child3, 0, "Level2") - assertResourceFileLabels(child3, 0, appendFolder("l/Parent/Level2")) - assertItem(child3.linkedItem, 0, 0, 0, 2, 1, "Level2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, false, "Level2") - assertMismatchingLabels(child3.linkedItem, 0, "Level2") - assertResourceFileLabels(child3.linkedItem, 0, appendFolder("r/Parent/Level2")) - - let childVI4 = childVI3.children[0] // Level2 <--> Level2 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // Level2 <-> Level2 - assertItem(child4, 0, 0, 0, 2, 2, "FolderWithLabels", .orphan, 16) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertFolderLabels(child4, false, "FolderWithLabels") - assertMismatchingLabels(child4, 0, "FolderWithLabels") - assertResourceFileLabels(child4, 0, appendFolder("l/Parent/Level2/FolderWithLabels")) - assertItem(child4.linkedItem, 0, 0, 0, 2, 2, "FolderWithLabels", .orphan, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, false, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 0, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let childVI5 = childVI4.children[0] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 0, "file1.txt") - assertResourceFileLabels(child5, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file1.txt")) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 0, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let childVI6 = childVI4.children[1] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertResourceFileLabels(child6, 3, appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MetaData/LabelsCreateTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/LabelsCreateTests.swift deleted file mode 100644 index 4726102..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/LabelsCreateTests.swift +++ /dev/null @@ -1,219 +0,0 @@ -// -// LabelsCreateTests.swift -// VisualDiffer -// -// Created by davide ficano on 17/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping function_body_length -final class LabelsCreateTests: BaseTests { - @Test func createLabels() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase, .finderLabel], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithLabels") - try createFolder("r/Parent/FolderWithLabels") - - // create files - try add(labelNumber: 3, fullPath: appendFolder("r/Parent/FolderWithLabels")) - try createFile("l/Parent/FolderWithLabels/attachment_one.txt", "123456") - try createFile("r/Parent/FolderWithLabels/attachment_one.txt", "123456") - try createFile("l/Parent/FolderWithLabels/file1.txt", "123456") - try createFile("r/Parent/FolderWithLabels/file1.txt", "123456") - try add(labelNumber: 7, fullPath: appendFolder("r/Parent/FolderWithLabels/file1.txt")) - try createFile("l/Parent/FolderWithLabels/file2.txt", "1234567890") - try createFile("r/Parent/FolderWithLabels/file2.txt", "1234567890") - try createFile("l/line1.txt", "12345") - try createFile("r/line1.txt", "12345") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 3, 2, "l", .orphan, 27) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 3, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1, false, "r") - assertMismatchingLabels(child1, 2, "r") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 2, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 2, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 2, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 2, "Parent") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 2, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 2, 3, "FolderWithLabels", .mismatchingLabels, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 2, 3, "FolderWithLabels", .mismatchingLabels, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3, true, "FolderWithLabels") - assertMismatchingLabels(child3, 1, "FolderWithLabels") - assertFolderLabels(child3.linkedItem, true, "FolderWithLabels") - assertMismatchingLabels(child3.linkedItem, 1, "FolderWithLabels") - - let child4 = child3.children[0] // FolderWithLabels <-> FolderWithLabels - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderLabels(child4, false, "attachment_one.txt") - assertMismatchingLabels(child4, 0, "attachment_one.txt") - assertFolderLabels(child4.linkedItem, false, "attachment_one.txt") - assertMismatchingLabels(child4.linkedItem, 0, "attachment_one.txt") - - let child5 = child3.children[1] // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 1, "file1.txt") - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - - let child7 = child1.children[1] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child7, false, "line1.txt") - assertMismatchingLabels(child7, 0, "line1.txt") - assertFolderLabels(child7.linkedItem, false, "line1.txt") - assertMismatchingLabels(child7.linkedItem, 0, "line1.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 3, 2, "l", .orphan, 27) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 3, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1, false, "r") - assertMismatchingLabels(child1, 2, "r") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 2, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 2, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 2, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 2, "Parent") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 2, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 2, 3, "FolderWithLabels", .mismatchingLabels, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 2, 3, "FolderWithLabels", .mismatchingLabels, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3, true, "FolderWithLabels") - assertMismatchingLabels(child3, 1, "FolderWithLabels") - assertFolderLabels(child3.linkedItem, true, "FolderWithLabels") - assertMismatchingLabels(child3.linkedItem, 1, "FolderWithLabels") - - let childVI4 = childVI3.children[0] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithLabels <-> FolderWithLabels - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderLabels(child4, false, "attachment_one.txt") - assertMismatchingLabels(child4, 0, "attachment_one.txt") - assertFolderLabels(child4.linkedItem, false, "attachment_one.txt") - assertMismatchingLabels(child4.linkedItem, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 1, "file1.txt") - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 1, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - - let childVI7 = childVI1.children[1] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child7, false, "line1.txt") - assertMismatchingLabels(child7, 0, "line1.txt") - assertFolderLabels(child7.linkedItem, false, "line1.txt") - assertMismatchingLabels(child7.linkedItem, 0, "line1.txt") - } - } -} - -// swiftlint:enable force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MetaData/LabelsMoveTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/LabelsMoveTests.swift deleted file mode 100644 index 7b0baff..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/LabelsMoveTests.swift +++ /dev/null @@ -1,482 +0,0 @@ -// -// LabelsMoveTests.swift -// VisualDiffer -// -// Created by davide ficano on 17/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class LabelsMoveTests: BaseTests { - @Test func moveFolderWithLabels() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase, .finderLabel], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/Level2") - try createFolder("r/Parent/Level2") - try createFolder("l/Parent/Level2/FolderWithLabels") - try createFolder("r/Parent/Level2/FolderWithLabels") - - // create files - try add(labelNumber: 3, fullPath: appendFolder("r/Parent/Level2/FolderWithLabels")) - try createFile("l/Parent/Level2/FolderWithLabels/file1.txt", "123456") - try createFile("r/Parent/Level2/FolderWithLabels/file1.txt", "123456") - try add(labelNumber: 7, fullPath: appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - try createFile("l/Parent/Level2/FolderWithLabels/file2.txt", "1234567890") - try createFile("r/Parent/Level2/FolderWithLabels/file2.txt", "1234567890") - try createFile("l/Parent/line2.txt", "12345") - try createFile("r/Parent/line2.txt", "12345") - try add(labelNumber: 2, fullPath: appendFolder("r/Parent/line2.txt")) - try createFile("l/line1.txt", "12345") - try createFile("r/line1.txt", "12345") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 26) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 3, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 3, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 3, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 3, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 1, "Level2", .orphan, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertFolderLabels(child3, false, "Level2") - assertMismatchingLabels(child3, 2, "Level2") - assertResourceFileLabels(child3, 0, appendFolder("l/Parent/Level2")) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "Level2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, false, "Level2") - assertMismatchingLabels(child3.linkedItem, 2, "Level2") - assertResourceFileLabels(child3.linkedItem, 0, appendFolder("r/Parent/Level2")) - - let child4 = child3.children[0] // Level2 <-> Level2 - assertItem(child4, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertFolderLabels(child4, true, "FolderWithLabels") - assertMismatchingLabels(child4, 1, "FolderWithLabels") - assertResourceFileLabels(child4, 0, appendFolder("l/Parent/Level2/FolderWithLabels")) - assertItem(child4.linkedItem, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, true, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 1, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let child5 = child4.children[0] // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 1, "file1.txt") - assertResourceFileLabels(child5, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file1.txt")) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 1, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 7, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let child6 = child4.children[1] // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertResourceFileLabels(child6, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 26) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 3, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 3, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 3, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 0, 1, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 3, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // Parent <-> Parent - operationElement = child3 - assertItem(child3, 0, 0, 0, 1, 1, "Level2", .orphan, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertFolderLabels(child3, false, "Level2") - assertMismatchingLabels(child3, 2, "Level2") - assertResourceFileLabels(child3, 0, appendFolder("l/Parent/Level2")) - assertItem(child3.linkedItem, 0, 0, 0, 1, 1, "Level2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, false, "Level2") - assertMismatchingLabels(child3.linkedItem, 2, "Level2") - assertResourceFileLabels(child3.linkedItem, 0, appendFolder("r/Parent/Level2")) - - let childVI4 = childVI3.children[0] // Level2 <--> Level2 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // Level2 <-> Level2 - assertItem(child4, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.orphanFolders)") - assertFolderLabels(child4, true, "FolderWithLabels") - assertMismatchingLabels(child4, 1, "FolderWithLabels") - assertResourceFileLabels(child4, 0, appendFolder("l/Parent/Level2/FolderWithLabels")) - assertItem(child4.linkedItem, 0, 0, 0, 1, 2, "FolderWithLabels", .mismatchingLabels, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, true, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 1, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 3, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let childVI5 = childVI4.children[0] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithLabels <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5, false, "file1.txt") - assertMismatchingLabels(child5, 1, "file1.txt") - assertResourceFileLabels(child5, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file1.txt")) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 1, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 7, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let childVI6 = childVI4.children[1] // FolderWithLabels <--> FolderWithLabels - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithLabels <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6, false, "file2.txt") - assertMismatchingLabels(child6, 0, "file2.txt") - assertResourceFileLabels(child6, 0, appendFolder("l/Parent/Level2/FolderWithLabels/file2.txt")) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - fileOperation.move( - srcRoot: operationElement, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 1, 2, "l", .orphan, 10) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 1, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 2, 1, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 1, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 2, "Parent", .orphan, 5) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 1, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 2, 0, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 1, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertFolderLabels(child3, false, nil) - assertMismatchingLabels(child3, 0, nil) - assertItem(child3.linkedItem, 0, 0, 2, 0, 1, "Level2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, false, "Level2") - assertMismatchingLabels(child3.linkedItem, 0, "Level2") - assertResourceFileLabels(child3.linkedItem, 0, appendFolder("r/Parent/Level2")) - - let child4 = child3.children[0] // (null) <-> Level2 - assertItem(child4, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertFolderLabels(child4, false, nil) - assertMismatchingLabels(child4, 0, nil) - assertItem(child4.linkedItem, 0, 0, 2, 0, 2, "FolderWithLabels", .orphan, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, false, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 0, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let child5 = child4.children[0] // (null) <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertFolderLabels(child5, false, nil) - assertMismatchingLabels(child5, 0, nil) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 0, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let child6 = child4.children[1] // (null) <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertFolderLabels(child6, false, nil) - assertMismatchingLabels(child6, 0, nil) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let child8 = child1.children[1] // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 1, 2, "l", .orphan, 10) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertFolderLabels(child1, false, "l") - assertMismatchingLabels(child1, 1, "l") - assertResourceFileLabels(child1, 0, appendFolder("l")) - assertItem(child1.linkedItem, 0, 0, 2, 1, 2, "r", .orphan, 26) - #expect(child1.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child1.linkedItem!.orphanFolders)") - assertFolderLabels(child1.linkedItem, false, "r") - assertMismatchingLabels(child1.linkedItem, 1, "r") - assertResourceFileLabels(child1.linkedItem, 0, appendFolder("r")) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 2, "Parent", .orphan, 5) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertFolderLabels(child2, false, "Parent") - assertMismatchingLabels(child2, 1, "Parent") - assertResourceFileLabels(child2, 0, appendFolder("l/Parent")) - assertItem(child2.linkedItem, 0, 0, 2, 0, 2, "Parent", .orphan, 21) - #expect(child2.linkedItem!.orphanFolders == 2, "OrphanFolder: Expected count \(2) found \(child2.linkedItem!.orphanFolders)") - assertFolderLabels(child2.linkedItem, false, "Parent") - assertMismatchingLabels(child2.linkedItem, 1, "Parent") - assertResourceFileLabels(child2.linkedItem, 0, appendFolder("r/Parent")) - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertFolderLabels(child3, false, nil) - assertMismatchingLabels(child3, 0, nil) - assertItem(child3.linkedItem, 0, 0, 2, 0, 1, "Level2", .orphan, 16) - #expect(child3.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child3.linkedItem!.orphanFolders)") - assertFolderLabels(child3.linkedItem, false, "Level2") - assertMismatchingLabels(child3.linkedItem, 0, "Level2") - assertResourceFileLabels(child3.linkedItem, 0, appendFolder("r/Parent/Level2")) - - let childVI4 = childVI3.children[0] // (null) <--> Level2 - assertArrayCount(childVI4.children, 2) - let child4 = childVI4.item // (null) <-> Level2 - assertItem(child4, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertFolderLabels(child4, false, nil) - assertMismatchingLabels(child4, 0, nil) - assertItem(child4.linkedItem, 0, 0, 2, 0, 2, "FolderWithLabels", .orphan, 16) - #expect(child4.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child4.linkedItem!.orphanFolders)") - assertFolderLabels(child4.linkedItem, false, "FolderWithLabels") - assertMismatchingLabels(child4.linkedItem, 0, "FolderWithLabels") - assertResourceFileLabels(child4.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels")) - - let childVI5 = childVI4.children[0] // (null) <--> FolderWithLabels - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> FolderWithLabels - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertFolderLabels(child5, false, nil) - assertMismatchingLabels(child5, 0, nil) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - assertFolderLabels(child5.linkedItem, false, "file1.txt") - assertMismatchingLabels(child5.linkedItem, 0, "file1.txt") - assertResourceFileLabels(child5.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file1.txt")) - - let childVI6 = childVI4.children[1] // (null) <--> FolderWithLabels - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // (null) <-> FolderWithLabels - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertFolderLabels(child6, false, nil) - assertMismatchingLabels(child6, 0, nil) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 10) - assertFolderLabels(child6.linkedItem, false, "file2.txt") - assertMismatchingLabels(child6.linkedItem, 0, "file2.txt") - assertResourceFileLabels(child6.linkedItem, 0, appendFolder("r/Parent/Level2/FolderWithLabels/file2.txt")) - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7, false, "line2.txt") - assertMismatchingLabels(child7, 1, "line2.txt") - assertResourceFileLabels(child7, 0, appendFolder("l/Parent/line2.txt")) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "line2.txt", .same, 5) - assertFolderLabels(child7.linkedItem, false, "line2.txt") - assertMismatchingLabels(child7.linkedItem, 1, "line2.txt") - assertResourceFileLabels(child7.linkedItem, 2, appendFolder("r/Parent/line2.txt")) - - let childVI8 = childVI1.children[1] // l <--> r - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // l <-> r - assertItem(child8, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8, false, "line1.txt") - assertMismatchingLabels(child8, 0, "line1.txt") - assertResourceFileLabels(child8, 0, appendFolder("l/line1.txt")) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertFolderLabels(child8.linkedItem, false, "line1.txt") - assertMismatchingLabels(child8.linkedItem, 0, "line1.txt") - assertResourceFileLabels(child8.linkedItem, 0, appendFolder("r/line1.txt")) - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MetaData/TagsCopyTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/TagsCopyTests.swift deleted file mode 100644 index 43392a8..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/TagsCopyTests.swift +++ /dev/null @@ -1,589 +0,0 @@ -// -// TagsCopyTests.swift -// VisualDiffer -// -// Created by davide ficano on 06/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class TagsCopyTests: BaseTests { - @Test func copyFileWithTags() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .finderTags, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithTags") - try createFolder("r/Parent/FolderWithTags") - - // create files - try add(tags: ["Red"], fullPath: appendFolder("l/Parent/FolderWithTags")) - try createFile("l/Parent/FolderWithTags/attachment_one.txt", "1234567") - try createFile("r/Parent/FolderWithTags/attachment_one.txt", "123456") - try setFileTimestamp("r/Parent/FolderWithTags/attachment_one.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("l/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file1.txt")) - try createFile("r/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file1.txt")) - try createFile("l/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file2.txt")) - try createFile("r/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file2.txt")) - try createFile("l/line1.txt", "12345") - try createFile("r/line1.txt", "12345") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 1, 0, 1, 2, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 0, 1, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 3, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 1, 0, 0, 1, "Parent", .orphan, 23) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 0, 0, 0, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 3, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 1, 0, 0, 3, "FolderWithTags", .mismatchingTags, 23) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 0, 0, 0, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 1, 0, 0, 0, "attachment_one.txt", .changed, 7) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "attachment_one.txt", .old, 6) - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - operationElement = child5 - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertMismatchingTags(child5, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let child7 = child1.children[1] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 1, 0, 1, 2, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 0, 1, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 3, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 1, 0, 0, 1, "Parent", .orphan, 23) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 0, 0, 0, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 3, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 1, 0, 0, 3, "FolderWithTags", .mismatchingTags, 23) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 0, 0, 0, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 1, 0, 0, 0, "attachment_one.txt", .changed, 7) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "attachment_one.txt", .old, 6) - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertMismatchingTags(child5, 1, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let childVI7 = childVI1.children[1] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.copy( - srcRoot: operationElement, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 1, 0, 2, 2, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 0, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 2, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 1, 0, 1, 1, "Parent", .orphan, 23) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 2, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 1, 0, 1, 3, "FolderWithTags", .mismatchingTags, 23) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 1, 0, 0, 0, "attachment_one.txt", .changed, 7) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "attachment_one.txt", .old, 6) - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertMismatchingTags(child5, 0, "file1.txt") - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let child7 = child1.children[1] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 1, 0, 2, 2, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 0, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 2, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 1, 0, 1, 1, "Parent", .orphan, 23) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 2, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 1, 0, 1, 3, "FolderWithTags", .mismatchingTags, 23) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 1, 0, 0, 0, "attachment_one.txt", .changed, 7) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "attachment_one.txt", .old, 6) - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let childVI7 = childVI1.children[1] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - } - - @Test func copyFolderWithTags() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .finderTags, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithTags") - try createFolder("r/Parent/FolderWithTags") - - // create files - try add(tags: ["Red"], fullPath: appendFolder("l/Parent/FolderWithTags")) - try createFile("l/Parent/FolderWithTags/attachment_one.txt", "1234567") - try createFile("r/Parent/FolderWithTags/attachment_one.txt", "123456") - try setFileTimestamp("r/Parent/FolderWithTags/attachment_one.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("l/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file1.txt")) - try createFile("r/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file1.txt")) - try createFile("l/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file2.txt")) - try createFile("r/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file2.txt")) - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 1, 0, 0, 1, "l", .orphan, 23) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 0, 0, 1, "r", .orphan, 22) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 3, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 1, 0, 0, 1, "Parent", .orphan, 23) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 0, 0, 0, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 3, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - operationElement = child3.linkedItem! - assertItem(child3, 0, 1, 0, 0, 3, "FolderWithTags", .mismatchingTags, 23) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 0, 0, 0, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 1, 0, 0, 0, "attachment_one.txt", .changed, 7) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "attachment_one.txt", .old, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 1, "file2.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 1, 0, 0, 1, "l", .orphan, 23) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 1, 0, 0, 0, 1, "r", .orphan, 22) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 3, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 1, 0, 0, 1, "Parent", .orphan, 23) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 1, 0, 0, 0, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 3, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 1, 0, 0, 3, "FolderWithTags", .mismatchingTags, 23) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 1, 0, 0, 0, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 1, 0, 0, 0, "attachment_one.txt", .changed, 7) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "attachment_one.txt", .old, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 1, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 1, "file2.txt") - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = CopyCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - fileOperation.copy( - srcRoot: operationElement, - srcBaseDir: appendFolder("r"), - destBaseDir: appendFolder("l") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 3, 1, "l", .orphan, 22) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 3, 1, "r", .orphan, 22) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 0, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 3, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 3, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 0, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 3, 3, "FolderWithTags", .orphan, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 3, 3, "FolderWithTags", .orphan, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, false, "FolderWithTags") - assertMismatchingTags(child3, 0, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 0, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 3, 1, "l", .orphan, 22) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 3, 1, "r", .orphan, 22) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 0, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 3, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 3, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 0, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 3, 3, "FolderWithTags", .orphan, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 3, 3, "FolderWithTags", .orphan, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, false, "FolderWithTags") - assertMismatchingTags(child3, 0, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 0, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MetaData/TagsCreateTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/TagsCreateTests.swift deleted file mode 100644 index f0d922d..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/TagsCreateTests.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// TagsCreateTests.swift -// VisualDiffer -// -// Created by davide ficano on 06/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping function_body_length -final class TagsCreateTests: BaseTests { - @Test func createTags() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .finderTags, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithTags") - try createFolder("r/Parent/FolderWithTags") - - // create files - try add(tags: ["Red"], fullPath: appendFolder("l/Parent/FolderWithTags")) - try createFile("l/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("r/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("l/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file1.txt")) - try createFile("r/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file1.txt")) - try createFile("l/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file2.txt")) - try createFile("r/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file2.txt")) - try createFile("l/line1.txt", "12345") - try createFile("r/line1.txt", "12345") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 27) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 3, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 3, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertMismatchingTags(child5, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let child7 = child1.children[1] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 27) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 3, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 3, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertMismatchingTags(child5, 1, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let childVI7 = childVI1.children[1] // l <--> r - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - } -} - -// swiftlint:enable force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MetaData/TagsDeleteTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/TagsDeleteTests.swift deleted file mode 100644 index 9a573da..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/TagsDeleteTests.swift +++ /dev/null @@ -1,582 +0,0 @@ -// -// TagsDeleteTests.swift -// VisualDiffer -// -// Created by davide ficano on 06/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class TagsDeleteTests: BaseTests { - @Test func deleteFileWithTags() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .finderTags, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithTags") - try createFolder("r/Parent/FolderWithTags") - - // create files - try add(tags: ["Red"], fullPath: appendFolder("l/Parent/FolderWithTags")) - try createFile("l/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("r/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("l/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file1.txt")) - try createFile("r/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file1.txt")) - try createFile("l/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow"], fullPath: appendFolder("l/Parent/FolderWithTags/file2.txt")) - try createFile("r/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file2.txt")) - try createFile("l/line1.txt", "12345") - try createFile("r/line1.txt", "12345") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 27) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 3, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 3, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - operationElement = child5 - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertMismatchingTags(child5, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let child7 = child1.children[1] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 27) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 3, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 3, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 2, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertMismatchingTags(child4, 1, "file1.txt") - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child5, 1, "file2.txt") - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - operationElement, - baseDir: appendFolder("l") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 21) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 1, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 2, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 1, "Parent", .orphan, 16) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 1, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 2, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 1, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - operationElement = child5 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child6, 1, "file2.txt") - - let child7 = child1.children[1] // l <-> r - assertItem(child7, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "line1.txt", .same, 5) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 2, "l", .orphan, 21) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 1, 2, 2, "r", .orphan, 27) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertMismatchingTags(child1, 2, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 1, "Parent", .orphan, 16) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 1, 1, 1, "Parent", .orphan, 22) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertMismatchingTags(child2, 2, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 1, 3, "FolderWithTags", .mismatchingTags, 16) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 1, 1, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file2.txt", .same, 10) - assertMismatchingTags(child5, 1, "file2.txt") - } - } - - @Test func deleteFolderWithTags() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .finderTags, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithTags") - try createFolder("r/Parent/FolderWithTags") - - // create files - try add(tags: ["Red"], fullPath: appendFolder("l/Parent/FolderWithTags")) - try createFile("l/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("r/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("l/Parent/FolderWithTags/file1.txt", "123456") - try createFile("r/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file1.txt")) - try createFile("l/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("l/Parent/FolderWithTags/file2.txt")) - try createFile("r/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file2.txt")) - try createFile("l/Parent/anotherFile.txt", "123456") - try add(tags: ["Blue", "Purple"], fullPath: appendFolder("l/Parent/anotherFile.txt")) - try createFile("r/Parent/anotherFile.txt", "123456") - try add(tags: ["Blue"], fullPath: appendFolder("r/Parent/anotherFile.txt")) - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 1, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 3, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 3, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 1, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 3, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 3, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - operationElement = child3 - assertItem(child3, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 1, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = DeleteCompareItem(operationManager: fileOperationManager) - - fileOperation.delete( - operationElement, - baseDir: appendFolder("l") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 6) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 0, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 1, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 2, "Parent", .orphan, 6) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 0, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 1, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "FolderWithTags", .orphan, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, false, "FolderWithTags") - assertMismatchingTags(child3, 0, "FolderWithTags") - - let child4 = child3.children[0] // (null) <-> FolderWithTags - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "attachment_one.txt", .orphan, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let child5 = child3.children[1] // (null) <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 0, "file1.txt") - - let child6 = child3.children[2] // (null) <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 6) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 0, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 1, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 2, "Parent", .orphan, 6) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 0, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 1, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "FolderWithTags", .orphan, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, false, "FolderWithTags") - assertMismatchingTags(child3, 0, "FolderWithTags") - - let childVI4 = childVI3.children[0] // (null) <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // (null) <-> FolderWithTags - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "attachment_one.txt", .orphan, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // (null) <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 0, "file1.txt") - - let childVI6 = childVI3.children[2] // (null) <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // (null) <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MetaData/TagsMoveTests.swift b/Tests/Sources/FileSystem/Tests/MetaData/TagsMoveTests.swift deleted file mode 100644 index 8834896..0000000 --- a/Tests/Sources/FileSystem/Tests/MetaData/TagsMoveTests.swift +++ /dev/null @@ -1,359 +0,0 @@ -// -// TagsMoveTests.swift -// VisualDiffer -// -// Created by davide ficano on 06/12/21. -// Copyright (c) 2021 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping function_body_length -final class TagsMoveTests: BaseTests { - @Test func moveFolderWithTags() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .finderTags, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 9100, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - try createFolder("l/Parent") - try createFolder("r/Parent") - try createFolder("l/Parent/FolderWithTags") - try createFolder("r/Parent/FolderWithTags") - - // create files - try add(tags: ["Red"], fullPath: appendFolder("l/Parent/FolderWithTags")) - try createFile("l/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("r/Parent/FolderWithTags/attachment_one.txt", "123456") - try createFile("l/Parent/FolderWithTags/file1.txt", "123456") - try createFile("r/Parent/FolderWithTags/file1.txt", "123456") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file1.txt")) - try createFile("l/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("l/Parent/FolderWithTags/file2.txt")) - try createFile("r/Parent/FolderWithTags/file2.txt", "1234567890") - try add(tags: ["Yellow", "Red"], fullPath: appendFolder("r/Parent/FolderWithTags/file2.txt")) - try createFile("l/Parent/anotherFile.txt", "123456") - try add(tags: ["Blue", "Purple"], fullPath: appendFolder("l/Parent/anotherFile.txt")) - try createFile("r/Parent/anotherFile.txt", "123456") - try add(tags: ["Blue"], fullPath: appendFolder("r/Parent/anotherFile.txt")) - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var operationElement: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 2, 1, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 3, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 3, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let child4 = child3.children[0] // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let child5 = child3.children[1] // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 1, "file1.txt") - - let child6 = child3.children[2] // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 2, 1, "l", .orphan, 28) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 0, 2, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 3, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 0, 2, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 3, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - operationElement = child3 - assertItem(child3, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.orphanFolders)") - assertItem(child3.linkedItem, 0, 0, 0, 2, 3, "FolderWithTags", .mismatchingTags, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, true, "FolderWithTags") - assertMismatchingTags(child3, 1, "FolderWithTags") - - let childVI4 = childVI3.children[0] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // FolderWithTags <-> FolderWithTags - assertItem(child4, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "attachment_one.txt", .same, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // FolderWithTags <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "file1.txt", .same, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 1, "file1.txt") - - let childVI6 = childVI3.children[2] // FolderWithTags <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // FolderWithTags <-> FolderWithTags - assertItem(child6, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file2.txt", .same, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: operationElement, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 6) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 0, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 1, "r") - assertFolderTags(child1.linkedItem, false, "r") - assertMismatchingTags(child1.linkedItem, 1, "r") - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 2, "Parent", .orphan, 6) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 0, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 1, "Parent") - assertFolderTags(child2.linkedItem, false, "Parent") - assertMismatchingTags(child2.linkedItem, 1, "Parent") - - let child3 = child2.children[0] // Parent <-> Parent - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "FolderWithTags", .orphan, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, false, "FolderWithTags") - assertMismatchingTags(child3, 0, "FolderWithTags") - assertFolderTags(child3.linkedItem, false, "FolderWithTags") - assertMismatchingTags(child3.linkedItem, 0, "FolderWithTags") - - let child4 = child3.children[0] // (null) <-> FolderWithTags - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "attachment_one.txt", .orphan, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - assertFolderTags(child4.linkedItem, false, "attachment_one.txt") - assertMismatchingTags(child4.linkedItem, 0, "attachment_one.txt") - - let child5 = child3.children[1] // (null) <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 0, "file1.txt") - assertFolderTags(child5.linkedItem, false, "file1.txt") - assertMismatchingTags(child5.linkedItem, 0, "file1.txt") - - let child6 = child3.children[2] // (null) <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - assertFolderTags(child6.linkedItem, false, "file2.txt") - assertMismatchingTags(child6.linkedItem, 0, "file2.txt") - - let child7 = child2.children[1] // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - assertFolderTags(child7.linkedItem, false, "anotherFile.txt") - assertMismatchingTags(child7.linkedItem, 1, "anotherFile.txt") - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 6) - #expect(child1.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child1.orphanFolders)") - assertItem(child1.linkedItem, 0, 0, 3, 0, 1, "r", .orphan, 28) - #expect(child1.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child1.linkedItem!.orphanFolders)") - assertFolderTags(child1, false, "r") - assertMismatchingTags(child1, 1, "r") - assertFolderTags(child1.linkedItem, false, "r") - assertMismatchingTags(child1.linkedItem, 1, "r") - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 2, "Parent", .orphan, 6) - #expect(child2.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child2.orphanFolders)") - assertItem(child2.linkedItem, 0, 0, 3, 0, 2, "Parent", .orphan, 28) - #expect(child2.linkedItem!.orphanFolders == 1, "OrphanFolder: Expected count \(1) found \(child2.linkedItem!.orphanFolders)") - assertFolderTags(child2, false, "Parent") - assertMismatchingTags(child2, 1, "Parent") - assertFolderTags(child2.linkedItem, false, "Parent") - assertMismatchingTags(child2.linkedItem, 1, "Parent") - - let childVI3 = childVI2.children[0] // Parent <--> Parent - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // Parent <-> Parent - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "FolderWithTags", .orphan, 22) - #expect(child3.linkedItem!.orphanFolders == 0, "OrphanFolder: Expected count \(0) found \(child3.linkedItem!.orphanFolders)") - assertFolderTags(child3, false, "FolderWithTags") - assertMismatchingTags(child3, 0, "FolderWithTags") - assertFolderTags(child3.linkedItem, false, "FolderWithTags") - assertMismatchingTags(child3.linkedItem, 0, "FolderWithTags") - - let childVI4 = childVI3.children[0] // (null) <--> FolderWithTags - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // (null) <-> FolderWithTags - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "attachment_one.txt", .orphan, 6) - assertFolderTags(child4, false, "attachment_one.txt") - assertMismatchingTags(child4, 0, "attachment_one.txt") - assertFolderTags(child4.linkedItem, false, "attachment_one.txt") - assertMismatchingTags(child4.linkedItem, 0, "attachment_one.txt") - - let childVI5 = childVI3.children[1] // (null) <--> FolderWithTags - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> FolderWithTags - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file1.txt", .orphan, 6) - assertFolderTags(child5, false, "file1.txt") - assertMismatchingTags(child5, 0, "file1.txt") - assertFolderTags(child5.linkedItem, false, "file1.txt") - assertMismatchingTags(child5.linkedItem, 0, "file1.txt") - - let childVI6 = childVI3.children[2] // (null) <--> FolderWithTags - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // (null) <-> FolderWithTags - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file2.txt", .orphan, 10) - assertFolderTags(child6, false, "file2.txt") - assertMismatchingTags(child6, 0, "file2.txt") - assertFolderTags(child6.linkedItem, false, "file2.txt") - assertMismatchingTags(child6.linkedItem, 0, "file2.txt") - - let childVI7 = childVI2.children[1] // Parent <--> Parent - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // Parent <-> Parent - assertItem(child7, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "anotherFile.txt", .same, 6) - assertFolderTags(child7, false, "anotherFile.txt") - assertMismatchingTags(child7, 1, "anotherFile.txt") - assertFolderTags(child7.linkedItem, false, "anotherFile.txt") - assertMismatchingTags(child7.linkedItem, 1, "anotherFile.txt") - } - } -} - -// swiftlint:enable force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/MoveFilesTests.swift b/Tests/Sources/FileSystem/Tests/MoveFilesTests.swift deleted file mode 100644 index cdd4b32..0000000 --- a/Tests/Sources/FileSystem/Tests/MoveFilesTests.swift +++ /dev/null @@ -1,1988 +0,0 @@ -// -// MoveFilesTests.swift -// VisualDiffer -// -// Created by davide ficano on 11/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class MoveFilesTests: BaseTests { - @Test func moveFilesPresentOnBothSidesWithFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder_1/folder_1_1/folder_2_1") - try createFolder("l/folder_1/folder_1_2") - - try createFolder("r/folder_1/folder_1_1/folder_2_1") - try createFolder("r/folder_1/folder_1_2") - - // create files - try createFile("l/folder_1/folder_1_1/folder_2_1/file_changed.m", "1234567890") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_matched.txt", "1") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "1234567") - try createFile("l/folder_1/folder_1_2/match_2_1.m", "12") - try createFile("l/folder_1/file.txt", "123") - - try createFile("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "1") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_matched.txt", "1") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_older.txt", "1234") - try createFile("r/folder_1/folder_1_2/match_2_1.m", "12") - try createFile("r/folder_1/file.txt", "123") - try createFile("r/folder_1/right_orphan.txt", "1234567890") - - try setFileTimestamp("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "2001-03-24 10: 45: 32 +0600") - try setFileTimestamp("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var l = rootL.children[0] - assertItem(l, 1, 1, 0, 3, 4, "folder_1", .orphan, 23) - assertItem(l.linkedItem, 1, 1, 1, 3, 4, "folder_1", .orphan, 21) - - var child1 = l.children[0] - assertItem(child1, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 18) - assertItem(child1.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 6) - - var child2 = child1.children[0] - assertItem(child2, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 18) - assertItem(child2.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 6) - - var child3 = child2.children[0] - assertItem(child3, 0, 1, 0, 0, 0, "file_changed.m", .changed, 10) - assertItem(child3.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 1) - - var child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 1, 0, "file_matched.txt", .same, 1) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 1) - - var child5 = child2.children[2] - assertItem(child5, 1, 0, 0, 0, 0, "file_older.txt", .old, 7) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 4) - - var child6 = l.children[1] - assertItem(child6, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - - var child7 = child6.children[0] - assertItem(child7, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - - var child8 = l.children[2] - assertItem(child8, 0, 0, 0, 1, 0, "file.txt", .same, 3) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 3) - - var child9 = l.children[3] - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 10) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 4) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 1, 0, 3, 4, "folder_1", .orphan, 23) - assertItem(child1.linkedItem, 1, 1, 1, 3, 4, "folder_1", .orphan, 21) - - let childVI2 = childVI1.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_1 <-> folder_1 - assertItem(child2, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 18) - assertItem(child2.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 6) - - let childVI3 = childVI2.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // folder_1_1 <-> folder_1_1 - assertItem(child3, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 18) - assertItem(child3.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 6) - - let childVI4 = childVI3.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder_2_1 <-> folder_2_1 - assertItem(child4, 0, 1, 0, 0, 0, "file_changed.m", .changed, 10) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 1) - - let childVI5 = childVI3.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 0, 0, 1, 0, "file_matched.txt", .same, 1) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 1) - - let childVI6 = childVI3.children[2] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder_2_1 <-> folder_2_1 - assertItem(child6, 1, 0, 0, 0, 0, "file_older.txt", .old, 7) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 4) - - let childVI7 = childVI1.children[1] // folder_1 <--> folder_1 - assertArrayCount(childVI7.children, 1) - let child7 = childVI7.item // folder_1 <-> folder_1 - assertItem(child7, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - assertItem(child7.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - - let childVI8 = childVI7.children[0] // folder_1_2 <--> folder_1_2 - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // folder_1_2 <-> folder_1_2 - assertItem(child8, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - - let childVI9 = childVI1.children[2] // folder_1 <--> folder_1 - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // folder_1 <-> folder_1 - assertItem(child9, 0, 0, 0, 1, 0, "file.txt", .same, 3) - assertItem(child9.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 3) - - let childVI10 = childVI1.children[3] // folder_1 <--> folder_1 - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // folder_1 <-> folder_1 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 10) - } - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child1, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - l = rootL.children[0] - assertItem(l, 0, 0, 0, 2, 4, "folder_1", .orphan, 5) - assertItem(l.linkedItem, 0, 0, 4, 2, 4, "folder_1", .orphan, 33) - - child1 = l.children[0] - assertItem(child1, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 3, 0, 1, "folder_1_1", .orphan, 18) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 3, 0, 3, "folder_2_1", .orphan, 18) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 10) - - child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file_matched.txt", .orphan, 1) - - child5 = child2.children[2] - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 7) - - child6 = l.children[1] - assertItem(child6, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - - child7 = child6.children[0] - assertItem(child7, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - - child8 = l.children[2] - assertItem(child8, 0, 0, 0, 1, 0, "file.txt", .same, 3) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 3) - - child9 = l.children[3] - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 10) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 4) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 2, 4, "folder_1", .orphan, 5) - assertItem(child1.linkedItem, 0, 0, 4, 2, 4, "folder_1", .orphan, 33) - - let childVI2 = childVI1.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_1 <-> folder_1 - assertItem(child2, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 3, 0, 1, "folder_1_1", .orphan, 18) - - let childVI3 = childVI2.children[0] // (null) <--> folder_1_1 - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // (null) <-> folder_1_1 - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "folder_2_1", .orphan, 18) - - let childVI4 = childVI3.children[0] // (null) <--> folder_2_1 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // (null) <-> folder_2_1 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 10) - - let childVI5 = childVI3.children[1] // (null) <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> folder_2_1 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_matched.txt", .orphan, 1) - - let childVI6 = childVI3.children[2] // (null) <--> folder_2_1 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // (null) <-> folder_2_1 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 7) - - let childVI7 = childVI1.children[1] // folder_1 <--> folder_1 - assertArrayCount(childVI7.children, 1) - let child7 = childVI7.item // folder_1 <-> folder_1 - assertItem(child7, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - assertItem(child7.linkedItem, 0, 0, 0, 1, 1, "folder_1_2", .orphan, 2) - - let childVI8 = childVI7.children[0] // folder_1_2 <--> folder_1_2 - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // folder_1_2 <-> folder_1_2 - assertItem(child8, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "match_2_1.m", .same, 2) - - let childVI9 = childVI1.children[2] // folder_1 <--> folder_1 - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // folder_1 <-> folder_1 - assertItem(child9, 0, 0, 0, 1, 0, "file.txt", .same, 3) - assertItem(child9.linkedItem, 0, 0, 0, 1, 0, "file.txt", .same, 3) - - let childVI10 = childVI1.children[3] // folder_1 <--> folder_1 - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // folder_1 <-> folder_1 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "right_orphan.txt", .orphan, 10) - } - } - - @Test func moveFilesPresentOnBothSides() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder_1/folder_1_1/folder_2_1") - - try createFolder("r/folder_1/folder_1_1/folder_2_1") - - // create files - try createFile("l/folder_1/folder_1_1/folder_2_1/file_matched.txt", "12") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "12345") - - try createFile("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "1") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_matched.txt", "12") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_older.txt", "1") - - try setFileTimestamp("l/folder_1/folder_1_1/folder_2_1/file_older.txt", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var l = rootL.children[0] - assertItem(l, 1, 0, 0, 1, 1, "folder_1", .orphan, 7) - assertItem(l.linkedItem, 0, 1, 1, 1, 1, "folder_1", .orphan, 4) - - var child1 = l.children[0] - assertItem(child1, 1, 0, 0, 1, 1, "folder_1_1", .orphan, 7) - assertItem(child1.linkedItem, 0, 1, 1, 1, 1, "folder_1_1", .orphan, 4) - - var child2 = child1.children[0] - assertItem(child2, 1, 0, 0, 1, 3, "folder_2_1", .orphan, 7) - assertItem(child2.linkedItem, 0, 1, 1, 1, 3, "folder_2_1", .orphan, 4) - - var child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 1) - - var child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - - var child5 = child2.children[2] - assertItem(child5, 1, 0, 0, 0, 0, "file_older.txt", .old, 5) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 1) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 0, 0, 1, 1, "folder_1", .orphan, 7) - assertItem(child1.linkedItem, 0, 1, 1, 1, 1, "folder_1", .orphan, 4) - - let childVI2 = childVI1.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_1 <-> folder_1 - assertItem(child2, 1, 0, 0, 1, 1, "folder_1_1", .orphan, 7) - assertItem(child2.linkedItem, 0, 1, 1, 1, 1, "folder_1_1", .orphan, 4) - - let childVI3 = childVI2.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // folder_1_1 <-> folder_1_1 - assertItem(child3, 1, 0, 0, 1, 3, "folder_2_1", .orphan, 7) - assertItem(child3.linkedItem, 0, 1, 1, 1, 3, "folder_2_1", .orphan, 4) - - let childVI4 = childVI3.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder_2_1 <-> folder_2_1 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 1) - - let childVI5 = childVI3.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 1, 0, 0, 0, 0, "file_older.txt", .old, 5) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file_older.txt", .changed, 1) - } - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child1, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - l = rootL.children[0] - assertItem(l, 0, 0, 0, 1, 1, "folder_1", .orphan, 2) - assertItem(l.linkedItem, 0, 0, 2, 1, 1, "folder_1", .orphan, 8) - - child1 = l.children[0] - assertItem(child1, 0, 0, 0, 1, 1, "folder_1_1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 2, 1, 1, "folder_1_1", .orphan, 8) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 1, 3, "folder_2_1", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 1, 3, "folder_2_1", .orphan, 8) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 1) - - child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_matched.txt", .same, 2) - - child5 = child2.children[2] - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 5) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 1, 1, "folder_1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 2, 1, 1, "folder_1", .orphan, 8) - - let childVI2 = childVI1.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_1 <-> folder_1 - assertItem(child2, 0, 0, 0, 1, 1, "folder_1_1", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 1, 1, "folder_1_1", .orphan, 8) - - let childVI3 = childVI2.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // folder_1_1 <-> folder_1_1 - assertItem(child3, 0, 0, 0, 1, 3, "folder_2_1", .orphan, 2) - assertItem(child3.linkedItem, 0, 0, 2, 1, 3, "folder_2_1", .orphan, 8) - - let childVI4 = childVI3.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder_2_1 <-> folder_2_1 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file_changed.m", .orphan, 1) - - let childVI5 = childVI3.children[1] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder_2_1 <-> folder_2_1 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_older.txt", .orphan, 5) - } - } - - @Test func moveFilesOrphansNoFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/bootstrap") - try createFolder("r/bootstrap") - - try createFolder("l/bootstrap/data") - try createFolder("r/bootstrap/data") - - try createFolder("l/bootstrap/data/hypersonic") - try createFolder("r/bootstrap/data/hypersonic") - - // create files - try createFile("l/bootstrap/data/hypersonic/dvd.log", "1") - - try createFile("l/bootstrap/data/hypersonic/dvd.properties", "12") - - try createFile("l/bootstrap/data/hypersonic/localDB.lck", "123") - try createFile("l/bootstrap/data/hypersonic/localDB.log", "1234") - - try createFile("r/bootstrap/data/hypersonic/localDB.log", "1234") - - try createFile("l/bootstrap/data/hypersonic/localDB.properties", "123456") - - try createFile("l/bootstrap/data/hypersonic/localDB.script", "1234567") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let l = rootL.children[0] - assertItem(l, 0, 0, 5, 1, 1, "bootstrap", .orphan, 23) - assertItem(l.linkedItem, 0, 0, 0, 1, 1, "bootstrap", .orphan, 4) - - let child = l.children[0] - assertItem(child, 0, 0, 5, 1, 1, "data", .orphan, 23) - assertItem(child.linkedItem, 0, 0, 0, 1, 1, "data", .orphan, 4) - - var child1 = child.children[0] - assertItem(child1, 0, 0, 5, 1, 6, "hypersonic", .orphan, 23) - assertItem(child1.linkedItem, 0, 0, 0, 1, 6, "hypersonic", .orphan, 4) - - var child2 = child1.children[0] - assertItem(child2, 0, 0, 1, 0, 0, "dvd.log", .orphan, 1) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child3 = child1.children[1] - assertItem(child3, 0, 0, 1, 0, 0, "dvd.properties", .orphan, 2) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child4 = child1.children[2] - assertItem(child4, 0, 0, 1, 0, 0, "localDB.lck", .orphan, 3) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child5 = child1.children[3] - assertItem(child5, 0, 0, 0, 1, 0, "localDB.log", .same, 4) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "localDB.log", .same, 4) - - var child6 = child1.children[4] - assertItem(child6, 0, 0, 1, 0, 0, "localDB.properties", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child7 = child1.children[5] - assertItem(child7, 0, 0, 1, 0, 0, "localDB.script", .orphan, 7) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 5, 1, 1, "bootstrap", .orphan, 23) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "bootstrap", .orphan, 4) - - let childVI2 = childVI1.children[0] // bootstrap <--> bootstrap - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // bootstrap <-> bootstrap - assertItem(child2, 0, 0, 5, 1, 1, "data", .orphan, 23) - assertItem(child2.linkedItem, 0, 0, 0, 1, 1, "data", .orphan, 4) - - let childVI3 = childVI2.children[0] // data <--> data - assertArrayCount(childVI3.children, 5) - let child3 = childVI3.item // data <-> data - assertItem(child3, 0, 0, 5, 1, 6, "hypersonic", .orphan, 23) - assertItem(child3.linkedItem, 0, 0, 0, 1, 6, "hypersonic", .orphan, 4) - - let childVI4 = childVI3.children[0] // hypersonic <--> hypersonic - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // hypersonic <-> hypersonic - assertItem(child4, 0, 0, 1, 0, 0, "dvd.log", .orphan, 1) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI3.children[1] // hypersonic <--> hypersonic - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // hypersonic <-> hypersonic - assertItem(child5, 0, 0, 1, 0, 0, "dvd.properties", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI3.children[2] // hypersonic <--> hypersonic - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // hypersonic <-> hypersonic - assertItem(child6, 0, 0, 1, 0, 0, "localDB.lck", .orphan, 3) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI7 = childVI3.children[3] // hypersonic <--> hypersonic - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // hypersonic <-> hypersonic - assertItem(child7, 0, 0, 1, 0, 0, "localDB.properties", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI8 = childVI3.children[4] // hypersonic <--> hypersonic - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // hypersonic <-> hypersonic - assertItem(child8, 0, 0, 1, 0, 0, "localDB.script", .orphan, 7) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child1, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 1, 1, "bootstrap", .orphan, 4) - assertItem(child1.linkedItem, 0, 0, 5, 1, 1, "bootstrap", .orphan, 23) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 1, 1, "data", .orphan, 4) - assertItem(child2.linkedItem, 0, 0, 5, 1, 1, "data", .orphan, 23) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 1, 6, "hypersonic", .orphan, 4) - assertItem(child3.linkedItem, 0, 0, 5, 1, 6, "hypersonic", .orphan, 23) - - child4 = child3.children[0] - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "dvd.log", .orphan, 1) - - child5 = child3.children[1] - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "dvd.properties", .orphan, 2) - - child6 = child3.children[2] - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "localDB.lck", .orphan, 3) - - child7 = child3.children[3] - assertItem(child7, 0, 0, 0, 1, 0, "localDB.log", .same, 4) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "localDB.log", .same, 4) - - let child8 = child3.children[4] - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "localDB.properties", .orphan, 6) - - let child9 = child3.children[5] - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "localDB.script", .orphan, 7) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 1, 1, "bootstrap", .orphan, 4) - assertItem(child1.linkedItem, 0, 0, 5, 1, 1, "bootstrap", .orphan, 23) - - let childVI2 = childVI1.children[0] // bootstrap <--> bootstrap - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // bootstrap <-> bootstrap - assertItem(child2, 0, 0, 0, 1, 1, "data", .orphan, 4) - assertItem(child2.linkedItem, 0, 0, 5, 1, 1, "data", .orphan, 23) - - let childVI3 = childVI2.children[0] // data <--> data - assertArrayCount(childVI3.children, 5) - let child3 = childVI3.item // data <-> data - assertItem(child3, 0, 0, 0, 1, 6, "hypersonic", .orphan, 4) - assertItem(child3.linkedItem, 0, 0, 5, 1, 6, "hypersonic", .orphan, 23) - - let childVI4 = childVI3.children[0] // hypersonic <--> hypersonic - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // hypersonic <-> hypersonic - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "dvd.log", .orphan, 1) - - let childVI5 = childVI3.children[1] // hypersonic <--> hypersonic - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // hypersonic <-> hypersonic - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "dvd.properties", .orphan, 2) - - let childVI6 = childVI3.children[2] // hypersonic <--> hypersonic - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // hypersonic <-> hypersonic - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "localDB.lck", .orphan, 3) - - let childVI7 = childVI3.children[3] // hypersonic <--> hypersonic - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // hypersonic <-> hypersonic - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "localDB.properties", .orphan, 6) - - let childVI8 = childVI3.children[4] // hypersonic <--> hypersonic - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // hypersonic <-> hypersonic - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "localDB.script", .orphan, 7) - } - } - - @Test func moveFilesAllFilesFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/src/it/unipa/cuc/codicefiscale") - try createFolder("r/src/it/unipa/cuc/codicefiscale") - - // create files - try createFile("l/src/it/unipa/cuc/codicefiscale/CodiceFiscale.java", "12345") - try createFile("r/src/it/unipa/cuc/codicefiscale/CodiceFiscale.java", "12345") - try createFile("l/src/it/unipa/cuc/codicefiscale/CodiceFiscaleChecker.java", "123") - try createFile("r/src/it/unipa/cuc/codicefiscale/CodiceFiscaleChecker.java", "123") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 2, 1, "src", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 0, 2, 1, "src", .orphan, 8) - - var child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 2, 1, "it", .orphan, 8) - assertItem(child2.linkedItem, 0, 0, 0, 2, 1, "it", .orphan, 8) - - var child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 2, 1, "unipa", .orphan, 8) - assertItem(child3.linkedItem, 0, 0, 0, 2, 1, "unipa", .orphan, 8) - - var child4 = child3.children[0] - assertItem(child4, 0, 0, 0, 2, 1, "cuc", .orphan, 8) - assertItem(child4.linkedItem, 0, 0, 0, 2, 1, "cuc", .orphan, 8) - - var child5 = child4.children[0] - assertItem(child5, 0, 0, 0, 2, 2, "codicefiscale", .orphan, 8) - assertItem(child5.linkedItem, 0, 0, 0, 2, 2, "codicefiscale", .orphan, 8) - - var child6 = child5.children[0] - assertItem(child6, 0, 0, 0, 1, 0, "CodiceFiscale.java", .same, 5) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "CodiceFiscale.java", .same, 5) - - var child7 = child5.children[1] - assertItem(child7, 0, 0, 0, 1, 0, "CodiceFiscaleChecker.java", .same, 3) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "CodiceFiscaleChecker.java", .same, 3) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 2, 1, "src", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 0, 2, 1, "src", .orphan, 8) - - let childVI2 = childVI1.children[0] // src <--> src - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // src <-> src - assertItem(child2, 0, 0, 0, 2, 1, "it", .orphan, 8) - assertItem(child2.linkedItem, 0, 0, 0, 2, 1, "it", .orphan, 8) - - let childVI3 = childVI2.children[0] // it <--> it - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // it <-> it - assertItem(child3, 0, 0, 0, 2, 1, "unipa", .orphan, 8) - assertItem(child3.linkedItem, 0, 0, 0, 2, 1, "unipa", .orphan, 8) - - let childVI4 = childVI3.children[0] // unipa <--> unipa - assertArrayCount(childVI4.children, 1) - let child4 = childVI4.item // unipa <-> unipa - assertItem(child4, 0, 0, 0, 2, 1, "cuc", .orphan, 8) - assertItem(child4.linkedItem, 0, 0, 0, 2, 1, "cuc", .orphan, 8) - - let childVI5 = childVI4.children[0] // cuc <--> cuc - assertArrayCount(childVI5.children, 2) - let child5 = childVI5.item // cuc <-> cuc - assertItem(child5, 0, 0, 0, 2, 2, "codicefiscale", .orphan, 8) - assertItem(child5.linkedItem, 0, 0, 0, 2, 2, "codicefiscale", .orphan, 8) - - let childVI6 = childVI5.children[0] // codicefiscale <--> codicefiscale - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // codicefiscale <-> codicefiscale - assertItem(child6, 0, 0, 0, 1, 0, "CodiceFiscale.java", .same, 5) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "CodiceFiscale.java", .same, 5) - - let childVI7 = childVI5.children[1] // codicefiscale <--> codicefiscale - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // codicefiscale <-> codicefiscale - assertItem(child7, 0, 0, 0, 1, 0, "CodiceFiscaleChecker.java", .same, 3) - assertItem(child7.linkedItem, 0, 0, 0, 1, 0, "CodiceFiscaleChecker.java", .same, 3) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child2, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 0, 1, "src", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 0, 1, "src", .orphan, 8) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 2, 0, 1, "it", .orphan, 8) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 2, 0, 1, "unipa", .orphan, 8) - - child4 = child3.children[0] - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 2, 0, 1, "cuc", .orphan, 8) - - child5 = child4.children[0] - assertItem(child5, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 2, 0, 2, "codicefiscale", .orphan, 8) - - child6 = child5.children[0] - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "CodiceFiscale.java", .orphan, 5) - - child7 = child5.children[1] - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "CodiceFiscaleChecker.java", .orphan, 3) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "src", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 2, 0, 1, "src", .orphan, 8) - - let childVI2 = childVI1.children[0] // src <--> src - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // src <-> src - assertItem(child2, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 2, 0, 1, "it", .orphan, 8) - - let childVI3 = childVI2.children[0] // (null) <--> it - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // (null) <-> it - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 2, 0, 1, "unipa", .orphan, 8) - - let childVI4 = childVI3.children[0] // (null) <--> unipa - assertArrayCount(childVI4.children, 1) - let child4 = childVI4.item // (null) <-> unipa - assertItem(child4, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 2, 0, 1, "cuc", .orphan, 8) - - let childVI5 = childVI4.children[0] // (null) <--> cuc - assertArrayCount(childVI5.children, 2) - let child5 = childVI5.item // (null) <-> cuc - assertItem(child5, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 2, 0, 2, "codicefiscale", .orphan, 8) - - let childVI6 = childVI5.children[0] // (null) <--> codicefiscale - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // (null) <-> codicefiscale - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "CodiceFiscale.java", .orphan, 5) - - let childVI7 = childVI5.children[1] // (null) <--> codicefiscale - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // (null) <-> codicefiscale - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "CodiceFiscaleChecker.java", .orphan, 3) - } - } - - @Test func moveFilesOnlyMatches() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder_1") - try createFolder("r/folder_1") - try createFolder("l/folder_1/folder_1_1") - try createFolder("r/folder_1/folder_1_1") - try createFolder("l/folder_1/folder_1_1/folder_2_1") - try createFolder("r/folder_1/folder_1_1/folder_2_1") - - // create files - try createFile("l/folder_1/folder_1_1/folder_2_1/file_changed.m", "123") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "1234567890") - try setFileTimestamp("r/folder_1/folder_1_1/folder_2_1/file_changed.m", "2001-03-24 10: 45: 32 +0600") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_matched.m", "12345") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_matched.m", "12345") - try createFile("l/folder_1/folder_1_1/folder_2_1/file_older.m", "1234") - try setFileTimestamp("l/folder_1/folder_1_1/folder_2_1/file_older.m", "2001-03-24 10: 45: 32 +0600") - try createFile("r/folder_1/folder_1_1/folder_2_1/file_older.m", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 1, 1, 0, 1, 1, "folder_1", .orphan, 12) - assertItem(child1.linkedItem, 1, 1, 0, 1, 1, "folder_1", .orphan, 21) - - var child2 = child1.children[0] - assertItem(child2, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 12) - assertItem(child2.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 21) - - var child3 = child2.children[0] - assertItem(child3, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 12) - assertItem(child3.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 21) - - var child4 = child3.children[0] - assertItem(child4, 0, 1, 0, 0, 0, "file_changed.m", .changed, 3) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 10) - - var child5 = child3.children[1] - assertItem(child5, 0, 0, 0, 1, 0, "file_matched.m", .same, 5) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "file_matched.m", .same, 5) - - var child6 = child3.children[2] - assertItem(child6, 1, 0, 0, 0, 0, "file_older.m", .old, 4) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "file_older.m", .changed, 6) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 1, 0, 1, 1, "folder_1", .orphan, 12) - assertItem(child1.linkedItem, 1, 1, 0, 1, 1, "folder_1", .orphan, 21) - - let childVI2 = childVI1.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_1 <-> folder_1 - assertItem(child2, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 12) - assertItem(child2.linkedItem, 1, 1, 0, 1, 1, "folder_1_1", .orphan, 21) - - let childVI3 = childVI2.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // folder_1_1 <-> folder_1_1 - assertItem(child3, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 12) - assertItem(child3.linkedItem, 1, 1, 0, 1, 3, "folder_2_1", .orphan, 21) - - let childVI4 = childVI3.children[0] // folder_2_1 <--> folder_2_1 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder_2_1 <-> folder_2_1 - assertItem(child4, 0, 0, 0, 1, 0, "file_matched.m", .same, 5) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file_matched.m", .same, 5) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child5, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] - assertItem(child1, 1, 1, 0, 0, 1, "folder_1", .orphan, 7) - assertItem(child1.linkedItem, 1, 1, 1, 0, 1, "folder_1", .orphan, 21) - - child2 = child1.children[0] - assertItem(child2, 1, 1, 0, 0, 1, "folder_1_1", .orphan, 7) - assertItem(child2.linkedItem, 1, 1, 1, 0, 1, "folder_1_1", .orphan, 21) - - child3 = child2.children[0] - assertItem(child3, 1, 1, 0, 0, 3, "folder_2_1", .orphan, 7) - assertItem(child3.linkedItem, 1, 1, 1, 0, 3, "folder_2_1", .orphan, 21) - - child4 = child3.children[0] - assertItem(child4, 0, 1, 0, 0, 0, "file_changed.m", .changed, 3) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "file_changed.m", .old, 10) - - child5 = child3.children[1] - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file_matched.m", .orphan, 5) - - child6 = child3.children[2] - assertItem(child6, 1, 0, 0, 0, 0, "file_older.m", .old, 4) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "file_older.m", .changed, 6) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 1, 0, 0, 1, "folder_1", .orphan, 7) - assertItem(child1.linkedItem, 1, 1, 1, 0, 1, "folder_1", .orphan, 21) - - let childVI2 = childVI1.children[0] // folder_1 <--> folder_1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder_1 <-> folder_1 - assertItem(child2, 1, 1, 0, 0, 1, "folder_1_1", .orphan, 7) - assertItem(child2.linkedItem, 1, 1, 1, 0, 1, "folder_1_1", .orphan, 21) - - let childVI3 = childVI2.children[0] // folder_1_1 <--> folder_1_1 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder_1_1 <-> folder_1_1 - assertItem(child3, 1, 1, 0, 0, 3, "folder_2_1", .orphan, 7) - assertItem(child3.linkedItem, 1, 1, 1, 0, 3, "folder_2_1", .orphan, 21) - } - } - - @Test func moveOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/only_on_left") - try createFolder("l/only_on_left/second_folder") - try createFolder("l/only_on_left/second_folder/cartella senza titolo") - try createFolder("l/only_on_left/second_folder/cartella senza titolo 2") - - try createFolder("r") - - // create files - try createFile("l/only_on_left/second_folder/symlinks copia.zip", "12345") - try createFile("l/only_on_left/symlinks.zip", "12345678") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 13) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - var child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - var child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child5 = child2.children[2] - assertItem(child5, 0, 0, 0, 0, 0, "symlinks copia.zip", .orphan, 5) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child6 = child1.children[1] - assertItem(child6, 0, 0, 0, 0, 0, "symlinks.zip", .orphan, 8) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 13) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, nil, .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> (null) - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // only_on_left <-> (null) - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // second_folder <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // second_folder <-> (null) - assertItem(child4, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child2, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 13) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, "only_on_left", .orphan, 0) - - child2 = child1.children[0] - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "second_folder", .orphan, 0) - - child3 = child2.children[0] - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - - child4 = child2.children[1] - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - - child5 = child2.children[2] - assertItem(child5, 0, 0, 0, 0, 0, "symlinks copia.zip", .orphan, 5) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child6 = child1.children[1] - assertItem(child6, 0, 0, 0, 0, 0, "symlinks.zip", .orphan, 8) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 2, "only_on_left", .orphan, 13) - assertItem(child1.linkedItem, 0, 0, 0, 0, 2, "only_on_left", .orphan, 0) - - let childVI2 = childVI1.children[0] // only_on_left <--> only_on_left - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // only_on_left <-> only_on_left - assertItem(child2, 0, 0, 0, 0, 3, "second_folder", .orphan, 5) - assertItem(child2.linkedItem, 0, 0, 0, 0, 3, "second_folder", .orphan, 0) - - let childVI3 = childVI2.children[0] // second_folder <--> second_folder - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // second_folder <-> second_folder - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo", .orphan, 0) - - let childVI4 = childVI2.children[1] // second_folder <--> second_folder - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // second_folder <-> second_folder - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "cartella senza titolo 2", .orphan, 0) - } - } - - @Test func moveDontFollowSymLink() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder1") - try createFolder("l/folder1/folder2") - try createFolder("l/folder1/folder2/folder3") - try createSymlink("l/folder1/folder2/symlink1", "symlink_test1") - - try createFolder("r/folder1") - try createFolder("r/folder1/folder2") - - // folders out of comparison but used to create symlinks to them - try createFolder("symlink_test1") - try createFolder("symlink_test2") - - // create files - try createSymlink("r/folder1/folder2/folder3", "symlink_test1") - try createSymlink("r/folder1/folder2/symlink1", "symlink_test2") - try createSymlink("r/folder1/folder2/orphan_symlink", "symlink_test2") - - try createFile("symlink_test1/file1.txt", "12345") - try createFile("symlink_test2/file2.txt", "123") - try createFile("r/folder1/folder2/sample.txt", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - - var child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 0, 5, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 5, "folder2", .orphan, 2) - - var child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - var child4 = child2.children[1] // folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - - var child5 = child2.children[2] // folder2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - - var child6 = child2.children[3] // folder2 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - - var child7 = child2.children[4] // folder2 - assertItem(child7, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 5) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 0, 5, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 5, "folder2", .orphan, 2) - - let childVI3 = childVI2.children[0] // folder2 <--> folder2 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder2 <-> folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // folder2 <--> folder2 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder2 <-> folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - - let childVI5 = childVI2.children[2] // folder2 <--> folder2 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder2 <-> folder2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - - let childVI6 = childVI2.children[3] // folder2 <--> folder2 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder2 <-> folder2 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - - let childVI7 = childVI2.children[4] // folder2 <--> folder2 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // folder2 <-> folder2 - assertItem(child7, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - } - - try assertOnlySetup() - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child2.linkedItem!, - srcBaseDir: appendFolder("r"), - destBaseDir: appendFolder("l") - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 1, 0, 5, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 5, "folder2", .orphan, 0) - - child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child4 = child2.children[1] // folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - try assertSymlink(child4.linkedItem!, "symlink_test1", true) - - child5 = child2.children[2] // folder2 - assertItem(child5, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - try assertSymlink(child5, "symlink_test2", true) - - child6 = child2.children[3] // folder2 - assertItem(child6, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - child7 = child2.children[4] // folder2 - assertItem(child7, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - try assertSymlink(child7, "symlink_test2", true) - - assertErrors(fileOperationDelegate.errors, [ - FileError.createSymLink(path: child3.path!), - ]) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "folder1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 5) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 1, 0, 5, "folder2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 5, "folder2", .orphan, 0) - - let childVI3 = childVI2.children[0] // folder2 <--> folder2 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder2 <-> folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[1] // folder2 <--> folder2 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // folder2 <-> folder2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - - let childVI5 = childVI2.children[2] // folder2 <--> folder2 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // folder2 <-> folder2 - assertItem(child5, 0, 0, 0, 0, 0, "orphan_symlink", .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI2.children[3] // folder2 <--> folder2 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // folder2 <-> folder2 - assertItem(child6, 0, 0, 1, 0, 0, "sample.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI7 = childVI2.children[4] // folder2 <--> folder2 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // folder2 <-> folder2 - assertItem(child7, 0, 0, 0, 0, 0, "symlink1", .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func moveFailure() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("l/dir1/dir2/dir3") - - // create files - try createFile("l/dir1/dir2/dir3/file1.txt", "1234567") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // dir3 - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - // simulate a copy error deleting the file to copy - do { - try fm.removeItem(atPath: child4.path!) - } catch { - Issue.record("Found error \(error)") - } - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // dir2 <--> (null) - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // dir2 <-> (null) - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // dir3 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir3 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child4, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - child1 = rootL.children[0] // l - do { - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child4 = child3.children[0] // dir3 - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 7) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 1, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // dir2 <--> (null) - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // dir2 <-> (null) - assertItem(child3, 0, 0, 1, 0, 1, "dir3", .orphan, 7) - assertItem(child3.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI4 = childVI3.children[0] // dir3 <--> (null) - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir3 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file1.txt", .orphan, 7) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func movePreserveFolderTimestamp() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/folder1/folder2/folder3") - try createFolder("r/folder1") - - try setFileCreationTime("l/folder1", "2010-01-01 00: 00: 00 +0000") - try setFileTimestamp("l/folder1", "2010-02-02 02: 02: 00 +0000") - - try setFileCreationTime("l/folder1/folder2", "2010-04-04 04: 04: 00 +0000") - try setFileTimestamp("l/folder1/folder2", "2010-05-05 05: 05: 00 +0000") - - try setFileCreationTime("l/folder1/folder2/folder3", "2011-05-05 05: 00: 30 +0000") - try setFileTimestamp("l/folder1/folder2/folder3", "2011-06-06 06: 00: 30 +0000") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 0, 1, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let child3 = child2.children[0] // folder2 - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 0, 1, "folder2", .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, nil, .orphan, 0) - - let childVI3 = childVI2.children[0] // folder2 <--> (null) - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // folder2 <-> (null) - assertItem(child3, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: child2, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let child2 = child1.children[0] // folder1 - assertItem(child2, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "folder2", .orphan, 0) - try assertTimestamps(child2.linkedItem, "2010-04-04 04: 04: 00 +0000", "2010-05-05 05: 05: 00 +0000") - - let child3 = child2.children[0] // (null) - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - try assertTimestamps(child3.linkedItem, "2011-05-05 05: 00: 30 +0000", "2011-06-06 06: 00: 30 +0000") - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "folder1", .orphan, 0) - - let childVI2 = childVI1.children[0] // folder1 <--> folder1 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // folder1 <-> folder1 - assertItem(child2, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 0, 0, 1, "folder2", .orphan, 0) - - let childVI3 = childVI2.children[0] // (null) <--> folder2 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // (null) <-> folder2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 0, 0, 0, "folder3", .orphan, 0) - } - } - - @Test(.disabled("BUG 0000230: Orphan file not colored correctly after move: Not yet fixed, this test fails")) func moveMatchFileBecomeFiltered() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp, .size, .alignMatchCase], - delegate: comparatorDelegate, - bufferSize: 8192, - isLeftCaseSensitive: false, - isRightCaseSensitive: false - ) - let filterConfig = FilterConfig( - showFilteredFiles: true, - hideEmptyFolders: true, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .noOrphan - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l") - try createFolder("r") - - // create files - try createFile("l/file1.html", "12345678901") - try createFile("r/file1.html", "12345678901") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let moveItem: CompareItem - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 1, 1, "l", .orphan, 11) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "r", .orphan, 11) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 1, 0, "file1.html", .same, 11) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "file1.html", .same, 11) - - moveItem = child2 - } - - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 1, 1, "l", .orphan, 11) - assertItem(child1.linkedItem, 0, 0, 0, 1, 1, "r", .orphan, 11) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 1, 0, "file1.html", .same, 11) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "file1.html", .same, 11) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: false) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = MoveCompareItem( - operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000 - ) - - fileOperation.move( - srcRoot: moveItem, - srcBaseDir: appendFolder("l"), - destBaseDir: appendFolder("r") - ) - - do { - let child1 = rootL // l <-> r - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 1, 0, 1, "r", .orphan, 11) - - let child2 = child1.children[0] // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "file1.html", .orphan, 11) - } - do { - // VisibleItems - let childVI1 = vi // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // (null) <-> (null) - assertItem(child1, 0, 0, 0, 0, 1, "l", .orphan, 0) - assertItem(child1.linkedItem, 0, 0, 1, 0, 1, "r", .orphan, 11) - - let childVI2 = childVI1.children[0] // l <--> r - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // l <-> r - assertItem(child2, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 1, 0, 0, "file1.html", .orphan, 11) - #expect(child2.isFiltered == true, "\(child2.fileName!) must be filtered") - #expect(child2.linkedItem!.isFiltered == true, "\(child2.linkedItem!.fileName!) must be filtered") - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/RenameFilesTests.swift b/Tests/Sources/FileSystem/Tests/RenameFilesTests.swift deleted file mode 100644 index 4c99593..0000000 --- a/Tests/Sources/FileSystem/Tests/RenameFilesTests.swift +++ /dev/null @@ -1,2296 +0,0 @@ -// -// RenameFilesTests.swift -// VisualDiffer -// -// Created by davide ficano on 27/04/13. -// Copyright (c) 2013 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable file_length force_unwrapping function_body_length -final class RenameFilesTests: BaseTests { - @Test func renameFileOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/dir1") - try createFolder("r/dir1") - - try createFile("l/dir1/100.txt", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - var child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 0, "100.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child2, - toName: "110.txt" - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func renameFileOrphanToOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("r/dir1/dir2") - - // create files - try createFile("r/dir1/dir2/010.txt", "123") - try createFile("l/dir1/dir2/100.txt", "12") - try createFile("r/dir1/dir2/109.txt", "1234") - try createFile("l/dir1/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 2, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 2, 0, 2, "dir1", .orphan, 7) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 0, 3, "dir2", .orphan, 7) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 - assertItem(child4, 0, 0, 1, 0, 0, "100.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child2.children[2] // dir2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "109.txt", .orphan, 4) - - let child6 = child1.children[1] // dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - fileOperation.rename( - srcRoot: child4, - toName: "110.txt" - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 2, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 2, 0, 2, "dir1", .orphan, 7) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 0, 3, "dir2", .orphan, 7) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "109.txt", .orphan, 4) - - let child5 = child2.children[2] // dir2 - assertItem(child5, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child1.children[1] // dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - // must be recreated - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 2, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 2, 0, 2, "dir1", .orphan, 7) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 0, 3, "dir2", .orphan, 7) - - let childVI3 = childVI2.children[0] // dir2 <--> dir2 - let child3 = childVI3.item // dir2 <-> dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let childVI4 = childVI2.children[1] // dir2 <--> dir2 - let child4 = childVI4.item // dir2 <-> dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "109.txt", .orphan, 4) - - let childVI5 = childVI2.children[2] // dir2 <--> dir2 - let child5 = childVI5.item // dir2 <-> dir2 - assertItem(child5, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI6 = childVI1.children[1] // dir1 <--> dir1 - let child6 = childVI6.item // dir1 <-> dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func renameFileOrphanToMatching() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("r/dir1/dir2") - - // create files - try createFile("r/dir1/dir2/010.txt", "123") - try createFile("l/dir1/dir2/100.txt", "12") - try createFile("r/dir1/dir2/109.txt", "1234") - try createFile("l/dir1/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 2, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 2, 0, 2, "dir1", .orphan, 7) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 0, 3, "dir2", .orphan, 7) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 - assertItem(child4, 0, 0, 1, 0, 0, "100.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child2.children[2] // dir2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "109.txt", .orphan, 4) - - let child6 = child1.children[1] // dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child4, - toName: "109.txt" - ) - - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 1, 1, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 1, 1, 0, 2, "dir1", .orphan, 7) - - let child2 = child1.children[0] // dir1 <-> dir1 - assertItem(child2, 0, 1, 0, 0, 2, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 1, 1, 0, 2, "dir2", .orphan, 7) - - let child3 = child2.children[0] // dir2 <-> dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 <-> dir2 - assertItem(child4, 0, 1, 0, 0, 0, "109.txt", .changed, 2) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "109.txt", .changed, 4) - - let child5 = child1.children[1] // dir1 <-> dir1 - assertItem(child5, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 1, 1, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 1, 1, 0, 2, "dir1", .orphan, 7) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 1, 0, 0, 2, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 1, 1, 0, 2, "dir2", .orphan, 7) - - let childVI3 = childVI2.children[0] // dir2 <--> dir2 - let child3 = childVI3.item // dir2 <-> dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let childVI4 = childVI2.children[1] // dir2 <--> dir2 - let child4 = childVI4.item // dir2 <-> dir2 - assertItem(child4, 0, 1, 0, 0, 0, "109.txt", .changed, 2) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "109.txt", .changed, 4) - - let childVI5 = childVI1.children[1] // dir1 <--> dir1 - let child5 = childVI5.item // dir1 <-> dir1 - assertItem(child5, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func renameFileMismatch() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("r/dir1/dir2") - - // create files - try createFile("r/dir1/dir2/010.txt", "123") - try createFile("r/dir1/dir2/50.txt", "1234") - try createFile("l/dir1/dir2/100.txt", "12") - try createFile("r/dir1/dir2/100.txt", "1234") - try createFile("l/dir1/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 1, 1, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 1, 2, 0, 2, "dir1", .orphan, 11) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 1, 0, 0, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 1, 2, 0, 3, "dir2", .orphan, 11) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child5 = child2.children[1] // dir2 - assertItem(child5, 0, 1, 0, 0, 0, "100.txt", .changed, 2) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "100.txt", .changed, 4) - - let child4 = child2.children[2] // dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "50.txt", .orphan, 4) - - let child6 = child1.children[1] // dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child5, - toName: "110.txt" - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 2, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 3, 0, 2, "dir1", .orphan, 11) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 4, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 3, 0, 4, "dir2", .orphan, 11) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child5 = child2.children[1] // dir2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "100.txt", .orphan, 4) - - let child6 = child2.children[2] // dir2 - assertItem(child6, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child4 = child2.children[3] // dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "50.txt", .orphan, 4) - - let child7 = child1.children[1] // dir1 - assertItem(child7, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 2, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 3, 0, 2, "dir1", .orphan, 11) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 4, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 3, 0, 4, "dir2", .orphan, 11) - - let childVI3 = childVI2.children[0] // dir2 <--> dir2 - let child3 = childVI3.item // dir2 <-> dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let childVI5 = childVI2.children[1] // dir2 <--> dir2 - let child5 = childVI5.item // dir2 <-> dir2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "100.txt", .orphan, 4) - - let childVI6 = childVI2.children[2] // dir2 <--> dir2 - let child6 = childVI6.item // dir2 <-> dir2 - assertItem(child6, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI4 = childVI2.children[3] // dir2 <--> dir2 - let child4 = childVI4.item // dir2 <-> dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "50.txt", .orphan, 4) - - let childVI7 = childVI1.children[1] // dir1 <--> dir1 - let child7 = childVI7.item // dir1 <-> dir1 - assertItem(child7, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func renameFileMatchingToMatching() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("r/dir1/dir2") - - // create files - try createFile("r/dir1/dir2/010.txt", "123") - try createFile("l/dir1/dir2/100.txt", "12") - try createFile("r/dir1/dir2/100.txt", "1234") - try createFile("r/dir1/dir2/110.txt", "12") - try createFile("l/dir1/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 1, 1, 0, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 1, 2, 0, 2, "dir1", .orphan, 9) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 1, 0, 0, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 1, 2, 0, 3, "dir2", .orphan, 9) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 - assertItem(child4, 0, 1, 0, 0, 0, "100.txt", .changed, 2) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "100.txt", .changed, 4) - - let child5 = child2.children[2] // dir2 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - - let child6 = child1.children[1] // dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child4, - toName: "110.txt" - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 1, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 2, 1, 2, "dir1", .orphan, 9) - - let child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 0, 1, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 1, 3, "dir2", .orphan, 9) - - let child3 = child2.children[0] // dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "100.txt", .orphan, 4) - - let child5 = child2.children[2] // dir2 - assertItem(child5, 0, 0, 0, 1, 0, "110.txt", .same, 2) - assertItem(child5.linkedItem, 0, 0, 0, 1, 0, "110.txt", .same, 2) - - let child6 = child1.children[1] // dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 2) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 1, 2, "dir1", .orphan, 8) - assertItem(child1.linkedItem, 0, 0, 2, 1, 2, "dir1", .orphan, 9) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 0, 1, 3, "dir2", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 2, 1, 3, "dir2", .orphan, 9) - - let childVI3 = childVI2.children[0] // dir2 <--> dir2 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // dir2 <-> dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let childVI4 = childVI2.children[1] // dir2 <--> dir2 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir2 <-> dir2 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "100.txt", .orphan, 4) - - let childVI5 = childVI1.children[1] // dir1 <--> dir1 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // dir1 <-> dir1 - assertItem(child5, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func renameFileOrphanToOrphan_OnlyOrphan_Right() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyOrphans - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir1") - try createFolder("r/dir1") - try createFolder("l/dir1/dir2") - try createFolder("r/dir1/dir2") - - // create files - try createFile("r/dir1/dir2/010.txt", "123") - try createFile("l/dir1/dir2/100.txt", "12345") - try createFile("r/dir1/dir2/100.txt", "1234") - try setFileTimestamp("r/dir1/dir2/100.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("l/dir1/dir2/110.txt", "12") - try createFile("l/dir1/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 1, 2, 0, 2, "dir1", .orphan, 13) - assertItem(child1.linkedItem, 1, 0, 1, 0, 2, "dir1", .orphan, 7) - - let child2 = child1.children[0] // dir1 <-> dir1 - assertItem(child2, 0, 1, 1, 0, 3, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 1, 0, 1, 0, 3, "dir2", .orphan, 7) - - let child3 = child2.children[0] // dir2 <-> dir2 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "010.txt", .orphan, 3) - - let child4 = child2.children[1] // dir2 <-> dir2 - assertItem(child4, 0, 1, 0, 0, 0, "100.txt", .changed, 5) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "100.txt", .old, 4) - - let child5 = child2.children[2] // dir2 <-> dir2 - assertItem(child5, 0, 0, 1, 0, 0, "110.txt", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child1.children[1] // dir1 <-> dir1 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child3.linkedItem!, - toName: "110.txt" - ) - - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 2, 1, 0, 2, "dir1", .orphan, 13) - assertItem(child1.linkedItem, 1, 1, 0, 0, 2, "dir1", .orphan, 7) - - let child2 = child1.children[0] // dir1 <-> dir1 - assertItem(child2, 0, 2, 0, 0, 2, "dir2", .orphan, 7) - assertItem(child2.linkedItem, 1, 1, 0, 0, 2, "dir2", .orphan, 7) - - let child3 = child2.children[0] // dir2 <-> dir2 - assertItem(child3, 0, 1, 0, 0, 0, "100.txt", .changed, 5) - assertItem(child3.linkedItem, 1, 0, 0, 0, 0, "100.txt", .old, 4) - - let child4 = child2.children[1] // dir2 <-> dir2 - assertItem(child4, 0, 1, 0, 0, 0, "110.txt", .changed, 2) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "110.txt", .changed, 3) - - let child5 = child1.children[1] // dir1 <-> dir1 - assertItem(child5, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 2, 1, 0, 2, "dir1", .orphan, 13) - assertItem(child1.linkedItem, 1, 1, 0, 0, 2, "dir1", .orphan, 7) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - // MARK: - - - // MARK: Folders rename tests - - @Test func renameFolderFromBothSidesToOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir100") - try createFolder("r/dir100") - try createFolder("l/dir100/dir110") - try createFolder("r/dir100/dir110") - - // create files - try createFile("l/dir100/dir110/file001.txt", "12") - try createFile("r/dir100/dir110/file001.txt", "123") - try createFile("l/dir100/dir110/file002.txt", "123456") - try createFile("r/dir100/dir110/file002.txt", "123456") - try createFile("l/dir100/dir110/file003.txt", "12") - try createFile("l/dir100/011.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 1, 2, 1, 2, "dir100", .orphan, 16) - assertItem(child1.linkedItem, 0, 1, 0, 1, 2, "dir100", .orphan, 9) - - let child2 = child1.children[0] // dir100 - assertItem(child2, 0, 1, 1, 1, 3, "dir110", .orphan, 10) - assertItem(child2.linkedItem, 0, 1, 0, 1, 3, "dir110", .orphan, 9) - - let child3 = child2.children[0] // dir110 - assertItem(child3, 0, 1, 0, 0, 0, "file001.txt", .changed, 2) - assertItem(child3.linkedItem, 0, 1, 0, 0, 0, "file001.txt", .changed, 3) - - let child4 = child2.children[1] // dir110 - assertItem(child4, 0, 0, 0, 1, 0, "file002.txt", .same, 6) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file002.txt", .same, 6) - - let child5 = child2.children[2] // dir110 - assertItem(child5, 0, 0, 1, 0, 0, "file003.txt", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child1.children[1] // dir100 - assertItem(child6, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child2, - toName: "dir120" - ) - - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 4, 0, 3, "dir100", .orphan, 16) - assertItem(child1.linkedItem, 0, 0, 2, 0, 3, "dir100", .orphan, 9) - - let child2 = child1.children[0] // dir100 - assertItem(child2, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 9) - - let child3 = child2.children[0] // (null) - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file001.txt", .orphan, 3) - - let child4 = child2.children[1] // (null) - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file002.txt", .orphan, 6) - - let child5 = child1.children[1] // dir100 - assertItem(child5, 0, 0, 3, 0, 3, "dir120", .orphan, 10) - assertItem(child5.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child6 = child5.children[0] // dir120 - assertItem(child6, 0, 0, 1, 0, 0, "file001.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child5.children[1] // dir120 - assertItem(child7, 0, 0, 1, 0, 0, "file002.txt", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child8 = child5.children[2] // dir120 - assertItem(child8, 0, 0, 1, 0, 0, "file003.txt", .orphan, 2) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child9 = child1.children[2] // dir100 - assertItem(child9, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 3) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 4, 0, 3, "dir100", .orphan, 16) - assertItem(child1.linkedItem, 0, 0, 2, 0, 3, "dir100", .orphan, 9) - - let childVI2 = childVI1.children[0] // dir100 <--> dir100 - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // dir100 <-> dir100 - assertItem(child2, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 9) - - let childVI3 = childVI2.children[0] // (null) <--> dir110 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // (null) <-> dir110 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file001.txt", .orphan, 3) - - let childVI4 = childVI2.children[1] // (null) <--> dir110 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file002.txt", .orphan, 6) - - let childVI5 = childVI1.children[1] // dir100 <--> dir100 - assertArrayCount(childVI5.children, 3) - let child5 = childVI5.item // dir100 <-> dir100 - assertItem(child5, 0, 0, 3, 0, 3, "dir120", .orphan, 10) - assertItem(child5.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let childVI6 = childVI5.children[0] // dir120 <--> (null) - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // dir120 <-> (null) - assertItem(child6, 0, 0, 1, 0, 0, "file001.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI7 = childVI5.children[1] // dir120 <--> (null) - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // dir120 <-> (null) - assertItem(child7, 0, 0, 1, 0, 0, "file002.txt", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI8 = childVI5.children[2] // dir120 <--> (null) - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // dir120 <-> (null) - assertItem(child8, 0, 0, 1, 0, 0, "file003.txt", .orphan, 2) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI9 = childVI1.children[2] // dir100 <--> dir100 - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // dir100 <-> dir100 - assertItem(child9, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } - - @Test func renameFolderOrphanToOrphan() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir050") - try createFolder("r/dir050") - try createFolder("l/dir050/dir100") - try createFolder("r/dir050/dir100") - try createFolder("l/dir050/dir100/dir105") - try createFolder("r/dir050/dir100/dir110") - - // create files - try createFile("l/dir050/dir100/dir105/file101.txt", "12") - try createFile("l/dir050/dir100/dir105/file102.txt", "123456") - try createFile("l/dir050/dir100/dir105/file103.txt", "12") - try createFile("r/dir050/dir100/dir110/file401.txt", "12") - try createFile("r/dir050/dir100/dir110/file403.txt", "123") - try createFile("l/dir050/dir100/011.txt", "123456") - try createFile("r/dir050/dir100/file301.txt", "123") - try createFile("r/dir050/dir100/file302.txt", "123456") - try createFile("l/dir050/020.txt", "123456") - try createFile("l/dir050/040.txt", "123456") - try createFile("r/dir050/file201.txt", "123") - try createFile("r/dir050/file202.txt", "123") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 6, 0, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 0, 6, 0, 5, "dir050", .orphan, 20) - - let child2 = child1.children[0] // dir050 - assertItem(child2, 0, 0, 4, 0, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 4, 0, 5, "dir100", .orphan, 14) - - let child3 = child2.children[0] // dir100 - assertItem(child3, 0, 0, 3, 0, 3, "dir105", .orphan, 10) - assertItem(child3.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child4 = child3.children[0] // dir105 - assertItem(child4, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child3.children[1] // dir105 - assertItem(child5, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child3.children[2] // dir105 - assertItem(child6, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child2.children[1] // dir100 - assertItem(child7, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 5) - - let child8 = child7.children[0] // (null) - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "file401.txt", .orphan, 2) - - let child9 = child7.children[1] // (null) - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "file403.txt", .orphan, 3) - - let child10 = child2.children[2] // dir100 - assertItem(child10, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child10.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child11 = child2.children[3] // dir100 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let child12 = child2.children[4] // dir100 - assertItem(child12, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let child13 = child1.children[1] // dir050 - assertItem(child13, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child14 = child1.children[2] // dir050 - assertItem(child14, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child14.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child15 = child1.children[3] // dir050 - assertItem(child15, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child15.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let child16 = child1.children[4] // dir050 - assertItem(child16, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child16.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child3, - toName: "dir120" - ) - do { - let child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 6, 0, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 0, 6, 0, 5, "dir050", .orphan, 20) - - let child2 = child1.children[0] // dir050 - assertItem(child2, 0, 0, 4, 0, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 4, 0, 5, "dir100", .orphan, 14) - - let child3 = child2.children[0] // dir100 - assertItem(child3, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 5) - - let child4 = child3.children[0] // (null) - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file401.txt", .orphan, 2) - - let child5 = child3.children[1] // (null) - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file403.txt", .orphan, 3) - - let child6 = child2.children[1] // dir100 - assertItem(child6, 0, 0, 3, 0, 3, "dir120", .orphan, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child7 = child6.children[0] // dir120 - assertItem(child7, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child8 = child6.children[1] // dir120 - assertItem(child8, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child9 = child6.children[2] // dir120 - assertItem(child9, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child10 = child2.children[2] // dir100 - assertItem(child10, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child10.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child11 = child2.children[3] // dir100 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let child12 = child2.children[4] // dir100 - assertItem(child12, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let child13 = child1.children[1] // dir050 - assertItem(child13, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child14 = child1.children[2] // dir050 - assertItem(child14, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child14.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child15 = child1.children[3] // dir050 - assertItem(child15, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child15.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let child16 = child1.children[4] // dir050 - assertItem(child16, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child16.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 5) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 6, 0, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 0, 6, 0, 5, "dir050", .orphan, 20) - - let childVI2 = childVI1.children[0] // dir050 <--> dir050 - assertArrayCount(childVI2.children, 5) - let child2 = childVI2.item // dir050 <-> dir050 - assertItem(child2, 0, 0, 4, 0, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 4, 0, 5, "dir100", .orphan, 14) - - let childVI3 = childVI2.children[0] // dir100 <--> dir100 - assertArrayCount(childVI3.children, 2) - let child3 = childVI3.item // dir100 <-> dir100 - assertItem(child3, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 5) - - let childVI4 = childVI3.children[0] // (null) <--> dir110 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file401.txt", .orphan, 2) - - let childVI5 = childVI3.children[1] // (null) <--> dir110 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> dir110 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file403.txt", .orphan, 3) - - let childVI6 = childVI2.children[1] // dir100 <--> dir100 - assertArrayCount(childVI6.children, 3) - let child6 = childVI6.item // dir100 <-> dir100 - assertItem(child6, 0, 0, 3, 0, 3, "dir120", .orphan, 10) - assertItem(child6.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let childVI7 = childVI6.children[0] // dir120 <--> (null) - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // dir120 <-> (null) - assertItem(child7, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI8 = childVI6.children[1] // dir120 <--> (null) - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // dir120 <-> (null) - assertItem(child8, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI9 = childVI6.children[2] // dir120 <--> (null) - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // dir120 <-> (null) - assertItem(child9, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI10 = childVI2.children[2] // dir100 <--> dir100 - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // dir100 <-> dir100 - assertItem(child10, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child10.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI11 = childVI2.children[3] // dir100 <--> dir100 - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // dir100 <-> dir100 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let childVI12 = childVI2.children[4] // dir100 <--> dir100 - assertArrayCount(childVI12.children, 0) - let child12 = childVI12.item // dir100 <-> dir100 - assertItem(child12, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let childVI13 = childVI1.children[1] // dir050 <--> dir050 - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // dir050 <-> dir050 - assertItem(child13, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI14 = childVI1.children[2] // dir050 <--> dir050 - assertArrayCount(childVI14.children, 0) - let child14 = childVI14.item // dir050 <-> dir050 - assertItem(child14, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child14.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI15 = childVI1.children[3] // dir050 <--> dir050 - assertArrayCount(childVI15.children, 0) - let child15 = childVI15.item // dir050 <-> dir050 - assertItem(child15, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child15.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let childVI16 = childVI1.children[4] // dir050 <--> dir050 - assertArrayCount(childVI16.children, 0) - let child16 = childVI16.item // dir050 <-> dir050 - assertItem(child16, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child16.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - } - } - - @Test func renameFolderMatchingToMatching() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/dir050") - try createFolder("r/dir050") - try createFolder("l/dir050/dir100") - try createFolder("r/dir050/dir100") - try createFolder("l/dir050/dir100/dir110") - try createFolder("r/dir050/dir100/dir110") - try createFolder("r/dir050/dir100/dir120") - - // create files - try createFile("l/dir050/dir100/dir110/file101.txt", "12") - try createFile("r/dir050/dir100/dir110/file101.txt", "12") - try createFile("l/dir050/dir100/dir110/file102.txt", "123456") - try createFile("l/dir050/dir100/dir110/file103.txt", "12") - try setFileTimestamp("l/dir050/dir100/dir110/file103.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/dir050/dir100/dir110/file103.txt", "123") - try createFile("r/dir050/dir100/dir110/file104.txt", "1234567") - try createFile("r/dir050/dir100/dir120/file101.txt", "12") - try createFile("r/dir050/dir100/dir120/file102.txt", "123456") - try createFile("r/dir050/dir100/dir120/file103.txt", "12") - try createFile("l/dir050/dir100/011.txt", "123456") - try createFile("r/dir050/dir100/file301.txt", "123") - try createFile("r/dir050/dir100/file302.txt", "123456") - try createFile("l/dir050/020.txt", "123456") - try createFile("l/dir050/040.txt", "123456") - try createFile("r/dir050/file201.txt", "123") - try createFile("r/dir050/file202.txt", "123") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 1, 0, 4, 1, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 1, 8, 1, 5, "dir050", .orphan, 37) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 1, 0, 2, 1, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 1, 6, 1, 5, "dir100", .orphan, 31) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 1, 0, 1, 1, 4, "dir110", .orphan, 10) - assertItem(child3.linkedItem, 0, 1, 1, 1, 4, "dir110", .orphan, 12) - - let child4 = child3.children[0] // dir110 <-> dir110 - assertItem(child4, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - assertItem(child4.linkedItem, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - - let child5 = child3.children[1] // dir110 <-> dir110 - assertItem(child5, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child3.children[2] // dir110 <-> dir110 - assertItem(child6, 1, 0, 0, 0, 0, "file103.txt", .old, 2) - assertItem(child6.linkedItem, 0, 1, 0, 0, 0, "file103.txt", .changed, 3) - - let child7 = child3.children[3] // dir110 <-> dir110 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file104.txt", .orphan, 7) - - let child8 = child2.children[1] // dir100 <-> dir100 - assertItem(child8, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 3, 0, 3, "dir120", .orphan, 10) - - let child9 = child8.children[0] // (null) <-> dir120 - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let child10 = child8.children[1] // (null) <-> dir120 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - - let child11 = child8.children[2] // (null) <-> dir120 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - - let child12 = child2.children[2] // dir100 <-> dir100 - assertItem(child12, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child12.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child13 = child2.children[3] // dir100 <-> dir100 - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let child14 = child2.children[4] // dir100 <-> dir100 - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let child15 = child1.children[1] // dir050 <-> dir050 - assertItem(child15, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child15.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child16 = child1.children[2] // dir050 <-> dir050 - assertItem(child16, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child16.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child17 = child1.children[3] // dir050 <-> dir050 - assertItem(child17, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child17.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let child18 = child1.children[4] // dir050 <-> dir050 - assertItem(child18, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child18.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child3, - toName: "dir120" - ) - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 0, 3, 3, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 0, 7, 3, 5, "dir050", .orphan, 37) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 0, 0, 1, 3, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 5, 3, 5, "dir100", .orphan, 31) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "dir110", .orphan, 12) - - let child4 = child3.children[0] // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let child5 = child3.children[1] // (null) <-> dir110 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file103.txt", .orphan, 3) - - let child6 = child3.children[2] // (null) <-> dir110 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file104.txt", .orphan, 7) - - let child7 = child2.children[1] // dir100 <-> dir100 - assertItem(child7, 0, 0, 0, 3, 3, "dir120", .orphan, 10) - assertItem(child7.linkedItem, 0, 0, 0, 3, 3, "dir120", .orphan, 10) - - let child8 = child7.children[0] // dir120 <-> dir120 - assertItem(child8, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - - let child9 = child7.children[1] // dir120 <-> dir120 - assertItem(child9, 0, 0, 0, 1, 0, "file102.txt", .same, 6) - assertItem(child9.linkedItem, 0, 0, 0, 1, 0, "file102.txt", .same, 6) - - let child10 = child7.children[2] // dir120 <-> dir120 - assertItem(child10, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - assertItem(child10.linkedItem, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - - let child11 = child2.children[2] // dir100 <-> dir100 - assertItem(child11, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child12 = child2.children[3] // dir100 <-> dir100 - assertItem(child12, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let child13 = child2.children[4] // dir100 <-> dir100 - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let child14 = child1.children[1] // dir050 <-> dir050 - assertItem(child14, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child14.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child15 = child1.children[2] // dir050 <-> dir050 - assertItem(child15, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child15.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child16 = child1.children[3] // dir050 <-> dir050 - assertItem(child16, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child16.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let child17 = child1.children[4] // dir050 <-> dir050 - assertItem(child17, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child17.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 5) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 3, 3, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 0, 7, 3, 5, "dir050", .orphan, 37) - - let childVI2 = childVI1.children[0] // dir050 <--> dir050 - assertArrayCount(childVI2.children, 4) - let child2 = childVI2.item // dir050 <-> dir050 - assertItem(child2, 0, 0, 1, 3, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 5, 3, 5, "dir100", .orphan, 31) - - let childVI3 = childVI2.children[0] // dir100 <--> dir100 - assertArrayCount(childVI3.children, 3) - let child3 = childVI3.item // dir100 <-> dir100 - assertItem(child3, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 3, 0, 3, "dir110", .orphan, 12) - - let childVI4 = childVI3.children[0] // (null) <--> dir110 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let childVI5 = childVI3.children[1] // (null) <--> dir110 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // (null) <-> dir110 - assertItem(child5, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 1, 0, 0, "file103.txt", .orphan, 3) - - let childVI6 = childVI3.children[2] // (null) <--> dir110 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // (null) <-> dir110 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file104.txt", .orphan, 7) - - let childVI7 = childVI2.children[1] // dir100 <--> dir100 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // dir100 <-> dir100 - assertItem(child7, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI8 = childVI2.children[2] // dir100 <--> dir100 - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // dir100 <-> dir100 - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let childVI9 = childVI2.children[3] // dir100 <--> dir100 - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // dir100 <-> dir100 - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let childVI10 = childVI1.children[1] // dir050 <--> dir050 - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // dir050 <-> dir050 - assertItem(child10, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child10.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI11 = childVI1.children[2] // dir050 <--> dir050 - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // dir050 <-> dir050 - assertItem(child11, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI12 = childVI1.children[3] // dir050 <--> dir050 - assertArrayCount(childVI12.children, 0) - let child12 = childVI12.item // dir050 <-> dir050 - assertItem(child12, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let childVI13 = childVI1.children[4] // dir050 <--> dir050 - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // dir050 <-> dir050 - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - } - } - - @Test func renameFolder_NoOrphan_Left() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .noOrphan - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir050") - try createFolder("r/dir050") - try createFolder("l/dir050/dir100") - try createFolder("r/dir050/dir100") - try createFolder("r/dir050/dir100/dir110") - try createFolder("l/dir050/dir100/dir120") - try createFolder("r/dir050/dir100/dir120") - - // create files - try createFile("r/dir050/dir100/dir110/file101.txt", "12") - try createFile("l/dir050/dir100/dir120/file101.txt", "12") - try createFile("r/dir050/dir100/dir120/file101.txt", "12") - try createFile("l/dir050/dir100/dir120/file102.txt", "123456") - try setFileTimestamp("l/dir050/dir100/dir120/file102.txt", "2001-03-24 10: 45: 32 +0600") - try createFile("r/dir050/dir100/dir120/file102.txt", "123") - try createFile("l/dir050/dir100/dir120/file103.txt", "12") - try createFile("r/dir050/dir100/dir120/file103.txt", "12") - try createFile("l/dir050/dir100/011.txt", "123456") - try createFile("l/dir050/050.txt", "123456") - try createFile("r/dir050/050.txt", "123456") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 1, 0, 1, 3, 2, "dir050", .orphan, 22) - assertItem(child1.linkedItem, 0, 1, 1, 3, 2, "dir050", .orphan, 15) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 1, 0, 1, 2, 3, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 1, 1, 2, 3, "dir100", .orphan, 9) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 1, "dir110", .orphan, 2) - - let child4 = child3.children[0] // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let child5 = child2.children[1] // dir100 <-> dir100 - assertItem(child5, 1, 0, 0, 2, 3, "dir120", .orphan, 10) - assertItem(child5.linkedItem, 0, 1, 0, 2, 3, "dir120", .orphan, 7) - - let child6 = child5.children[0] // dir120 <-> dir120 - assertItem(child6, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - assertItem(child6.linkedItem, 0, 0, 0, 1, 0, "file101.txt", .same, 2) - - let child7 = child5.children[1] // dir120 <-> dir120 - assertItem(child7, 1, 0, 0, 0, 0, "file102.txt", .old, 6) - assertItem(child7.linkedItem, 0, 1, 0, 0, 0, "file102.txt", .changed, 3) - - let child8 = child5.children[2] // dir120 <-> dir120 - assertItem(child8, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - assertItem(child8.linkedItem, 0, 0, 0, 1, 0, "file103.txt", .same, 2) - - let child9 = child2.children[2] // dir100 <-> dir100 - assertItem(child9, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child9.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child10 = child1.children[1] // dir050 <-> dir050 - assertItem(child10, 0, 0, 0, 1, 0, "050.txt", .same, 6) - assertItem(child10.linkedItem, 0, 0, 0, 1, 0, "050.txt", .same, 6) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child5, - toName: "dir150" - ) - - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 0, 4, 1, 2, "dir050", .orphan, 22) - assertItem(child1.linkedItem, 0, 0, 4, 1, 2, "dir050", .orphan, 15) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 0, 0, 4, 0, 4, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 4, 0, 4, "dir100", .orphan, 9) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 0, 0, 0, 0, 1, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 1, "dir110", .orphan, 2) - - let child4 = child3.children[0] // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let child5 = child2.children[1] // dir100 <-> dir100 - assertItem(child5, 0, 0, 0, 0, 3, nil, .orphan, 0) - assertItem(child5.linkedItem, 0, 0, 3, 0, 3, "dir120", .orphan, 7) - - let child6 = child5.children[0] // (null) <-> dir120 - assertItem(child6, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child6.linkedItem, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - - let child7 = child5.children[1] // (null) <-> dir120 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file102.txt", .orphan, 3) - - let child8 = child5.children[2] // (null) <-> dir120 - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - - let child9 = child2.children[2] // dir100 <-> dir100 - assertItem(child9, 0, 0, 3, 0, 3, "dir150", .orphan, 10) - assertItem(child9.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child10 = child9.children[0] // dir150 <-> (null) - assertItem(child10, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child10.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child11 = child9.children[1] // dir150 <-> (null) - assertItem(child11, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child12 = child9.children[2] // dir150 <-> (null) - assertItem(child12, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child12.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child13 = child2.children[3] // dir100 <-> dir100 - assertItem(child13, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child14 = child1.children[1] // dir050 <-> dir050 - assertItem(child14, 0, 0, 0, 1, 0, "050.txt", .same, 6) - assertItem(child14.linkedItem, 0, 0, 0, 1, 0, "050.txt", .same, 6) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 4, 1, 2, "dir050", .orphan, 22) - assertItem(child1.linkedItem, 0, 0, 4, 1, 2, "dir050", .orphan, 15) - - let childVI2 = childVI1.children[0] // dir050 <--> dir050 - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // dir050 <-> dir050 - assertItem(child2, 0, 0, 0, 1, 0, "050.txt", .same, 6) - assertItem(child2.linkedItem, 0, 0, 0, 1, 0, "050.txt", .same, 6) - } - } - - @Test func renameFolderOrphanToOrphan_OnlyMismatches_Right() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: true, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir050") - try createFolder("r/dir050") - try createFolder("l/dir050/dir100") - try createFolder("r/dir050/dir100") - try createFolder("l/dir050/dir100/dir105") - try createFolder("r/dir050/dir100/dir110") - - // create files - try createFile("l/dir050/dir100/dir105/file101.txt", "12") - try createFile("l/dir050/dir100/dir105/file102.txt", "123456") - try createFile("l/dir050/dir100/dir105/file103.txt", "12") - try createFile("r/dir050/dir100/dir110/file102.txt", "123") - try createFile("r/dir050/dir100/dir110/file401.txt", "12") - try createFile("l/dir050/dir100/011.txt", "123456") - try createFile("r/dir050/dir100/file301.txt", "123") - try createFile("r/dir050/dir100/file302.txt", "123456") - try createFile("l/dir050/020.txt", "123456") - try createFile("l/dir050/040.txt", "123456") - try createFile("r/dir050/file201.txt", "123") - try createFile("r/dir050/file202.txt", "123") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 0, 6, 0, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 0, 6, 0, 5, "dir050", .orphan, 20) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 0, 0, 4, 0, 5, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 0, 4, 0, 5, "dir100", .orphan, 14) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 0, 0, 3, 0, 3, "dir105", .orphan, 10) - assertItem(child3.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child4 = child3.children[0] // dir105 <-> (null) - assertItem(child4, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child3.children[1] // dir105 <-> (null) - assertItem(child5, 0, 0, 1, 0, 0, "file102.txt", .orphan, 6) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child6 = child3.children[2] // dir105 <-> (null) - assertItem(child6, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child2.children[1] // dir100 <-> dir100 - assertItem(child7, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 5) - - let child8 = child7.children[0] // (null) <-> dir110 - assertItem(child8, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child8.linkedItem, 0, 0, 1, 0, 0, "file102.txt", .orphan, 3) - - let child9 = child7.children[1] // (null) <-> dir110 - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "file401.txt", .orphan, 2) - - let child10 = child2.children[2] // dir100 <-> dir100 - assertItem(child10, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child10.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child11 = child2.children[3] // dir100 <-> dir100 - assertItem(child11, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child11.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let child12 = child2.children[4] // dir100 <-> dir100 - assertItem(child12, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child12.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let child13 = child1.children[1] // dir050 <-> dir050 - assertItem(child13, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child13.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child14 = child1.children[2] // dir050 <-> dir050 - assertItem(child14, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child14.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child15 = child1.children[3] // dir050 <-> dir050 - assertItem(child15, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child15.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let child16 = child1.children[4] // dir050 <-> dir050 - assertItem(child16, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child16.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child7.linkedItem!, - toName: "dir105" - ) - - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 1, 5, 0, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 1, 5, 0, 5, "dir050", .orphan, 20) - - let child2 = child1.children[0] // dir050 <-> dir050 - assertItem(child2, 0, 1, 3, 0, 4, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 1, 3, 0, 4, "dir100", .orphan, 14) - - let child3 = child2.children[0] // dir100 <-> dir100 - assertItem(child3, 0, 1, 2, 0, 4, "dir105", .orphan, 10) - assertItem(child3.linkedItem, 0, 1, 1, 0, 4, "dir105", .orphan, 5) - - let child4 = child3.children[0] // dir105 <-> dir105 - assertItem(child4, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child5 = child3.children[1] // dir105 <-> dir105 - assertItem(child5, 0, 1, 0, 0, 0, "file102.txt", .changed, 6) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file102.txt", .changed, 3) - - let child6 = child3.children[2] // dir105 <-> dir105 - assertItem(child6, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child3.children[3] // dir105 <-> dir105 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file401.txt", .orphan, 2) - - let child8 = child2.children[1] // dir100 <-> dir100 - assertItem(child8, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child9 = child2.children[2] // dir100 <-> dir100 - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let child10 = child2.children[3] // dir100 <-> dir100 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let child11 = child1.children[1] // dir050 <-> dir050 - assertItem(child11, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child12 = child1.children[2] // dir050 <-> dir050 - assertItem(child12, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child12.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child13 = child1.children[3] // dir050 <-> dir050 - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let child14 = child1.children[4] // dir050 <-> dir050 - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 5) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 1, 5, 0, 5, "dir050", .orphan, 28) - assertItem(child1.linkedItem, 0, 1, 5, 0, 5, "dir050", .orphan, 20) - - let childVI2 = childVI1.children[0] // dir050 <--> dir050 - assertArrayCount(childVI2.children, 4) - let child2 = childVI2.item // dir050 <-> dir050 - assertItem(child2, 0, 1, 3, 0, 4, "dir100", .orphan, 16) - assertItem(child2.linkedItem, 0, 1, 3, 0, 4, "dir100", .orphan, 14) - - let childVI3 = childVI2.children[0] // dir100 <--> dir100 - assertArrayCount(childVI3.children, 4) - let child3 = childVI3.item // dir100 <-> dir100 - assertItem(child3, 0, 1, 2, 0, 4, "dir105", .orphan, 10) - assertItem(child3.linkedItem, 0, 1, 1, 0, 4, "dir105", .orphan, 5) - - let childVI4 = childVI3.children[0] // dir105 <--> dir105 - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // dir105 <-> dir105 - assertItem(child4, 0, 0, 1, 0, 0, "file101.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI5 = childVI3.children[1] // dir105 <--> dir105 - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // dir105 <-> dir105 - assertItem(child5, 0, 1, 0, 0, 0, "file102.txt", .changed, 6) - assertItem(child5.linkedItem, 0, 1, 0, 0, 0, "file102.txt", .changed, 3) - - let childVI6 = childVI3.children[2] // dir105 <--> dir105 - assertArrayCount(childVI6.children, 0) - let child6 = childVI6.item // dir105 <-> dir105 - assertItem(child6, 0, 0, 1, 0, 0, "file103.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI7 = childVI3.children[3] // dir105 <--> dir105 - assertArrayCount(childVI7.children, 0) - let child7 = childVI7.item // dir105 <-> dir105 - assertItem(child7, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child7.linkedItem, 0, 0, 1, 0, 0, "file401.txt", .orphan, 2) - - let childVI8 = childVI2.children[1] // dir100 <--> dir100 - assertArrayCount(childVI8.children, 0) - let child8 = childVI8.item // dir100 <-> dir100 - assertItem(child8, 0, 0, 1, 0, 0, "011.txt", .orphan, 6) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI9 = childVI2.children[2] // dir100 <--> dir100 - assertArrayCount(childVI9.children, 0) - let child9 = childVI9.item // dir100 <-> dir100 - assertItem(child9, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child9.linkedItem, 0, 0, 1, 0, 0, "file301.txt", .orphan, 3) - - let childVI10 = childVI2.children[3] // dir100 <--> dir100 - assertArrayCount(childVI10.children, 0) - let child10 = childVI10.item // dir100 <-> dir100 - assertItem(child10, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child10.linkedItem, 0, 0, 1, 0, 0, "file302.txt", .orphan, 6) - - let childVI11 = childVI1.children[1] // dir050 <--> dir050 - assertArrayCount(childVI11.children, 0) - let child11 = childVI11.item // dir050 <-> dir050 - assertItem(child11, 0, 0, 1, 0, 0, "020.txt", .orphan, 6) - assertItem(child11.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI12 = childVI1.children[2] // dir050 <--> dir050 - assertArrayCount(childVI12.children, 0) - let child12 = childVI12.item // dir050 <-> dir050 - assertItem(child12, 0, 0, 1, 0, 0, "040.txt", .orphan, 6) - assertItem(child12.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let childVI13 = childVI1.children[3] // dir050 <--> dir050 - assertArrayCount(childVI13.children, 0) - let child13 = childVI13.item // dir050 <-> dir050 - assertItem(child13, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child13.linkedItem, 0, 0, 1, 0, 0, "file201.txt", .orphan, 3) - - let childVI14 = childVI1.children[4] // dir050 <--> dir050 - assertArrayCount(childVI14.children, 0) - let child14 = childVI14.item // dir050 <-> dir050 - assertItem(child14, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child14.linkedItem, 0, 0, 1, 0, 0, "file202.txt", .orphan, 3) - } - } - - @Test func renameFolderOrphanToMatching_OnlyMatches_Right_ShowEmptyFolder() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.size, .contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/dir100") - try createFolder("r/dir100") - try createFolder("r/dir100/dir110") - try createFolder("l/dir100/dir120") - - // create files - try createFile("r/dir100/dir110/file001.txt", "12") - try createFile("r/dir100/dir110/file002.txt", "12345") - try createFile("l/dir100/dir120/file001.txt", "12") - try createFile("l/dir100/dir120/file002.txt", "123456") - try createFile("l/dir100/dir120/file003.txt", "12") - - try setFileTimestamp("l/dir100/dir120/file002.txt", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 0, 0, 3, 0, 2, "dir100", .orphan, 10) - assertItem(child1.linkedItem, 0, 0, 2, 0, 2, "dir100", .orphan, 7) - - let child2 = child1.children[0] // dir100 <-> dir100 - assertItem(child2, 0, 0, 0, 0, 2, nil, .orphan, 0) - assertItem(child2.linkedItem, 0, 0, 2, 0, 2, "dir110", .orphan, 7) - - let child3 = child2.children[0] // (null) <-> dir110 - assertItem(child3, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child3.linkedItem, 0, 0, 1, 0, 0, "file001.txt", .orphan, 2) - - let child4 = child2.children[1] // (null) <-> dir110 - assertItem(child4, 0, 0, 0, 0, 0, nil, .orphan, 0) - assertItem(child4.linkedItem, 0, 0, 1, 0, 0, "file002.txt", .orphan, 5) - - let child5 = child1.children[1] // dir100 <-> dir100 - assertItem(child5, 0, 0, 3, 0, 3, "dir120", .orphan, 10) - assertItem(child5.linkedItem, 0, 0, 0, 0, 3, nil, .orphan, 0) - - let child6 = child5.children[0] // dir120 <-> (null) - assertItem(child6, 0, 0, 1, 0, 0, "file001.txt", .orphan, 2) - assertItem(child6.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child7 = child5.children[1] // dir120 <-> (null) - assertItem(child7, 0, 0, 1, 0, 0, "file002.txt", .orphan, 6) - assertItem(child7.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - let child8 = child5.children[2] // dir120 <-> (null) - assertItem(child8, 0, 0, 1, 0, 0, "file003.txt", .orphan, 2) - assertItem(child8.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = RenameCompareItem(operationManager: fileOperationManager) - - fileOperation.rename( - srcRoot: child2.linkedItem!, - toName: "dir120" - ) - - do { - let child1 = rootL.children[0] // l <-> r - assertItem(child1, 1, 0, 1, 1, 1, "dir100", .orphan, 10) - assertItem(child1.linkedItem, 0, 1, 0, 1, 1, "dir100", .orphan, 7) - - let child2 = child1.children[0] // dir100 <-> dir100 - assertItem(child2, 1, 0, 1, 1, 3, "dir120", .orphan, 10) - assertItem(child2.linkedItem, 0, 1, 0, 1, 3, "dir120", .orphan, 7) - - let child3 = child2.children[0] // dir120 <-> dir120 - assertItem(child3, 0, 0, 0, 1, 0, "file001.txt", .same, 2) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "file001.txt", .same, 2) - - let child4 = child2.children[1] // dir120 <-> dir120 - assertItem(child4, 1, 0, 0, 0, 0, "file002.txt", .old, 6) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "file002.txt", .changed, 5) - - let child5 = child2.children[2] // dir120 <-> dir120 - assertItem(child5, 0, 0, 1, 0, 0, "file003.txt", .orphan, 2) - assertItem(child5.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 0, 1, 1, 1, "dir100", .orphan, 10) - assertItem(child1.linkedItem, 0, 1, 0, 1, 1, "dir100", .orphan, 7) - - let childVI2 = childVI1.children[0] // dir100 <--> dir100 - assertArrayCount(childVI2.children, 1) - let child2 = childVI2.item // dir100 <-> dir100 - assertItem(child2, 1, 0, 1, 1, 3, "dir120", .orphan, 10) - assertItem(child2.linkedItem, 0, 1, 0, 1, 3, "dir120", .orphan, 7) - - let childVI3 = childVI2.children[0] // dir120 <--> dir120 - assertArrayCount(childVI3.children, 0) - let child3 = childVI3.item // dir120 <-> dir120 - assertItem(child3, 0, 0, 0, 1, 0, "file001.txt", .same, 2) - assertItem(child3.linkedItem, 0, 0, 0, 1, 0, "file001.txt", .same, 2) - } - } -} - -// swiftlint:enable file_length force_unwrapping function_body_length diff --git a/Tests/Sources/FileSystem/Tests/SymbolicLinkTests.swift b/Tests/Sources/FileSystem/Tests/SymbolicLinkTests.swift deleted file mode 100644 index 7e31fcc..0000000 --- a/Tests/Sources/FileSystem/Tests/SymbolicLinkTests.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// SymbolicLinkTests.swift -// VisualDiffer -// -// Created by davide ficano on 15/12/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -final class SymbolicLinkTests: BaseTests { - @Test func symbolicLinkLoop() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .onlyMismatches - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/parent") - try createSymlink("l/parent/sym_parent", "../parent") - - try createFolder("r") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - assertErrors( - folderReaderDelegate.errors, [ - FileError.symlinkLoop(path: appendFolder("l/parent/sym_parent").osPath), - ] - ) - } -} diff --git a/Tests/Sources/FileSystem/Tests/TouchFilesTests.swift b/Tests/Sources/FileSystem/Tests/TouchFilesTests.swift deleted file mode 100644 index 9d167c9..0000000 --- a/Tests/Sources/FileSystem/Tests/TouchFilesTests.swift +++ /dev/null @@ -1,312 +0,0 @@ -// -// TouchFilesTests.swift -// VisualDiffer -// -// Created by davide ficano on 11/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping function_body_length -final class TouchFilesTests: BaseTests { - @Test func touchOlderFiles() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/a/bb/cc") - try createFolder("r/a/bb/cc") - - try createFile("l/a/bb/cc/cc_file.txt", "12") - try createFile("l/a/bb/bb_file.txt", "12") - - try createFile("r/a/bb/cc/cc_file.txt", "123") - try createFile("r/a/bb/bb_file.txt", "123") - - try setFileTimestamp("l/a/bb/cc/cc_file.txt", "2001-03-24 10: 45: 32 +0600") - try setFileTimestamp("r/a/bb/cc/cc_file.txt", "2002-03-24 10: 45: 32 +0600") - - try setFileTimestamp("l/a/bb/bb_file.txt", "2002-03-24 10: 45: 32 +0600") - try setFileTimestamp("r/a/bb/bb_file.txt", "2001-03-24 10: 45: 32 +0600") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] - assertItem(child1, 1, 1, 0, 0, 1, "a", .orphan, 4) - assertItem(child1.linkedItem, 1, 1, 0, 0, 1, "a", .orphan, 6) - - var child2 = child1.children[0] // a - assertItem(child2, 1, 1, 0, 0, 2, "bb", .orphan, 4) - assertItem(child2.linkedItem, 1, 1, 0, 0, 2, "bb", .orphan, 6) - - var child3 = child2.children[0] // bb - assertItem(child3, 1, 0, 0, 0, 1, "cc", .orphan, 2) - assertItem(child3.linkedItem, 0, 1, 0, 0, 1, "cc", .orphan, 3) - - var child4 = child3.children[0] // cc - assertItem(child4, 1, 0, 0, 0, 0, "cc_file.txt", .old, 2) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "cc_file.txt", .changed, 3) - - var child5 = child2.children[1] // bb - assertItem(child5, 0, 1, 0, 0, 0, "bb_file.txt", .changed, 2) - assertItem(child5.linkedItem, 1, 0, 0, 0, 0, "bb_file.txt", .old, 3) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 1, 1, 0, 0, 1, "a", .orphan, 4) - assertItem(child1.linkedItem, 1, 1, 0, 0, 1, "a", .orphan, 6) - - let childVI2 = childVI1.children[0] // a <--> a - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // a <-> a - assertItem(child2, 1, 1, 0, 0, 2, "bb", .orphan, 4) - assertItem(child2.linkedItem, 1, 1, 0, 0, 2, "bb", .orphan, 6) - - let childVI3 = childVI2.children[0] // bb <--> bb - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // bb <-> bb - assertItem(child3, 1, 0, 0, 0, 1, "cc", .orphan, 2) - assertItem(child3.linkedItem, 0, 1, 0, 0, 1, "cc", .orphan, 3) - - let childVI4 = childVI3.children[0] // cc <--> cc - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // cc <-> cc - assertItem(child4, 1, 0, 0, 0, 0, "cc_file.txt", .old, 2) - assertItem(child4.linkedItem, 0, 1, 0, 0, 0, "cc_file.txt", .changed, 3) - - let childVI5 = childVI2.children[1] // bb <--> bb - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // bb <-> bb - assertItem(child5, 0, 1, 0, 0, 0, "bb_file.txt", .changed, 2) - assertItem(child5.linkedItem, 1, 0, 0, 0, 0, "bb_file.txt", .old, 3) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let touchDate = try buildDate("2012-05-05 11: 00: 11 +0000") - - let fileOperationDelegate = MockFileOperationManagerDelegate() - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = TouchCompareItem(operationManager: fileOperationManager) - - fileOperation.touch( - srcRoot: child1, - includeSubfolders: true, - touchDate: touchDate - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 2, 0, 0, 1, "a", .orphan, 4) - assertItem(child1.linkedItem, 2, 0, 0, 0, 1, "a", .orphan, 6) - try assertTimestamps(child1, nil, "2012-05-05 11: 00: 11 +0000") - - child2 = child1.children[0] // a - assertItem(child2, 0, 2, 0, 0, 2, "bb", .orphan, 4) - assertItem(child2.linkedItem, 2, 0, 0, 0, 2, "bb", .orphan, 6) - try assertTimestamps(child2, nil, "2012-05-05 11: 00: 11 +0000") - - child3 = child2.children[0] // bb - assertItem(child3, 0, 1, 0, 0, 1, "cc", .orphan, 2) - assertItem(child3.linkedItem, 1, 0, 0, 0, 1, "cc", .orphan, 3) - try assertTimestamps(child3, nil, "2012-05-05 11: 00: 11 +0000") - - child4 = child3.children[0] // cc - assertItem(child4, 0, 1, 0, 0, 0, "cc_file.txt", .changed, 2) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "cc_file.txt", .old, 3) - try assertTimestamps(child4, nil, "2012-05-05 11: 00: 11 +0000") - - child5 = child2.children[1] // bb - assertItem(child5, 0, 1, 0, 0, 0, "bb_file.txt", .changed, 2) - assertItem(child5.linkedItem, 1, 0, 0, 0, 0, "bb_file.txt", .old, 3) - try assertTimestamps(child5, nil, "2012-05-05 11: 00: 11 +0000") - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 2, 0, 0, 1, "a", .orphan, 4) - assertItem(child1.linkedItem, 2, 0, 0, 0, 1, "a", .orphan, 6) - - let childVI2 = childVI1.children[0] // a <--> a - assertArrayCount(childVI2.children, 2) - let child2 = childVI2.item // a <-> a - assertItem(child2, 0, 2, 0, 0, 2, "bb", .orphan, 4) - assertItem(child2.linkedItem, 2, 0, 0, 0, 2, "bb", .orphan, 6) - - let childVI3 = childVI2.children[0] // bb <--> bb - assertArrayCount(childVI3.children, 1) - let child3 = childVI3.item // bb <-> bb - assertItem(child3, 0, 1, 0, 0, 1, "cc", .orphan, 2) - assertItem(child3.linkedItem, 1, 0, 0, 0, 1, "cc", .orphan, 3) - - let childVI4 = childVI3.children[0] // cc <--> cc - assertArrayCount(childVI4.children, 0) - let child4 = childVI4.item // cc <-> cc - assertItem(child4, 0, 1, 0, 0, 0, "cc_file.txt", .changed, 2) - assertItem(child4.linkedItem, 1, 0, 0, 0, 0, "cc_file.txt", .old, 3) - - let childVI5 = childVI2.children[1] // bb <--> bb - assertArrayCount(childVI5.children, 0) - let child5 = childVI5.item // bb <-> bb - assertItem(child5, 0, 1, 0, 0, 0, "bb_file.txt", .changed, 2) - assertItem(child5.linkedItem, 1, 0, 0, 0, 0, "bb_file.txt", .old, 3) - } - } - - @Test func touchOrphanFile() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: .contentTimestamp, - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: true, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - try createFolder("l/dir1") - try createFolder("r/dir1") - - try createFile("l/dir1/file1.txt", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - let vi = rootL.visibleItem! - - var child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - var child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 0, "file1.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 0, "file1.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - - try assertOnlySetup() - - // VDLocalFileManager doesn't hold the delegate so allocate it as local variable - // otherwise is released to early it the test crashes - let touchDate = try buildDate("2012-05-05 11: 00: 11 +0000") - - let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: true) - let fileOperationManager = FileOperationManager( - filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate, - includesFiltered: false - ) - let fileOperation = TouchCompareItem(operationManager: fileOperationManager) - fileOperation.touch( - srcRoot: child2, - includeSubfolders: false, - touchDate: touchDate - ) - - child1 = rootL.children[0] // l - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - child2 = child1.children[0] // dir1 - assertItem(child2, 0, 0, 1, 0, 0, "file1.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - try assertTimestamps(child2, nil, "2012-05-05 11: 00: 11 +0000") - do { - // VisibleItems - let childVI1 = vi.children[0] // l <--> r - assertArrayCount(childVI1.children, 1) - let child1 = childVI1.item // l <-> r - assertItem(child1, 0, 0, 1, 0, 1, "dir1", .orphan, 2) - assertItem(child1.linkedItem, 0, 0, 0, 0, 1, "dir1", .orphan, 0) - - let childVI2 = childVI1.children[0] // dir1 <--> dir1 - assertArrayCount(childVI2.children, 0) - let child2 = childVI2.item // dir1 <-> dir1 - assertItem(child2, 0, 0, 1, 0, 0, "file1.txt", .orphan, 2) - assertItem(child2.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } - } -} - -// swiftlint:enable force_unwrapping function_body_length diff --git a/Tests/Sources/Path/LeafPathTests.swift b/Tests/Sources/Path/LeafPathTests.swift deleted file mode 100644 index 99fef49..0000000 --- a/Tests/Sources/Path/LeafPathTests.swift +++ /dev/null @@ -1,221 +0,0 @@ -// -// LeafPathTests.swift -// VisualDiffer -// -// Created by davide ficano on 30/01/14. -// Copyright (c) 2014 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable function_body_length -final class LeafPathTests: BaseTests { - func findLeafPaths(_ paths: [String]) -> [CompareItem] { - let arr = paths.map { CompareItem( - path: $0, - attrs: nil, - fileExtraOptions: [], - parent: nil - ) - } - - return CompareItem.findLeafPaths(arr) - } - - @Test func findLeafPaths1() { - let paths = [ - "/Users/dave/trash/test_suite/createDir/l/dir1/dir2/dir3/test_date", - "/Users/dave/trash/test_suite/createDir/l/dir1/dir2/dir3/test_date copia", - ] - let result = findLeafPaths(paths) - assertArrayCount(result, 2) - #expect(result[0].path == paths[0], "\(String(describing: result[0].path))") - #expect(result[1].path == paths[1], "\(String(describing: result[1].path))") - } - - @Test func findLeafPaths2() { - let paths = [ - "/Users/dave/trash/test_suite/createDir/l/dir1/dir2", - "/Users/dave/trash/test_suite/createDir/l/dir1/dir2/dir3", - "/Users/dave/trash/test_suite/createDir/l/dir1/dir2/dir3/test_date", - ] - let result = findLeafPaths(paths) - assertArrayCount(result, 1) - #expect(result[0].path == paths[2], "\(String(describing: result[0].path))") - } - - @Test func findLeafPaths3() { - let paths = [ - "/Users/dave/trash/test_suite/createDir/test/10/20/30/40", - "/Users/dave/trash/test_suite/createDir/test/10/40", - ] - let result = findLeafPaths(paths) - assertArrayCount(result, 2) - #expect(result[0].path == paths[0], "\(String(describing: result[0].path))") - #expect(result[1].path == paths[1], "\(String(describing: result[1].path))") - } - - @Test func findLeafPaths4() { - let paths = [ - "/Users/dave/trash/app/VisualDiffer.xcodeproj", - "/Users/dave/trash/app/VisualDiffer.xcodeproj/project.xcworkspace", - "/Users/dave/trash/app/VisualDiffer.xcodeproj/project.xcworkspace/xcuserdata", - "/Users/dave/trash/app/VisualDiffer.xcodeproj/project.xcworkspace/xcuserdata/dave.xcuserdatad", - "/Users/dave/trash/app/VisualDiffer.xcodeproj/project.xcworkspace/xcuserdata/dave.xcuserdatad/UserInterfaceState.xcuserstate", - "/Users/dave/trash/app/VisualDiffer.xcodeproj/xcuserdata", - ] - let result = findLeafPaths(paths) - assertArrayCount(result, 2) - #expect(result[0].path == paths[4], "\(String(describing: result[0].path))") - #expect(result[1].path == paths[5], "\(String(describing: result[1].path))") - } - - @Test func closestPath() { - var arr = [ - "/Users/dave/trash/test_suite/bug244/r.txt", - "/Users/dave/trash/0latest_stable/app", - "/opt/devel/0dafiprj/git.github/android/ListViewOverlay", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrep", - "/opt/devel/0dafiprj/git.github/android/BatteryChargerNotifier/BatteryChargerNotifier", - "/Users/dave/trash/photoshelf_all/photoshelf_cached/app", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrep/en.lproj/MainMenu.xib", - "/opt/devel/0dafiprj/git.github/android/TestApp", - "/Users/dave/trash/Crawler/src", - "/Users/dave/Dropbox/applicazioni/photoshelf/tags (1).csv", - "/opt/devel/0dafiprj/git.github/dafizilla/slamtracker", - "/opt/devel/0dafiprj/sourceforge/dafizilla/trunk/webapp/viewsourcewith/update.rdf", - "/opt/devel/0dafiprj/osx/visualdiffer/website", - "/Users/dave/trash/photoshelf_all/photoshelf_before_module/app/src/main/res", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrep/utils/document", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app/src", - "/Volumes/Seneca/devel/tests/visualdiffer/immaweb/svn/immaweb", - "/Users/dave/trash/vd_localized/Localizable.strings", - "/Users/dave/trash/test_suite/refactor_visibileItem/r", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrepTests/support/ita-001-input.txt", - "/Users/dave/trash/photoshelf_all/photoshelf_before_module/app/src/main/java", - "/opt/devel/0dafiprj/osx/visualdiffer/website/css", - "/Volumes/Seneca/mm/movies/xxx/app", - "/Volumes/Seneca/devel/mycocoapod/TOPKit/TOPKit", - "/Users/dave/trash/test_suite/bug239/r.csv", - "/Users/dave/trash/test-encoding-word-file.doc", - "/Users/dave/trash/Crawler", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app/src/main/java/com", - "/opt/devel/0dafiprj/osx/visualdiffer/app/common", - "/Users/dave/trash", - "/opt/devel/0dafiprj/osx/visualdiffer/visualdiffer.github.com/css", - "/opt/devel/0dafiprj/sourceforge/dafizilla_old/trunk", - "/Users/dave/trash/progressbar", - "/Users/dave/trash/photoshelf_all/photoshelf_gridview2", - "/opt/devel/0dafiprj/sourceforge/dafizilla", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app/src/main/java/com/ternaryop", - "/Users/dave/trash/test_suite/bug255", - "/opt/devel/0dafiprj/git.github/dafizilla/viewsourcewith/profile/ff/extensions/{eecba28f-b68b-4b3a-b501-6ce12e6b8696}/chrome/plainfiles", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrep/utils", - "/Users/dave/trash/test_suite/bug244/l.txt", - "/opt/devel/0dafiprj/osx/visualdiffer/visualdiffer.github.com/_plugins/tocGenerator.rb", - "/Users/dave/trash/test_suite/FileSystemUtilsTests/testMoveMatchFileBecomeFiltered/r", - "/Volumes/Seneca/devel/tests/visualdiffer/immaweb/svn/immaweb/trunk", - "/opt/devel/0dafiprj/sourceforge/dafizilla_old", - "/Users/dave/trash/test_suite/bug255/l", - "/Volumes/Seneca/mm/movies/xxx/Archivio/commonutils-master", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app/src/main/res", - "/Users/dave/trash/update.rdf", - "/Users/dave/trash/photoshelf_all/tags.csv", - "/Users/dave/trash/test_suite/bug255/r", - "/Users/dave/trash/hidefiles/sql1.txt", - "/opt/devel/0dafiprj/osx/visualgrepswift", - "/Volumes/Seneca/mm/movies/xxx/Archivio/photoshelf", - "/Users/dave/trash/photoshelf_all/photoshelf_cached", - "/Users/dave/trash/VisualDiffer/common/VDPathViewController.m", - "/Volumes/Seneca/devel/examples/android/apk/PhotoShelf-release/AndroidManifest.xml", - "/Users/dave/trash/test_suite/bug239/l.csv", - "/Users/dave/trash/visualdi_mant1-141116.sql", - "/Volumes/Seneca/mm/movies/person.of.interest", - "/Users/dave/trash/test_suite/FileSystemUtilsTests/testMoveMatchFileBecomeFiltered/l", - "/Volumes/Seneca/mm/movies/temp/visualdi_mant1.sql", - "/opt/devel/0dafiprj/git.github/dafizilla/viewsourcewith/dist/bin/viewsourcewith-0.9.4.3", - "/opt/devel/0dafiprj/osx/visualdiffer/website/new/css", - "/Users/dave/trash/test_suite/refactor_visibileItem/l", - "/Users/dave/Dropbox/applicazioni/photoshelf/tags.csv", - "/opt/devel/0dafiprj/git.github/dafizilla/viewsourcewith/dist/bin/amo", - "/Users/dave/trash/hidefiles/list-only-files.txt", - "/opt/devel/0dafiprj/osx/visualdiffer/app/common/VDPathViewController.m", - "/Users/dave/trash/tocgen_html_site", - "/opt/devel/0dafiprj/git.github/android/photoshelf", - "/Users/dave/trash/tocgen_xhtml_site", - "/Users/dave/trash/ShareActionProvider-main.xml", - "/Users/dave/trash/0latest_stable/app 2", - "/Volumes/Seneca/mm/movies/xxx/Archivio/photoshelf-master", - "/Volumes/Seneca/devel/tests/visualdiffer/immaweb/svn/immaweb/trunk/src", - "/Users/dave/trash/test_suite/a.txt", - "/opt/devel/0dafiprj/git.github/android/commonutils/app/src/main/java/com", - "/opt/devel/0dafiprj/osx/visualdiffer/app/utils/document", - "/opt/devel/0dafiprj/git.github/jekyll-toc-generator/_plugins/tocGenerator.rb", - "/opt/devel/0dafiprj/git.github/dafizilla/viewsourcewith/dist/bin/debug", - "/opt/devel/0dafiprj/osx/visualdiffer/app", - "/opt/devel/0dafiprj/osx/visualdiffer/visualdiffer.github.com", - "/Users/dave/Dropbox/Applicazioni/PhotoShelf/birthdays.csv", - "/opt/devel/0dafiprj/sourceforge/dafizilla/trunk/bespin", - "/Users/dave/trash/photoshelf_all/photoshelf_toolbar", - "/Volumes/Seneca/mm/movies/temp", - "/Volumes/Seneca/devel/examples/android/dropbox-android-sync-sdk-3.0.2/libs", - "/private/var", - "/Users/dave/trash/visualgrepswift/VisualGrepSwift", - "/Users/dave/trash/test_suite/FileSystemUtilsTests/testCopyFollowSymLink/l", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrep/utils/io", - "/Users/dave/trash/photoshelf_all/CommonUtilsModule", - "/opt/devel/0dafiprj/osx/visualdiffer/app/utils", - "/opt/devel/0dafiprj/git.github/android/commonutils", - "/Users/dave/trash/VisualDiffer", - "/Users/dave/.Trash/update.rdf", - "/Users/dave/trash/test_suite/bug218/l", - "/opt/devel/0dafiprj/osx/visualgrep/app/VisualGrepTests/support/ita-001-pattern.txt", - "/Volumes/Seneca/devel/tests/visualdiffer/immaweb/prj/immaweb", - "/Users/dave/trash/birthdays-150129.csv", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app", - "/opt/devel/0dafiprj/git.github/dafizilla/viewsourcewith/dist/bin/amo/viewsourcewith-0.9.4.3", - "/Users/dave/trash/Project_Default.xml", - "/Users/dave/trash/VisualDiffer/common", - "/Users/dave/trash/test_suite/FileSystemUtilsTests/testCopyFollowSymLink/r", - "/Users/dave/trash/test_suite/bug218/r", - "/Volumes/Seneca/devel/tests/visualdiffer/immaweb/prj/immaweb/src", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app/src/main/java", - "/opt/devel/0dafiprj/osx/visualdiffer/app/English.lproj/Localizable.strings", - "/Users/dave/trash/test_suite/bug251/l", - "/opt/devel/0dafiprj/osx/visualgrep/site", - "/Users/dave/trash/photoshelf_all/photoshelf_added_icons", - "/opt/devel/0dafiprj/git.github/dafizilla/viewsourcewith/src/main", - "/opt/devel/0dafiprj/git.github/android/commonutils/app/src/main/java", - "/opt/devel/0dafiprj/git.github/android/photoshelf/app/libs", - "/Users/dave/trash/test_suite/bug251/r", - ] - - var found: String? - var expectedResult: String? - - expectedResult = "/opt/devel/0dafiprj/git.github/android/BatteryChargerNotifier/BatteryChargerNotifier" - found = SecureBookmark.shared.findClosestPath(to: URL(filePath: "/opt/devel/0dafiprj/git.github/android/BatteryChargerNotifier/BatteryChargerNotifier/gradle"), searchPaths: arr) - #expect(found == expectedResult, "found \(String(describing: found)) expected \(String(describing: expectedResult))") - - expectedResult = "/Users/dave/trash/0latest_stable/app 2" - found = SecureBookmark.shared.findClosestPath(to: URL(filePath: "/Users/dave/trash/0latest_stable/app 2"), searchPaths: arr) - #expect(found == expectedResult, "found \(String(describing: found)) expected \(String(describing: expectedResult))") - - expectedResult = "/opt/devel/0dafiprj/osx/visualdiffer/app" - found = SecureBookmark.shared.findClosestPath(to: URL(filePath: "/opt/devel/0dafiprj/osx/visualdiffer/app"), searchPaths: arr) - #expect(found == expectedResult, "found \(String(describing: found)) expected \(String(describing: expectedResult))") - - arr = ["/Users/app 2 3"] - expectedResult = nil - found = SecureBookmark.shared.findClosestPath(to: URL(filePath: "/Users/app 2"), searchPaths: arr) - #expect(found == expectedResult, "found \(String(describing: found)) expected \(String(describing: expectedResult))") - - arr = ["/Users/app", "/All/aa", "/Zoom/hello"] - expectedResult = "/Users/app" - found = SecureBookmark.shared.findClosestPath(to: URL(filePath: "/Users/app/file"), searchPaths: arr) - #expect(found == expectedResult, "found \(String(describing: found)) expected \(String(describing: expectedResult))") - } -} - -// swiftlint:enable function_body_length diff --git a/Tests/Sources/UI/Tests/FoldersWindowControllerTests.swift b/Tests/Sources/UI/Tests/FoldersWindowControllerTests.swift deleted file mode 100644 index ec495b6..0000000 --- a/Tests/Sources/UI/Tests/FoldersWindowControllerTests.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// FoldersWindowControllerTests.swift -// VisualDiffer -// -// Created by davide ficano on 22/11/12. -// Copyright (c) 2012 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -// swiftlint:disable force_unwrapping -final class FoldersWindowControllerTests: BaseTests { - // This create a test case to generate the error trapped on - // [FoldersWindowController (id)outlineView: outlineView child: (NSInteger)index ofItem: (id)item] - @Test func savedFromArrayOutOfBound() throws { - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/empty_folder1") - try createFolder("r/empty_folder1") - try createFolder("l/empty_folder2") - try createFolder("r/folder_one_file_inside") - - // create files - try createFile("r/folder_one_file_inside/AppDelegate.m", "12345") - } - - @Test("This isn't a test but a way to prepare the folders for the test") func excludingByName() throws { - let comparatorDelegate = MockItemComparatorDelegate() - let comparator = ItemComparator( - options: [.contentTimestamp], - delegate: comparatorDelegate, - bufferSize: 8192 - ) - let filterConfig = FilterConfig( - showFilteredFiles: false, - hideEmptyFolders: false, - followSymLinks: false, - skipPackages: false, - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileExtraOptions: [], - displayOptions: .showAll - ) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader( - with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true) - ) - - try removeItem("l") - try removeItem("r") - - // create folders - try createFolder("l/folder1") - try createFolder("r/folder1") - try createFolder("l/folder1/folder2") - try createFolder("r/folder1/folder2") - - // create files - try createFile("l/folder1/folder2/file3.txt", "12345") - try createFile("r/folder1/folder2/file3.txt", "12") - try createFile("l/folder1/file2.txt", "12") - - folderReader.start( - withLeftRoot: nil, - rightRoot: nil, - leftPath: appendFolder("l"), - rightPath: appendFolder("r") - ) - - let rootL = folderReader.leftRoot! - // let rootR = folderReader.rightRoot! - - let child1 = rootL.children[0] // l - assertItem(child1, 0, 1, 1, 0, 2, "folder1", .orphan, 7) - assertItem(child1.linkedItem, 0, 1, 0, 0, 2, "folder1", .orphan, 2) - - let child2 = child1.children[0] // folder1 - assertItem(child2, 0, 1, 0, 0, 1, "folder2", .orphan, 5) - assertItem(child2.linkedItem, 0, 1, 0, 0, 1, "folder2", .orphan, 2) - - let child3 = child2.children[0] // folder2 - assertItem(child3, 0, 1, 0, 0, 0, "file3.txt", .changed, 5) - assertItem(child3.linkedItem, 0, 1, 0, 0, 0, "file3.txt", .changed, 2) - - let child4 = child1.children[1] // folder1 - assertItem(child4, 0, 0, 1, 0, 0, "file2.txt", .orphan, 2) - assertItem(child4.linkedItem, 0, 0, 0, 0, 0, nil, .orphan, 0) - } -} - -// swiftlint:enable force_unwrapping diff --git a/Tests/Sources/UI/Tests/RefreshInfoTests.swift b/Tests/Sources/UI/Tests/RefreshInfoTests.swift deleted file mode 100644 index bdd6f95..0000000 --- a/Tests/Sources/UI/Tests/RefreshInfoTests.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// RefreshInfoTests.swift -// VisualDiffer -// -// Created by davide ficano on 24/08/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Testing -@testable import VisualDiffer - -final class RefreshInfoTests: BaseTests { - @Test func refreshInfoStartComparisonNoExpand() { - let expected = RefreshInfo( - initState: true, - expandAllFolders: false - ) - - #expect(expected.refreshFolders == true) - #expect(expected.realign == true) - #expect(expected.refreshComparison == true) - #expect(expected.expandAllFolders == false) - } - - @Test func refreshInfoExcludeByNameNoExpand() { - let expected = RefreshInfo( - initState: false - ) - - #expect(expected.refreshFolders == false) - #expect(expected.realign == false) - #expect(expected.refreshComparison == false) - #expect(expected.expandAllFolders == false) - } - - @Test func refreshInfoSelectComparisonNoExpand() { - let expected = RefreshInfo( - initState: false, - refreshComparison: true - ) - - #expect(expected.refreshFolders == false) - #expect(expected.realign == false) - #expect(expected.refreshComparison == true) - #expect(expected.expandAllFolders == false) - } - - @Test func refreshInfoSelectDisplayFlagsNoExpand() { - let expected = RefreshInfo( - initState: false - ) - #expect(expected.refreshFolders == false) - #expect(expected.realign == false) - #expect(expected.refreshComparison == false) - #expect(expected.expandAllFolders == false) - } - - @Test func refreshInfoSetBaseFoldersSrcNoExpand() { - let expected = RefreshInfo( - initState: false, - realign: true - ) - - #expect(expected.refreshFolders == false) - #expect(expected.realign == true) - #expect(expected.refreshComparison == true) - #expect(expected.expandAllFolders == false) - } - - // MARK: - expand - - @Test func refreshInfoStartComparisonExpand() { - let expected = RefreshInfo( - initState: true - ) - #expect(expected.refreshFolders == true) - #expect(expected.realign == true) - #expect(expected.refreshComparison == true) - #expect(expected.expandAllFolders == true) - } - - @Test func refreshInfoExcludeByNameExpand() { - let expected = RefreshInfo( - initState: false, - expandAllFolders: true - ) - #expect(expected.refreshFolders == false) - #expect(expected.realign == false) - #expect(expected.refreshComparison == false) - #expect(expected.expandAllFolders == true) - } - - @Test func refreshInfoSelectComparisonExpand() { - let expected = RefreshInfo( - initState: false, - refreshComparison: true, - expandAllFolders: true - ) - - #expect(expected.refreshFolders == false) - #expect(expected.realign == false) - #expect(expected.refreshComparison == true) - #expect(expected.expandAllFolders == true) - } - - @Test func refreshInfoSelectDisplayFlagsExpand() { - let expected = RefreshInfo( - initState: false, - expandAllFolders: true - ) - - #expect(expected.refreshFolders == false) - #expect(expected.realign == false) - #expect(expected.refreshComparison == false) - #expect(expected.expandAllFolders == true) - } - - @Test func refreshInfoSetBaseFoldersSrcExpand() { - let expected = RefreshInfo( - initState: false, - realign: true, - expandAllFolders: true - ) - - #expect(expected.refreshFolders == false) - #expect(expected.realign == true) - #expect(expected.refreshComparison == true) - #expect(expected.expandAllFolders == true) - } -} diff --git a/Tests/Sources/Utils/StringUtils.swift b/Tests/Sources/Utils/StringUtils.swift deleted file mode 100644 index f5eae2e..0000000 --- a/Tests/Sources/Utils/StringUtils.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// StringUtils.swift -// VisualDiffer -// -// Created by davide ficano on 01/03/25. -// Copyright (c) 2025 visualdiffer.com -// - -import Foundation - -public func generateAsciiChar() -> Character { - // swiftlint:disable:next force_unwrapping - Character(Unicode.Scalar(Int.random(in: 0 ..< 26) + 97)!) -} - -public func invertCase(_ str: inout String, index: Int) { - let len = str.count - guard len > 0 else { - return - } - let index = str.index(str.startIndex, offsetBy: index) - let ch: Character = str[index] - let inverted = ch.isUppercase ? str[index].lowercased() : str[index].uppercased() - str.replaceSubrange(index ... index, with: inverted) -} diff --git a/Versions-Template.xcconfig b/Versions-Template.xcconfig deleted file mode 100644 index daf4d96..0000000 --- a/Versions-Template.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -// -// Versions.xcconfig -// VisualDiffer -// -// Created by davide on 01/02/22. -// Copyright (c) 2022 visualdiffer.com -// - -// Copy this file to Versions.xcconfig and edit the values - -APP_VERSION = 1.0.0 -APP_BUNDLE_VERSION = $(APP_VERSION) - -// Remove comment to generate tests setup without running tests -//SWIFT_ACTIVE_COMPILATION_CONDITIONS = TEST_ONLY_SETUP diff --git a/VisualDiffer-Bridging-Header.h b/VisualDiffer-Bridging-Header.h deleted file mode 100644 index 2e1fe33..0000000 --- a/VisualDiffer-Bridging-Header.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - -#import "BigFileFileOperationManager.h" -#import "MGScopeBar.h" -#import "MGScopeBarDelegateProtocol.h" -#import "NSPredicate+Objc.h" -#import "TitlePredicateEditorRowTemplate.h" -#import "TOPFileSizePredicateEditorRowTemplate.h" -#import "TOPTimestampPredicateEditorRowTemplate.h" -#import "UnifiedDiff.h" -#import "UDiffScriptBuilder.h" diff --git a/VisualDiffer-Info.plist b/VisualDiffer-Info.plist deleted file mode 100644 index 114350f..0000000 --- a/VisualDiffer-Info.plist +++ /dev/null @@ -1,75 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - VisualDiffer - CFBundleDocumentTypes - - - CFBundleTypeExtensions - - vdiff - - CFBundleTypeIconFile - vd - CFBundleTypeMIMETypes - - application/octet-stream - - CFBundleTypeName - VisualDiffer - CFBundleTypeRole - Editor - LSTypeIsPackage - - LSHandlerRank - Owner - NSDocumentClass - VDDocument - NSPersistentStoreTypeKey - Binary - - - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleHelpBookFolder - VisualDifferHelp - CFBundleHelpBookName - com.visualdiffer.help - CFBundleIconFile - vd.icns - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - $(APP_VERSION) - CFBundleSignature - ???? - CFBundleVersion - $(APP_BUNDLE_VERSION) - LSApplicationCategoryType - public.app-category.utilities - LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} - NSHumanReadableCopyright - 2010, visualdiffer.com - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - ITSAppUsesNonExemptEncryption - - NSAppleScriptEnabled - - OSAScriptingDefinition - ${PRODUCT_NAME}.sdef - - diff --git a/VisualDiffer-Sparkle-Info.plist b/VisualDiffer-Sparkle-Info.plist deleted file mode 100644 index 7f6ba8c..0000000 --- a/VisualDiffer-Sparkle-Info.plist +++ /dev/null @@ -1,85 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - VisualDiffer - CFBundleDocumentTypes - - - CFBundleTypeExtensions - - vdiff - - CFBundleTypeIconFile - vd - CFBundleTypeMIMETypes - - application/octet-stream - - CFBundleTypeName - VisualDiffer - CFBundleTypeRole - Editor - LSTypeIsPackage - - LSHandlerRank - Owner - NSDocumentClass - VDDocument - NSPersistentStoreTypeKey - Binary - - - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleHelpBookFolder - VisualDifferHelp - CFBundleHelpBookName - com.visualdiffer.help - CFBundleIconFile - vd.icns - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - $(APP_VERSION) - CFBundleSignature - ???? - CFBundleVersion - $(APP_BUNDLE_VERSION) - LSApplicationCategoryType - public.app-category.utilities - LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} - NSHumanReadableCopyright - 2010, visualdiffer.com - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - ITSAppUsesNonExemptEncryption - - NSAppleScriptEnabled - - OSAScriptingDefinition - ${PRODUCT_NAME}.sdef - - SUFeedURL - https://app.visualdiffer.com/appcast.xml - - SUPublicEDKey - kuAijnKlIhNOPvlWkldxU/ZRNY2d35Ltwcd1JnO3DzA= - - SUEnableInstallerLauncherService - - - - diff --git a/VisualDiffer-Sparkle.entitlements b/VisualDiffer-Sparkle.entitlements deleted file mode 100644 index a32a63f..0000000 --- a/VisualDiffer-Sparkle.entitlements +++ /dev/null @@ -1,19 +0,0 @@ - - - - - com.apple.security.application-groups - - $(TeamIdentifierPrefix)com.appgroup.visualdiffer - - com.apple.security.files.bookmarks.app-scope - - - com.apple.security.temporary-exception.mach-lookup.global-name - - $(PRODUCT_BUNDLE_IDENTIFIER)-spks - $(PRODUCT_BUNDLE_IDENTIFIER)-spki - - - - diff --git a/VisualDiffer.entitlements b/VisualDiffer.entitlements deleted file mode 100644 index 3b2397e..0000000 --- a/VisualDiffer.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.application-groups - - $(TeamIdentifierPrefix)com.appgroup.visualdiffer - - com.apple.security.files.bookmarks.app-scope - - - diff --git a/VisualDiffer.xcodeproj/project.pbxproj b/VisualDiffer.xcodeproj/project.pbxproj deleted file mode 100644 index fd309e2..0000000 --- a/VisualDiffer.xcodeproj/project.pbxproj +++ /dev/null @@ -1,4481 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 2F7446AB0DB6BCF400F9684A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2F7446A70DB6BCF400F9684A /* MainMenu.xib */; }; - 55157D7512898F8800826FF8 /* folder-000-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6012898F8800826FF8 /* folder-000-open.png */; }; - 55157D7612898F8800826FF8 /* folder-000.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6112898F8800826FF8 /* folder-000.png */; }; - 55157D7712898F8800826FF8 /* folder-001-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6212898F8800826FF8 /* folder-001-open.png */; }; - 55157D7812898F8800826FF8 /* folder-001.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6312898F8800826FF8 /* folder-001.png */; }; - 55157D7912898F8800826FF8 /* folder-010-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6412898F8800826FF8 /* folder-010-open.png */; }; - 55157D7A12898F8800826FF8 /* folder-010.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6512898F8800826FF8 /* folder-010.png */; }; - 55157D7B12898F8800826FF8 /* folder-011-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6612898F8800826FF8 /* folder-011-open.png */; }; - 55157D7C12898F8800826FF8 /* folder-011.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6712898F8800826FF8 /* folder-011.png */; }; - 55157D7D12898F8800826FF8 /* folder-100-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6812898F8800826FF8 /* folder-100-open.png */; }; - 55157D7E12898F8800826FF8 /* folder-100.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6912898F8800826FF8 /* folder-100.png */; }; - 55157D7F12898F8800826FF8 /* folder-101-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6A12898F8800826FF8 /* folder-101-open.png */; }; - 55157D8012898F8800826FF8 /* folder-101.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6B12898F8800826FF8 /* folder-101.png */; }; - 55157D8112898F8800826FF8 /* folder-110-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6C12898F8800826FF8 /* folder-110-open.png */; }; - 55157D8212898F8800826FF8 /* folder-110.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6D12898F8800826FF8 /* folder-110.png */; }; - 55157D8312898F8800826FF8 /* folder-111-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6E12898F8800826FF8 /* folder-111-open.png */; }; - 55157D8412898F8800826FF8 /* folder-111.png in Resources */ = {isa = PBXBuildFile; fileRef = 55157D6F12898F8800826FF8 /* folder-111.png */; }; - 551BF0DA1643B91B009CC9D1 /* folder-000-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0C81643B91B009CC9D1 /* folder-000-open@2x.png */; }; - 551BF0DB1643B91B009CC9D1 /* folder-000@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0C91643B91B009CC9D1 /* folder-000@2x.png */; }; - 551BF0DC1643B91B009CC9D1 /* folder-001-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0CA1643B91B009CC9D1 /* folder-001-open@2x.png */; }; - 551BF0DD1643B91B009CC9D1 /* folder-001@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0CB1643B91B009CC9D1 /* folder-001@2x.png */; }; - 551BF0DE1643B91B009CC9D1 /* folder-010-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0CC1643B91B009CC9D1 /* folder-010-open@2x.png */; }; - 551BF0DF1643B91B009CC9D1 /* folder-010@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0CD1643B91B009CC9D1 /* folder-010@2x.png */; }; - 551BF0E01643B91B009CC9D1 /* folder-011-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0CE1643B91B009CC9D1 /* folder-011-open@2x.png */; }; - 551BF0E11643B91B009CC9D1 /* folder-011@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0CF1643B91B009CC9D1 /* folder-011@2x.png */; }; - 551BF0E21643B91B009CC9D1 /* folder-100-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D01643B91B009CC9D1 /* folder-100-open@2x.png */; }; - 551BF0E31643B91B009CC9D1 /* folder-100@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D11643B91B009CC9D1 /* folder-100@2x.png */; }; - 551BF0E41643B91B009CC9D1 /* folder-101-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D21643B91B009CC9D1 /* folder-101-open@2x.png */; }; - 551BF0E51643B91B009CC9D1 /* folder-101@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D31643B91B009CC9D1 /* folder-101@2x.png */; }; - 551BF0E61643B91B009CC9D1 /* folder-110-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D41643B91B009CC9D1 /* folder-110-open@2x.png */; }; - 551BF0E71643B91B009CC9D1 /* folder-110@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D51643B91B009CC9D1 /* folder-110@2x.png */; }; - 551BF0E81643B91B009CC9D1 /* folder-111-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D61643B91B009CC9D1 /* folder-111-open@2x.png */; }; - 551BF0E91643B91B009CC9D1 /* folder-111@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D71643B91B009CC9D1 /* folder-111@2x.png */; }; - 551BF0EA1643B91B009CC9D1 /* folder-999-open@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D81643B91B009CC9D1 /* folder-999-open@2x.png */; }; - 551BF0EB1643B91B009CC9D1 /* folder-999@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 551BF0D91643B91B009CC9D1 /* folder-999@2x.png */; }; - 551DE39F2ED422820067AB18 /* FilesWindowController+SessionPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551DE39E2ED422820067AB18 /* FilesWindowController+SessionPreferences.swift */; }; - 551DE3A62ED4237B0067AB18 /* FileSessionPreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551DE3A42ED4237B0067AB18 /* FileSessionPreferencesWindow.swift */; }; - 551DE3AA2ED428B40067AB18 /* FilePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551DE3A92ED428A50067AB18 /* FilePreferences.swift */; }; - 551DE3B42ED42AD90067AB18 /* FilePreferencesComparisonPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551DE3B32ED42AD00067AB18 /* FilePreferencesComparisonPanel.swift */; }; - 551DE3B62ED42B4E0067AB18 /* FileComparisonBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551DE3B52ED42B4E0067AB18 /* FileComparisonBox.swift */; }; - 551DE3B82ED439AF0067AB18 /* PreferencesBoxDataSource+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551DE3B72ED4399B0067AB18 /* PreferencesBoxDataSource+Default.swift */; }; - 551DEDE1160477CA0014DF88 /* prefs_folder.png in Resources */ = {isa = PBXBuildFile; fileRef = 551DEDE0160477CA0014DF88 /* prefs_folder.png */; }; - 5532516C2765F06B0092D65F /* mask-back.png in Resources */ = {isa = PBXBuildFile; fileRef = 553251672765F06B0092D65F /* mask-back.png */; }; - 5532516D2765F06B0092D65F /* mask-full.png in Resources */ = {isa = PBXBuildFile; fileRef = 553251682765F06B0092D65F /* mask-full.png */; }; - 5532516E2765F06B0092D65F /* mask-middle.png in Resources */ = {isa = PBXBuildFile; fileRef = 553251692765F06B0092D65F /* mask-middle.png */; }; - 5532516F2765F06B0092D65F /* mask-back-white.png in Resources */ = {isa = PBXBuildFile; fileRef = 5532516A2765F06B0092D65F /* mask-back-white.png */; }; - 553251702765F06B0092D65F /* mask-front.png in Resources */ = {isa = PBXBuildFile; fileRef = 5532516B2765F06B0092D65F /* mask-front.png */; }; - 5536ADBA2EF169820019EFBF /* SessionDiff+ExtraData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5536ADB92EF169820019EFBF /* SessionDiff+ExtraData.swift */; }; - 553741792E9372DF00AB56D0 /* NSAppearance+DarkMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740E52E9372DF00AB56D0 /* NSAppearance+DarkMode.swift */; }; - 5537417A2E9372DF00AB56D0 /* QLPreviewPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740FD2E9372DF00AB56D0 /* QLPreviewPanel.swift */; }; - 5537417B2E9372DF00AB56D0 /* TrustedPathsPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740A12E9372DF00AB56D0 /* TrustedPathsPreferencesPanel.swift */; }; - 5537417C2E9372DF00AB56D0 /* TouchPickersStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740142E9372DF00AB56D0 /* TouchPickersStackView.swift */; }; - 5537417D2E9372DF00AB56D0 /* BasePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740862E9372DF00AB56D0 /* BasePreferences.swift */; }; - 5537417E2E9372DF00AB56D0 /* DualPaneSplitViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741532E9372DF00AB56D0 /* DualPaneSplitViewDelegate.swift */; }; - 5537417F2E9372DF00AB56D0 /* FoldersOutlineView+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FED2E9372DF00AB56D0 /* FoldersOutlineView+Menu.swift */; }; - 553741802E9372DF00AB56D0 /* AlignRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F8F2E9372DF00AB56D0 /* AlignRule.swift */; }; - 553741812E9372DF00AB56D0 /* VisualizationBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537409F2E9372DF00AB56D0 /* VisualizationBox.swift */; }; - 553741822E9372DF00AB56D0 /* CommonPrefs+FileCompare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F992E9372DF00AB56D0 /* CommonPrefs+FileCompare.swift */; }; - 553741832E9372DF00AB56D0 /* TablePanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741732E9372DF00AB56D0 /* TablePanelView.swift */; }; - 553741852E9372DF00AB56D0 /* NSOpenPanel+Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740FC2E9372DF00AB56D0 /* NSOpenPanel+Application.swift */; }; - 553741862E9372DF00AB56D0 /* ConfirmReplace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740012E9372DF00AB56D0 /* ConfirmReplace.swift */; }; - 553741872E9372DF00AB56D0 /* SessionDiff.ItemType+Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA62E9372DF00AB56D0 /* SessionDiff.ItemType+Paths.swift */; }; - 553741882E9372DF00AB56D0 /* FileSizeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537412C2E9372DF00AB56D0 /* FileSizeFormatter.swift */; }; - 553741892E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740DB2E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.m */; }; - 5537418A2E9372DF00AB56D0 /* DocumentWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F9F2E9372DF00AB56D0 /* DocumentWindow.swift */; }; - 5537418B2E9372DF00AB56D0 /* CommonPrefs+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F9D2E9372DF00AB56D0 /* CommonPrefs+Name.swift */; }; - 5537418C2E9372DF00AB56D0 /* ImageNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F922E9372DF00AB56D0 /* ImageNames.swift */; }; - 5537418D2E9372DF00AB56D0 /* FilePathTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537416D2E9372DF00AB56D0 /* FilePathTextField.swift */; }; - 5537418E2E9372DF00AB56D0 /* CopyCompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740392E9372DF00AB56D0 /* CopyCompareItem.swift */; }; - 5537418F2E9372DF00AB56D0 /* ColoredFoldersManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE62E9372DF00AB56D0 /* ColoredFoldersManager.swift */; }; - 553741902E9372DF00AB56D0 /* FilesTableView+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FB72E9372DF00AB56D0 /* FilesTableView+Menu.swift */; }; - 553741912E9372DF00AB56D0 /* NSBox+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741172E9372DF00AB56D0 /* NSBox+Helper.swift */; }; - 553741922E9372DF00AB56D0 /* FontPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740942E9372DF00AB56D0 /* FontPreferencesPanel.swift */; }; - 553741932E9372DF00AB56D0 /* DisplayOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537404B2E9372DF00AB56D0 /* DisplayOptions.swift */; }; - 553741942E9372DF00AB56D0 /* FilesWindowController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FCE2E9372DF00AB56D0 /* FilesWindowController+Menu.swift */; }; - 553741952E9372DF00AB56D0 /* OptionSet+Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740F92E9372DF00AB56D0 /* OptionSet+Toggle.swift */; }; - 553741962E9372DF00AB56D0 /* URL+Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741102E9372DF00AB56D0 /* URL+Finder.swift */; }; - 553741982E9372DF00AB56D0 /* CommonPrefs+FolderCompare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F9A2E9372DF00AB56D0 /* CommonPrefs+FolderCompare.swift */; }; - 553741992E9372DF00AB56D0 /* FoldersWindowController+PathControlDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537402E2E9372DF00AB56D0 /* FoldersWindowController+PathControlDelegate.swift */; }; - 5537419A2E9372DF00AB56D0 /* FontBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740932E9372DF00AB56D0 /* FontBox.swift */; }; - 5537419B2E9372DF00AB56D0 /* FilesTableViewFindTextDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FB82E9372DF00AB56D0 /* FilesTableViewFindTextDelegate.swift */; }; - 5537419C2E9372DF00AB56D0 /* DeleteFileOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FFD2E9372DF00AB56D0 /* DeleteFileOperationExecutor.swift */; }; - 5537419D2E9372DF00AB56D0 /* DualPaneSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741522E9372DF00AB56D0 /* DualPaneSplitView.swift */; }; - 5537419E2E9372DF00AB56D0 /* StandardButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537414D2E9372DF00AB56D0 /* StandardButtons.swift */; }; - 5537419F2E9372DF00AB56D0 /* ConfirmationsFoldersBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537408B2E9372DF00AB56D0 /* ConfirmationsFoldersBox.swift */; }; - 553741A02E9372DF00AB56D0 /* DifferenceNavigatorBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537408E2E9372DF00AB56D0 /* DifferenceNavigatorBox.swift */; }; - 553741A12E9372DF00AB56D0 /* FolderReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740602E9372DF00AB56D0 /* FolderReaderDelegate.swift */; }; - 553741A22E9372DF00AB56D0 /* UDiffScriptBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740BD2E9372DF00AB56D0 /* UDiffScriptBuilder.m */; }; - 553741A32E9372DF00AB56D0 /* String+RegularExpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741072E9372DF00AB56D0 /* String+RegularExpression.swift */; }; - 553741A42E9372DF00AB56D0 /* FileSystemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740182E9372DF00AB56D0 /* FileSystemController.swift */; }; - 553741A52E9372DF00AB56D0 /* ComparatorOptions+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740482E9372DF00AB56D0 /* ComparatorOptions+Helper.swift */; }; - 553741A62E9372DF00AB56D0 /* AppearanceBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740962E9372DF00AB56D0 /* AppearanceBox.swift */; }; - 553741A72E9372DF00AB56D0 /* FolderComparisonBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740972E9372DF00AB56D0 /* FolderComparisonBox.swift */; }; - 553741A82E9372DF00AB56D0 /* FoldersWindowController+Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740242E9372DF00AB56D0 /* FoldersWindowController+Document.swift */; }; - 553741A92E9372DF00AB56D0 /* FilterConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537405D2E9372DF00AB56D0 /* FilterConfig.swift */; }; - 553741AA2E9372DF00AB56D0 /* IconUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537412F2E9372DF00AB56D0 /* IconUtils.swift */; }; - 553741AB2E9372DF00AB56D0 /* ScopeBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740D22E9372DF00AB56D0 /* ScopeBarItem.swift */; }; - 553741AC2E9372DF00AB56D0 /* NSTextView+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741202E9372DF00AB56D0 /* NSTextView+Style.swift */; }; - 553741AD2E9372DF00AB56D0 /* FoldersOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE72E9372DF00AB56D0 /* FoldersOutlineView.swift */; }; - 553741AE2E9372DF00AB56D0 /* PreferredEditorBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740992E9372DF00AB56D0 /* PreferredEditorBox.swift */; }; - 553741AF2E9372DF00AB56D0 /* MoveCompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740412E9372DF00AB56D0 /* MoveCompareItem.swift */; }; - 553741B12E9372DF00AB56D0 /* TextFieldSelectionHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537415C2E9372DF00AB56D0 /* TextFieldSelectionHolder.swift */; }; - 553741B22E9372DF00AB56D0 /* TouchFileOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740132E9372DF00AB56D0 /* TouchFileOperationExecutor.swift */; }; - 553741B32E9372DF00AB56D0 /* NSTextFieldCell+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741452E9372DF00AB56D0 /* NSTextFieldCell+Helper.swift */; }; - 553741B42E9372DF00AB56D0 /* NSApplication.ModalResponse+ReplaceFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740032E9372DF00AB56D0 /* NSApplication.ModalResponse+ReplaceFile.swift */; }; - 553741B52E9372DF00AB56D0 /* DiffOpenerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA42E9372DF00AB56D0 /* DiffOpenerDelegate.swift */; }; - 553741B62E9372DF00AB56D0 /* MGScopeBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740D02E9372DF00AB56D0 /* MGScopeBar.m */; }; - 553741B72E9372DF00AB56D0 /* PreferencesCheckbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740832E9372DF00AB56D0 /* PreferencesCheckbox.swift */; }; - 553741B82E9372DF00AB56D0 /* FileManager+Attributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740F12E9372DF00AB56D0 /* FileManager+Attributes.swift */; }; - 553741B92E9372DF00AB56D0 /* MainThreadFolderReaderDelegateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537401C2E9372DF00AB56D0 /* MainThreadFolderReaderDelegateBridge.swift */; }; - 553741BA2E9372DF00AB56D0 /* VisibleWhitespaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537413F2E9372DF00AB56D0 /* VisibleWhitespaces.swift */; }; - 553741BB2E9372DF00AB56D0 /* DisplayPositionable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F912E9372DF00AB56D0 /* DisplayPositionable.swift */; }; - 553741BC2E9372DF00AB56D0 /* AlignRuleWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740AA2E9372DF00AB56D0 /* AlignRuleWindow.swift */; }; - 553741BD2E9372DF00AB56D0 /* DeleteCompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537403A2E9372DF00AB56D0 /* DeleteCompareItem.swift */; }; - 553741BE2E9372DF00AB56D0 /* DiffChangeType+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FDD2E9372DF00AB56D0 /* DiffChangeType+Color.swift */; }; - 553741BF2E9372DF00AB56D0 /* DescriptionOutlineNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741412E9372DF00AB56D0 /* DescriptionOutlineNode.swift */; }; - 553741C02E9372DF00AB56D0 /* FilesTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FB62E9372DF00AB56D0 /* FilesTableView.swift */; }; - 553741C12E9372DF00AB56D0 /* String+Occcurrences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741052E9372DF00AB56D0 /* String+Occcurrences.swift */; }; - 553741C22E9372DF00AB56D0 /* FoldersWindowController+QLPreviewPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537402F2E9372DF00AB56D0 /* FoldersWindowController+QLPreviewPanel.swift */; }; - 553741C32E9372DF00AB56D0 /* FoldersOutlineView+DifferenceNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537405A2E9372DF00AB56D0 /* FoldersOutlineView+DifferenceNavigator.swift */; }; - 553741C42E9372DF00AB56D0 /* JumpToLineWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE32E9372DF00AB56D0 /* JumpToLineWindow.swift */; }; - 553741C52E9372DF00AB56D0 /* EncodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741282E9372DF00AB56D0 /* EncodingError.swift */; }; - 553741C62E9372DF00AB56D0 /* FileExtraOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537404E2E9372DF00AB56D0 /* FileExtraOptions.swift */; }; - 553741C72E9372DF00AB56D0 /* Array+MoveIndexes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740E72E9372DF00AB56D0 /* Array+MoveIndexes.swift */; }; - 553741C82E9372DF00AB56D0 /* BigFileFileOperationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740C82E9372DF00AB56D0 /* BigFileFileOperationManager.m */; }; - 553741C92E9372DF00AB56D0 /* FoldersWindowController+BaseFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740202E9372DF00AB56D0 /* FoldersWindowController+BaseFolder.swift */; }; - 553741CB2E9372DF00AB56D0 /* FoldersWindowController+UICreation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740322E9372DF00AB56D0 /* FoldersWindowController+UICreation.swift */; }; - 553741CC2E9372DF00AB56D0 /* FilesWindowController+UICreation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD82E9372DF00AB56D0 /* FilesWindowController+UICreation.swift */; }; - 553741CD2E9372DF00AB56D0 /* NSTableView+Row.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537410A2E9372DF00AB56D0 /* NSTableView+Row.swift */; }; - 553741CE2E9372DF00AB56D0 /* FindText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537416E2E9372DF00AB56D0 /* FindText.swift */; }; - 553741CF2E9372DF00AB56D0 /* FoldersTraversalBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740902E9372DF00AB56D0 /* FoldersTraversalBox.swift */; }; - 553741D02E9372DF00AB56D0 /* FileError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741342E9372DF00AB56D0 /* FileError.swift */; }; - 553741D12E9372DF00AB56D0 /* OpenEditorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537413B2E9372DF00AB56D0 /* OpenEditorError.swift */; }; - 553741D22E9372DF00AB56D0 /* GeneralPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740982E9372DF00AB56D0 /* GeneralPreferencesPanel.swift */; }; - 553741D32E9372DF00AB56D0 /* CompareItem+DifferenceNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537405B2E9372DF00AB56D0 /* CompareItem+DifferenceNavigator.swift */; }; - 553741D42E9372DF00AB56D0 /* PreferencesBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740812E9372DF00AB56D0 /* PreferencesBox.swift */; }; - 553741D52E9372DF00AB56D0 /* NSAlert+ReplaceFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740022E9372DF00AB56D0 /* NSAlert+ReplaceFile.swift */; }; - 553741D72E9372DF00AB56D0 /* URL+SymLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741152E9372DF00AB56D0 /* URL+SymLink.swift */; }; - 553741D82E9372DF00AB56D0 /* RecentDocumentPopupMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA22E9372DF00AB56D0 /* RecentDocumentPopupMenu.swift */; }; - 553741D92E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineViewContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740282E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineViewContextMenu.swift */; }; - 553741DA2E9372DF00AB56D0 /* FileAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537403B2E9372DF00AB56D0 /* FileAttributes.swift */; }; - 553741DB2E9372DF00AB56D0 /* SyncFileController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537400C2E9372DF00AB56D0 /* SyncFileController.swift */; }; - 553741DC2E9372DF00AB56D0 /* ConfirmationsFilesBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537408A2E9372DF00AB56D0 /* ConfirmationsFilesBox.swift */; }; - 553741DD2E9372DF00AB56D0 /* CompareItem+Accessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740502E9372DF00AB56D0 /* CompareItem+Accessors.swift */; }; - 553741DE2E9372DF00AB56D0 /* FilesWindowController+JumpLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FCC2E9372DF00AB56D0 /* FilesWindowController+JumpLine.swift */; }; - 553741DF2E9372DF00AB56D0 /* CommonPrefs+ConsoleLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740362E9372DF00AB56D0 /* CommonPrefs+ConsoleLog.swift */; }; - 553741E02E9372DF00AB56D0 /* NSToolbar+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537410C2E9372DF00AB56D0 /* NSToolbar+Create.swift */; }; - 553741E12E9372DF00AB56D0 /* String+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741032E9372DF00AB56D0 /* String+Helper.swift */; }; - 553741E32E9372DF00AB56D0 /* LineNumberTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC02E9372DF00AB56D0 /* LineNumberTableRowView.swift */; }; - 553741E42E9372DF00AB56D0 /* FolderReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537405E2E9372DF00AB56D0 /* FolderReader.swift */; }; - 553741E52E9372DF00AB56D0 /* DateFormatter+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740F32E9372DF00AB56D0 /* DateFormatter+Helper.swift */; }; - 553741E62E9372DF00AB56D0 /* FoldersWindowController+Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537402B2E9372DF00AB56D0 /* FoldersWindowController+Navigation.swift */; }; - 553741E72E9372DF00AB56D0 /* HistoryEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FAB2E9372DF00AB56D0 /* HistoryEntity.swift */; }; - 553741E82E9372DF00AB56D0 /* CompareItem+Clone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740512E9372DF00AB56D0 /* CompareItem+Clone.swift */; }; - 553741E92E9372DF00AB56D0 /* URL+Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741122E9372DF00AB56D0 /* URL+Path.swift */; }; - 553741EA2E9372DF00AB56D0 /* PathChooser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA12E9372DF00AB56D0 /* PathChooser.swift */; }; - 553741EB2E9372DF00AB56D0 /* VDDocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA92E9372DF00AB56D0 /* VDDocumentController.swift */; }; - 553741EC2E9372DF00AB56D0 /* CopyFileOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FFB2E9372DF00AB56D0 /* CopyFileOperationExecutor.swift */; }; - 553741ED2E9372DF00AB56D0 /* FoldersOutlineView+Columns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE92E9372DF00AB56D0 /* FoldersOutlineView+Columns.swift */; }; - 553741EE2E9372DF00AB56D0 /* NSTextField+CenterVertically.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537416F2E9372DF00AB56D0 /* NSTextField+CenterVertically.swift */; }; - 553741EF2E9372DF00AB56D0 /* BufferedInputStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741322E9372DF00AB56D0 /* BufferedInputStream.swift */; }; - 553741F02E9372DF00AB56D0 /* ComparisonStandardUserDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740802E9372DF00AB56D0 /* ComparisonStandardUserDataSource.swift */; }; - 553741F12E9372DF00AB56D0 /* TextPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537409E2E9372DF00AB56D0 /* TextPreferencesPanel.swift */; }; - 553741F22E9372DF00AB56D0 /* AlignPopupButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741642E9372DF00AB56D0 /* AlignPopupButtonCell.swift */; }; - 553741F32E9372DF00AB56D0 /* FolderSelectionInfo+ViewerActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537406D2E9372DF00AB56D0 /* FolderSelectionInfo+ViewerActionValidator.swift */; }; - 553741F42E9372DF00AB56D0 /* PopUpButtonUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741722E9372DF00AB56D0 /* PopUpButtonUrl.swift */; }; - 553741F52E9372DF00AB56D0 /* OpenEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741392E9372DF00AB56D0 /* OpenEditor.swift */; }; - 553741F62E9372DF00AB56D0 /* NSMenu+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740F72E9372DF00AB56D0 /* NSMenu+File.swift */; }; - 553741F72E9372DF00AB56D0 /* FilesWindowController+FileInfoBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC92E9372DF00AB56D0 /* FilesWindowController+FileInfoBarDelegate.swift */; }; - 553741F82E9372DF00AB56D0 /* KeyboardPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537409C2E9372DF00AB56D0 /* KeyboardPreferencesPanel.swift */; }; - 553741F92E9372DF00AB56D0 /* CompareItem+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740402E9372DF00AB56D0 /* CompareItem+Metadata.swift */; }; - 553741FA2E9372DF00AB56D0 /* TextFieldVerticalCentered.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC32E9372DF00AB56D0 /* TextFieldVerticalCentered.swift */; }; - 553741FB2E9372DF00AB56D0 /* KeyEquivalent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F932E9372DF00AB56D0 /* KeyEquivalent.swift */; }; - 553741FC2E9372DF00AB56D0 /* CompareItem+VisibleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740572E9372DF00AB56D0 /* CompareItem+VisibleItem.swift */; }; - 553741FD2E9372DF00AB56D0 /* FoldersWindowController+SessionPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740312E9372DF00AB56D0 /* FoldersWindowController+SessionPreferences.swift */; }; - 553741FE2E9372DF00AB56D0 /* CompareItemTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF62E9372DF00AB56D0 /* CompareItemTableCellView.swift */; }; - 553741FF2E9372DF00AB56D0 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741582E9372DF00AB56D0 /* ProgressBarView.swift */; }; - 553742002E9372DF00AB56D0 /* FolderManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537403F2E9372DF00AB56D0 /* FolderManagerError.swift */; }; - 553742012E9372DF00AB56D0 /* FolderSelectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537401E2E9372DF00AB56D0 /* FolderSelectionInfo.swift */; }; - 553742022E9372DF00AB56D0 /* SynchroScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537415A2E9372DF00AB56D0 /* SynchroScrollView.swift */; }; - 553742032E9372DF00AB56D0 /* FilesWindowController+Save.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD52E9372DF00AB56D0 /* FilesWindowController+Save.swift */; }; - 553742042E9372DF00AB56D0 /* FoldersWindowController+ConsoleViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740222E9372DF00AB56D0 /* FoldersWindowController+ConsoleViewDelegate.swift */; }; - 553742052E9372DF00AB56D0 /* PreferencesBoxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740822E9372DF00AB56D0 /* PreferencesBoxDataSource.swift */; }; - 553742062E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740D92E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.m */; }; - 553742072E9372DF00AB56D0 /* FolderReader+Detached.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537401B2E9372DF00AB56D0 /* FolderReader+Detached.swift */; }; - 553742082E9372DF00AB56D0 /* ViewLinkable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F962E9372DF00AB56D0 /* ViewLinkable.swift */; }; - 553742092E9372DF00AB56D0 /* SessionPreferencesFiltersBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740B42E9372DF00AB56D0 /* SessionPreferencesFiltersBox.swift */; }; - 5537420A2E9372DF00AB56D0 /* FilesWindowController+Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC72E9372DF00AB56D0 /* FilesWindowController+Common.swift */; }; - 5537420B2E9372DF00AB56D0 /* FilesWindowController+NSToolbarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD12E9372DF00AB56D0 /* FilesWindowController+NSToolbarDelegate.swift */; }; - 5537420C2E9372DF00AB56D0 /* FilesWindowController+TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD72E9372DF00AB56D0 /* FilesWindowController+TableView.swift */; }; - 5537420D2E9372DF00AB56D0 /* NSProgressIndicator+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537411C2E9372DF00AB56D0 /* NSProgressIndicator+Helper.swift */; }; - 5537420E2E9372DF00AB56D0 /* ComparatorOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740462E9372DF00AB56D0 /* ComparatorOptions.swift */; }; - 5537420F2E9372DF00AB56D0 /* PathTimestamps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740422E9372DF00AB56D0 /* PathTimestamps.swift */; }; - 553742102E9372DF00AB56D0 /* FilesWindowController+Clipboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC62E9372DF00AB56D0 /* FilesWindowController+Clipboard.swift */; }; - 553742112E9372DF00AB56D0 /* RSVerticallyCenteredTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741462E9372DF00AB56D0 /* RSVerticallyCenteredTextFieldCell.swift */; }; - 553742122E9372DF00AB56D0 /* CompareItem+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740532E9372DF00AB56D0 /* CompareItem+Description.swift */; }; - 553742132E9372DF00AB56D0 /* SessionPreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740A62E9372DF00AB56D0 /* SessionPreferencesWindow.swift */; }; - 553742142E9372DF00AB56D0 /* UserDefinedRulesBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740AF2E9372DF00AB56D0 /* UserDefinedRulesBox.swift */; }; - 553742162E9372DF00AB56D0 /* ProgressIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740092E9372DF00AB56D0 /* ProgressIndicatorController.swift */; }; - 553742172E9372DF00AB56D0 /* FolderPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF52E9372DF00AB56D0 /* FolderPanelView.swift */; }; - 553742182E9372DF00AB56D0 /* ComparatorOptions+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740472E9372DF00AB56D0 /* ComparatorOptions+Description.swift */; }; - 553742192E9372DF00AB56D0 /* FoldersOutlineView+Clipboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE82E9372DF00AB56D0 /* FoldersOutlineView+Clipboard.swift */; }; - 5537421A2E9372DF00AB56D0 /* DatePickersStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740112E9372DF00AB56D0 /* DatePickersStackView.swift */; }; - 5537421B2E9372DF00AB56D0 /* UnifiedDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740BF2E9372DF00AB56D0 /* UnifiedDiff.m */; }; - 5537421C2E9372DF00AB56D0 /* SyncOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537400F2E9372DF00AB56D0 /* SyncOutlineView.swift */; }; - 5537421D2E9372DF00AB56D0 /* CompareItem+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740522E9372DF00AB56D0 /* CompareItem+Comparison.swift */; }; - 5537421E2E9372DF00AB56D0 /* NSError+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740EF2E9372DF00AB56D0 /* NSError+Format.swift */; }; - 5537421F2E9372DF00AB56D0 /* RefreshInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740612E9372DF00AB56D0 /* RefreshInfo.swift */; }; - 553742202E9372DF00AB56D0 /* FileDropView+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741562E9372DF00AB56D0 /* FileDropView+Helper.swift */; }; - 553742212E9372DF00AB56D0 /* SyncFileOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537400D2E9372DF00AB56D0 /* SyncFileOperationExecutor.swift */; }; - 553742222E9372DF00AB56D0 /* CompareItem+FilterConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740542E9372DF00AB56D0 /* CompareItem+FilterConfig.swift */; }; - 553742232E9372DF00AB56D0 /* NSStackView+PreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740A32E9372DF00AB56D0 /* NSStackView+PreferencesPanel.swift */; }; - 553742242E9372DF00AB56D0 /* CustomValidationToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741482E9372DF00AB56D0 /* CustomValidationToolbarItem.swift */; }; - 553742252E9372DF00AB56D0 /* VisibleItem+Sort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740732E9372DF00AB56D0 /* VisibleItem+Sort.swift */; }; - 553742262E9372DF00AB56D0 /* LineNumberTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FBF2E9372DF00AB56D0 /* LineNumberTableCellView.swift */; }; - 553742272E9372DF00AB56D0 /* FilesWindowController+Slider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD62E9372DF00AB56D0 /* FilesWindowController+Slider.swift */; }; - 553742282E9372DF00AB56D0 /* VDDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA82E9372DF00AB56D0 /* VDDocument.swift */; }; - 553742292E9372DF00AB56D0 /* FilesWindowController+Read.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD42E9372DF00AB56D0 /* FilesWindowController+Read.swift */; }; - 5537422A2E9372DF00AB56D0 /* PowerAssertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741352E9372DF00AB56D0 /* PowerAssertion.swift */; }; - 5537422C2E9372DF00AB56D0 /* FilesWindowController+UISetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD92E9372DF00AB56D0 /* FilesWindowController+UISetup.swift */; }; - 5537422D2E9372DF00AB56D0 /* CompareUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741332E9372DF00AB56D0 /* CompareUtil.swift */; }; - 5537422E2E9372DF00AB56D0 /* DisplayFiltersScopeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF42E9372DF00AB56D0 /* DisplayFiltersScopeBar.swift */; }; - 5537422F2E9372DF00AB56D0 /* HistorySessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FAC2E9372DF00AB56D0 /* HistorySessionManager.swift */; }; - 553742302E9372DF00AB56D0 /* FilePanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FBB2E9372DF00AB56D0 /* FilePanelView.swift */; }; - 553742312E9372DF00AB56D0 /* FilesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC52E9372DF00AB56D0 /* FilesWindowController.swift */; }; - 553742322E9372DF00AB56D0 /* FilesWindowController+MenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FCF2E9372DF00AB56D0 /* FilesWindowController+MenuDelegate.swift */; }; - 553742332E9372DF00AB56D0 /* DifferenceCounters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537416B2E9372DF00AB56D0 /* DifferenceCounters.swift */; }; - 553742342E9372DF00AB56D0 /* ReplaceFileAttributeKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740042E9372DF00AB56D0 /* ReplaceFileAttributeKey.swift */; }; - 553742352E9372DF00AB56D0 /* FoldersWindowController+Select.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740302E9372DF00AB56D0 /* FoldersWindowController+Select.swift */; }; - 553742362E9372DF00AB56D0 /* FoldersWindowController+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740212E9372DF00AB56D0 /* FoldersWindowController+Comparison.swift */; }; - 553742372E9372DF00AB56D0 /* PathView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741712E9372DF00AB56D0 /* PathView.swift */; }; - 553742382E9372DF00AB56D0 /* DiffLine+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FDB2E9372DF00AB56D0 /* DiffLine+Color.swift */; }; - 553742392E9372DF00AB56D0 /* FileDropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741552E9372DF00AB56D0 /* FileDropView.swift */; }; - 5537423A2E9372DF00AB56D0 /* PreferredEditorPopupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537413D2E9372DF00AB56D0 /* PreferredEditorPopupCell.swift */; }; - 5537423B2E9372DF00AB56D0 /* FilePanelView+File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FBC2E9372DF00AB56D0 /* FilePanelView+File.swift */; }; - 5537423C2E9372DF00AB56D0 /* KeyboardDocumentBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537409B2E9372DF00AB56D0 /* KeyboardDocumentBox.swift */; }; - 5537423D2E9372DF00AB56D0 /* FoldersOutlineView+VisibleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FEE2E9372DF00AB56D0 /* FoldersOutlineView+VisibleItem.swift */; }; - 5537423E2E9372DF00AB56D0 /* FileNameCaseBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740AE2E9372DF00AB56D0 /* FileNameCaseBox.swift */; }; - 5537423F2E9372DF00AB56D0 /* URL+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741112E9372DF00AB56D0 /* URL+Metadata.swift */; }; - 553742402E9372DF00AB56D0 /* FoldersWindowController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740292E9372DF00AB56D0 /* FoldersWindowController+Menu.swift */; }; - 553742412E9372DF00AB56D0 /* OperationSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740192E9372DF00AB56D0 /* OperationSummaryView.swift */; }; - 553742422E9372DF00AB56D0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F8D2E9372DF00AB56D0 /* AppDelegate.swift */; }; - 553742432E9372DF00AB56D0 /* ExpressionBox+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740AD2E9372DF00AB56D0 /* ExpressionBox+Menu.swift */; }; - 553742442E9372DF00AB56D0 /* FilesWindowController+PathControlDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD32E9372DF00AB56D0 /* FilesWindowController+PathControlDelegate.swift */; }; - 553742452E9372DF00AB56D0 /* VisibleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537406F2E9372DF00AB56D0 /* VisibleItem.swift */; }; - 553742462E9372DF00AB56D0 /* DiffChangeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FDE2E9372DF00AB56D0 /* DiffChangeType.swift */; }; - 553742472E9372DF00AB56D0 /* NotificationCenter+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F942E9372DF00AB56D0 /* NotificationCenter+Helper.swift */; }; - 553742482E9372DF00AB56D0 /* NSImage+Tint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740F52E9372DF00AB56D0 /* NSImage+Tint.swift */; }; - 553742492E9372DF00AB56D0 /* NSButton+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741192E9372DF00AB56D0 /* NSButton+Helper.swift */; }; - 5537424A2E9372DF00AB56D0 /* FoldersWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537401F2E9372DF00AB56D0 /* FoldersWindowController.swift */; }; - 5537424B2E9372DF00AB56D0 /* VisibleItem+Find.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740702E9372DF00AB56D0 /* VisibleItem+Find.swift */; }; - 5537424C2E9372DF00AB56D0 /* String+Highlights.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741042E9372DF00AB56D0 /* String+Highlights.swift */; }; - 5537424D2E9372DF00AB56D0 /* SyncItemsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537400E2E9372DF00AB56D0 /* SyncItemsInfo.swift */; }; - 5537424E2E9372DF00AB56D0 /* FileOperationManager+FileOperationManagerAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537403D2E9372DF00AB56D0 /* FileOperationManager+FileOperationManagerAction.swift */; }; - 5537424F2E9372DF00AB56D0 /* NoodleCustomImageRep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741302E9372DF00AB56D0 /* NoodleCustomImageRep.swift */; }; - 553742502E9372DF00AB56D0 /* FoldersWindowController+FileSystemControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740262E9372DF00AB56D0 /* FoldersWindowController+FileSystemControllerDelegate.swift */; }; - 553742512E9372DF00AB56D0 /* FilesWindowController+NSWindowDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD22E9372DF00AB56D0 /* FilesWindowController+NSWindowDelegate.swift */; }; - 553742522E9372DF00AB56D0 /* FilesWindowController+Navigate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FD02E9372DF00AB56D0 /* FilesWindowController+Navigate.swift */; }; - 553742532E9372DF00AB56D0 /* DiffCountersItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741692E9372DF00AB56D0 /* DiffCountersItem.swift */; }; - 553742542E9372DF00AB56D0 /* SaveFileAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC22E9372DF00AB56D0 /* SaveFileAccessoryView.swift */; }; - 553742552E9372DF00AB56D0 /* FileThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FBE2E9372DF00AB56D0 /* FileThumbnailView.swift */; }; - 553742562E9372DF00AB56D0 /* FilesTableView+EditorData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE12E9372DF00AB56D0 /* FilesTableView+EditorData.swift */; }; - 553742572E9372DF00AB56D0 /* ComparatorPopUpButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741652E9372DF00AB56D0 /* ComparatorPopUpButtonCell.swift */; }; - 553742582E9372DF00AB56D0 /* IntegerFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537412D2E9372DF00AB56D0 /* IntegerFormatter.swift */; }; - 553742592E9372DF00AB56D0 /* SessionDiff+ResolvePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FB22E9372DF00AB56D0 /* SessionDiff+ResolvePath.swift */; }; - 5537425A2E9372DF00AB56D0 /* CompareItem+NSTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF12E9372DF00AB56D0 /* CompareItem+NSTableCellView.swift */; }; - 5537425B2E9372DF00AB56D0 /* DocumentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA52E9372DF00AB56D0 /* DocumentError.swift */; }; - 5537425C2E9372DF00AB56D0 /* FolderSelectionInfo+FileSystemActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537406A2E9372DF00AB56D0 /* FolderSelectionInfo+FileSystemActionValidator.swift */; }; - 5537425D2E9372DF00AB56D0 /* FoldersWindowController+Exclude.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740252E9372DF00AB56D0 /* FoldersWindowController+Exclude.swift */; }; - 5537425E2E9372DF00AB56D0 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740872E9372DF00AB56D0 /* Preferences.swift */; }; - 5537425F2E9372DF00AB56D0 /* OutlineViewItemDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740342E9372DF00AB56D0 /* OutlineViewItemDelegate.swift */; }; - 553742612E9372DF00AB56D0 /* NSImage+SymbolCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F952E9372DF00AB56D0 /* NSImage+SymbolCompat.swift */; }; - 553742622E9372DF00AB56D0 /* TableViewContextMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741432E9372DF00AB56D0 /* TableViewContextMenuDelegate.swift */; }; - 553742632E9372DF00AB56D0 /* NSPredicate+Objc.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740D52E9372DF00AB56D0 /* NSPredicate+Objc.m */; }; - 553742642E9372DF00AB56D0 /* FileSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740172E9372DF00AB56D0 /* FileSummaryView.swift */; }; - 553742652E9372DF00AB56D0 /* CompareItemTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF72E9372DF00AB56D0 /* CompareItemTableRowView.swift */; }; - 553742662E9372DF00AB56D0 /* FolderSelectionInfo+FolderActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537406C2E9372DF00AB56D0 /* FolderSelectionInfo+FolderActionValidator.swift */; }; - 553742672E9372DF00AB56D0 /* CommonPrefs+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F9C2E9372DF00AB56D0 /* CommonPrefs+Helper.swift */; }; - 553742682E9372DF00AB56D0 /* FilePathTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741672E9372DF00AB56D0 /* FilePathTableCellView.swift */; }; - 553742692E9372DF00AB56D0 /* AttributedMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741372E9372DF00AB56D0 /* AttributedMenuItem.swift */; }; - 5537426A2E9372DF00AB56D0 /* URL+StructuredContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741142E9372DF00AB56D0 /* URL+StructuredContent.swift */; }; - 5537426B2E9372DF00AB56D0 /* SessionPreferencesComparisonPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740B12E9372DF00AB56D0 /* SessionPreferencesComparisonPanel.swift */; }; - 5537426C2E9372DF00AB56D0 /* FolderCompareInfoWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740762E9372DF00AB56D0 /* FolderCompareInfoWindow.swift */; }; - 5537426D2E9372DF00AB56D0 /* NSToolbarItem+Create.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537410D2E9372DF00AB56D0 /* NSToolbarItem+Create.swift */; }; - 5537426E2E9372DF00AB56D0 /* MoveFileOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FFF2E9372DF00AB56D0 /* MoveFileOperationExecutor.swift */; }; - 5537426F2E9372DF00AB56D0 /* URL+FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537410F2E9372DF00AB56D0 /* URL+FileManager.swift */; }; - 553742712E9372DF00AB56D0 /* ConfirmationsDocumentsBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740892E9372DF00AB56D0 /* ConfirmationsDocumentsBox.swift */; }; - 553742722E9372DF00AB56D0 /* ExpressionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740AC2E9372DF00AB56D0 /* ExpressionBox.swift */; }; - 553742732E9372DF00AB56D0 /* CompareItem+LeafPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF02E9372DF00AB56D0 /* CompareItem+LeafPath.swift */; }; - 553742742E9372DF00AB56D0 /* SessionDiff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FAE2E9372DF00AB56D0 /* SessionDiff.swift */; }; - 553742762E9372DF00AB56D0 /* TimeToleranceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741742E9372DF00AB56D0 /* TimeToleranceView.swift */; }; - 553742782E9372DF00AB56D0 /* FoldersOutlineView+Enumerate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FEA2E9372DF00AB56D0 /* FoldersOutlineView+Enumerate.swift */; }; - 553742792E9372DF00AB56D0 /* HistoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537407A2E9372DF00AB56D0 /* HistoryController.swift */; }; - 5537427A2E9372DF00AB56D0 /* ConsoleToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537414F2E9372DF00AB56D0 /* ConsoleToolbarView.swift */; }; - 5537427B2E9372DF00AB56D0 /* HistorySearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537407D2E9372DF00AB56D0 /* HistorySearchField.swift */; }; - 5537427C2E9372DF00AB56D0 /* URL+ResourceFork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741132E9372DF00AB56D0 /* URL+ResourceFork.swift */; }; - 5537427D2E9372DF00AB56D0 /* LinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537414C2E9372DF00AB56D0 /* LinkButton.swift */; }; - 5537427E2E9372DF00AB56D0 /* ReplaceInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537400A2E9372DF00AB56D0 /* ReplaceInfoView.swift */; }; - 5537427F2E9372DF00AB56D0 /* NSAlert+DirtyFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC12E9372DF00AB56D0 /* NSAlert+DirtyFiles.swift */; }; - 553742802E9372DF00AB56D0 /* CommonPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F982E9372DF00AB56D0 /* CommonPrefs.swift */; }; - 553742812E9372DF00AB56D0 /* FilesWindowController+LinesDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FCD2E9372DF00AB56D0 /* FilesWindowController+LinesDetail.swift */; }; - 553742822E9372DF00AB56D0 /* FileOperationManager+Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537403E2E9372DF00AB56D0 /* FileOperationManager+Util.swift */; }; - 553742832E9372DF00AB56D0 /* ConsoleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741502E9372DF00AB56D0 /* ConsoleView.swift */; }; - 553742842E9372DF00AB56D0 /* CommonPrefs+DifferenceNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740592E9372DF00AB56D0 /* CommonPrefs+DifferenceNavigator.swift */; }; - 553742852E9372DF00AB56D0 /* FileOperationExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740162E9372DF00AB56D0 /* FileOperationExecutor.swift */; }; - 553742862E9372DF00AB56D0 /* AlignmentPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740A92E9372DF00AB56D0 /* AlignmentPanel.swift */; }; - 553742872E9372DF00AB56D0 /* FoldersOutlineView+ExternalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FEB2E9372DF00AB56D0 /* FoldersOutlineView+ExternalApp.swift */; }; - 553742882E9372DF00AB56D0 /* PreferencesPanelDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740B92E9372DF00AB56D0 /* PreferencesPanelDataSource.swift */; }; - 553742892E9372DF00AB56D0 /* DiffCountersTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537416A2E9372DF00AB56D0 /* DiffCountersTextFieldCell.swift */; }; - 5537428A2E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740272E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineView.swift */; }; - 5537428B2E9372DF00AB56D0 /* TableViewCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741422E9372DF00AB56D0 /* TableViewCommon.swift */; }; - 5537428C2E9372DF00AB56D0 /* CompareItem+Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740562E9372DF00AB56D0 /* CompareItem+Path.swift */; }; - 5537428D2E9372DF00AB56D0 /* FolderPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537408F2E9372DF00AB56D0 /* FolderPreferencesPanel.swift */; }; - 5537428E2E9372DF00AB56D0 /* FileOperationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537403C2E9372DF00AB56D0 /* FileOperationManager.swift */; }; - 5537428F2E9372DF00AB56D0 /* StandardUserPreferencesBoxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740842E9372DF00AB56D0 /* StandardUserPreferencesBoxDataSource.swift */; }; - 553742902E9372DF00AB56D0 /* SecureBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741262E9372DF00AB56D0 /* SecureBookmark.swift */; }; - 553742912E9372DF00AB56D0 /* FilesScopeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FBD2E9372DF00AB56D0 /* FilesScopeBar.swift */; }; - 553742922E9372DF00AB56D0 /* OpenEditorAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537413A2E9372DF00AB56D0 /* OpenEditorAttribute.swift */; }; - 553742932E9372DF00AB56D0 /* WindowCancelOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537415F2E9372DF00AB56D0 /* WindowCancelOperation.swift */; }; - 553742942E9372DF00AB56D0 /* VisibleItem+QLPreviewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740722E9372DF00AB56D0 /* VisibleItem+QLPreviewItem.swift */; }; - 553742952E9372DF00AB56D0 /* FilesWindowController+FilesTableViewContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FCB2E9372DF00AB56D0 /* FilesWindowController+FilesTableViewContextMenu.swift */; }; - 553742962E9372DF00AB56D0 /* FolderSelectionInfo+CompareActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740692E9372DF00AB56D0 /* FolderSelectionInfo+CompareActionValidator.swift */; }; - 553742972E9372DF00AB56D0 /* MainThreadComparatorDelegateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF92E9372DF00AB56D0 /* MainThreadComparatorDelegateBridge.swift */; }; - 553742982E9372DF00AB56D0 /* DiffCountersItem+DiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FE02E9372DF00AB56D0 /* DiffCountersItem+DiffResult.swift */; }; - 553742992E9372DF00AB56D0 /* FileOperationManagerDelegateImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740082E9372DF00AB56D0 /* FileOperationManagerDelegateImpl.swift */; }; - 5537429A2E9372DF00AB56D0 /* SessionDiff+AlignRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FAF2E9372DF00AB56D0 /* SessionDiff+AlignRule.swift */; }; - 5537429B2E9372DF00AB56D0 /* FoldersWindowController+MenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537402A2E9372DF00AB56D0 /* FoldersWindowController+MenuDelegate.swift */; }; - 5537429C2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740CE2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.m */; }; - 5537429D2E9372DF00AB56D0 /* FolderSelectionInfo+FilterActionValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537406B2E9372DF00AB56D0 /* FolderSelectionInfo+FilterActionValidator.swift */; }; - 5537429E2E9372DF00AB56D0 /* OpenDiffCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740E22E9372DF00AB56D0 /* OpenDiffCommand.swift */; }; - 5537429F2E9372DF00AB56D0 /* RenameCompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740432E9372DF00AB56D0 /* RenameCompareItem.swift */; }; - 553742A02E9372DF00AB56D0 /* DisplayOptions+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537404C2E9372DF00AB56D0 /* DisplayOptions+Helper.swift */; }; - 553742A12E9372DF00AB56D0 /* SessionPreferencesWindow+Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740A72E9372DF00AB56D0 /* SessionPreferencesWindow+Data.swift */; }; - 553742A22E9372DF00AB56D0 /* FolderReader+Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537405F2E9372DF00AB56D0 /* FolderReader+Log.swift */; }; - 553742A32E9372DF00AB56D0 /* NSArrayController+Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA02E9372DF00AB56D0 /* NSArrayController+Paths.swift */; }; - 553742A42E9372DF00AB56D0 /* NSPredicateEditor+Fix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741012E9372DF00AB56D0 /* NSPredicateEditor+Fix.swift */; }; - 553742A52E9372DF00AB56D0 /* FoldersWindowController+NSToolbarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537402C2E9372DF00AB56D0 /* FoldersWindowController+NSToolbarDelegate.swift */; }; - 553742A62E9372DF00AB56D0 /* NSAlert+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740FB2E9372DF00AB56D0 /* NSAlert+Helper.swift */; }; - 553742A72E9372DF00AB56D0 /* FoldersWindowController+NSWindowDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537402D2E9372DF00AB56D0 /* FoldersWindowController+NSWindowDelegate.swift */; }; - 553742A82E9372DF00AB56D0 /* FilesWindowController+FilesScopeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FCA2E9372DF00AB56D0 /* FilesWindowController+FilesScopeBar.swift */; }; - 553742A92E9372DF00AB56D0 /* NSManagedObjectContext+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740EB2E9372DF00AB56D0 /* NSManagedObjectContext+Helpers.swift */; }; - 553742AA2E9372DF00AB56D0 /* String+Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741062E9372DF00AB56D0 /* String+Path.swift */; }; - 553742AB2E9372DF00AB56D0 /* TimeInterval+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740ED2E9372DF00AB56D0 /* TimeInterval+Helper.swift */; }; - 553742AC2E9372DF00AB56D0 /* ColorScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F902E9372DF00AB56D0 /* ColorScheme.swift */; }; - 553742AD2E9372DF00AB56D0 /* FilesWindowController+Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FC82E9372DF00AB56D0 /* FilesWindowController+Document.swift */; }; - 553742AE2E9372DF00AB56D0 /* PathControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741702E9372DF00AB56D0 /* PathControl.swift */; }; - 553742AF2E9372DF00AB56D0 /* TouchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740122E9372DF00AB56D0 /* TouchController.swift */; }; - 553742B02E9372DF00AB56D0 /* CompletionIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740062E9372DF00AB56D0 /* CompletionIndicator.swift */; }; - 553742B12E9372DF00AB56D0 /* FoldersWindowController+UISetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740332E9372DF00AB56D0 /* FoldersWindowController+UISetup.swift */; }; - 553742B22E9372DF00AB56D0 /* NSTableView+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741092E9372DF00AB56D0 /* NSTableView+Font.swift */; }; - 553742B32E9372DF00AB56D0 /* ActionBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537414A2E9372DF00AB56D0 /* ActionBarView.swift */; }; - 553742B42E9372DF00AB56D0 /* TouchCompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740442E9372DF00AB56D0 /* TouchCompareItem.swift */; }; - 553742B52E9372DF00AB56D0 /* HistoryFetchedResultsControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537407C2E9372DF00AB56D0 /* HistoryFetchedResultsControllerDelegate.swift */; }; - 553742B62E9372DF00AB56D0 /* CommonPrefs+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373F9B2E9372DF00AB56D0 /* CommonPrefs+Font.swift */; }; - 553742B72E9372DF00AB56D0 /* NSWorkspace+Finder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741232E9372DF00AB56D0 /* NSWorkspace+Finder.swift */; }; - 553742B82E9372DF00AB56D0 /* FoldersOutlineViewFindTextDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FEF2E9372DF00AB56D0 /* FoldersOutlineViewFindTextDelegate.swift */; }; - 553742B92E9372DF00AB56D0 /* AlignTestResultBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740AB2E9372DF00AB56D0 /* AlignTestResultBox.swift */; }; - 553742BA2E9372DF00AB56D0 /* VisibleItem+Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740712E9372DF00AB56D0 /* VisibleItem+Log.swift */; }; - 553742BB2E9372DF00AB56D0 /* HistoryTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537407E2E9372DF00AB56D0 /* HistoryTableView.swift */; }; - 553742BC2E9372DF00AB56D0 /* SessionDiff+NSSortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FB12E9372DF00AB56D0 /* SessionDiff+NSSortDescriptor.swift */; }; - 553742BD2E9372DF00AB56D0 /* NSPopUpButton+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537411A2E9372DF00AB56D0 /* NSPopUpButton+Helper.swift */; }; - 553742BE2E9372DF00AB56D0 /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740E92E9372DF00AB56D0 /* NSColor+Hex.swift */; }; - 553742BF2E9372DF00AB56D0 /* FileInfoBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FBA2E9372DF00AB56D0 /* FileInfoBar.swift */; }; - 553742C02E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 553740D72E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.m */; }; - 553742C12E9372DF00AB56D0 /* ErrorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740072E9372DF00AB56D0 /* ErrorsView.swift */; }; - 553742C22E9372DF00AB56D0 /* IconUtils+CompareItemIconUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FF22E9372DF00AB56D0 /* IconUtils+CompareItemIconUtils.swift */; }; - 553742C32E9372DF00AB56D0 /* SessionTypeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FA72E9372DF00AB56D0 /* SessionTypeError.swift */; }; - 553742C42E9372DF00AB56D0 /* FoldersOutlineView+FileCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FEC2E9372DF00AB56D0 /* FoldersOutlineView+FileCount.swift */; }; - 553742C52E9372DF00AB56D0 /* SessionPreferencesFiltersPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740B52E9372DF00AB56D0 /* SessionPreferencesFiltersPanel.swift */; }; - 553742C62E9372DF00AB56D0 /* FoldersWindowController+DisplayFiltersScopeBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740232E9372DF00AB56D0 /* FoldersWindowController+DisplayFiltersScopeBarDelegate.swift */; }; - 553742C72E9372DF00AB56D0 /* DiffCountersItem+CompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740372E9372DF00AB56D0 /* DiffCountersItem+CompareItem.swift */; }; - 553742C82E9372DF00AB56D0 /* DisplayFiltersPopUpButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741662E9372DF00AB56D0 /* DisplayFiltersPopUpButtonCell.swift */; }; - 553742C92E9372DF00AB56D0 /* NSEvent+VirtualKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537412A2E9372DF00AB56D0 /* NSEvent+VirtualKeys.swift */; }; - 553742CA2E9372DF00AB56D0 /* DescriptionOutlineNode+FolderCompareInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740752E9372DF00AB56D0 /* DescriptionOutlineNode+FolderCompareInfo.swift */; }; - 553742CB2E9372DF00AB56D0 /* WindowOSD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553741602E9372DF00AB56D0 /* WindowOSD.swift */; }; - 553742CC2E9372DF00AB56D0 /* NSPasteboard+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740FF2E9372DF00AB56D0 /* NSPasteboard+Helper.swift */; }; - 553742CD2E9372DF00AB56D0 /* ConfirmationsPreferencesPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537408C2E9372DF00AB56D0 /* ConfirmationsPreferencesPanel.swift */; }; - 553742CE2E9372DF00AB56D0 /* CompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537404F2E9372DF00AB56D0 /* CompareItem.swift */; }; - 553742CF2E9372DF00AB56D0 /* NSTextField+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537411E2E9372DF00AB56D0 /* NSTextField+Helper.swift */; }; - 553742D02E9372DF00AB56D0 /* FolderViewBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740912E9372DF00AB56D0 /* FolderViewBox.swift */; }; - 553742D12E9372DF00AB56D0 /* SessionDiff+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55373FB32E9372DF00AB56D0 /* SessionDiff+Types.swift */; }; - 553742D22E9372DF00AB56D0 /* FiltersPredicateEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553740B32E9372DF00AB56D0 /* FiltersPredicateEditor.swift */; }; - 553742D32E9372DF00AB56D0 /* HistoryEntityTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5537407B2E9372DF00AB56D0 /* HistoryEntityTableCellView.swift */; }; - 5537434E2E937B7F00AB56D0 /* VDDefaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5537434C2E937B7F00AB56D0 /* VDDefaults.plist */; }; - 5537434F2E937B7F00AB56D0 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = 5537434A2E937B7F00AB56D0 /* colors.json */; }; - 553743502E937B7F00AB56D0 /* colorsDark.json in Resources */ = {isa = PBXBuildFile; fileRef = 5537434B2E937B7F00AB56D0 /* colorsDark.json */; }; - 553743512E937B7F00AB56D0 /* VisualDiffer.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 5537434D2E937B7F00AB56D0 /* VisualDiffer.sdef */; }; - 553D10D9163BD0BC008D3F65 /* prefs_confirmations.png in Resources */ = {isa = PBXBuildFile; fileRef = 553D10D8163BD0BC008D3F65 /* prefs_confirmations.png */; }; - 5542D19616317DAC00F801AA /* vd.iconset in Resources */ = {isa = PBXBuildFile; fileRef = 5542D19516317DAC00F801AA /* vd.iconset */; }; - 55450B172ED4790B0048619F /* TextPreferencesPanel+PreferencesBoxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55450B162ED4790B0048619F /* TextPreferencesPanel+PreferencesBoxDataSource.swift */; }; - 55450B192ED47B700048619F /* FileSessionPreferencesWindow+PreferencesBoxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55450B182ED47B680048619F /* FileSessionPreferencesWindow+PreferencesBoxDataSource.swift */; }; - 554B4A452E9B7B8F0023AAF0 /* EndOfLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A3A2E9B7B8F0023AAF0 /* EndOfLine.swift */; }; - 554B4A462E9B7B8F0023AAF0 /* DiffSide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A402E9B7B8F0023AAF0 /* DiffSide.swift */; }; - 554B4A472E9B7B8F0023AAF0 /* DiffSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A412E9B7B8F0023AAF0 /* DiffSection.swift */; }; - 554B4A482E9B7B8F0023AAF0 /* DiffResult+Dump.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A3D2E9B7B8F0023AAF0 /* DiffResult+Dump.swift */; }; - 554B4A4A2E9B7B8F0023AAF0 /* DiffResult+Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A3F2E9B7B8F0023AAF0 /* DiffResult+Section.swift */; }; - 554B4A4B2E9B7B8F0023AAF0 /* DiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A3B2E9B7B8F0023AAF0 /* DiffResult.swift */; }; - 554B4A4D2E9B7B8F0023AAF0 /* DiffResult+Lines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A3E2E9B7B8F0023AAF0 /* DiffResult+Lines.swift */; }; - 554B4A4E2E9B7B8F0023AAF0 /* DiffLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 554B4A432E9B7B8F0023AAF0 /* DiffLine.swift */; }; - 554E0EB2163EFD51001418EA /* prefs_confirmations@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 554E0EA0163EFD51001418EA /* prefs_confirmations@2x.png */; }; - 554E0EB3163EFD51001418EA /* prefs_paths@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 554E0EA1163EFD51001418EA /* prefs_paths@2x.png */; }; - 554E0EB4163EFD51001418EA /* prefs_text@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 554E0EA2163EFD51001418EA /* prefs_text@2x.png */; }; - 554E0EBD163EFD6A001418EA /* dropzone@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 554E0EBA163EFD6A001418EA /* dropzone@2x.png */; }; - 554E0EBE163EFD6A001418EA /* rewind@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 554E0EBB163EFD6A001418EA /* rewind@2x.png */; }; - 5550B5931A21CD1F009BC4A4 /* aliasbadge@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5550B5921A21CD1F009BC4A4 /* aliasbadge@2x.png */; }; - 5550B5951A21D2F3009BC4A4 /* empty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5550B5941A21D2F3009BC4A4 /* empty@2x.png */; }; - 555392621889732900D26258 /* deleteRed.png in Resources */ = {isa = PBXBuildFile; fileRef = 555392601889732900D26258 /* deleteRed.png */; }; - 555392631889732900D26258 /* deleteRed@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 555392611889732900D26258 /* deleteRed@2x.png */; }; - 5560823F12AD0EF8003DC79D /* folder-999-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 5560823D12AD0EF8003DC79D /* folder-999-open.png */; }; - 5560824012AD0EF8003DC79D /* folder-999.png in Resources */ = {isa = PBXBuildFile; fileRef = 5560823E12AD0EF8003DC79D /* folder-999.png */; }; - 557315B12551307800CF4372 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 557315B02551307800CF4372 /* Images.xcassets */; }; - 55740BF512D710EC004BF6CC /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55740BF412D710EC004BF6CC /* IOKit.framework */; }; - 55740BF912D71103004BF6CC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55740BF812D71103004BF6CC /* Security.framework */; }; - 557BBB0E2ED1BBB00016CBD4 /* DiffLineComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557BBB0D2ED1BBB00016CBD4 /* DiffLineComponent.swift */; }; - 557C18182E7E6E0100381A3A /* big_file_5000_lines.txt in Resources */ = {isa = PBXBuildFile; fileRef = 557C18162E7E6E0100381A3A /* big_file_5000_lines.txt */; }; - 557C18232E7E8C0D00381A3A /* Signing.local.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 557C18212E7E8C0D00381A3A /* Signing.local.xcconfig */; }; - 557C18242E7E8C0D00381A3A /* Versions.local.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 557C18222E7E8C0D00381A3A /* Versions.local.xcconfig */; }; - 557C18252E7E8C0D00381A3A /* AppConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */; }; - 557EC74212CC7A8700B43F57 /* rewind.png in Resources */ = {isa = PBXBuildFile; fileRef = 557EC74112CC7A8700B43F57 /* rewind.png */; }; - 558DEAC62EA263DE00F05A10 /* CFStringEncoding+StringEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DEAC42EA263DE00F05A10 /* CFStringEncoding+StringEncoding.swift */; }; - 558DEACB2EA263F500F05A10 /* EncodingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DEAC72EA263F500F05A10 /* EncodingManager.swift */; }; - 558DEACC2EA263F500F05A10 /* SelectEncodingsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DEAC92EA263F500F05A10 /* SelectEncodingsPanel.swift */; }; - 558DEACD2EA263F500F05A10 /* EncodingPopUpButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DEAC82EA263F500F05A10 /* EncodingPopUpButtonCell.swift */; }; - 558DEAD02EA2642100F05A10 /* NSWindow+Editing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DEACE2EA2642100F05A10 /* NSWindow+Editing.swift */; }; - 558DEAD22EA26C5B00F05A10 /* Logger+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 558DEAD12EA26C5800F05A10 /* Logger+App.swift */; }; - 5590B2FF15B0449F00B0E047 /* prefs_paths.png in Resources */ = {isa = PBXBuildFile; fileRef = 5590B2FE15B0449F00B0E047 /* prefs_paths.png */; }; - 55916A102E8A814200ED6629 /* MyDocument.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 55916A092E8A814200ED6629 /* MyDocument.xcdatamodeld */; }; - 55916A112E8A814200ED6629 /* History.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 55916A082E8A814200ED6629 /* History.xcdatamodeld */; }; - 559186412E9B821B00A30620 /* DiffSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559186402E9B821B00A30620 /* DiffSummary.swift */; }; - 5595901D2EABA7FE00B5AB0B /* CompareSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5595901C2EABA7FE00B5AB0B /* CompareSummary.swift */; }; - 5595901F2EABA8A100B5AB0B /* CompareChangeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5595901E2EABA8A100B5AB0B /* CompareChangeType.swift */; }; - 559590212EABAFC200B5AB0B /* SessionDiff+ItemComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559590202EABAFC200B5AB0B /* SessionDiff+ItemComparator.swift */; }; - 559590262EABB06100B5AB0B /* ItemComparator+AlignRegularExpression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559590242EABB06100B5AB0B /* ItemComparator+AlignRegularExpression.swift */; }; - 559590272EABB06100B5AB0B /* ItemComparator+Align.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559590232EABB06100B5AB0B /* ItemComparator+Align.swift */; }; - 559590282EABB06100B5AB0B /* ItemComparator+Compare.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559590252EABB06100B5AB0B /* ItemComparator+Compare.swift */; }; - 559590292EABB06100B5AB0B /* ItemComparator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559590222EABB06100B5AB0B /* ItemComparator.swift */; }; - 5595902B2EABB15F00B5AB0B /* MockItemComparatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5595902A2EABB15F00B5AB0B /* MockItemComparatorDelegate.swift */; }; - 559614B114B0471E0075ED34 /* aliasbadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 559614B014B0471E0075ED34 /* aliasbadge.png */; }; - 559E927E1514A63100B76349 /* dropzone.png in Resources */ = {isa = PBXBuildFile; fileRef = 559E927D1514A63100B76349 /* dropzone.png */; }; - 559EA70E14FA1D8F00BC9B82 /* VisualDifferHelp in Resources */ = {isa = PBXBuildFile; fileRef = 559EA70D14FA1D8F00BC9B82 /* VisualDifferHelp */; }; - 55A027D4158242310004583F /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55A027D3158242310004583F /* Quartz.framework */; }; - 55A4DF322E9A49B000ED11BB /* EndOfLineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A4DF312E9A49A700ED11BB /* EndOfLineTests.swift */; }; - 55B1CE2B2E9934E900426FEC /* DateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B1CE2A2E9934E900426FEC /* DateBuilder.swift */; }; - 55B1CE312E9934F800426FEC /* DiffResultBaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B1CE2E2E9934F800426FEC /* DiffResultBaseTests.swift */; }; - 55B1CE322E9934F800426FEC /* DiffResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55B1CE2C2E9934F800426FEC /* DiffResultTests.swift */; }; - 55B6760018956D90000D5A0D /* prefs_keyboard.png in Resources */ = {isa = PBXBuildFile; fileRef = 55B675FE18956D90000D5A0D /* prefs_keyboard.png */; }; - 55B6760118956D90000D5A0D /* prefs_keyboard@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 55B675FF18956D90000D5A0D /* prefs_keyboard@2x.png */; }; - 55BC333E2EDAC69C004035A0 /* RowHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55BC333D2EDAC69C004035A0 /* RowHeightCalculator.swift */; }; - 55BC33402EDAC96F004035A0 /* TiledImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55BC333F2EDAC96F004035A0 /* TiledImageView.swift */; }; - 55BC33432EDACB09004035A0 /* FilesWindowController+RowHeightDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55BC33412EDACB09004035A0 /* FilesWindowController+RowHeightDataSource.swift */; }; - 55BC33442EDACB09004035A0 /* FilesWindowController+SplitViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55BC33422EDACB09004035A0 /* FilesWindowController+SplitViewDelegate.swift */; }; - 55C165622EE444050082A24C /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 55C165612EE444050082A24C /* Sparkle */; }; - 55C1CBFD2E8BB6AD004FE322 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55C1CBF42E8BB6AD004FE322 /* main.swift */; }; - 55C1CC012E8BB755004FE322 /* visdiff in Copy Visdiff to Helpers */ = {isa = PBXBuildFile; fileRef = 55C1CBE22E8BB6A2004FE322 /* visdiff */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 55C1CC072E8BBD85004FE322 /* DocumentWaiter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55C1CC062E8BBAD1004FE322 /* DocumentWaiter.swift */; }; - 55C84A712DA93ADB00BA39DA /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55C84A702DA93ADB00BA39DA /* UserNotifications.framework */; }; - 55D352E72EFD0C5B00C944D5 /* FileSystemTestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55D352E62EFD0C4D00C944D5 /* FileSystemTestHelper.swift */; }; - 55D71FD62EE584B7007E0FA0 /* AppUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55D71FD52EE584B7007E0FA0 /* AppUpdater.swift */; }; - 55DF3DD22E924BB10044CC0C /* BaseTests+FileSystemOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC52E924BB10044CC0C /* BaseTests+FileSystemOperations.swift */; }; - 55DF3DD32E924BB10044CC0C /* FiltersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DBF2E924BB10044CC0C /* FiltersTests.swift */; }; - 55DF3DD42E924BB10044CC0C /* LeafPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC82E924BB10044CC0C /* LeafPathTests.swift */; }; - 55DF3DD52E924BB10044CC0C /* DeleteFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DBC2E924BB10044CC0C /* DeleteFileTests.swift */; }; - 55DF3DD62E924BB10044CC0C /* TagsCopyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB32E924BB10044CC0C /* TagsCopyTests.swift */; }; - 55DF3DD82E924BB10044CC0C /* CopyFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DBB2E924BB10044CC0C /* CopyFilesTests.swift */; }; - 55DF3DD92E924BB10044CC0C /* StringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DCE2E924BB10044CC0C /* StringUtils.swift */; }; - 55DF3DDB2E924BB10044CC0C /* BufferedInputStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB92E924BB10044CC0C /* BufferedInputStreamTests.swift */; }; - 55DF3DDC2E924BB10044CC0C /* TagsMoveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB62E924BB10044CC0C /* TagsMoveTests.swift */; }; - 55DF3DDD2E924BB10044CC0C /* CaseSensitiveBaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC62E924BB10044CC0C /* CaseSensitiveBaseTest.swift */; }; - 55DF3DDE2E924BB10044CC0C /* BaseTests+AssertCompareItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DAA2E924BB10044CC0C /* BaseTests+AssertCompareItem.swift */; }; - 55DF3DDF2E924BB10044CC0C /* AlignmentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB82E924BB10044CC0C /* AlignmentTests.swift */; }; - 55DF3DE02E924BB10044CC0C /* RefreshInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DCB2E924BB10044CC0C /* RefreshInfoTests.swift */; }; - 55DF3DE12E924BB10044CC0C /* EmptyFolderColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DBE2E924BB10044CC0C /* EmptyFolderColorTests.swift */; }; - 55DF3DE22E924BB10044CC0C /* BaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DA92E924BB10044CC0C /* BaseTests.swift */; }; - 55DF3DE32E924BB10044CC0C /* FoldersWindowControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DCA2E924BB10044CC0C /* FoldersWindowControllerTests.swift */; }; - 55DF3DE42E924BB10044CC0C /* TagsDeleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB52E924BB10044CC0C /* TagsDeleteTests.swift */; }; - 55DF3DE52E924BB10044CC0C /* ComparatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DBA2E924BB10044CC0C /* ComparatorTests.swift */; }; - 55DF3DE62E924BB10044CC0C /* TouchFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC22E924BB10044CC0C /* TouchFilesTests.swift */; }; - 55DF3DE72E924BB10044CC0C /* DisplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DBD2E924BB10044CC0C /* DisplayTests.swift */; }; - 55DF3DE82E924BB10044CC0C /* BaseTests+AssertFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC42E924BB10044CC0C /* BaseTests+AssertFileSystem.swift */; }; - 55DF3DE92E924BB10044CC0C /* LabelsCreateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB12E924BB10044CC0C /* LabelsCreateTests.swift */; }; - 55DF3DEA2E924BB10044CC0C /* MockFileOperationManagerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DAC2E924BB10044CC0C /* MockFileOperationManagerDelegate.swift */; }; - 55DF3DEB2E924BB10044CC0C /* LabelsMoveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB22E924BB10044CC0C /* LabelsMoveTests.swift */; }; - 55DF3DEC2E924BB10044CC0C /* MoveFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC02E924BB10044CC0C /* MoveFilesTests.swift */; }; - 55DF3DED2E924BB10044CC0C /* TagsCreateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB42E924BB10044CC0C /* TagsCreateTests.swift */; }; - 55DF3DEE2E924BB10044CC0C /* LabelsCopyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DB02E924BB10044CC0C /* LabelsCopyTests.swift */; }; - 55DF3DEF2E924BB10044CC0C /* MockFolderReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DAD2E924BB10044CC0C /* MockFolderReaderDelegate.swift */; }; - 55DF3DF02E924BB10044CC0C /* RenameFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DF3DC12E924BB10044CC0C /* RenameFilesTests.swift */; }; - 55E1029A12C9E17300432C61 /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E1029912C9E17300432C61 /* empty.png */; }; - 55E31E011BC572F3001ACE4D /* top.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E31DFF1BC572F3001ACE4D /* top.png */; }; - 55E31E021BC572F3001ACE4D /* top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E31E001BC572F3001ACE4D /* top@2x.png */; }; - 55E31E051BC57373001ACE4D /* bottom.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E31E031BC57373001ACE4D /* bottom.png */; }; - 55E31E061BC57373001ACE4D /* bottom@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E31E041BC57373001ACE4D /* bottom@2x.png */; }; - 55E381C12EEFFD0500CB2F00 /* SymbolicLinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E381C02EEFFCF500CB2F00 /* SymbolicLinkTests.swift */; }; - 55E3A5661826552A002A890B /* lockedbadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E3A5641826552A002A890B /* lockedbadge.png */; }; - 55E3A5671826552A002A890B /* lockedbadge@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 55E3A5651826552A002A890B /* lockedbadge@2x.png */; }; - 55ECD8621476B03400D8BEB4 /* prefs_text.png in Resources */ = {isa = PBXBuildFile; fileRef = 55ECD8611476B03400D8BEB4 /* prefs_text.png */; }; - 55F6B0631AEA93E30096E55E /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 55F6B0611AEA93E30096E55E /* Localizable.stringsdict */; }; - 55F6B0661AEA99680096E55E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 55F6B0641AEA99680096E55E /* Localizable.strings */; }; - 55F9E79A2EDD5DBD001218C7 /* DiffResult.Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55F9E7992EDD5DAD001218C7 /* DiffResult.Options.swift */; }; - 55F9E79C2EDD6221001218C7 /* WhitespacesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55F9E79B2EDD6221001218C7 /* WhitespacesTests.swift */; }; - 55FD7E28180C484500CF473C /* prefs_folder@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 55FD7E25180C484500CF473C /* prefs_folder@2x.png */; }; - 55FE894A2F054B260003864A /* FlagSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55FE89492F054B260003864A /* FlagSet.swift */; }; - 775DFF38067A968500C5B868 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; - 8D15AC2C0486D014006FF6A4 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2A37F4B9FDCFA73011CA2CEA /* Credits.rtf */; }; - 8D15AC2F0486D014006FF6A4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165FFE840EACC02AAC07 /* InfoPlist.strings */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 55944CCB2E5C8D9F00FB1F8C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 2A37F4A9FDCFA73011CA2CEA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8D15AC270486D014006FF6A4; - remoteInfo = VisualDiffer; - }; - 55C1CBFF2E8BB749004FE322 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 2A37F4A9FDCFA73011CA2CEA /* Project object */; - proxyType = 1; - remoteGlobalIDString = 55C1CBE12E8BB6A2004FE322; - remoteInfo = visdiff; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 5504B6FF255FCBBA00CDE04A /* Copy Visdiff to Helpers */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = Contents/Helpers; - dstSubfolderSpec = 1; - files = ( - 55C1CC012E8BB755004FE322 /* visdiff in Copy Visdiff to Helpers */, - ); - name = "Copy Visdiff to Helpers"; - runOnlyForDeploymentPostprocessing = 0; - }; - 55943C852EE43E3800DE513B /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 12; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 55C1CBE02E8BB6A2004FE322 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - 2A37F4C4FDCFA73011CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; - 2A37F4C5FDCFA73011CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; - 32DBCF750370BD2300C91783 /* VisualDiffer_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VisualDiffer_Prefix.pch; sourceTree = ""; }; - 550A2CC113FF973B00981A9B /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; }; - 55157D6012898F8800826FF8 /* folder-000-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-000-open.png"; sourceTree = ""; }; - 55157D6112898F8800826FF8 /* folder-000.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-000.png"; sourceTree = ""; }; - 55157D6212898F8800826FF8 /* folder-001-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-001-open.png"; sourceTree = ""; }; - 55157D6312898F8800826FF8 /* folder-001.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-001.png"; sourceTree = ""; }; - 55157D6412898F8800826FF8 /* folder-010-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-010-open.png"; sourceTree = ""; }; - 55157D6512898F8800826FF8 /* folder-010.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-010.png"; sourceTree = ""; }; - 55157D6612898F8800826FF8 /* folder-011-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-011-open.png"; sourceTree = ""; }; - 55157D6712898F8800826FF8 /* folder-011.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-011.png"; sourceTree = ""; }; - 55157D6812898F8800826FF8 /* folder-100-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-100-open.png"; sourceTree = ""; }; - 55157D6912898F8800826FF8 /* folder-100.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-100.png"; sourceTree = ""; }; - 55157D6A12898F8800826FF8 /* folder-101-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-101-open.png"; sourceTree = ""; }; - 55157D6B12898F8800826FF8 /* folder-101.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-101.png"; sourceTree = ""; }; - 55157D6C12898F8800826FF8 /* folder-110-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-110-open.png"; sourceTree = ""; }; - 55157D6D12898F8800826FF8 /* folder-110.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-110.png"; sourceTree = ""; }; - 55157D6E12898F8800826FF8 /* folder-111-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-111-open.png"; sourceTree = ""; }; - 55157D6F12898F8800826FF8 /* folder-111.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-111.png"; sourceTree = ""; }; - 551BF0C81643B91B009CC9D1 /* folder-000-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-000-open@2x.png"; sourceTree = ""; }; - 551BF0C91643B91B009CC9D1 /* folder-000@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-000@2x.png"; sourceTree = ""; }; - 551BF0CA1643B91B009CC9D1 /* folder-001-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-001-open@2x.png"; sourceTree = ""; }; - 551BF0CB1643B91B009CC9D1 /* folder-001@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-001@2x.png"; sourceTree = ""; }; - 551BF0CC1643B91B009CC9D1 /* folder-010-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-010-open@2x.png"; sourceTree = ""; }; - 551BF0CD1643B91B009CC9D1 /* folder-010@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-010@2x.png"; sourceTree = ""; }; - 551BF0CE1643B91B009CC9D1 /* folder-011-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-011-open@2x.png"; sourceTree = ""; }; - 551BF0CF1643B91B009CC9D1 /* folder-011@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-011@2x.png"; sourceTree = ""; }; - 551BF0D01643B91B009CC9D1 /* folder-100-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-100-open@2x.png"; sourceTree = ""; }; - 551BF0D11643B91B009CC9D1 /* folder-100@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-100@2x.png"; sourceTree = ""; }; - 551BF0D21643B91B009CC9D1 /* folder-101-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-101-open@2x.png"; sourceTree = ""; }; - 551BF0D31643B91B009CC9D1 /* folder-101@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-101@2x.png"; sourceTree = ""; }; - 551BF0D41643B91B009CC9D1 /* folder-110-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-110-open@2x.png"; sourceTree = ""; }; - 551BF0D51643B91B009CC9D1 /* folder-110@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-110@2x.png"; sourceTree = ""; }; - 551BF0D61643B91B009CC9D1 /* folder-111-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-111-open@2x.png"; sourceTree = ""; }; - 551BF0D71643B91B009CC9D1 /* folder-111@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-111@2x.png"; sourceTree = ""; }; - 551BF0D81643B91B009CC9D1 /* folder-999-open@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-999-open@2x.png"; sourceTree = ""; }; - 551BF0D91643B91B009CC9D1 /* folder-999@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-999@2x.png"; sourceTree = ""; }; - 551DE39E2ED422820067AB18 /* FilesWindowController+SessionPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+SessionPreferences.swift"; sourceTree = ""; }; - 551DE3A42ED4237B0067AB18 /* FileSessionPreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSessionPreferencesWindow.swift; sourceTree = ""; }; - 551DE3A92ED428A50067AB18 /* FilePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreferences.swift; sourceTree = ""; }; - 551DE3B32ED42AD00067AB18 /* FilePreferencesComparisonPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreferencesComparisonPanel.swift; sourceTree = ""; }; - 551DE3B52ED42B4E0067AB18 /* FileComparisonBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileComparisonBox.swift; sourceTree = ""; }; - 551DE3B72ED4399B0067AB18 /* PreferencesBoxDataSource+Default.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreferencesBoxDataSource+Default.swift"; sourceTree = ""; }; - 551DEDE0160477CA0014DF88 /* prefs_folder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = prefs_folder.png; sourceTree = ""; }; - 553033AC2E5DBABF0016F101 /* VisualDiffer.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = VisualDiffer.xctestplan; sourceTree = SOURCE_ROOT; }; - 553251672765F06B0092D65F /* mask-back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mask-back.png"; sourceTree = ""; }; - 553251682765F06B0092D65F /* mask-full.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mask-full.png"; sourceTree = ""; }; - 553251692765F06B0092D65F /* mask-middle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mask-middle.png"; sourceTree = ""; }; - 5532516A2765F06B0092D65F /* mask-back-white.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mask-back-white.png"; sourceTree = ""; }; - 5532516B2765F06B0092D65F /* mask-front.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mask-front.png"; sourceTree = ""; }; - 5536ADB92EF169820019EFBF /* SessionDiff+ExtraData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff+ExtraData.swift"; sourceTree = ""; }; - 55373F8D2E9372DF00AB56D0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 55373F8F2E9372DF00AB56D0 /* AlignRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignRule.swift; sourceTree = ""; }; - 55373F902E9372DF00AB56D0 /* ColorScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorScheme.swift; sourceTree = ""; }; - 55373F912E9372DF00AB56D0 /* DisplayPositionable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayPositionable.swift; sourceTree = ""; }; - 55373F922E9372DF00AB56D0 /* ImageNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageNames.swift; sourceTree = ""; }; - 55373F932E9372DF00AB56D0 /* KeyEquivalent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyEquivalent.swift; sourceTree = ""; }; - 55373F942E9372DF00AB56D0 /* NotificationCenter+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+Helper.swift"; sourceTree = ""; }; - 55373F952E9372DF00AB56D0 /* NSImage+SymbolCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+SymbolCompat.swift"; sourceTree = ""; }; - 55373F962E9372DF00AB56D0 /* ViewLinkable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewLinkable.swift; sourceTree = ""; }; - 55373F982E9372DF00AB56D0 /* CommonPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonPrefs.swift; sourceTree = ""; }; - 55373F992E9372DF00AB56D0 /* CommonPrefs+FileCompare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+FileCompare.swift"; sourceTree = ""; }; - 55373F9A2E9372DF00AB56D0 /* CommonPrefs+FolderCompare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+FolderCompare.swift"; sourceTree = ""; }; - 55373F9B2E9372DF00AB56D0 /* CommonPrefs+Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+Font.swift"; sourceTree = ""; }; - 55373F9C2E9372DF00AB56D0 /* CommonPrefs+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+Helper.swift"; sourceTree = ""; }; - 55373F9D2E9372DF00AB56D0 /* CommonPrefs+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+Name.swift"; sourceTree = ""; }; - 55373F9F2E9372DF00AB56D0 /* DocumentWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentWindow.swift; sourceTree = ""; }; - 55373FA02E9372DF00AB56D0 /* NSArrayController+Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSArrayController+Paths.swift"; sourceTree = ""; }; - 55373FA12E9372DF00AB56D0 /* PathChooser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathChooser.swift; sourceTree = ""; }; - 55373FA22E9372DF00AB56D0 /* RecentDocumentPopupMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentDocumentPopupMenu.swift; sourceTree = ""; }; - 55373FA42E9372DF00AB56D0 /* DiffOpenerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffOpenerDelegate.swift; sourceTree = ""; }; - 55373FA52E9372DF00AB56D0 /* DocumentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentError.swift; sourceTree = ""; }; - 55373FA62E9372DF00AB56D0 /* SessionDiff.ItemType+Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff.ItemType+Paths.swift"; sourceTree = ""; }; - 55373FA72E9372DF00AB56D0 /* SessionTypeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionTypeError.swift; sourceTree = ""; }; - 55373FA82E9372DF00AB56D0 /* VDDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDDocument.swift; sourceTree = ""; }; - 55373FA92E9372DF00AB56D0 /* VDDocumentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDDocumentController.swift; sourceTree = ""; }; - 55373FAB2E9372DF00AB56D0 /* HistoryEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryEntity.swift; sourceTree = ""; }; - 55373FAC2E9372DF00AB56D0 /* HistorySessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySessionManager.swift; sourceTree = ""; }; - 55373FAE2E9372DF00AB56D0 /* SessionDiff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDiff.swift; sourceTree = ""; }; - 55373FAF2E9372DF00AB56D0 /* SessionDiff+AlignRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff+AlignRule.swift"; sourceTree = ""; }; - 55373FB12E9372DF00AB56D0 /* SessionDiff+NSSortDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff+NSSortDescriptor.swift"; sourceTree = ""; }; - 55373FB22E9372DF00AB56D0 /* SessionDiff+ResolvePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff+ResolvePath.swift"; sourceTree = ""; }; - 55373FB32E9372DF00AB56D0 /* SessionDiff+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff+Types.swift"; sourceTree = ""; }; - 55373FB62E9372DF00AB56D0 /* FilesTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesTableView.swift; sourceTree = ""; }; - 55373FB72E9372DF00AB56D0 /* FilesTableView+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesTableView+Menu.swift"; sourceTree = ""; }; - 55373FB82E9372DF00AB56D0 /* FilesTableViewFindTextDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesTableViewFindTextDelegate.swift; sourceTree = ""; }; - 55373FBA2E9372DF00AB56D0 /* FileInfoBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileInfoBar.swift; sourceTree = ""; }; - 55373FBB2E9372DF00AB56D0 /* FilePanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePanelView.swift; sourceTree = ""; }; - 55373FBC2E9372DF00AB56D0 /* FilePanelView+File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilePanelView+File.swift"; sourceTree = ""; }; - 55373FBD2E9372DF00AB56D0 /* FilesScopeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesScopeBar.swift; sourceTree = ""; }; - 55373FBE2E9372DF00AB56D0 /* FileThumbnailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileThumbnailView.swift; sourceTree = ""; }; - 55373FBF2E9372DF00AB56D0 /* LineNumberTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineNumberTableCellView.swift; sourceTree = ""; }; - 55373FC02E9372DF00AB56D0 /* LineNumberTableRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineNumberTableRowView.swift; sourceTree = ""; }; - 55373FC12E9372DF00AB56D0 /* NSAlert+DirtyFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+DirtyFiles.swift"; sourceTree = ""; }; - 55373FC22E9372DF00AB56D0 /* SaveFileAccessoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveFileAccessoryView.swift; sourceTree = ""; }; - 55373FC32E9372DF00AB56D0 /* TextFieldVerticalCentered.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldVerticalCentered.swift; sourceTree = ""; }; - 55373FC52E9372DF00AB56D0 /* FilesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesWindowController.swift; sourceTree = ""; }; - 55373FC62E9372DF00AB56D0 /* FilesWindowController+Clipboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Clipboard.swift"; sourceTree = ""; }; - 55373FC72E9372DF00AB56D0 /* FilesWindowController+Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Common.swift"; sourceTree = ""; }; - 55373FC82E9372DF00AB56D0 /* FilesWindowController+Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Document.swift"; sourceTree = ""; }; - 55373FC92E9372DF00AB56D0 /* FilesWindowController+FileInfoBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+FileInfoBarDelegate.swift"; sourceTree = ""; }; - 55373FCA2E9372DF00AB56D0 /* FilesWindowController+FilesScopeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+FilesScopeBar.swift"; sourceTree = ""; }; - 55373FCB2E9372DF00AB56D0 /* FilesWindowController+FilesTableViewContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+FilesTableViewContextMenu.swift"; sourceTree = ""; }; - 55373FCC2E9372DF00AB56D0 /* FilesWindowController+JumpLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+JumpLine.swift"; sourceTree = ""; }; - 55373FCD2E9372DF00AB56D0 /* FilesWindowController+LinesDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+LinesDetail.swift"; sourceTree = ""; }; - 55373FCE2E9372DF00AB56D0 /* FilesWindowController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Menu.swift"; sourceTree = ""; }; - 55373FCF2E9372DF00AB56D0 /* FilesWindowController+MenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+MenuDelegate.swift"; sourceTree = ""; }; - 55373FD02E9372DF00AB56D0 /* FilesWindowController+Navigate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Navigate.swift"; sourceTree = ""; }; - 55373FD12E9372DF00AB56D0 /* FilesWindowController+NSToolbarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+NSToolbarDelegate.swift"; sourceTree = ""; }; - 55373FD22E9372DF00AB56D0 /* FilesWindowController+NSWindowDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+NSWindowDelegate.swift"; sourceTree = ""; }; - 55373FD32E9372DF00AB56D0 /* FilesWindowController+PathControlDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+PathControlDelegate.swift"; sourceTree = ""; }; - 55373FD42E9372DF00AB56D0 /* FilesWindowController+Read.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Read.swift"; sourceTree = ""; }; - 55373FD52E9372DF00AB56D0 /* FilesWindowController+Save.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Save.swift"; sourceTree = ""; }; - 55373FD62E9372DF00AB56D0 /* FilesWindowController+Slider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+Slider.swift"; sourceTree = ""; }; - 55373FD72E9372DF00AB56D0 /* FilesWindowController+TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+TableView.swift"; sourceTree = ""; }; - 55373FD82E9372DF00AB56D0 /* FilesWindowController+UICreation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+UICreation.swift"; sourceTree = ""; }; - 55373FD92E9372DF00AB56D0 /* FilesWindowController+UISetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+UISetup.swift"; sourceTree = ""; }; - 55373FDB2E9372DF00AB56D0 /* DiffLine+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffLine+Color.swift"; sourceTree = ""; }; - 55373FDD2E9372DF00AB56D0 /* DiffChangeType+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffChangeType+Color.swift"; sourceTree = ""; }; - 55373FDE2E9372DF00AB56D0 /* DiffChangeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffChangeType.swift; sourceTree = ""; }; - 55373FE02E9372DF00AB56D0 /* DiffCountersItem+DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffCountersItem+DiffResult.swift"; sourceTree = ""; }; - 55373FE12E9372DF00AB56D0 /* FilesTableView+EditorData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesTableView+EditorData.swift"; sourceTree = ""; }; - 55373FE32E9372DF00AB56D0 /* JumpToLineWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpToLineWindow.swift; sourceTree = ""; }; - 55373FE62E9372DF00AB56D0 /* ColoredFoldersManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColoredFoldersManager.swift; sourceTree = ""; }; - 55373FE72E9372DF00AB56D0 /* FoldersOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoldersOutlineView.swift; sourceTree = ""; }; - 55373FE82E9372DF00AB56D0 /* FoldersOutlineView+Clipboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+Clipboard.swift"; sourceTree = ""; }; - 55373FE92E9372DF00AB56D0 /* FoldersOutlineView+Columns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+Columns.swift"; sourceTree = ""; }; - 55373FEA2E9372DF00AB56D0 /* FoldersOutlineView+Enumerate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+Enumerate.swift"; sourceTree = ""; }; - 55373FEB2E9372DF00AB56D0 /* FoldersOutlineView+ExternalApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+ExternalApp.swift"; sourceTree = ""; }; - 55373FEC2E9372DF00AB56D0 /* FoldersOutlineView+FileCount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+FileCount.swift"; sourceTree = ""; }; - 55373FED2E9372DF00AB56D0 /* FoldersOutlineView+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+Menu.swift"; sourceTree = ""; }; - 55373FEE2E9372DF00AB56D0 /* FoldersOutlineView+VisibleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+VisibleItem.swift"; sourceTree = ""; }; - 55373FEF2E9372DF00AB56D0 /* FoldersOutlineViewFindTextDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoldersOutlineViewFindTextDelegate.swift; sourceTree = ""; }; - 55373FF02E9372DF00AB56D0 /* CompareItem+LeafPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+LeafPath.swift"; sourceTree = ""; }; - 55373FF12E9372DF00AB56D0 /* CompareItem+NSTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+NSTableCellView.swift"; sourceTree = ""; }; - 55373FF22E9372DF00AB56D0 /* IconUtils+CompareItemIconUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IconUtils+CompareItemIconUtils.swift"; sourceTree = ""; }; - 55373FF42E9372DF00AB56D0 /* DisplayFiltersScopeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayFiltersScopeBar.swift; sourceTree = ""; }; - 55373FF52E9372DF00AB56D0 /* FolderPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPanelView.swift; sourceTree = ""; }; - 55373FF62E9372DF00AB56D0 /* CompareItemTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompareItemTableCellView.swift; sourceTree = ""; }; - 55373FF72E9372DF00AB56D0 /* CompareItemTableRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompareItemTableRowView.swift; sourceTree = ""; }; - 55373FF92E9372DF00AB56D0 /* MainThreadComparatorDelegateBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainThreadComparatorDelegateBridge.swift; sourceTree = ""; }; - 55373FFB2E9372DF00AB56D0 /* CopyFileOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyFileOperationExecutor.swift; sourceTree = ""; }; - 55373FFD2E9372DF00AB56D0 /* DeleteFileOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteFileOperationExecutor.swift; sourceTree = ""; }; - 55373FFF2E9372DF00AB56D0 /* MoveFileOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveFileOperationExecutor.swift; sourceTree = ""; }; - 553740012E9372DF00AB56D0 /* ConfirmReplace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmReplace.swift; sourceTree = ""; }; - 553740022E9372DF00AB56D0 /* NSAlert+ReplaceFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+ReplaceFile.swift"; sourceTree = ""; }; - 553740032E9372DF00AB56D0 /* NSApplication.ModalResponse+ReplaceFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication.ModalResponse+ReplaceFile.swift"; sourceTree = ""; }; - 553740042E9372DF00AB56D0 /* ReplaceFileAttributeKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceFileAttributeKey.swift; sourceTree = ""; }; - 553740062E9372DF00AB56D0 /* CompletionIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionIndicator.swift; sourceTree = ""; }; - 553740072E9372DF00AB56D0 /* ErrorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorsView.swift; sourceTree = ""; }; - 553740082E9372DF00AB56D0 /* FileOperationManagerDelegateImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOperationManagerDelegateImpl.swift; sourceTree = ""; }; - 553740092E9372DF00AB56D0 /* ProgressIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicatorController.swift; sourceTree = ""; }; - 5537400A2E9372DF00AB56D0 /* ReplaceInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceInfoView.swift; sourceTree = ""; }; - 5537400C2E9372DF00AB56D0 /* SyncFileController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncFileController.swift; sourceTree = ""; }; - 5537400D2E9372DF00AB56D0 /* SyncFileOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncFileOperationExecutor.swift; sourceTree = ""; }; - 5537400E2E9372DF00AB56D0 /* SyncItemsInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncItemsInfo.swift; sourceTree = ""; }; - 5537400F2E9372DF00AB56D0 /* SyncOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncOutlineView.swift; sourceTree = ""; }; - 553740112E9372DF00AB56D0 /* DatePickersStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickersStackView.swift; sourceTree = ""; }; - 553740122E9372DF00AB56D0 /* TouchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchController.swift; sourceTree = ""; }; - 553740132E9372DF00AB56D0 /* TouchFileOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchFileOperationExecutor.swift; sourceTree = ""; }; - 553740142E9372DF00AB56D0 /* TouchPickersStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchPickersStackView.swift; sourceTree = ""; }; - 553740162E9372DF00AB56D0 /* FileOperationExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOperationExecutor.swift; sourceTree = ""; }; - 553740172E9372DF00AB56D0 /* FileSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSummaryView.swift; sourceTree = ""; }; - 553740182E9372DF00AB56D0 /* FileSystemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemController.swift; sourceTree = ""; }; - 553740192E9372DF00AB56D0 /* OperationSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationSummaryView.swift; sourceTree = ""; }; - 5537401B2E9372DF00AB56D0 /* FolderReader+Detached.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderReader+Detached.swift"; sourceTree = ""; }; - 5537401C2E9372DF00AB56D0 /* MainThreadFolderReaderDelegateBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainThreadFolderReaderDelegateBridge.swift; sourceTree = ""; }; - 5537401E2E9372DF00AB56D0 /* FolderSelectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderSelectionInfo.swift; sourceTree = ""; }; - 5537401F2E9372DF00AB56D0 /* FoldersWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoldersWindowController.swift; sourceTree = ""; }; - 553740202E9372DF00AB56D0 /* FoldersWindowController+BaseFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+BaseFolder.swift"; sourceTree = ""; }; - 553740212E9372DF00AB56D0 /* FoldersWindowController+Comparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+Comparison.swift"; sourceTree = ""; }; - 553740222E9372DF00AB56D0 /* FoldersWindowController+ConsoleViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+ConsoleViewDelegate.swift"; sourceTree = ""; }; - 553740232E9372DF00AB56D0 /* FoldersWindowController+DisplayFiltersScopeBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+DisplayFiltersScopeBarDelegate.swift"; sourceTree = ""; }; - 553740242E9372DF00AB56D0 /* FoldersWindowController+Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+Document.swift"; sourceTree = ""; }; - 553740252E9372DF00AB56D0 /* FoldersWindowController+Exclude.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+Exclude.swift"; sourceTree = ""; }; - 553740262E9372DF00AB56D0 /* FoldersWindowController+FileSystemControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+FileSystemControllerDelegate.swift"; sourceTree = ""; }; - 553740272E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+FoldersOutlineView.swift"; sourceTree = ""; }; - 553740282E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineViewContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+FoldersOutlineViewContextMenu.swift"; sourceTree = ""; }; - 553740292E9372DF00AB56D0 /* FoldersWindowController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+Menu.swift"; sourceTree = ""; }; - 5537402A2E9372DF00AB56D0 /* FoldersWindowController+MenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+MenuDelegate.swift"; sourceTree = ""; }; - 5537402B2E9372DF00AB56D0 /* FoldersWindowController+Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+Navigation.swift"; sourceTree = ""; }; - 5537402C2E9372DF00AB56D0 /* FoldersWindowController+NSToolbarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+NSToolbarDelegate.swift"; sourceTree = ""; }; - 5537402D2E9372DF00AB56D0 /* FoldersWindowController+NSWindowDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+NSWindowDelegate.swift"; sourceTree = ""; }; - 5537402E2E9372DF00AB56D0 /* FoldersWindowController+PathControlDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+PathControlDelegate.swift"; sourceTree = ""; }; - 5537402F2E9372DF00AB56D0 /* FoldersWindowController+QLPreviewPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+QLPreviewPanel.swift"; sourceTree = ""; }; - 553740302E9372DF00AB56D0 /* FoldersWindowController+Select.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+Select.swift"; sourceTree = ""; }; - 553740312E9372DF00AB56D0 /* FoldersWindowController+SessionPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+SessionPreferences.swift"; sourceTree = ""; }; - 553740322E9372DF00AB56D0 /* FoldersWindowController+UICreation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+UICreation.swift"; sourceTree = ""; }; - 553740332E9372DF00AB56D0 /* FoldersWindowController+UISetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersWindowController+UISetup.swift"; sourceTree = ""; }; - 553740342E9372DF00AB56D0 /* OutlineViewItemDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutlineViewItemDelegate.swift; sourceTree = ""; }; - 553740362E9372DF00AB56D0 /* CommonPrefs+ConsoleLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+ConsoleLog.swift"; sourceTree = ""; }; - 553740372E9372DF00AB56D0 /* DiffCountersItem+CompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffCountersItem+CompareItem.swift"; sourceTree = ""; }; - 553740392E9372DF00AB56D0 /* CopyCompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyCompareItem.swift; sourceTree = ""; }; - 5537403A2E9372DF00AB56D0 /* DeleteCompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteCompareItem.swift; sourceTree = ""; }; - 5537403B2E9372DF00AB56D0 /* FileAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileAttributes.swift; sourceTree = ""; }; - 5537403C2E9372DF00AB56D0 /* FileOperationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOperationManager.swift; sourceTree = ""; }; - 5537403D2E9372DF00AB56D0 /* FileOperationManager+FileOperationManagerAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileOperationManager+FileOperationManagerAction.swift"; sourceTree = ""; }; - 5537403E2E9372DF00AB56D0 /* FileOperationManager+Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileOperationManager+Util.swift"; sourceTree = ""; }; - 5537403F2E9372DF00AB56D0 /* FolderManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderManagerError.swift; sourceTree = ""; }; - 553740402E9372DF00AB56D0 /* CompareItem+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+Metadata.swift"; sourceTree = ""; }; - 553740412E9372DF00AB56D0 /* MoveCompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveCompareItem.swift; sourceTree = ""; }; - 553740422E9372DF00AB56D0 /* PathTimestamps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathTimestamps.swift; sourceTree = ""; }; - 553740432E9372DF00AB56D0 /* RenameCompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameCompareItem.swift; sourceTree = ""; }; - 553740442E9372DF00AB56D0 /* TouchCompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchCompareItem.swift; sourceTree = ""; }; - 553740462E9372DF00AB56D0 /* ComparatorOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparatorOptions.swift; sourceTree = ""; }; - 553740472E9372DF00AB56D0 /* ComparatorOptions+Description.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComparatorOptions+Description.swift"; sourceTree = ""; }; - 553740482E9372DF00AB56D0 /* ComparatorOptions+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComparatorOptions+Helper.swift"; sourceTree = ""; }; - 5537404B2E9372DF00AB56D0 /* DisplayOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayOptions.swift; sourceTree = ""; }; - 5537404C2E9372DF00AB56D0 /* DisplayOptions+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DisplayOptions+Helper.swift"; sourceTree = ""; }; - 5537404E2E9372DF00AB56D0 /* FileExtraOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExtraOptions.swift; sourceTree = ""; }; - 5537404F2E9372DF00AB56D0 /* CompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompareItem.swift; sourceTree = ""; }; - 553740502E9372DF00AB56D0 /* CompareItem+Accessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+Accessors.swift"; sourceTree = ""; }; - 553740512E9372DF00AB56D0 /* CompareItem+Clone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+Clone.swift"; sourceTree = ""; }; - 553740522E9372DF00AB56D0 /* CompareItem+Comparison.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+Comparison.swift"; sourceTree = ""; }; - 553740532E9372DF00AB56D0 /* CompareItem+Description.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+Description.swift"; sourceTree = ""; }; - 553740542E9372DF00AB56D0 /* CompareItem+FilterConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+FilterConfig.swift"; sourceTree = ""; }; - 553740562E9372DF00AB56D0 /* CompareItem+Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+Path.swift"; sourceTree = ""; }; - 553740572E9372DF00AB56D0 /* CompareItem+VisibleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+VisibleItem.swift"; sourceTree = ""; }; - 553740592E9372DF00AB56D0 /* CommonPrefs+DifferenceNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonPrefs+DifferenceNavigator.swift"; sourceTree = ""; }; - 5537405A2E9372DF00AB56D0 /* FoldersOutlineView+DifferenceNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FoldersOutlineView+DifferenceNavigator.swift"; sourceTree = ""; }; - 5537405B2E9372DF00AB56D0 /* CompareItem+DifferenceNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompareItem+DifferenceNavigator.swift"; sourceTree = ""; }; - 5537405D2E9372DF00AB56D0 /* FilterConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterConfig.swift; sourceTree = ""; }; - 5537405E2E9372DF00AB56D0 /* FolderReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderReader.swift; sourceTree = ""; }; - 5537405F2E9372DF00AB56D0 /* FolderReader+Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderReader+Log.swift"; sourceTree = ""; }; - 553740602E9372DF00AB56D0 /* FolderReaderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderReaderDelegate.swift; sourceTree = ""; }; - 553740612E9372DF00AB56D0 /* RefreshInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshInfo.swift; sourceTree = ""; }; - 553740692E9372DF00AB56D0 /* FolderSelectionInfo+CompareActionValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderSelectionInfo+CompareActionValidator.swift"; sourceTree = ""; }; - 5537406A2E9372DF00AB56D0 /* FolderSelectionInfo+FileSystemActionValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderSelectionInfo+FileSystemActionValidator.swift"; sourceTree = ""; }; - 5537406B2E9372DF00AB56D0 /* FolderSelectionInfo+FilterActionValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderSelectionInfo+FilterActionValidator.swift"; sourceTree = ""; }; - 5537406C2E9372DF00AB56D0 /* FolderSelectionInfo+FolderActionValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderSelectionInfo+FolderActionValidator.swift"; sourceTree = ""; }; - 5537406D2E9372DF00AB56D0 /* FolderSelectionInfo+ViewerActionValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderSelectionInfo+ViewerActionValidator.swift"; sourceTree = ""; }; - 5537406F2E9372DF00AB56D0 /* VisibleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleItem.swift; sourceTree = ""; }; - 553740702E9372DF00AB56D0 /* VisibleItem+Find.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleItem+Find.swift"; sourceTree = ""; }; - 553740712E9372DF00AB56D0 /* VisibleItem+Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleItem+Log.swift"; sourceTree = ""; }; - 553740722E9372DF00AB56D0 /* VisibleItem+QLPreviewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleItem+QLPreviewItem.swift"; sourceTree = ""; }; - 553740732E9372DF00AB56D0 /* VisibleItem+Sort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleItem+Sort.swift"; sourceTree = ""; }; - 553740752E9372DF00AB56D0 /* DescriptionOutlineNode+FolderCompareInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DescriptionOutlineNode+FolderCompareInfo.swift"; sourceTree = ""; }; - 553740762E9372DF00AB56D0 /* FolderCompareInfoWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderCompareInfoWindow.swift; sourceTree = ""; }; - 5537407A2E9372DF00AB56D0 /* HistoryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryController.swift; sourceTree = ""; }; - 5537407B2E9372DF00AB56D0 /* HistoryEntityTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryEntityTableCellView.swift; sourceTree = ""; }; - 5537407C2E9372DF00AB56D0 /* HistoryFetchedResultsControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryFetchedResultsControllerDelegate.swift; sourceTree = ""; }; - 5537407D2E9372DF00AB56D0 /* HistorySearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySearchField.swift; sourceTree = ""; }; - 5537407E2E9372DF00AB56D0 /* HistoryTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryTableView.swift; sourceTree = ""; }; - 553740802E9372DF00AB56D0 /* ComparisonStandardUserDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparisonStandardUserDataSource.swift; sourceTree = ""; }; - 553740812E9372DF00AB56D0 /* PreferencesBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesBox.swift; sourceTree = ""; }; - 553740822E9372DF00AB56D0 /* PreferencesBoxDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesBoxDataSource.swift; sourceTree = ""; }; - 553740832E9372DF00AB56D0 /* PreferencesCheckbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesCheckbox.swift; sourceTree = ""; }; - 553740842E9372DF00AB56D0 /* StandardUserPreferencesBoxDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardUserPreferencesBoxDataSource.swift; sourceTree = ""; }; - 553740862E9372DF00AB56D0 /* BasePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasePreferences.swift; sourceTree = ""; }; - 553740872E9372DF00AB56D0 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; - 553740892E9372DF00AB56D0 /* ConfirmationsDocumentsBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationsDocumentsBox.swift; sourceTree = ""; }; - 5537408A2E9372DF00AB56D0 /* ConfirmationsFilesBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationsFilesBox.swift; sourceTree = ""; }; - 5537408B2E9372DF00AB56D0 /* ConfirmationsFoldersBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationsFoldersBox.swift; sourceTree = ""; }; - 5537408C2E9372DF00AB56D0 /* ConfirmationsPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationsPreferencesPanel.swift; sourceTree = ""; }; - 5537408E2E9372DF00AB56D0 /* DifferenceNavigatorBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DifferenceNavigatorBox.swift; sourceTree = ""; }; - 5537408F2E9372DF00AB56D0 /* FolderPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPreferencesPanel.swift; sourceTree = ""; }; - 553740902E9372DF00AB56D0 /* FoldersTraversalBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoldersTraversalBox.swift; sourceTree = ""; }; - 553740912E9372DF00AB56D0 /* FolderViewBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderViewBox.swift; sourceTree = ""; }; - 553740932E9372DF00AB56D0 /* FontBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontBox.swift; sourceTree = ""; }; - 553740942E9372DF00AB56D0 /* FontPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontPreferencesPanel.swift; sourceTree = ""; }; - 553740962E9372DF00AB56D0 /* AppearanceBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceBox.swift; sourceTree = ""; }; - 553740972E9372DF00AB56D0 /* FolderComparisonBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderComparisonBox.swift; sourceTree = ""; }; - 553740982E9372DF00AB56D0 /* GeneralPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesPanel.swift; sourceTree = ""; }; - 553740992E9372DF00AB56D0 /* PreferredEditorBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferredEditorBox.swift; sourceTree = ""; }; - 5537409B2E9372DF00AB56D0 /* KeyboardDocumentBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardDocumentBox.swift; sourceTree = ""; }; - 5537409C2E9372DF00AB56D0 /* KeyboardPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPreferencesPanel.swift; sourceTree = ""; }; - 5537409E2E9372DF00AB56D0 /* TextPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextPreferencesPanel.swift; sourceTree = ""; }; - 5537409F2E9372DF00AB56D0 /* VisualizationBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualizationBox.swift; sourceTree = ""; }; - 553740A12E9372DF00AB56D0 /* TrustedPathsPreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrustedPathsPreferencesPanel.swift; sourceTree = ""; }; - 553740A32E9372DF00AB56D0 /* NSStackView+PreferencesPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSStackView+PreferencesPanel.swift"; sourceTree = ""; }; - 553740A62E9372DF00AB56D0 /* SessionPreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionPreferencesWindow.swift; sourceTree = ""; }; - 553740A72E9372DF00AB56D0 /* SessionPreferencesWindow+Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionPreferencesWindow+Data.swift"; sourceTree = ""; }; - 553740A92E9372DF00AB56D0 /* AlignmentPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignmentPanel.swift; sourceTree = ""; }; - 553740AA2E9372DF00AB56D0 /* AlignRuleWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignRuleWindow.swift; sourceTree = ""; }; - 553740AB2E9372DF00AB56D0 /* AlignTestResultBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignTestResultBox.swift; sourceTree = ""; }; - 553740AC2E9372DF00AB56D0 /* ExpressionBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpressionBox.swift; sourceTree = ""; }; - 553740AD2E9372DF00AB56D0 /* ExpressionBox+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ExpressionBox+Menu.swift"; sourceTree = ""; }; - 553740AE2E9372DF00AB56D0 /* FileNameCaseBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileNameCaseBox.swift; sourceTree = ""; }; - 553740AF2E9372DF00AB56D0 /* UserDefinedRulesBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefinedRulesBox.swift; sourceTree = ""; }; - 553740B12E9372DF00AB56D0 /* SessionPreferencesComparisonPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionPreferencesComparisonPanel.swift; sourceTree = ""; }; - 553740B32E9372DF00AB56D0 /* FiltersPredicateEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersPredicateEditor.swift; sourceTree = ""; }; - 553740B42E9372DF00AB56D0 /* SessionPreferencesFiltersBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionPreferencesFiltersBox.swift; sourceTree = ""; }; - 553740B52E9372DF00AB56D0 /* SessionPreferencesFiltersPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionPreferencesFiltersPanel.swift; sourceTree = ""; }; - 553740B92E9372DF00AB56D0 /* PreferencesPanelDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesPanelDataSource.swift; sourceTree = ""; }; - 553740BC2E9372DF00AB56D0 /* UDiffScriptBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UDiffScriptBuilder.h; sourceTree = ""; }; - 553740BD2E9372DF00AB56D0 /* UDiffScriptBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UDiffScriptBuilder.m; sourceTree = ""; }; - 553740BE2E9372DF00AB56D0 /* UnifiedDiff.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnifiedDiff.h; sourceTree = ""; }; - 553740BF2E9372DF00AB56D0 /* UnifiedDiff.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnifiedDiff.m; sourceTree = ""; }; - 553740C72E9372DF00AB56D0 /* BigFileFileOperationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BigFileFileOperationManager.h; sourceTree = ""; }; - 553740C82E9372DF00AB56D0 /* BigFileFileOperationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BigFileFileOperationManager.m; sourceTree = ""; }; - 553740CD2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGRecessedPopUpButtonCell.h; sourceTree = ""; }; - 553740CE2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGRecessedPopUpButtonCell.m; sourceTree = ""; }; - 553740CF2E9372DF00AB56D0 /* MGScopeBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGScopeBar.h; sourceTree = ""; }; - 553740D02E9372DF00AB56D0 /* MGScopeBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGScopeBar.m; sourceTree = ""; }; - 553740D12E9372DF00AB56D0 /* MGScopeBarDelegateProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGScopeBarDelegateProtocol.h; sourceTree = ""; }; - 553740D22E9372DF00AB56D0 /* ScopeBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScopeBarItem.swift; sourceTree = ""; }; - 553740D42E9372DF00AB56D0 /* NSPredicate+Objc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+Objc.h"; sourceTree = ""; }; - 553740D52E9372DF00AB56D0 /* NSPredicate+Objc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSPredicate+Objc.m"; sourceTree = ""; }; - 553740D62E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TitlePredicateEditorRowTemplate.h; sourceTree = ""; }; - 553740D72E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TitlePredicateEditorRowTemplate.m; sourceTree = ""; }; - 553740D82E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOPFileSizePredicateEditorRowTemplate.h; sourceTree = ""; }; - 553740D92E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOPFileSizePredicateEditorRowTemplate.m; sourceTree = ""; }; - 553740DA2E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TOPTimestampPredicateEditorRowTemplate.h; sourceTree = ""; }; - 553740DB2E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TOPTimestampPredicateEditorRowTemplate.m; sourceTree = ""; }; - 553740E22E9372DF00AB56D0 /* OpenDiffCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenDiffCommand.swift; sourceTree = ""; }; - 553740E52E9372DF00AB56D0 /* NSAppearance+DarkMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAppearance+DarkMode.swift"; sourceTree = ""; }; - 553740E72E9372DF00AB56D0 /* Array+MoveIndexes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+MoveIndexes.swift"; sourceTree = ""; }; - 553740E92E9372DF00AB56D0 /* NSColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = ""; }; - 553740EB2E9372DF00AB56D0 /* NSManagedObjectContext+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Helpers.swift"; sourceTree = ""; }; - 553740ED2E9372DF00AB56D0 /* TimeInterval+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Helper.swift"; sourceTree = ""; }; - 553740EF2E9372DF00AB56D0 /* NSError+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+Format.swift"; sourceTree = ""; }; - 553740F12E9372DF00AB56D0 /* FileManager+Attributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Attributes.swift"; sourceTree = ""; }; - 553740F32E9372DF00AB56D0 /* DateFormatter+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Helper.swift"; sourceTree = ""; }; - 553740F52E9372DF00AB56D0 /* NSImage+Tint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+Tint.swift"; sourceTree = ""; }; - 553740F72E9372DF00AB56D0 /* NSMenu+File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMenu+File.swift"; sourceTree = ""; }; - 553740F92E9372DF00AB56D0 /* OptionSet+Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OptionSet+Toggle.swift"; sourceTree = ""; }; - 553740FB2E9372DF00AB56D0 /* NSAlert+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+Helper.swift"; sourceTree = ""; }; - 553740FC2E9372DF00AB56D0 /* NSOpenPanel+Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSOpenPanel+Application.swift"; sourceTree = ""; }; - 553740FD2E9372DF00AB56D0 /* QLPreviewPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QLPreviewPanel.swift; sourceTree = ""; }; - 553740FF2E9372DF00AB56D0 /* NSPasteboard+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPasteboard+Helper.swift"; sourceTree = ""; }; - 553741012E9372DF00AB56D0 /* NSPredicateEditor+Fix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPredicateEditor+Fix.swift"; sourceTree = ""; }; - 553741032E9372DF00AB56D0 /* String+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Helper.swift"; sourceTree = ""; }; - 553741042E9372DF00AB56D0 /* String+Highlights.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Highlights.swift"; sourceTree = ""; }; - 553741052E9372DF00AB56D0 /* String+Occcurrences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Occcurrences.swift"; sourceTree = ""; }; - 553741062E9372DF00AB56D0 /* String+Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Path.swift"; sourceTree = ""; }; - 553741072E9372DF00AB56D0 /* String+RegularExpression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+RegularExpression.swift"; sourceTree = ""; }; - 553741092E9372DF00AB56D0 /* NSTableView+Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Font.swift"; sourceTree = ""; }; - 5537410A2E9372DF00AB56D0 /* NSTableView+Row.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Row.swift"; sourceTree = ""; }; - 5537410C2E9372DF00AB56D0 /* NSToolbar+Create.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSToolbar+Create.swift"; sourceTree = ""; }; - 5537410D2E9372DF00AB56D0 /* NSToolbarItem+Create.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSToolbarItem+Create.swift"; sourceTree = ""; }; - 5537410F2E9372DF00AB56D0 /* URL+FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+FileManager.swift"; sourceTree = ""; }; - 553741102E9372DF00AB56D0 /* URL+Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Finder.swift"; sourceTree = ""; }; - 553741112E9372DF00AB56D0 /* URL+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Metadata.swift"; sourceTree = ""; }; - 553741122E9372DF00AB56D0 /* URL+Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Path.swift"; sourceTree = ""; }; - 553741132E9372DF00AB56D0 /* URL+ResourceFork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+ResourceFork.swift"; sourceTree = ""; }; - 553741142E9372DF00AB56D0 /* URL+StructuredContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+StructuredContent.swift"; sourceTree = ""; }; - 553741152E9372DF00AB56D0 /* URL+SymLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+SymLink.swift"; sourceTree = ""; }; - 553741172E9372DF00AB56D0 /* NSBox+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSBox+Helper.swift"; sourceTree = ""; }; - 553741192E9372DF00AB56D0 /* NSButton+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSButton+Helper.swift"; sourceTree = ""; }; - 5537411A2E9372DF00AB56D0 /* NSPopUpButton+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSPopUpButton+Helper.swift"; sourceTree = ""; }; - 5537411C2E9372DF00AB56D0 /* NSProgressIndicator+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSProgressIndicator+Helper.swift"; sourceTree = ""; }; - 5537411E2E9372DF00AB56D0 /* NSTextField+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextField+Helper.swift"; sourceTree = ""; }; - 553741202E9372DF00AB56D0 /* NSTextView+Style.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextView+Style.swift"; sourceTree = ""; }; - 553741232E9372DF00AB56D0 /* NSWorkspace+Finder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWorkspace+Finder.swift"; sourceTree = ""; }; - 553741262E9372DF00AB56D0 /* SecureBookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBookmark.swift; sourceTree = ""; }; - 553741282E9372DF00AB56D0 /* EncodingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodingError.swift; sourceTree = ""; }; - 5537412A2E9372DF00AB56D0 /* NSEvent+VirtualKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSEvent+VirtualKeys.swift"; sourceTree = ""; }; - 5537412C2E9372DF00AB56D0 /* FileSizeFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSizeFormatter.swift; sourceTree = ""; }; - 5537412D2E9372DF00AB56D0 /* IntegerFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerFormatter.swift; sourceTree = ""; }; - 5537412F2E9372DF00AB56D0 /* IconUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconUtils.swift; sourceTree = ""; }; - 553741302E9372DF00AB56D0 /* NoodleCustomImageRep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoodleCustomImageRep.swift; sourceTree = ""; }; - 553741322E9372DF00AB56D0 /* BufferedInputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BufferedInputStream.swift; sourceTree = ""; }; - 553741332E9372DF00AB56D0 /* CompareUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompareUtil.swift; sourceTree = ""; }; - 553741342E9372DF00AB56D0 /* FileError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileError.swift; sourceTree = ""; }; - 553741352E9372DF00AB56D0 /* PowerAssertion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerAssertion.swift; sourceTree = ""; }; - 553741372E9372DF00AB56D0 /* AttributedMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedMenuItem.swift; sourceTree = ""; }; - 553741392E9372DF00AB56D0 /* OpenEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenEditor.swift; sourceTree = ""; }; - 5537413A2E9372DF00AB56D0 /* OpenEditorAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenEditorAttribute.swift; sourceTree = ""; }; - 5537413B2E9372DF00AB56D0 /* OpenEditorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenEditorError.swift; sourceTree = ""; }; - 5537413D2E9372DF00AB56D0 /* PreferredEditorPopupCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferredEditorPopupCell.swift; sourceTree = ""; }; - 5537413F2E9372DF00AB56D0 /* VisibleWhitespaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleWhitespaces.swift; sourceTree = ""; }; - 553741412E9372DF00AB56D0 /* DescriptionOutlineNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionOutlineNode.swift; sourceTree = ""; }; - 553741422E9372DF00AB56D0 /* TableViewCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCommon.swift; sourceTree = ""; }; - 553741432E9372DF00AB56D0 /* TableViewContextMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewContextMenuDelegate.swift; sourceTree = ""; }; - 553741452E9372DF00AB56D0 /* NSTextFieldCell+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextFieldCell+Helper.swift"; sourceTree = ""; }; - 553741462E9372DF00AB56D0 /* RSVerticallyCenteredTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSVerticallyCenteredTextFieldCell.swift; sourceTree = ""; }; - 553741482E9372DF00AB56D0 /* CustomValidationToolbarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomValidationToolbarItem.swift; sourceTree = ""; }; - 5537414A2E9372DF00AB56D0 /* ActionBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBarView.swift; sourceTree = ""; }; - 5537414C2E9372DF00AB56D0 /* LinkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkButton.swift; sourceTree = ""; }; - 5537414D2E9372DF00AB56D0 /* StandardButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardButtons.swift; sourceTree = ""; }; - 5537414F2E9372DF00AB56D0 /* ConsoleToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleToolbarView.swift; sourceTree = ""; }; - 553741502E9372DF00AB56D0 /* ConsoleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleView.swift; sourceTree = ""; }; - 553741522E9372DF00AB56D0 /* DualPaneSplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualPaneSplitView.swift; sourceTree = ""; }; - 553741532E9372DF00AB56D0 /* DualPaneSplitViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualPaneSplitViewDelegate.swift; sourceTree = ""; }; - 553741552E9372DF00AB56D0 /* FileDropView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDropView.swift; sourceTree = ""; }; - 553741562E9372DF00AB56D0 /* FileDropView+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileDropView+Helper.swift"; sourceTree = ""; }; - 553741582E9372DF00AB56D0 /* ProgressBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = ""; }; - 5537415A2E9372DF00AB56D0 /* SynchroScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchroScrollView.swift; sourceTree = ""; }; - 5537415C2E9372DF00AB56D0 /* TextFieldSelectionHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldSelectionHolder.swift; sourceTree = ""; }; - 5537415F2E9372DF00AB56D0 /* WindowCancelOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowCancelOperation.swift; sourceTree = ""; }; - 553741602E9372DF00AB56D0 /* WindowOSD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowOSD.swift; sourceTree = ""; }; - 553741642E9372DF00AB56D0 /* AlignPopupButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignPopupButtonCell.swift; sourceTree = ""; }; - 553741652E9372DF00AB56D0 /* ComparatorPopUpButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparatorPopUpButtonCell.swift; sourceTree = ""; }; - 553741662E9372DF00AB56D0 /* DisplayFiltersPopUpButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayFiltersPopUpButtonCell.swift; sourceTree = ""; }; - 553741672E9372DF00AB56D0 /* FilePathTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePathTableCellView.swift; sourceTree = ""; }; - 553741692E9372DF00AB56D0 /* DiffCountersItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffCountersItem.swift; sourceTree = ""; }; - 5537416A2E9372DF00AB56D0 /* DiffCountersTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffCountersTextFieldCell.swift; sourceTree = ""; }; - 5537416B2E9372DF00AB56D0 /* DifferenceCounters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DifferenceCounters.swift; sourceTree = ""; }; - 5537416D2E9372DF00AB56D0 /* FilePathTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePathTextField.swift; sourceTree = ""; }; - 5537416E2E9372DF00AB56D0 /* FindText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindText.swift; sourceTree = ""; }; - 5537416F2E9372DF00AB56D0 /* NSTextField+CenterVertically.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextField+CenterVertically.swift"; sourceTree = ""; }; - 553741702E9372DF00AB56D0 /* PathControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathControl.swift; sourceTree = ""; }; - 553741712E9372DF00AB56D0 /* PathView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathView.swift; sourceTree = ""; }; - 553741722E9372DF00AB56D0 /* PopUpButtonUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpButtonUrl.swift; sourceTree = ""; }; - 553741732E9372DF00AB56D0 /* TablePanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TablePanelView.swift; sourceTree = ""; }; - 553741742E9372DF00AB56D0 /* TimeToleranceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeToleranceView.swift; sourceTree = ""; }; - 5537434A2E937B7F00AB56D0 /* colors.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = colors.json; path = AppDefaults/colors.json; sourceTree = ""; }; - 5537434B2E937B7F00AB56D0 /* colorsDark.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = colorsDark.json; path = AppDefaults/colorsDark.json; sourceTree = ""; }; - 5537434C2E937B7F00AB56D0 /* VDDefaults.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = VDDefaults.plist; path = AppDefaults/VDDefaults.plist; sourceTree = ""; }; - 5537434D2E937B7F00AB56D0 /* VisualDiffer.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = VisualDiffer.sdef; path = AppDefaults/VisualDiffer.sdef; sourceTree = ""; }; - 553A58692EF2758800685D02 /* MyDocument1.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MyDocument1.4.xcdatamodel; sourceTree = ""; }; - 553D10D8163BD0BC008D3F65 /* prefs_confirmations.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = prefs_confirmations.png; sourceTree = ""; }; - 5542D19516317DAC00F801AA /* vd.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = vd.iconset; path = images/vd.iconset; sourceTree = ""; }; - 55450B162ED4790B0048619F /* TextPreferencesPanel+PreferencesBoxDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TextPreferencesPanel+PreferencesBoxDataSource.swift"; sourceTree = ""; }; - 55450B182ED47B680048619F /* FileSessionPreferencesWindow+PreferencesBoxDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileSessionPreferencesWindow+PreferencesBoxDataSource.swift"; sourceTree = ""; }; - 554B4A3A2E9B7B8F0023AAF0 /* EndOfLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndOfLine.swift; sourceTree = ""; }; - 554B4A3B2E9B7B8F0023AAF0 /* DiffResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffResult.swift; sourceTree = ""; }; - 554B4A3D2E9B7B8F0023AAF0 /* DiffResult+Dump.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffResult+Dump.swift"; sourceTree = ""; }; - 554B4A3E2E9B7B8F0023AAF0 /* DiffResult+Lines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffResult+Lines.swift"; sourceTree = ""; }; - 554B4A3F2E9B7B8F0023AAF0 /* DiffResult+Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffResult+Section.swift"; sourceTree = ""; }; - 554B4A402E9B7B8F0023AAF0 /* DiffSide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffSide.swift; sourceTree = ""; }; - 554B4A412E9B7B8F0023AAF0 /* DiffSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffSection.swift; sourceTree = ""; }; - 554B4A432E9B7B8F0023AAF0 /* DiffLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffLine.swift; sourceTree = ""; }; - 554E0EA0163EFD51001418EA /* prefs_confirmations@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "prefs_confirmations@2x.png"; sourceTree = ""; }; - 554E0EA1163EFD51001418EA /* prefs_paths@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "prefs_paths@2x.png"; sourceTree = ""; }; - 554E0EA2163EFD51001418EA /* prefs_text@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "prefs_text@2x.png"; sourceTree = ""; }; - 554E0EBA163EFD6A001418EA /* dropzone@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "dropzone@2x.png"; sourceTree = ""; }; - 554E0EBB163EFD6A001418EA /* rewind@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "rewind@2x.png"; sourceTree = ""; }; - 5550B5921A21CD1F009BC4A4 /* aliasbadge@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "aliasbadge@2x.png"; sourceTree = ""; }; - 5550B5941A21D2F3009BC4A4 /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "empty@2x.png"; sourceTree = ""; }; - 555392601889732900D26258 /* deleteRed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = deleteRed.png; sourceTree = ""; }; - 555392611889732900D26258 /* deleteRed@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "deleteRed@2x.png"; sourceTree = ""; }; - 555CAD9B1C2710D000371E4D /* Warnings.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 5560823D12AD0EF8003DC79D /* folder-999-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-999-open.png"; sourceTree = ""; }; - 5560823E12AD0EF8003DC79D /* folder-999.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder-999.png"; sourceTree = ""; }; - 55656A852DD34C5100DD5835 /* VisualDiffer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VisualDiffer-Bridging-Header.h"; sourceTree = ""; }; - 557315B02551307800CF4372 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = assets/Images.xcassets; sourceTree = SOURCE_ROOT; }; - 55740BF412D710EC004BF6CC /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; - 55740BF812D71103004BF6CC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - 557BBB0D2ED1BBB00016CBD4 /* DiffLineComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffLineComponent.swift; sourceTree = ""; }; - 557C18162E7E6E0100381A3A /* big_file_5000_lines.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = big_file_5000_lines.txt; sourceTree = ""; }; - 557C181B2E7E8BDD00381A3A /* Signing.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Signing.local.xcconfig; sourceTree = ""; }; - 557C181C2E7E8BDD00381A3A /* Versions.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Versions.local.xcconfig; sourceTree = ""; }; - 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppConfig.xcconfig; sourceTree = ""; }; - 557C18212E7E8C0D00381A3A /* Signing.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Signing.local.xcconfig; sourceTree = ""; }; - 557C18222E7E8C0D00381A3A /* Versions.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Versions.local.xcconfig; sourceTree = ""; }; - 557EC74112CC7A8700B43F57 /* rewind.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rewind.png; sourceTree = ""; }; - 5580A6E32E5D7B19008D3D3A /* NoSandbox.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NoSandbox.entitlements; sourceTree = ""; }; - 558DEAC42EA263DE00F05A10 /* CFStringEncoding+StringEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CFStringEncoding+StringEncoding.swift"; sourceTree = ""; }; - 558DEAC72EA263F500F05A10 /* EncodingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodingManager.swift; sourceTree = ""; }; - 558DEAC82EA263F500F05A10 /* EncodingPopUpButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodingPopUpButtonCell.swift; sourceTree = ""; }; - 558DEAC92EA263F500F05A10 /* SelectEncodingsPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectEncodingsPanel.swift; sourceTree = ""; }; - 558DEACE2EA2642100F05A10 /* NSWindow+Editing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+Editing.swift"; sourceTree = ""; }; - 558DEAD12EA26C5800F05A10 /* Logger+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+App.swift"; sourceTree = ""; }; - 5590B2FE15B0449F00B0E047 /* prefs_paths.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = prefs_paths.png; sourceTree = ""; }; - 55916A0B2E8A814200ED6629 /* History.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = History.xcdatamodel; sourceTree = ""; }; - 55916A0C2E8A814200ED6629 /* MyDocument1.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MyDocument1.0.xcdatamodel; sourceTree = ""; }; - 55916A0D2E8A814200ED6629 /* MyDocument1.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MyDocument1.1.xcdatamodel; sourceTree = ""; }; - 55916A0E2E8A814200ED6629 /* MyDocument1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MyDocument1.2.xcdatamodel; sourceTree = ""; }; - 55916A0F2E8A814200ED6629 /* MyDocument1.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MyDocument1.3.xcdatamodel; sourceTree = ""; }; - 559186402E9B821B00A30620 /* DiffSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffSummary.swift; sourceTree = ""; }; - 55944CC72E5C8D9F00FB1F8C /* VisualDifferTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VisualDifferTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 5595901C2EABA7FE00B5AB0B /* CompareSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompareSummary.swift; sourceTree = ""; }; - 5595901E2EABA8A100B5AB0B /* CompareChangeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompareChangeType.swift; sourceTree = ""; }; - 559590202EABAFC200B5AB0B /* SessionDiff+ItemComparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionDiff+ItemComparator.swift"; sourceTree = ""; }; - 559590222EABB06100B5AB0B /* ItemComparator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemComparator.swift; sourceTree = ""; }; - 559590232EABB06100B5AB0B /* ItemComparator+Align.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ItemComparator+Align.swift"; sourceTree = ""; }; - 559590242EABB06100B5AB0B /* ItemComparator+AlignRegularExpression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ItemComparator+AlignRegularExpression.swift"; sourceTree = ""; }; - 559590252EABB06100B5AB0B /* ItemComparator+Compare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ItemComparator+Compare.swift"; sourceTree = ""; }; - 5595902A2EABB15F00B5AB0B /* MockItemComparatorDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockItemComparatorDelegate.swift; sourceTree = ""; }; - 559614B014B0471E0075ED34 /* aliasbadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = aliasbadge.png; sourceTree = ""; }; - 559E927D1514A63100B76349 /* dropzone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dropzone.png; sourceTree = ""; }; - 559EA70D14FA1D8F00BC9B82 /* VisualDifferHelp */ = {isa = PBXFileReference; lastKnownFileType = folder; path = VisualDifferHelp; sourceTree = ""; }; - 55A027D3158242310004583F /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; - 55A4DF312E9A49A700ED11BB /* EndOfLineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndOfLineTests.swift; sourceTree = ""; }; - 55A67B5113FF95B5006DD5BB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - 55B1CE2A2E9934E900426FEC /* DateBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateBuilder.swift; sourceTree = ""; }; - 55B1CE2C2E9934F800426FEC /* DiffResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffResultTests.swift; sourceTree = ""; }; - 55B1CE2E2E9934F800426FEC /* DiffResultBaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffResultBaseTests.swift; sourceTree = ""; }; - 55B212092490AB3B001717DD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; - 55B2120A2490AB3B001717DD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 55B2120B2490AB3B001717DD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 55B2120C2490AB3B001717DD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; - 55B675FE18956D90000D5A0D /* prefs_keyboard.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = prefs_keyboard.png; sourceTree = ""; }; - 55B675FF18956D90000D5A0D /* prefs_keyboard@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "prefs_keyboard@2x.png"; sourceTree = ""; }; - 55BC333D2EDAC69C004035A0 /* RowHeightCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowHeightCalculator.swift; sourceTree = ""; }; - 55BC333F2EDAC96F004035A0 /* TiledImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TiledImageView.swift; sourceTree = ""; }; - 55BC33412EDACB09004035A0 /* FilesWindowController+RowHeightDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+RowHeightDataSource.swift"; sourceTree = ""; }; - 55BC33422EDACB09004035A0 /* FilesWindowController+SplitViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FilesWindowController+SplitViewDelegate.swift"; sourceTree = ""; }; - 55C1CBE22E8BB6A2004FE322 /* visdiff */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = visdiff; sourceTree = BUILT_PRODUCTS_DIR; }; - 55C1CBF32E8BB6AD004FE322 /* AppConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppConfig.xcconfig; sourceTree = ""; }; - 55C1CBF42E8BB6AD004FE322 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; - 55C1CBF52E8BB6AD004FE322 /* Signing.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Signing.local.xcconfig; sourceTree = ""; }; - 55C1CBF82E8BB6AD004FE322 /* Versions.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Versions.local.xcconfig; sourceTree = ""; }; - 55C1CBF92E8BB6AD004FE322 /* visdiff.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = visdiff.1; sourceTree = ""; }; - 55C1CBFA2E8BB6AD004FE322 /* visdiff.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = visdiff.entitlements; sourceTree = ""; }; - 55C1CBFB2E8BB6AD004FE322 /* visdiff-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "visdiff-Info.plist"; sourceTree = ""; }; - 55C1CC062E8BBAD1004FE322 /* DocumentWaiter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentWaiter.swift; sourceTree = ""; }; - 55C84A702DA93ADB00BA39DA /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; - 55D26E84140E2EAD00646F50 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - 55D352E62EFD0C4D00C944D5 /* FileSystemTestHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemTestHelper.swift; sourceTree = ""; }; - 55D71FD52EE584B7007E0FA0 /* AppUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdater.swift; sourceTree = ""; }; - 55D71FD72EE584FF007E0FA0 /* AppConfig-Sparkle.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "AppConfig-Sparkle.xcconfig"; sourceTree = ""; }; - 55D71FD82EE584FF007E0FA0 /* VisualDiffer-Sparkle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "VisualDiffer-Sparkle.entitlements"; sourceTree = ""; }; - 55D71FD92EE584FF007E0FA0 /* VisualDiffer-Sparkle-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VisualDiffer-Sparkle-Info.plist"; sourceTree = ""; }; - 55DF3DA92E924BB10044CC0C /* BaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTests.swift; sourceTree = ""; }; - 55DF3DAA2E924BB10044CC0C /* BaseTests+AssertCompareItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTests+AssertCompareItem.swift"; sourceTree = ""; }; - 55DF3DAC2E924BB10044CC0C /* MockFileOperationManagerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFileOperationManagerDelegate.swift; sourceTree = ""; }; - 55DF3DAD2E924BB10044CC0C /* MockFolderReaderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFolderReaderDelegate.swift; sourceTree = ""; }; - 55DF3DB02E924BB10044CC0C /* LabelsCopyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsCopyTests.swift; sourceTree = ""; }; - 55DF3DB12E924BB10044CC0C /* LabelsCreateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsCreateTests.swift; sourceTree = ""; }; - 55DF3DB22E924BB10044CC0C /* LabelsMoveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsMoveTests.swift; sourceTree = ""; }; - 55DF3DB32E924BB10044CC0C /* TagsCopyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsCopyTests.swift; sourceTree = ""; }; - 55DF3DB42E924BB10044CC0C /* TagsCreateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsCreateTests.swift; sourceTree = ""; }; - 55DF3DB52E924BB10044CC0C /* TagsDeleteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsDeleteTests.swift; sourceTree = ""; }; - 55DF3DB62E924BB10044CC0C /* TagsMoveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsMoveTests.swift; sourceTree = ""; }; - 55DF3DB82E924BB10044CC0C /* AlignmentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignmentTests.swift; sourceTree = ""; }; - 55DF3DB92E924BB10044CC0C /* BufferedInputStreamTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BufferedInputStreamTests.swift; sourceTree = ""; }; - 55DF3DBA2E924BB10044CC0C /* ComparatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparatorTests.swift; sourceTree = ""; }; - 55DF3DBB2E924BB10044CC0C /* CopyFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyFilesTests.swift; sourceTree = ""; }; - 55DF3DBC2E924BB10044CC0C /* DeleteFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteFileTests.swift; sourceTree = ""; }; - 55DF3DBD2E924BB10044CC0C /* DisplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayTests.swift; sourceTree = ""; }; - 55DF3DBE2E924BB10044CC0C /* EmptyFolderColorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyFolderColorTests.swift; sourceTree = ""; }; - 55DF3DBF2E924BB10044CC0C /* FiltersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersTests.swift; sourceTree = ""; }; - 55DF3DC02E924BB10044CC0C /* MoveFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveFilesTests.swift; sourceTree = ""; }; - 55DF3DC12E924BB10044CC0C /* RenameFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenameFilesTests.swift; sourceTree = ""; }; - 55DF3DC22E924BB10044CC0C /* TouchFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchFilesTests.swift; sourceTree = ""; }; - 55DF3DC42E924BB10044CC0C /* BaseTests+AssertFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTests+AssertFileSystem.swift"; sourceTree = ""; }; - 55DF3DC52E924BB10044CC0C /* BaseTests+FileSystemOperations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseTests+FileSystemOperations.swift"; sourceTree = ""; }; - 55DF3DC62E924BB10044CC0C /* CaseSensitiveBaseTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaseSensitiveBaseTest.swift; sourceTree = ""; }; - 55DF3DC82E924BB10044CC0C /* LeafPathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeafPathTests.swift; sourceTree = ""; }; - 55DF3DCA2E924BB10044CC0C /* FoldersWindowControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoldersWindowControllerTests.swift; sourceTree = ""; }; - 55DF3DCB2E924BB10044CC0C /* RefreshInfoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshInfoTests.swift; sourceTree = ""; }; - 55DF3DCE2E924BB10044CC0C /* StringUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtils.swift; sourceTree = ""; }; - 55E1029912C9E17300432C61 /* empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = empty.png; sourceTree = ""; }; - 55E31DFF1BC572F3001ACE4D /* top.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = top.png; sourceTree = ""; }; - 55E31E001BC572F3001ACE4D /* top@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "top@2x.png"; sourceTree = ""; }; - 55E31E031BC57373001ACE4D /* bottom.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bottom.png; sourceTree = ""; }; - 55E31E041BC57373001ACE4D /* bottom@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bottom@2x.png"; sourceTree = ""; }; - 55E381C02EEFFCF500CB2F00 /* SymbolicLinkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolicLinkTests.swift; sourceTree = ""; }; - 55E3A5641826552A002A890B /* lockedbadge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lockedbadge.png; sourceTree = ""; }; - 55E3A5651826552A002A890B /* lockedbadge@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "lockedbadge@2x.png"; sourceTree = ""; }; - 55E3EF3524C2D06800F7A95A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 55E78B57164FCE7800877721 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; - 55ECD8611476B03400D8BEB4 /* prefs_text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = prefs_text.png; sourceTree = ""; }; - 55ECD8671476B34600D8BEB4 /* VisualDiffer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = VisualDiffer.entitlements; sourceTree = ""; }; - 55F013FC27A90C46003EA2D4 /* AppConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppConfig.xcconfig; sourceTree = ""; }; - 55F9E7992EDD5DAD001218C7 /* DiffResult.Options.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiffResult.Options.swift; sourceTree = ""; }; - 55F9E79B2EDD6221001218C7 /* WhitespacesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhitespacesTests.swift; sourceTree = ""; }; - 55FC15F22557CD000075E30D /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = System/Library/Frameworks/ScriptingBridge.framework; sourceTree = SDKROOT; }; - 55FD7E25180C484500CF473C /* prefs_folder@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "prefs_folder@2x.png"; sourceTree = ""; }; - 55FE89492F054B260003864A /* FlagSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagSet.swift; sourceTree = ""; }; - 7788DA0506752A1600599AAD /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; - 8D15AC360486D014006FF6A4 /* VisualDiffer-Info.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.xml; fileEncoding = 4; path = "VisualDiffer-Info.plist"; sourceTree = ""; }; - 8D15AC370486D014006FF6A4 /* VisualDiffer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VisualDiffer.app; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 55944CC42E5C8D9F00FB1F8C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 55C1CBDF2E8BB6A2004FE322 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 8D15AC330486D014006FF6A4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 55C84A712DA93ADB00BA39DA /* UserNotifications.framework in Frameworks */, - 55A027D4158242310004583F /* Quartz.framework in Frameworks */, - 775DFF38067A968500C5B868 /* Cocoa.framework in Frameworks */, - 55C165622EE444050082A24C /* Sparkle in Frameworks */, - 55740BF512D710EC004BF6CC /* IOKit.framework in Frameworks */, - 55740BF912D71103004BF6CC /* Security.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 1058C7A6FEA54F5311CA2CBB /* Linked Frameworks */ = { - isa = PBXGroup; - children = ( - 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */, - ); - name = "Linked Frameworks"; - sourceTree = ""; - }; - 1058C7A8FEA54F5311CA2CBB /* Other Frameworks */ = { - isa = PBXGroup; - children = ( - 2A37F4C5FDCFA73011CA2CEA /* Foundation.framework */, - 7788DA0506752A1600599AAD /* CoreData.framework */, - 2A37F4C4FDCFA73011CA2CEA /* AppKit.framework */, - ); - name = "Other Frameworks"; - sourceTree = ""; - }; - 19C28FB0FE9D524F11CA2CBB /* Products */ = { - isa = PBXGroup; - children = ( - 8D15AC370486D014006FF6A4 /* VisualDiffer.app */, - 55944CC72E5C8D9F00FB1F8C /* VisualDifferTests.xctest */, - 55C1CBE22E8BB6A2004FE322 /* visdiff */, - ); - name = Products; - sourceTree = ""; - }; - 2A37F4AAFDCFA73011CA2CEA /* QuickDirtyDiff */ = { - isa = PBXGroup; - children = ( - 55D71FD72EE584FF007E0FA0 /* AppConfig-Sparkle.xcconfig */, - 55D71FD82EE584FF007E0FA0 /* VisualDiffer-Sparkle.entitlements */, - 55D71FD92EE584FF007E0FA0 /* VisualDiffer-Sparkle-Info.plist */, - 55F013FC27A90C46003EA2D4 /* AppConfig.xcconfig */, - 557C181B2E7E8BDD00381A3A /* Signing.local.xcconfig */, - 557C181C2E7E8BDD00381A3A /* Versions.local.xcconfig */, - 555CAD9B1C2710D000371E4D /* Warnings.xcconfig */, - 553741772E9372DF00AB56D0 /* Sources */, - 2A37F4C3FDCFA73011CA2CEA /* Frameworks */, - 55916A0A2E8A814200ED6629 /* Models */, - 2A37F4AFFDCFA73011CA2CEA /* Other Sources */, - 19C28FB0FE9D524F11CA2CBB /* Products */, - 2A37F4B8FDCFA73011CA2CEA /* Resources */, - 55944CDA2E5C8E5B00FB1F8C /* Tests */, - 55C1CBFC2E8BB6AD004FE322 /* visdiff */, - 55656A852DD34C5100DD5835 /* VisualDiffer-Bridging-Header.h */, - ); - name = QuickDirtyDiff; - sourceTree = ""; - }; - 2A37F4AFFDCFA73011CA2CEA /* Other Sources */ = { - isa = PBXGroup; - children = ( - 32DBCF750370BD2300C91783 /* VisualDiffer_Prefix.pch */, - ); - name = "Other Sources"; - sourceTree = ""; - }; - 2A37F4B8FDCFA73011CA2CEA /* Resources */ = { - isa = PBXGroup; - children = ( - 5537434A2E937B7F00AB56D0 /* colors.json */, - 5537434B2E937B7F00AB56D0 /* colorsDark.json */, - 5537434C2E937B7F00AB56D0 /* VDDefaults.plist */, - 5537434D2E937B7F00AB56D0 /* VisualDiffer.sdef */, - 2A37F4B9FDCFA73011CA2CEA /* Credits.rtf */, - 55157D5C12898F8800826FF8 /* images */, - 089C165FFE840EACC02AAC07 /* InfoPlist.strings */, - 55F6B0641AEA99680096E55E /* Localizable.strings */, - 55F6B0611AEA93E30096E55E /* Localizable.stringsdict */, - 2F7446A70DB6BCF400F9684A /* MainMenu.xib */, - 5542D19516317DAC00F801AA /* vd.iconset */, - 8D15AC360486D014006FF6A4 /* VisualDiffer-Info.plist */, - 55ECD8671476B34600D8BEB4 /* VisualDiffer.entitlements */, - 559EA70D14FA1D8F00BC9B82 /* VisualDifferHelp */, - ); - name = Resources; - sourceTree = ""; - }; - 2A37F4C3FDCFA73011CA2CEA /* Frameworks */ = { - isa = PBXGroup; - children = ( - 55C84A702DA93ADB00BA39DA /* UserNotifications.framework */, - 55FC15F22557CD000075E30D /* ScriptingBridge.framework */, - 55D26E84140E2EAD00646F50 /* AppKit.framework */, - 550A2CC113FF973B00981A9B /* ApplicationServices.framework */, - 55A67B5113FF95B5006DD5BB /* Foundation.framework */, - 55740BF412D710EC004BF6CC /* IOKit.framework */, - 1058C7A6FEA54F5311CA2CBB /* Linked Frameworks */, - 55E78B57164FCE7800877721 /* Cocoa.framework */, - 1058C7A8FEA54F5311CA2CBB /* Other Frameworks */, - 55A027D3158242310004583F /* Quartz.framework */, - 55740BF812D71103004BF6CC /* Security.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 55157D5C12898F8800826FF8 /* images */ = { - isa = PBXGroup; - children = ( - 557315B02551307800CF4372 /* Images.xcassets */, - 559614B014B0471E0075ED34 /* aliasbadge.png */, - 5550B5921A21CD1F009BC4A4 /* aliasbadge@2x.png */, - 55E31E031BC57373001ACE4D /* bottom.png */, - 55E31E041BC57373001ACE4D /* bottom@2x.png */, - 559E927D1514A63100B76349 /* dropzone.png */, - 554E0EBA163EFD6A001418EA /* dropzone@2x.png */, - 55E1029912C9E17300432C61 /* empty.png */, - 5550B5941A21D2F3009BC4A4 /* empty@2x.png */, - 55157D5D12898F8800826FF8 /* folder */, - 55E3A5641826552A002A890B /* lockedbadge.png */, - 55E3A5651826552A002A890B /* lockedbadge@2x.png */, - 557EC74112CC7A8700B43F57 /* rewind.png */, - 554E0EBB163EFD6A001418EA /* rewind@2x.png */, - 552D7032129AC0050013D012 /* toolbar */, - 55E31DFF1BC572F3001ACE4D /* top.png */, - 55E31E001BC572F3001ACE4D /* top@2x.png */, - ); - path = images; - sourceTree = ""; - }; - 55157D5D12898F8800826FF8 /* folder */ = { - isa = PBXGroup; - children = ( - 55157D6012898F8800826FF8 /* folder-000-open.png */, - 551BF0C81643B91B009CC9D1 /* folder-000-open@2x.png */, - 55157D6112898F8800826FF8 /* folder-000.png */, - 551BF0C91643B91B009CC9D1 /* folder-000@2x.png */, - 55157D6212898F8800826FF8 /* folder-001-open.png */, - 551BF0CA1643B91B009CC9D1 /* folder-001-open@2x.png */, - 55157D6312898F8800826FF8 /* folder-001.png */, - 551BF0CB1643B91B009CC9D1 /* folder-001@2x.png */, - 55157D6412898F8800826FF8 /* folder-010-open.png */, - 551BF0CC1643B91B009CC9D1 /* folder-010-open@2x.png */, - 55157D6512898F8800826FF8 /* folder-010.png */, - 551BF0CD1643B91B009CC9D1 /* folder-010@2x.png */, - 55157D6612898F8800826FF8 /* folder-011-open.png */, - 551BF0CE1643B91B009CC9D1 /* folder-011-open@2x.png */, - 55157D6712898F8800826FF8 /* folder-011.png */, - 551BF0CF1643B91B009CC9D1 /* folder-011@2x.png */, - 55157D6812898F8800826FF8 /* folder-100-open.png */, - 551BF0D01643B91B009CC9D1 /* folder-100-open@2x.png */, - 55157D6912898F8800826FF8 /* folder-100.png */, - 551BF0D11643B91B009CC9D1 /* folder-100@2x.png */, - 55157D6A12898F8800826FF8 /* folder-101-open.png */, - 551BF0D21643B91B009CC9D1 /* folder-101-open@2x.png */, - 55157D6B12898F8800826FF8 /* folder-101.png */, - 551BF0D31643B91B009CC9D1 /* folder-101@2x.png */, - 55157D6C12898F8800826FF8 /* folder-110-open.png */, - 551BF0D41643B91B009CC9D1 /* folder-110-open@2x.png */, - 55157D6D12898F8800826FF8 /* folder-110.png */, - 551BF0D51643B91B009CC9D1 /* folder-110@2x.png */, - 55157D6E12898F8800826FF8 /* folder-111-open.png */, - 551BF0D61643B91B009CC9D1 /* folder-111-open@2x.png */, - 55157D6F12898F8800826FF8 /* folder-111.png */, - 551BF0D71643B91B009CC9D1 /* folder-111@2x.png */, - 5560823D12AD0EF8003DC79D /* folder-999-open.png */, - 551BF0D81643B91B009CC9D1 /* folder-999-open@2x.png */, - 5560823E12AD0EF8003DC79D /* folder-999.png */, - 551BF0D91643B91B009CC9D1 /* folder-999@2x.png */, - 5532516A2765F06B0092D65F /* mask-back-white.png */, - 553251672765F06B0092D65F /* mask-back.png */, - 5532516B2765F06B0092D65F /* mask-front.png */, - 553251682765F06B0092D65F /* mask-full.png */, - 553251692765F06B0092D65F /* mask-middle.png */, - ); - path = folder; - sourceTree = ""; - }; - 551DE3A52ED4237B0067AB18 /* SessionPreferences */ = { - isa = PBXGroup; - children = ( - 551DE3A92ED428A50067AB18 /* FilePreferences.swift */, - 551DE3A42ED4237B0067AB18 /* FileSessionPreferencesWindow.swift */, - 55450B182ED47B680048619F /* FileSessionPreferencesWindow+PreferencesBoxDataSource.swift */, - 551DE3B12ED42AB20067AB18 /* Panels */, - ); - path = SessionPreferences; - sourceTree = ""; - }; - 551DE3B02ED42AB20067AB18 /* Comparison */ = { - isa = PBXGroup; - children = ( - 551DE3B32ED42AD00067AB18 /* FilePreferencesComparisonPanel.swift */, - ); - path = Comparison; - sourceTree = ""; - }; - 551DE3B12ED42AB20067AB18 /* Panels */ = { - isa = PBXGroup; - children = ( - 551DE3B02ED42AB20067AB18 /* Comparison */, - ); - path = Panels; - sourceTree = ""; - }; - 552D7032129AC0050013D012 /* toolbar */ = { - isa = PBXGroup; - children = ( - 555392601889732900D26258 /* deleteRed.png */, - 555392611889732900D26258 /* deleteRed@2x.png */, - 553D10D8163BD0BC008D3F65 /* prefs_confirmations.png */, - 554E0EA0163EFD51001418EA /* prefs_confirmations@2x.png */, - 551DEDE0160477CA0014DF88 /* prefs_folder.png */, - 55FD7E25180C484500CF473C /* prefs_folder@2x.png */, - 55B675FE18956D90000D5A0D /* prefs_keyboard.png */, - 55B675FF18956D90000D5A0D /* prefs_keyboard@2x.png */, - 5590B2FE15B0449F00B0E047 /* prefs_paths.png */, - 554E0EA1163EFD51001418EA /* prefs_paths@2x.png */, - 55ECD8611476B03400D8BEB4 /* prefs_text.png */, - 554E0EA2163EFD51001418EA /* prefs_text@2x.png */, - ); - path = toolbar; - sourceTree = ""; - }; - 55373F8E2E9372DF00AB56D0 /* App */ = { - isa = PBXGroup; - children = ( - 55373F8D2E9372DF00AB56D0 /* AppDelegate.swift */, - 55D71FD52EE584B7007E0FA0 /* AppUpdater.swift */, - 558DEAD12EA26C5800F05A10 /* Logger+App.swift */, - ); - path = App; - sourceTree = ""; - }; - 55373F972E9372DF00AB56D0 /* Common */ = { - isa = PBXGroup; - children = ( - 55373F8F2E9372DF00AB56D0 /* AlignRule.swift */, - 55373F902E9372DF00AB56D0 /* ColorScheme.swift */, - 55373F912E9372DF00AB56D0 /* DisplayPositionable.swift */, - 55373F922E9372DF00AB56D0 /* ImageNames.swift */, - 55373F932E9372DF00AB56D0 /* KeyEquivalent.swift */, - 55373F942E9372DF00AB56D0 /* NotificationCenter+Helper.swift */, - 55373F952E9372DF00AB56D0 /* NSImage+SymbolCompat.swift */, - 55373F962E9372DF00AB56D0 /* ViewLinkable.swift */, - ); - path = Common; - sourceTree = ""; - }; - 55373F9E2E9372DF00AB56D0 /* CommonPrefs */ = { - isa = PBXGroup; - children = ( - 55373F982E9372DF00AB56D0 /* CommonPrefs.swift */, - 55373F992E9372DF00AB56D0 /* CommonPrefs+FileCompare.swift */, - 55373F9A2E9372DF00AB56D0 /* CommonPrefs+FolderCompare.swift */, - 55373F9B2E9372DF00AB56D0 /* CommonPrefs+Font.swift */, - 55373F9C2E9372DF00AB56D0 /* CommonPrefs+Helper.swift */, - 55373F9D2E9372DF00AB56D0 /* CommonPrefs+Name.swift */, - ); - path = CommonPrefs; - sourceTree = ""; - }; - 55373FA32E9372DF00AB56D0 /* Window */ = { - isa = PBXGroup; - children = ( - 55373F9F2E9372DF00AB56D0 /* DocumentWindow.swift */, - 55373FA02E9372DF00AB56D0 /* NSArrayController+Paths.swift */, - 55373FA12E9372DF00AB56D0 /* PathChooser.swift */, - 55373FA22E9372DF00AB56D0 /* RecentDocumentPopupMenu.swift */, - ); - path = Window; - sourceTree = ""; - }; - 55373FAA2E9372DF00AB56D0 /* Document */ = { - isa = PBXGroup; - children = ( - 55373FA32E9372DF00AB56D0 /* Window */, - 55373FA42E9372DF00AB56D0 /* DiffOpenerDelegate.swift */, - 55373FA52E9372DF00AB56D0 /* DocumentError.swift */, - 55373FA62E9372DF00AB56D0 /* SessionDiff.ItemType+Paths.swift */, - 55373FA72E9372DF00AB56D0 /* SessionTypeError.swift */, - 55373FA82E9372DF00AB56D0 /* VDDocument.swift */, - 55373FA92E9372DF00AB56D0 /* VDDocumentController.swift */, - ); - path = Document; - sourceTree = ""; - }; - 55373FAD2E9372DF00AB56D0 /* History */ = { - isa = PBXGroup; - children = ( - 55373FAB2E9372DF00AB56D0 /* HistoryEntity.swift */, - 55373FAC2E9372DF00AB56D0 /* HistorySessionManager.swift */, - ); - path = History; - sourceTree = ""; - }; - 55373FB42E9372DF00AB56D0 /* SessionDiff */ = { - isa = PBXGroup; - children = ( - 55373FAE2E9372DF00AB56D0 /* SessionDiff.swift */, - 55373FAF2E9372DF00AB56D0 /* SessionDiff+AlignRule.swift */, - 5536ADB92EF169820019EFBF /* SessionDiff+ExtraData.swift */, - 559590202EABAFC200B5AB0B /* SessionDiff+ItemComparator.swift */, - 55373FB12E9372DF00AB56D0 /* SessionDiff+NSSortDescriptor.swift */, - 55373FB22E9372DF00AB56D0 /* SessionDiff+ResolvePath.swift */, - 55373FB32E9372DF00AB56D0 /* SessionDiff+Types.swift */, - ); - path = SessionDiff; - sourceTree = ""; - }; - 55373FB52E9372DF00AB56D0 /* Core */ = { - isa = PBXGroup; - children = ( - 55373F972E9372DF00AB56D0 /* Common */, - 55373F9E2E9372DF00AB56D0 /* CommonPrefs */, - 55373FAA2E9372DF00AB56D0 /* Document */, - 55373FAD2E9372DF00AB56D0 /* History */, - 55373FB42E9372DF00AB56D0 /* SessionDiff */, - ); - path = Core; - sourceTree = ""; - }; - 55373FB92E9372DF00AB56D0 /* FilesTableView */ = { - isa = PBXGroup; - children = ( - 55373FB62E9372DF00AB56D0 /* FilesTableView.swift */, - 55373FB72E9372DF00AB56D0 /* FilesTableView+Menu.swift */, - 55373FB82E9372DF00AB56D0 /* FilesTableViewFindTextDelegate.swift */, - ); - path = FilesTableView; - sourceTree = ""; - }; - 55373FC42E9372DF00AB56D0 /* Components */ = { - isa = PBXGroup; - children = ( - 55373FBA2E9372DF00AB56D0 /* FileInfoBar.swift */, - 55373FBB2E9372DF00AB56D0 /* FilePanelView.swift */, - 55373FBC2E9372DF00AB56D0 /* FilePanelView+File.swift */, - 55373FBD2E9372DF00AB56D0 /* FilesScopeBar.swift */, - 55373FB92E9372DF00AB56D0 /* FilesTableView */, - 55373FBE2E9372DF00AB56D0 /* FileThumbnailView.swift */, - 55373FBF2E9372DF00AB56D0 /* LineNumberTableCellView.swift */, - 55373FC02E9372DF00AB56D0 /* LineNumberTableRowView.swift */, - 55373FC12E9372DF00AB56D0 /* NSAlert+DirtyFiles.swift */, - 55BC333D2EDAC69C004035A0 /* RowHeightCalculator.swift */, - 55373FC22E9372DF00AB56D0 /* SaveFileAccessoryView.swift */, - 55373FC32E9372DF00AB56D0 /* TextFieldVerticalCentered.swift */, - ); - path = Components; - sourceTree = ""; - }; - 55373FDA2E9372DF00AB56D0 /* Controller */ = { - isa = PBXGroup; - children = ( - 55373FC52E9372DF00AB56D0 /* FilesWindowController.swift */, - 55373FC62E9372DF00AB56D0 /* FilesWindowController+Clipboard.swift */, - 55373FC72E9372DF00AB56D0 /* FilesWindowController+Common.swift */, - 55373FC82E9372DF00AB56D0 /* FilesWindowController+Document.swift */, - 55373FC92E9372DF00AB56D0 /* FilesWindowController+FileInfoBarDelegate.swift */, - 55373FCA2E9372DF00AB56D0 /* FilesWindowController+FilesScopeBar.swift */, - 55373FCB2E9372DF00AB56D0 /* FilesWindowController+FilesTableViewContextMenu.swift */, - 55373FCC2E9372DF00AB56D0 /* FilesWindowController+JumpLine.swift */, - 55373FCD2E9372DF00AB56D0 /* FilesWindowController+LinesDetail.swift */, - 55373FCE2E9372DF00AB56D0 /* FilesWindowController+Menu.swift */, - 55373FCF2E9372DF00AB56D0 /* FilesWindowController+MenuDelegate.swift */, - 55373FD02E9372DF00AB56D0 /* FilesWindowController+Navigate.swift */, - 55373FD12E9372DF00AB56D0 /* FilesWindowController+NSToolbarDelegate.swift */, - 55373FD22E9372DF00AB56D0 /* FilesWindowController+NSWindowDelegate.swift */, - 55373FD32E9372DF00AB56D0 /* FilesWindowController+PathControlDelegate.swift */, - 55373FD42E9372DF00AB56D0 /* FilesWindowController+Read.swift */, - 55BC33412EDACB09004035A0 /* FilesWindowController+RowHeightDataSource.swift */, - 55373FD52E9372DF00AB56D0 /* FilesWindowController+Save.swift */, - 551DE39E2ED422820067AB18 /* FilesWindowController+SessionPreferences.swift */, - 55373FD62E9372DF00AB56D0 /* FilesWindowController+Slider.swift */, - 55BC33422EDACB09004035A0 /* FilesWindowController+SplitViewDelegate.swift */, - 55373FD72E9372DF00AB56D0 /* FilesWindowController+TableView.swift */, - 55373FD82E9372DF00AB56D0 /* FilesWindowController+UICreation.swift */, - 55373FD92E9372DF00AB56D0 /* FilesWindowController+UISetup.swift */, - ); - path = Controller; - sourceTree = ""; - }; - 55373FDF2E9372DF00AB56D0 /* DiffResult */ = { - isa = PBXGroup; - children = ( - 55373FDB2E9372DF00AB56D0 /* DiffLine+Color.swift */, - 55373FDD2E9372DF00AB56D0 /* DiffChangeType+Color.swift */, - ); - path = DiffResult; - sourceTree = ""; - }; - 55373FE22E9372DF00AB56D0 /* Extensions */ = { - isa = PBXGroup; - children = ( - 55373FDF2E9372DF00AB56D0 /* DiffResult */, - 55373FE02E9372DF00AB56D0 /* DiffCountersItem+DiffResult.swift */, - 55373FE12E9372DF00AB56D0 /* FilesTableView+EditorData.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - 55373FE42E9372DF00AB56D0 /* Windows */ = { - isa = PBXGroup; - children = ( - 551DE3A52ED4237B0067AB18 /* SessionPreferences */, - 55373FE32E9372DF00AB56D0 /* JumpToLineWindow.swift */, - ); - path = Windows; - sourceTree = ""; - }; - 55373FE52E9372DF00AB56D0 /* FilesCompare */ = { - isa = PBXGroup; - children = ( - 55373FC42E9372DF00AB56D0 /* Components */, - 55373FDA2E9372DF00AB56D0 /* Controller */, - 55373FE22E9372DF00AB56D0 /* Extensions */, - 554B4A442E9B7B8F0023AAF0 /* DiffResult */, - 55373FE42E9372DF00AB56D0 /* Windows */, - ); - path = FilesCompare; - sourceTree = ""; - }; - 55373FF32E9372DF00AB56D0 /* FoldersOutlineView */ = { - isa = PBXGroup; - children = ( - 55373FE62E9372DF00AB56D0 /* ColoredFoldersManager.swift */, - 55373FE72E9372DF00AB56D0 /* FoldersOutlineView.swift */, - 55373FE82E9372DF00AB56D0 /* FoldersOutlineView+Clipboard.swift */, - 55373FE92E9372DF00AB56D0 /* FoldersOutlineView+Columns.swift */, - 55373FEA2E9372DF00AB56D0 /* FoldersOutlineView+Enumerate.swift */, - 55373FEB2E9372DF00AB56D0 /* FoldersOutlineView+ExternalApp.swift */, - 55373FEC2E9372DF00AB56D0 /* FoldersOutlineView+FileCount.swift */, - 55373FED2E9372DF00AB56D0 /* FoldersOutlineView+Menu.swift */, - 55373FEE2E9372DF00AB56D0 /* FoldersOutlineView+VisibleItem.swift */, - 55373FEF2E9372DF00AB56D0 /* FoldersOutlineViewFindTextDelegate.swift */, - 55373FF02E9372DF00AB56D0 /* CompareItem+LeafPath.swift */, - 55373FF12E9372DF00AB56D0 /* CompareItem+NSTableCellView.swift */, - 55373FF22E9372DF00AB56D0 /* IconUtils+CompareItemIconUtils.swift */, - ); - path = FoldersOutlineView; - sourceTree = ""; - }; - 55373FF82E9372DF00AB56D0 /* Components */ = { - isa = PBXGroup; - children = ( - 55373FF32E9372DF00AB56D0 /* FoldersOutlineView */, - 55373FF42E9372DF00AB56D0 /* DisplayFiltersScopeBar.swift */, - 55373FF52E9372DF00AB56D0 /* FolderPanelView.swift */, - 55373FF62E9372DF00AB56D0 /* CompareItemTableCellView.swift */, - 55373FF72E9372DF00AB56D0 /* CompareItemTableRowView.swift */, - ); - path = Components; - sourceTree = ""; - }; - 55373FFA2E9372DF00AB56D0 /* Comparator */ = { - isa = PBXGroup; - children = ( - 55373FF92E9372DF00AB56D0 /* MainThreadComparatorDelegateBridge.swift */, - ); - path = Comparator; - sourceTree = ""; - }; - 55373FFC2E9372DF00AB56D0 /* Copy */ = { - isa = PBXGroup; - children = ( - 55373FFB2E9372DF00AB56D0 /* CopyFileOperationExecutor.swift */, - ); - path = Copy; - sourceTree = ""; - }; - 55373FFE2E9372DF00AB56D0 /* Delete */ = { - isa = PBXGroup; - children = ( - 55373FFD2E9372DF00AB56D0 /* DeleteFileOperationExecutor.swift */, - ); - path = Delete; - sourceTree = ""; - }; - 553740002E9372DF00AB56D0 /* Move */ = { - isa = PBXGroup; - children = ( - 55373FFF2E9372DF00AB56D0 /* MoveFileOperationExecutor.swift */, - ); - path = Move; - sourceTree = ""; - }; - 553740052E9372DF00AB56D0 /* ReplaceFile */ = { - isa = PBXGroup; - children = ( - 553740012E9372DF00AB56D0 /* ConfirmReplace.swift */, - 553740022E9372DF00AB56D0 /* NSAlert+ReplaceFile.swift */, - 553740032E9372DF00AB56D0 /* NSApplication.ModalResponse+ReplaceFile.swift */, - 553740042E9372DF00AB56D0 /* ReplaceFileAttributeKey.swift */, - ); - path = ReplaceFile; - sourceTree = ""; - }; - 5537400B2E9372DF00AB56D0 /* Progress */ = { - isa = PBXGroup; - children = ( - 553740052E9372DF00AB56D0 /* ReplaceFile */, - 553740062E9372DF00AB56D0 /* CompletionIndicator.swift */, - 553740072E9372DF00AB56D0 /* ErrorsView.swift */, - 553740082E9372DF00AB56D0 /* FileOperationManagerDelegateImpl.swift */, - 553740092E9372DF00AB56D0 /* ProgressIndicatorController.swift */, - 5537400A2E9372DF00AB56D0 /* ReplaceInfoView.swift */, - ); - path = Progress; - sourceTree = ""; - }; - 553740102E9372DF00AB56D0 /* Sync */ = { - isa = PBXGroup; - children = ( - 5537400C2E9372DF00AB56D0 /* SyncFileController.swift */, - 5537400D2E9372DF00AB56D0 /* SyncFileOperationExecutor.swift */, - 5537400E2E9372DF00AB56D0 /* SyncItemsInfo.swift */, - 5537400F2E9372DF00AB56D0 /* SyncOutlineView.swift */, - ); - path = Sync; - sourceTree = ""; - }; - 553740152E9372DF00AB56D0 /* Touch */ = { - isa = PBXGroup; - children = ( - 553740112E9372DF00AB56D0 /* DatePickersStackView.swift */, - 553740122E9372DF00AB56D0 /* TouchController.swift */, - 553740132E9372DF00AB56D0 /* TouchFileOperationExecutor.swift */, - 553740142E9372DF00AB56D0 /* TouchPickersStackView.swift */, - ); - path = Touch; - sourceTree = ""; - }; - 5537401A2E9372DF00AB56D0 /* FileSystemController */ = { - isa = PBXGroup; - children = ( - 55373FFC2E9372DF00AB56D0 /* Copy */, - 55373FFE2E9372DF00AB56D0 /* Delete */, - 553740162E9372DF00AB56D0 /* FileOperationExecutor.swift */, - 553740172E9372DF00AB56D0 /* FileSummaryView.swift */, - 553740182E9372DF00AB56D0 /* FileSystemController.swift */, - 55D352E62EFD0C4D00C944D5 /* FileSystemTestHelper.swift */, - 553740002E9372DF00AB56D0 /* Move */, - 553740192E9372DF00AB56D0 /* OperationSummaryView.swift */, - 5537400B2E9372DF00AB56D0 /* Progress */, - 553740102E9372DF00AB56D0 /* Sync */, - 553740152E9372DF00AB56D0 /* Touch */, - ); - path = FileSystemController; - sourceTree = ""; - }; - 5537401D2E9372DF00AB56D0 /* FolderReader */ = { - isa = PBXGroup; - children = ( - 5537401B2E9372DF00AB56D0 /* FolderReader+Detached.swift */, - 5537401C2E9372DF00AB56D0 /* MainThreadFolderReaderDelegateBridge.swift */, - ); - path = FolderReader; - sourceTree = ""; - }; - 553740352E9372DF00AB56D0 /* Controller */ = { - isa = PBXGroup; - children = ( - 55373FFA2E9372DF00AB56D0 /* Comparator */, - 5537401A2E9372DF00AB56D0 /* FileSystemController */, - 5537401D2E9372DF00AB56D0 /* FolderReader */, - 5537401E2E9372DF00AB56D0 /* FolderSelectionInfo.swift */, - 5537401F2E9372DF00AB56D0 /* FoldersWindowController.swift */, - 553740202E9372DF00AB56D0 /* FoldersWindowController+BaseFolder.swift */, - 553740212E9372DF00AB56D0 /* FoldersWindowController+Comparison.swift */, - 553740222E9372DF00AB56D0 /* FoldersWindowController+ConsoleViewDelegate.swift */, - 553740232E9372DF00AB56D0 /* FoldersWindowController+DisplayFiltersScopeBarDelegate.swift */, - 553740242E9372DF00AB56D0 /* FoldersWindowController+Document.swift */, - 553740252E9372DF00AB56D0 /* FoldersWindowController+Exclude.swift */, - 553740262E9372DF00AB56D0 /* FoldersWindowController+FileSystemControllerDelegate.swift */, - 553740272E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineView.swift */, - 553740282E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineViewContextMenu.swift */, - 553740292E9372DF00AB56D0 /* FoldersWindowController+Menu.swift */, - 5537402A2E9372DF00AB56D0 /* FoldersWindowController+MenuDelegate.swift */, - 5537402B2E9372DF00AB56D0 /* FoldersWindowController+Navigation.swift */, - 5537402C2E9372DF00AB56D0 /* FoldersWindowController+NSToolbarDelegate.swift */, - 5537402D2E9372DF00AB56D0 /* FoldersWindowController+NSWindowDelegate.swift */, - 5537402E2E9372DF00AB56D0 /* FoldersWindowController+PathControlDelegate.swift */, - 5537402F2E9372DF00AB56D0 /* FoldersWindowController+QLPreviewPanel.swift */, - 553740302E9372DF00AB56D0 /* FoldersWindowController+Select.swift */, - 553740312E9372DF00AB56D0 /* FoldersWindowController+SessionPreferences.swift */, - 553740322E9372DF00AB56D0 /* FoldersWindowController+UICreation.swift */, - 553740332E9372DF00AB56D0 /* FoldersWindowController+UISetup.swift */, - 553740342E9372DF00AB56D0 /* OutlineViewItemDelegate.swift */, - ); - path = Controller; - sourceTree = ""; - }; - 553740382E9372DF00AB56D0 /* Extensions */ = { - isa = PBXGroup; - children = ( - 553740362E9372DF00AB56D0 /* CommonPrefs+ConsoleLog.swift */, - 553740372E9372DF00AB56D0 /* DiffCountersItem+CompareItem.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - 553740452E9372DF00AB56D0 /* FileManager */ = { - isa = PBXGroup; - children = ( - 553740392E9372DF00AB56D0 /* CopyCompareItem.swift */, - 5537403A2E9372DF00AB56D0 /* DeleteCompareItem.swift */, - 5537403B2E9372DF00AB56D0 /* FileAttributes.swift */, - 5537403C2E9372DF00AB56D0 /* FileOperationManager.swift */, - 5537403D2E9372DF00AB56D0 /* FileOperationManager+FileOperationManagerAction.swift */, - 5537403E2E9372DF00AB56D0 /* FileOperationManager+Util.swift */, - 5537403F2E9372DF00AB56D0 /* FolderManagerError.swift */, - 553740402E9372DF00AB56D0 /* CompareItem+Metadata.swift */, - 553740412E9372DF00AB56D0 /* MoveCompareItem.swift */, - 553740422E9372DF00AB56D0 /* PathTimestamps.swift */, - 553740432E9372DF00AB56D0 /* RenameCompareItem.swift */, - 553740442E9372DF00AB56D0 /* TouchCompareItem.swift */, - ); - path = FileManager; - sourceTree = ""; - }; - 553740492E9372DF00AB56D0 /* ComparatorOptions */ = { - isa = PBXGroup; - children = ( - 553740462E9372DF00AB56D0 /* ComparatorOptions.swift */, - 553740472E9372DF00AB56D0 /* ComparatorOptions+Description.swift */, - 553740482E9372DF00AB56D0 /* ComparatorOptions+Helper.swift */, - ); - path = ComparatorOptions; - sourceTree = ""; - }; - 553740582E9372DF00AB56D0 /* CompareItem */ = { - isa = PBXGroup; - children = ( - 553740492E9372DF00AB56D0 /* ComparatorOptions */, - 5595901E2EABA8A100B5AB0B /* CompareChangeType.swift */, - 5595901C2EABA7FE00B5AB0B /* CompareSummary.swift */, - 5537404B2E9372DF00AB56D0 /* DisplayOptions.swift */, - 5537404C2E9372DF00AB56D0 /* DisplayOptions+Helper.swift */, - 5537404E2E9372DF00AB56D0 /* FileExtraOptions.swift */, - 5537404F2E9372DF00AB56D0 /* CompareItem.swift */, - 553740502E9372DF00AB56D0 /* CompareItem+Accessors.swift */, - 553740512E9372DF00AB56D0 /* CompareItem+Clone.swift */, - 553740522E9372DF00AB56D0 /* CompareItem+Comparison.swift */, - 553740532E9372DF00AB56D0 /* CompareItem+Description.swift */, - 553740542E9372DF00AB56D0 /* CompareItem+FilterConfig.swift */, - 553740562E9372DF00AB56D0 /* CompareItem+Path.swift */, - 553740572E9372DF00AB56D0 /* CompareItem+VisibleItem.swift */, - ); - path = CompareItem; - sourceTree = ""; - }; - 5537405C2E9372DF00AB56D0 /* Navigator */ = { - isa = PBXGroup; - children = ( - 553740592E9372DF00AB56D0 /* CommonPrefs+DifferenceNavigator.swift */, - 5537405A2E9372DF00AB56D0 /* FoldersOutlineView+DifferenceNavigator.swift */, - 5537405B2E9372DF00AB56D0 /* CompareItem+DifferenceNavigator.swift */, - ); - path = Navigator; - sourceTree = ""; - }; - 553740622E9372DF00AB56D0 /* FolderReader */ = { - isa = PBXGroup; - children = ( - 5537405D2E9372DF00AB56D0 /* FilterConfig.swift */, - 5537405E2E9372DF00AB56D0 /* FolderReader.swift */, - 5537405F2E9372DF00AB56D0 /* FolderReader+Log.swift */, - 553740602E9372DF00AB56D0 /* FolderReaderDelegate.swift */, - 553740612E9372DF00AB56D0 /* RefreshInfo.swift */, - ); - path = FolderReader; - sourceTree = ""; - }; - 553740672E9372DF00AB56D0 /* ItemComparator */ = { - isa = PBXGroup; - children = ( - 559590222EABB06100B5AB0B /* ItemComparator.swift */, - 559590232EABB06100B5AB0B /* ItemComparator+Align.swift */, - 559590242EABB06100B5AB0B /* ItemComparator+AlignRegularExpression.swift */, - 559590252EABB06100B5AB0B /* ItemComparator+Compare.swift */, - ); - path = ItemComparator; - sourceTree = ""; - }; - 553740682E9372DF00AB56D0 /* Services */ = { - isa = PBXGroup; - children = ( - 553740622E9372DF00AB56D0 /* FolderReader */, - 553740672E9372DF00AB56D0 /* ItemComparator */, - ); - path = Services; - sourceTree = ""; - }; - 5537406E2E9372DF00AB56D0 /* Validators */ = { - isa = PBXGroup; - children = ( - 553740692E9372DF00AB56D0 /* FolderSelectionInfo+CompareActionValidator.swift */, - 5537406A2E9372DF00AB56D0 /* FolderSelectionInfo+FileSystemActionValidator.swift */, - 5537406B2E9372DF00AB56D0 /* FolderSelectionInfo+FilterActionValidator.swift */, - 5537406C2E9372DF00AB56D0 /* FolderSelectionInfo+FolderActionValidator.swift */, - 5537406D2E9372DF00AB56D0 /* FolderSelectionInfo+ViewerActionValidator.swift */, - ); - path = Validators; - sourceTree = ""; - }; - 553740742E9372DF00AB56D0 /* VisibleItem */ = { - isa = PBXGroup; - children = ( - 5537406F2E9372DF00AB56D0 /* VisibleItem.swift */, - 553740702E9372DF00AB56D0 /* VisibleItem+Find.swift */, - 553740712E9372DF00AB56D0 /* VisibleItem+Log.swift */, - 553740722E9372DF00AB56D0 /* VisibleItem+QLPreviewItem.swift */, - 553740732E9372DF00AB56D0 /* VisibleItem+Sort.swift */, - ); - path = VisibleItem; - sourceTree = ""; - }; - 553740772E9372DF00AB56D0 /* FolderCompareInfoWindow */ = { - isa = PBXGroup; - children = ( - 553740752E9372DF00AB56D0 /* DescriptionOutlineNode+FolderCompareInfo.swift */, - 553740762E9372DF00AB56D0 /* FolderCompareInfoWindow.swift */, - ); - path = FolderCompareInfoWindow; - sourceTree = ""; - }; - 553740782E9372DF00AB56D0 /* Windows */ = { - isa = PBXGroup; - children = ( - 553740772E9372DF00AB56D0 /* FolderCompareInfoWindow */, - ); - path = Windows; - sourceTree = ""; - }; - 553740792E9372DF00AB56D0 /* FoldersCompare */ = { - isa = PBXGroup; - children = ( - 55373FF82E9372DF00AB56D0 /* Components */, - 553740352E9372DF00AB56D0 /* Controller */, - 553740382E9372DF00AB56D0 /* Extensions */, - 553740452E9372DF00AB56D0 /* FileManager */, - 553740582E9372DF00AB56D0 /* CompareItem */, - 5537405C2E9372DF00AB56D0 /* Navigator */, - 553740682E9372DF00AB56D0 /* Services */, - 5537406E2E9372DF00AB56D0 /* Validators */, - 553740742E9372DF00AB56D0 /* VisibleItem */, - 553740782E9372DF00AB56D0 /* Windows */, - ); - path = FoldersCompare; - sourceTree = ""; - }; - 5537407F2E9372DF00AB56D0 /* HistoryController */ = { - isa = PBXGroup; - children = ( - 5537407A2E9372DF00AB56D0 /* HistoryController.swift */, - 5537407B2E9372DF00AB56D0 /* HistoryEntityTableCellView.swift */, - 5537407C2E9372DF00AB56D0 /* HistoryFetchedResultsControllerDelegate.swift */, - 5537407D2E9372DF00AB56D0 /* HistorySearchField.swift */, - 5537407E2E9372DF00AB56D0 /* HistoryTableView.swift */, - ); - path = HistoryController; - sourceTree = ""; - }; - 553740852E9372DF00AB56D0 /* Box */ = { - isa = PBXGroup; - children = ( - 551DE3B72ED4399B0067AB18 /* PreferencesBoxDataSource+Default.swift */, - 553740802E9372DF00AB56D0 /* ComparisonStandardUserDataSource.swift */, - 553740812E9372DF00AB56D0 /* PreferencesBox.swift */, - 553740822E9372DF00AB56D0 /* PreferencesBoxDataSource.swift */, - 553740832E9372DF00AB56D0 /* PreferencesCheckbox.swift */, - 553740842E9372DF00AB56D0 /* StandardUserPreferencesBoxDataSource.swift */, - ); - path = Box; - sourceTree = ""; - }; - 553740882E9372DF00AB56D0 /* Controllers */ = { - isa = PBXGroup; - children = ( - 553740862E9372DF00AB56D0 /* BasePreferences.swift */, - 553740872E9372DF00AB56D0 /* Preferences.swift */, - ); - path = Controllers; - sourceTree = ""; - }; - 5537408D2E9372DF00AB56D0 /* Confirmations */ = { - isa = PBXGroup; - children = ( - 553740892E9372DF00AB56D0 /* ConfirmationsDocumentsBox.swift */, - 5537408A2E9372DF00AB56D0 /* ConfirmationsFilesBox.swift */, - 5537408B2E9372DF00AB56D0 /* ConfirmationsFoldersBox.swift */, - 5537408C2E9372DF00AB56D0 /* ConfirmationsPreferencesPanel.swift */, - ); - path = Confirmations; - sourceTree = ""; - }; - 553740922E9372DF00AB56D0 /* Folder */ = { - isa = PBXGroup; - children = ( - 5537408E2E9372DF00AB56D0 /* DifferenceNavigatorBox.swift */, - 5537408F2E9372DF00AB56D0 /* FolderPreferencesPanel.swift */, - 553740902E9372DF00AB56D0 /* FoldersTraversalBox.swift */, - 553740912E9372DF00AB56D0 /* FolderViewBox.swift */, - ); - path = Folder; - sourceTree = ""; - }; - 553740952E9372DF00AB56D0 /* Font */ = { - isa = PBXGroup; - children = ( - 553740932E9372DF00AB56D0 /* FontBox.swift */, - 553740942E9372DF00AB56D0 /* FontPreferencesPanel.swift */, - ); - path = Font; - sourceTree = ""; - }; - 5537409A2E9372DF00AB56D0 /* General */ = { - isa = PBXGroup; - children = ( - 553740962E9372DF00AB56D0 /* AppearanceBox.swift */, - 553740972E9372DF00AB56D0 /* FolderComparisonBox.swift */, - 553740982E9372DF00AB56D0 /* GeneralPreferencesPanel.swift */, - 553740992E9372DF00AB56D0 /* PreferredEditorBox.swift */, - ); - path = General; - sourceTree = ""; - }; - 5537409D2E9372DF00AB56D0 /* Keyboard */ = { - isa = PBXGroup; - children = ( - 5537409B2E9372DF00AB56D0 /* KeyboardDocumentBox.swift */, - 5537409C2E9372DF00AB56D0 /* KeyboardPreferencesPanel.swift */, - ); - path = Keyboard; - sourceTree = ""; - }; - 553740A02E9372DF00AB56D0 /* Text */ = { - isa = PBXGroup; - children = ( - 551DE3B52ED42B4E0067AB18 /* FileComparisonBox.swift */, - 5537409E2E9372DF00AB56D0 /* TextPreferencesPanel.swift */, - 55450B162ED4790B0048619F /* TextPreferencesPanel+PreferencesBoxDataSource.swift */, - 5537409F2E9372DF00AB56D0 /* VisualizationBox.swift */, - ); - path = Text; - sourceTree = ""; - }; - 553740A22E9372DF00AB56D0 /* TrustedPaths */ = { - isa = PBXGroup; - children = ( - 553740A12E9372DF00AB56D0 /* TrustedPathsPreferencesPanel.swift */, - ); - path = TrustedPaths; - sourceTree = ""; - }; - 553740A42E9372DF00AB56D0 /* Panels */ = { - isa = PBXGroup; - children = ( - 5537408D2E9372DF00AB56D0 /* Confirmations */, - 553740922E9372DF00AB56D0 /* Folder */, - 553740952E9372DF00AB56D0 /* Font */, - 5537409A2E9372DF00AB56D0 /* General */, - 5537409D2E9372DF00AB56D0 /* Keyboard */, - 553740A02E9372DF00AB56D0 /* Text */, - 553740A22E9372DF00AB56D0 /* TrustedPaths */, - 553740A32E9372DF00AB56D0 /* NSStackView+PreferencesPanel.swift */, - ); - path = Panels; - sourceTree = ""; - }; - 553740A52E9372DF00AB56D0 /* Main */ = { - isa = PBXGroup; - children = ( - 553740882E9372DF00AB56D0 /* Controllers */, - 553740A42E9372DF00AB56D0 /* Panels */, - ); - path = Main; - sourceTree = ""; - }; - 553740A82E9372DF00AB56D0 /* Controllers */ = { - isa = PBXGroup; - children = ( - 553740A62E9372DF00AB56D0 /* SessionPreferencesWindow.swift */, - 553740A72E9372DF00AB56D0 /* SessionPreferencesWindow+Data.swift */, - ); - path = Controllers; - sourceTree = ""; - }; - 553740B02E9372DF00AB56D0 /* AlignRule */ = { - isa = PBXGroup; - children = ( - 553740A92E9372DF00AB56D0 /* AlignmentPanel.swift */, - 553740AA2E9372DF00AB56D0 /* AlignRuleWindow.swift */, - 553740AB2E9372DF00AB56D0 /* AlignTestResultBox.swift */, - 553740AC2E9372DF00AB56D0 /* ExpressionBox.swift */, - 553740AD2E9372DF00AB56D0 /* ExpressionBox+Menu.swift */, - 553740AE2E9372DF00AB56D0 /* FileNameCaseBox.swift */, - 553740AF2E9372DF00AB56D0 /* UserDefinedRulesBox.swift */, - ); - path = AlignRule; - sourceTree = ""; - }; - 553740B22E9372DF00AB56D0 /* Comparison */ = { - isa = PBXGroup; - children = ( - 553740B12E9372DF00AB56D0 /* SessionPreferencesComparisonPanel.swift */, - ); - path = Comparison; - sourceTree = ""; - }; - 553740B62E9372DF00AB56D0 /* Filters */ = { - isa = PBXGroup; - children = ( - 553740B32E9372DF00AB56D0 /* FiltersPredicateEditor.swift */, - 553740B42E9372DF00AB56D0 /* SessionPreferencesFiltersBox.swift */, - 553740B52E9372DF00AB56D0 /* SessionPreferencesFiltersPanel.swift */, - ); - path = Filters; - sourceTree = ""; - }; - 553740B72E9372DF00AB56D0 /* Panels */ = { - isa = PBXGroup; - children = ( - 553740B02E9372DF00AB56D0 /* AlignRule */, - 553740B22E9372DF00AB56D0 /* Comparison */, - 553740B62E9372DF00AB56D0 /* Filters */, - ); - path = Panels; - sourceTree = ""; - }; - 553740B82E9372DF00AB56D0 /* Session */ = { - isa = PBXGroup; - children = ( - 553740A82E9372DF00AB56D0 /* Controllers */, - 553740B72E9372DF00AB56D0 /* Panels */, - ); - path = Session; - sourceTree = ""; - }; - 553740BA2E9372DF00AB56D0 /* Preferences */ = { - isa = PBXGroup; - children = ( - 553740852E9372DF00AB56D0 /* Box */, - 553740A52E9372DF00AB56D0 /* Main */, - 553740B82E9372DF00AB56D0 /* Session */, - 553740B92E9372DF00AB56D0 /* PreferencesPanelDataSource.swift */, - ); - path = Preferences; - sourceTree = ""; - }; - 553740BB2E9372DF00AB56D0 /* Features */ = { - isa = PBXGroup; - children = ( - 55373FE52E9372DF00AB56D0 /* FilesCompare */, - 553740792E9372DF00AB56D0 /* FoldersCompare */, - 5537407F2E9372DF00AB56D0 /* HistoryController */, - 553740BA2E9372DF00AB56D0 /* Preferences */, - ); - path = Features; - sourceTree = ""; - }; - 553740C02E9372DF00AB56D0 /* DiffEngine */ = { - isa = PBXGroup; - children = ( - 553740BC2E9372DF00AB56D0 /* UDiffScriptBuilder.h */, - 553740BD2E9372DF00AB56D0 /* UDiffScriptBuilder.m */, - 553740BE2E9372DF00AB56D0 /* UnifiedDiff.h */, - 553740BF2E9372DF00AB56D0 /* UnifiedDiff.m */, - ); - path = DiffEngine; - sourceTree = ""; - }; - 553740C92E9372DF00AB56D0 /* FileManager */ = { - isa = PBXGroup; - children = ( - 553740C72E9372DF00AB56D0 /* BigFileFileOperationManager.h */, - 553740C82E9372DF00AB56D0 /* BigFileFileOperationManager.m */, - ); - path = FileManager; - sourceTree = ""; - }; - 553740D32E9372DF00AB56D0 /* MGScopeBar */ = { - isa = PBXGroup; - children = ( - 553740CD2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.h */, - 553740CE2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.m */, - 553740CF2E9372DF00AB56D0 /* MGScopeBar.h */, - 553740D02E9372DF00AB56D0 /* MGScopeBar.m */, - 553740D12E9372DF00AB56D0 /* MGScopeBarDelegateProtocol.h */, - 553740D22E9372DF00AB56D0 /* ScopeBarItem.swift */, - ); - path = MGScopeBar; - sourceTree = ""; - }; - 553740DC2E9372DF00AB56D0 /* Predicate */ = { - isa = PBXGroup; - children = ( - 553740D42E9372DF00AB56D0 /* NSPredicate+Objc.h */, - 553740D52E9372DF00AB56D0 /* NSPredicate+Objc.m */, - 553740D62E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.h */, - 553740D72E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.m */, - 553740D82E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.h */, - 553740D92E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.m */, - 553740DA2E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.h */, - 553740DB2E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.m */, - ); - path = Predicate; - sourceTree = ""; - }; - 553740E12E9372DF00AB56D0 /* Legacy */ = { - isa = PBXGroup; - children = ( - 553740C02E9372DF00AB56D0 /* DiffEngine */, - 553740C92E9372DF00AB56D0 /* FileManager */, - 553740D32E9372DF00AB56D0 /* MGScopeBar */, - 553740DC2E9372DF00AB56D0 /* Predicate */, - ); - path = Legacy; - sourceTree = ""; - }; - 553740E32E9372DF00AB56D0 /* AppleScript */ = { - isa = PBXGroup; - children = ( - 553740E22E9372DF00AB56D0 /* OpenDiffCommand.swift */, - ); - path = AppleScript; - sourceTree = ""; - }; - 553740E42E9372DF00AB56D0 /* Services */ = { - isa = PBXGroup; - children = ( - 553740E32E9372DF00AB56D0 /* AppleScript */, - ); - path = Services; - sourceTree = ""; - }; - 553740E62E9372DF00AB56D0 /* Appearance */ = { - isa = PBXGroup; - children = ( - 553740E52E9372DF00AB56D0 /* NSAppearance+DarkMode.swift */, - ); - path = Appearance; - sourceTree = ""; - }; - 553740E82E9372DF00AB56D0 /* Collections */ = { - isa = PBXGroup; - children = ( - 553740E72E9372DF00AB56D0 /* Array+MoveIndexes.swift */, - ); - path = Collections; - sourceTree = ""; - }; - 553740EA2E9372DF00AB56D0 /* Color */ = { - isa = PBXGroup; - children = ( - 553740E92E9372DF00AB56D0 /* NSColor+Hex.swift */, - ); - path = Color; - sourceTree = ""; - }; - 553740EC2E9372DF00AB56D0 /* CoreData */ = { - isa = PBXGroup; - children = ( - 553740EB2E9372DF00AB56D0 /* NSManagedObjectContext+Helpers.swift */, - ); - path = CoreData; - sourceTree = ""; - }; - 553740EE2E9372DF00AB56D0 /* Date */ = { - isa = PBXGroup; - children = ( - 553740ED2E9372DF00AB56D0 /* TimeInterval+Helper.swift */, - ); - path = Date; - sourceTree = ""; - }; - 553740F02E9372DF00AB56D0 /* Error */ = { - isa = PBXGroup; - children = ( - 553740EF2E9372DF00AB56D0 /* NSError+Format.swift */, - ); - path = Error; - sourceTree = ""; - }; - 553740F22E9372DF00AB56D0 /* FileManager */ = { - isa = PBXGroup; - children = ( - 553740F12E9372DF00AB56D0 /* FileManager+Attributes.swift */, - ); - path = FileManager; - sourceTree = ""; - }; - 553740F42E9372DF00AB56D0 /* Formatters */ = { - isa = PBXGroup; - children = ( - 553740F32E9372DF00AB56D0 /* DateFormatter+Helper.swift */, - ); - path = Formatters; - sourceTree = ""; - }; - 553740F62E9372DF00AB56D0 /* Image */ = { - isa = PBXGroup; - children = ( - 553740F52E9372DF00AB56D0 /* NSImage+Tint.swift */, - ); - path = Image; - sourceTree = ""; - }; - 553740F82E9372DF00AB56D0 /* Menu */ = { - isa = PBXGroup; - children = ( - 553740F72E9372DF00AB56D0 /* NSMenu+File.swift */, - ); - path = Menu; - sourceTree = ""; - }; - 553740FA2E9372DF00AB56D0 /* OptionSet */ = { - isa = PBXGroup; - children = ( - 55FE89492F054B260003864A /* FlagSet.swift */, - 553740F92E9372DF00AB56D0 /* OptionSet+Toggle.swift */, - ); - path = OptionSet; - sourceTree = ""; - }; - 553740FE2E9372DF00AB56D0 /* Panel */ = { - isa = PBXGroup; - children = ( - 553740FB2E9372DF00AB56D0 /* NSAlert+Helper.swift */, - 553740FC2E9372DF00AB56D0 /* NSOpenPanel+Application.swift */, - 553740FD2E9372DF00AB56D0 /* QLPreviewPanel.swift */, - ); - path = Panel; - sourceTree = ""; - }; - 553741002E9372DF00AB56D0 /* Pasteboard */ = { - isa = PBXGroup; - children = ( - 553740FF2E9372DF00AB56D0 /* NSPasteboard+Helper.swift */, - ); - path = Pasteboard; - sourceTree = ""; - }; - 553741022E9372DF00AB56D0 /* Predicate */ = { - isa = PBXGroup; - children = ( - 553741012E9372DF00AB56D0 /* NSPredicateEditor+Fix.swift */, - ); - path = Predicate; - sourceTree = ""; - }; - 553741082E9372DF00AB56D0 /* String */ = { - isa = PBXGroup; - children = ( - 553741032E9372DF00AB56D0 /* String+Helper.swift */, - 553741042E9372DF00AB56D0 /* String+Highlights.swift */, - 553741052E9372DF00AB56D0 /* String+Occcurrences.swift */, - 553741062E9372DF00AB56D0 /* String+Path.swift */, - 553741072E9372DF00AB56D0 /* String+RegularExpression.swift */, - ); - path = String; - sourceTree = ""; - }; - 5537410B2E9372DF00AB56D0 /* TableView */ = { - isa = PBXGroup; - children = ( - 553741092E9372DF00AB56D0 /* NSTableView+Font.swift */, - 5537410A2E9372DF00AB56D0 /* NSTableView+Row.swift */, - ); - path = TableView; - sourceTree = ""; - }; - 5537410E2E9372DF00AB56D0 /* Toolbar */ = { - isa = PBXGroup; - children = ( - 5537410C2E9372DF00AB56D0 /* NSToolbar+Create.swift */, - 5537410D2E9372DF00AB56D0 /* NSToolbarItem+Create.swift */, - ); - path = Toolbar; - sourceTree = ""; - }; - 553741162E9372DF00AB56D0 /* URL */ = { - isa = PBXGroup; - children = ( - 5537410F2E9372DF00AB56D0 /* URL+FileManager.swift */, - 553741102E9372DF00AB56D0 /* URL+Finder.swift */, - 553741112E9372DF00AB56D0 /* URL+Metadata.swift */, - 553741122E9372DF00AB56D0 /* URL+Path.swift */, - 553741132E9372DF00AB56D0 /* URL+ResourceFork.swift */, - 553741142E9372DF00AB56D0 /* URL+StructuredContent.swift */, - 553741152E9372DF00AB56D0 /* URL+SymLink.swift */, - ); - path = URL; - sourceTree = ""; - }; - 553741182E9372DF00AB56D0 /* Box */ = { - isa = PBXGroup; - children = ( - 553741172E9372DF00AB56D0 /* NSBox+Helper.swift */, - ); - path = Box; - sourceTree = ""; - }; - 5537411B2E9372DF00AB56D0 /* Button */ = { - isa = PBXGroup; - children = ( - 553741192E9372DF00AB56D0 /* NSButton+Helper.swift */, - 5537411A2E9372DF00AB56D0 /* NSPopUpButton+Helper.swift */, - ); - path = Button; - sourceTree = ""; - }; - 5537411D2E9372DF00AB56D0 /* ProgressIndicator */ = { - isa = PBXGroup; - children = ( - 5537411C2E9372DF00AB56D0 /* NSProgressIndicator+Helper.swift */, - ); - path = ProgressIndicator; - sourceTree = ""; - }; - 5537411F2E9372DF00AB56D0 /* TextField */ = { - isa = PBXGroup; - children = ( - 5537411E2E9372DF00AB56D0 /* NSTextField+Helper.swift */, - ); - path = TextField; - sourceTree = ""; - }; - 553741212E9372DF00AB56D0 /* TextView */ = { - isa = PBXGroup; - children = ( - 553741202E9372DF00AB56D0 /* NSTextView+Style.swift */, - ); - path = TextView; - sourceTree = ""; - }; - 553741222E9372DF00AB56D0 /* View */ = { - isa = PBXGroup; - children = ( - 553741182E9372DF00AB56D0 /* Box */, - 5537411B2E9372DF00AB56D0 /* Button */, - 5537411D2E9372DF00AB56D0 /* ProgressIndicator */, - 5537411F2E9372DF00AB56D0 /* TextField */, - 553741212E9372DF00AB56D0 /* TextView */, - ); - path = View; - sourceTree = ""; - }; - 553741242E9372DF00AB56D0 /* Workspace */ = { - isa = PBXGroup; - children = ( - 553741232E9372DF00AB56D0 /* NSWorkspace+Finder.swift */, - ); - path = Workspace; - sourceTree = ""; - }; - 553741252E9372DF00AB56D0 /* Extensions */ = { - isa = PBXGroup; - children = ( - 553740E62E9372DF00AB56D0 /* Appearance */, - 553740E82E9372DF00AB56D0 /* Collections */, - 553740EA2E9372DF00AB56D0 /* Color */, - 553740EC2E9372DF00AB56D0 /* CoreData */, - 553740EE2E9372DF00AB56D0 /* Date */, - 558DEAC52EA263DE00F05A10 /* Encoding */, - 553740F02E9372DF00AB56D0 /* Error */, - 553740F22E9372DF00AB56D0 /* FileManager */, - 553740F42E9372DF00AB56D0 /* Formatters */, - 553740F62E9372DF00AB56D0 /* Image */, - 553740F82E9372DF00AB56D0 /* Menu */, - 553740FA2E9372DF00AB56D0 /* OptionSet */, - 553740FE2E9372DF00AB56D0 /* Panel */, - 553741002E9372DF00AB56D0 /* Pasteboard */, - 553741022E9372DF00AB56D0 /* Predicate */, - 553741082E9372DF00AB56D0 /* String */, - 5537410B2E9372DF00AB56D0 /* TableView */, - 5537410E2E9372DF00AB56D0 /* Toolbar */, - 553741162E9372DF00AB56D0 /* URL */, - 553741222E9372DF00AB56D0 /* View */, - 558DEACF2EA2642100F05A10 /* Window */, - 553741242E9372DF00AB56D0 /* Workspace */, - ); - path = Extensions; - sourceTree = ""; - }; - 553741272E9372DF00AB56D0 /* Document */ = { - isa = PBXGroup; - children = ( - 553741262E9372DF00AB56D0 /* SecureBookmark.swift */, - ); - path = Document; - sourceTree = ""; - }; - 553741292E9372DF00AB56D0 /* Encoding */ = { - isa = PBXGroup; - children = ( - 553741282E9372DF00AB56D0 /* EncodingError.swift */, - ); - path = Encoding; - sourceTree = ""; - }; - 5537412B2E9372DF00AB56D0 /* Event */ = { - isa = PBXGroup; - children = ( - 5537412A2E9372DF00AB56D0 /* NSEvent+VirtualKeys.swift */, - ); - path = Event; - sourceTree = ""; - }; - 5537412E2E9372DF00AB56D0 /* Formatters */ = { - isa = PBXGroup; - children = ( - 5537412C2E9372DF00AB56D0 /* FileSizeFormatter.swift */, - 5537412D2E9372DF00AB56D0 /* IntegerFormatter.swift */, - ); - path = Formatters; - sourceTree = ""; - }; - 553741312E9372DF00AB56D0 /* Image */ = { - isa = PBXGroup; - children = ( - 5537412F2E9372DF00AB56D0 /* IconUtils.swift */, - 553741302E9372DF00AB56D0 /* NoodleCustomImageRep.swift */, - 55BC333F2EDAC96F004035A0 /* TiledImageView.swift */, - ); - path = Image; - sourceTree = ""; - }; - 553741362E9372DF00AB56D0 /* IO */ = { - isa = PBXGroup; - children = ( - 553741322E9372DF00AB56D0 /* BufferedInputStream.swift */, - 553741332E9372DF00AB56D0 /* CompareUtil.swift */, - 553741342E9372DF00AB56D0 /* FileError.swift */, - 553741352E9372DF00AB56D0 /* PowerAssertion.swift */, - ); - path = IO; - sourceTree = ""; - }; - 553741382E9372DF00AB56D0 /* Menu */ = { - isa = PBXGroup; - children = ( - 553741372E9372DF00AB56D0 /* AttributedMenuItem.swift */, - ); - path = Menu; - sourceTree = ""; - }; - 5537413C2E9372DF00AB56D0 /* OpenEditor */ = { - isa = PBXGroup; - children = ( - 553741392E9372DF00AB56D0 /* OpenEditor.swift */, - 5537413A2E9372DF00AB56D0 /* OpenEditorAttribute.swift */, - 5537413B2E9372DF00AB56D0 /* OpenEditorError.swift */, - ); - path = OpenEditor; - sourceTree = ""; - }; - 5537413E2E9372DF00AB56D0 /* PopupButton */ = { - isa = PBXGroup; - children = ( - 5537413D2E9372DF00AB56D0 /* PreferredEditorPopupCell.swift */, - ); - path = PopupButton; - sourceTree = ""; - }; - 553741402E9372DF00AB56D0 /* String */ = { - isa = PBXGroup; - children = ( - 5537413F2E9372DF00AB56D0 /* VisibleWhitespaces.swift */, - ); - path = String; - sourceTree = ""; - }; - 553741442E9372DF00AB56D0 /* TableView */ = { - isa = PBXGroup; - children = ( - 553741412E9372DF00AB56D0 /* DescriptionOutlineNode.swift */, - 553741422E9372DF00AB56D0 /* TableViewCommon.swift */, - 553741432E9372DF00AB56D0 /* TableViewContextMenuDelegate.swift */, - ); - path = TableView; - sourceTree = ""; - }; - 553741472E9372DF00AB56D0 /* TextField */ = { - isa = PBXGroup; - children = ( - 553741452E9372DF00AB56D0 /* NSTextFieldCell+Helper.swift */, - 553741462E9372DF00AB56D0 /* RSVerticallyCenteredTextFieldCell.swift */, - ); - path = TextField; - sourceTree = ""; - }; - 553741492E9372DF00AB56D0 /* Toolbar */ = { - isa = PBXGroup; - children = ( - 553741482E9372DF00AB56D0 /* CustomValidationToolbarItem.swift */, - ); - path = Toolbar; - sourceTree = ""; - }; - 5537414B2E9372DF00AB56D0 /* ActionBar */ = { - isa = PBXGroup; - children = ( - 5537414A2E9372DF00AB56D0 /* ActionBarView.swift */, - ); - path = ActionBar; - sourceTree = ""; - }; - 5537414E2E9372DF00AB56D0 /* Button */ = { - isa = PBXGroup; - children = ( - 5537414C2E9372DF00AB56D0 /* LinkButton.swift */, - 5537414D2E9372DF00AB56D0 /* StandardButtons.swift */, - ); - path = Button; - sourceTree = ""; - }; - 553741512E9372DF00AB56D0 /* Console */ = { - isa = PBXGroup; - children = ( - 5537414F2E9372DF00AB56D0 /* ConsoleToolbarView.swift */, - 553741502E9372DF00AB56D0 /* ConsoleView.swift */, - ); - path = Console; - sourceTree = ""; - }; - 553741542E9372DF00AB56D0 /* DualPane */ = { - isa = PBXGroup; - children = ( - 553741522E9372DF00AB56D0 /* DualPaneSplitView.swift */, - 553741532E9372DF00AB56D0 /* DualPaneSplitViewDelegate.swift */, - ); - path = DualPane; - sourceTree = ""; - }; - 553741572E9372DF00AB56D0 /* FileDrop */ = { - isa = PBXGroup; - children = ( - 553741552E9372DF00AB56D0 /* FileDropView.swift */, - 553741562E9372DF00AB56D0 /* FileDropView+Helper.swift */, - ); - path = FileDrop; - sourceTree = ""; - }; - 553741592E9372DF00AB56D0 /* ProgressBar */ = { - isa = PBXGroup; - children = ( - 553741582E9372DF00AB56D0 /* ProgressBarView.swift */, - ); - path = ProgressBar; - sourceTree = ""; - }; - 5537415B2E9372DF00AB56D0 /* SynchroScroll */ = { - isa = PBXGroup; - children = ( - 5537415A2E9372DF00AB56D0 /* SynchroScrollView.swift */, - ); - path = SynchroScroll; - sourceTree = ""; - }; - 5537415D2E9372DF00AB56D0 /* TextField */ = { - isa = PBXGroup; - children = ( - 5537415C2E9372DF00AB56D0 /* TextFieldSelectionHolder.swift */, - ); - path = TextField; - sourceTree = ""; - }; - 5537415E2E9372DF00AB56D0 /* View */ = { - isa = PBXGroup; - children = ( - 5537414B2E9372DF00AB56D0 /* ActionBar */, - 5537414E2E9372DF00AB56D0 /* Button */, - 553741512E9372DF00AB56D0 /* Console */, - 553741542E9372DF00AB56D0 /* DualPane */, - 558DEACA2EA263F500F05A10 /* Encoding */, - 553741572E9372DF00AB56D0 /* FileDrop */, - 553741592E9372DF00AB56D0 /* ProgressBar */, - 5537415B2E9372DF00AB56D0 /* SynchroScroll */, - 5537415D2E9372DF00AB56D0 /* TextField */, - ); - path = View; - sourceTree = ""; - }; - 553741612E9372DF00AB56D0 /* Window */ = { - isa = PBXGroup; - children = ( - 5537415F2E9372DF00AB56D0 /* WindowCancelOperation.swift */, - 553741602E9372DF00AB56D0 /* WindowOSD.swift */, - ); - path = Window; - sourceTree = ""; - }; - 553741622E9372DF00AB56D0 /* Utilities */ = { - isa = PBXGroup; - children = ( - 553741272E9372DF00AB56D0 /* Document */, - 553741292E9372DF00AB56D0 /* Encoding */, - 5537412B2E9372DF00AB56D0 /* Event */, - 5537412E2E9372DF00AB56D0 /* Formatters */, - 553741312E9372DF00AB56D0 /* Image */, - 553741362E9372DF00AB56D0 /* IO */, - 553741382E9372DF00AB56D0 /* Menu */, - 5537413C2E9372DF00AB56D0 /* OpenEditor */, - 5537413E2E9372DF00AB56D0 /* PopupButton */, - 553741402E9372DF00AB56D0 /* String */, - 553741442E9372DF00AB56D0 /* TableView */, - 553741472E9372DF00AB56D0 /* TextField */, - 553741492E9372DF00AB56D0 /* Toolbar */, - 5537415E2E9372DF00AB56D0 /* View */, - 553741612E9372DF00AB56D0 /* Window */, - ); - path = Utilities; - sourceTree = ""; - }; - 553741632E9372DF00AB56D0 /* SharedKit */ = { - isa = PBXGroup; - children = ( - 553741252E9372DF00AB56D0 /* Extensions */, - 553741622E9372DF00AB56D0 /* Utilities */, - ); - path = SharedKit; - sourceTree = ""; - }; - 553741682E9372DF00AB56D0 /* Cells */ = { - isa = PBXGroup; - children = ( - 553741642E9372DF00AB56D0 /* AlignPopupButtonCell.swift */, - 553741652E9372DF00AB56D0 /* ComparatorPopUpButtonCell.swift */, - 553741662E9372DF00AB56D0 /* DisplayFiltersPopUpButtonCell.swift */, - 553741672E9372DF00AB56D0 /* FilePathTableCellView.swift */, - ); - path = Cells; - sourceTree = ""; - }; - 5537416C2E9372DF00AB56D0 /* DiffCounters */ = { - isa = PBXGroup; - children = ( - 553741692E9372DF00AB56D0 /* DiffCountersItem.swift */, - 5537416A2E9372DF00AB56D0 /* DiffCountersTextFieldCell.swift */, - 5537416B2E9372DF00AB56D0 /* DifferenceCounters.swift */, - ); - path = DiffCounters; - sourceTree = ""; - }; - 553741752E9372DF00AB56D0 /* Components */ = { - isa = PBXGroup; - children = ( - 5537416C2E9372DF00AB56D0 /* DiffCounters */, - 5537416D2E9372DF00AB56D0 /* FilePathTextField.swift */, - 5537416E2E9372DF00AB56D0 /* FindText.swift */, - 5537416F2E9372DF00AB56D0 /* NSTextField+CenterVertically.swift */, - 553741702E9372DF00AB56D0 /* PathControl.swift */, - 553741712E9372DF00AB56D0 /* PathView.swift */, - 553741722E9372DF00AB56D0 /* PopUpButtonUrl.swift */, - 553741732E9372DF00AB56D0 /* TablePanelView.swift */, - 553741742E9372DF00AB56D0 /* TimeToleranceView.swift */, - ); - path = Components; - sourceTree = ""; - }; - 553741762E9372DF00AB56D0 /* SharedUI */ = { - isa = PBXGroup; - children = ( - 553741682E9372DF00AB56D0 /* Cells */, - 553741752E9372DF00AB56D0 /* Components */, - ); - path = SharedUI; - sourceTree = ""; - }; - 553741772E9372DF00AB56D0 /* Sources */ = { - isa = PBXGroup; - children = ( - 55373F8E2E9372DF00AB56D0 /* App */, - 55373FB52E9372DF00AB56D0 /* Core */, - 553740BB2E9372DF00AB56D0 /* Features */, - 553740E12E9372DF00AB56D0 /* Legacy */, - 553740E42E9372DF00AB56D0 /* Services */, - 553741632E9372DF00AB56D0 /* SharedKit */, - 553741762E9372DF00AB56D0 /* SharedUI */, - ); - path = Sources; - sourceTree = ""; - }; - 554B4A442E9B7B8F0023AAF0 /* DiffResult */ = { - isa = PBXGroup; - children = ( - 55373FDE2E9372DF00AB56D0 /* DiffChangeType.swift */, - 554B4A432E9B7B8F0023AAF0 /* DiffLine.swift */, - 557BBB0D2ED1BBB00016CBD4 /* DiffLineComponent.swift */, - 554B4A3B2E9B7B8F0023AAF0 /* DiffResult.swift */, - 55F9E7992EDD5DAD001218C7 /* DiffResult.Options.swift */, - 554B4A3D2E9B7B8F0023AAF0 /* DiffResult+Dump.swift */, - 554B4A3E2E9B7B8F0023AAF0 /* DiffResult+Lines.swift */, - 554B4A3F2E9B7B8F0023AAF0 /* DiffResult+Section.swift */, - 554B4A412E9B7B8F0023AAF0 /* DiffSection.swift */, - 554B4A402E9B7B8F0023AAF0 /* DiffSide.swift */, - 559186402E9B821B00A30620 /* DiffSummary.swift */, - 554B4A3A2E9B7B8F0023AAF0 /* EndOfLine.swift */, - ); - path = DiffResult; - sourceTree = ""; - }; - 557C18172E7E6E0100381A3A /* Resources */ = { - isa = PBXGroup; - children = ( - 557C18162E7E6E0100381A3A /* big_file_5000_lines.txt */, - ); - path = Resources; - sourceTree = ""; - }; - 558DEAC52EA263DE00F05A10 /* Encoding */ = { - isa = PBXGroup; - children = ( - 558DEAC42EA263DE00F05A10 /* CFStringEncoding+StringEncoding.swift */, - ); - path = Encoding; - sourceTree = ""; - }; - 558DEACA2EA263F500F05A10 /* Encoding */ = { - isa = PBXGroup; - children = ( - 558DEAC72EA263F500F05A10 /* EncodingManager.swift */, - 558DEAC82EA263F500F05A10 /* EncodingPopUpButtonCell.swift */, - 558DEAC92EA263F500F05A10 /* SelectEncodingsPanel.swift */, - ); - path = Encoding; - sourceTree = ""; - }; - 558DEACF2EA2642100F05A10 /* Window */ = { - isa = PBXGroup; - children = ( - 558DEACE2EA2642100F05A10 /* NSWindow+Editing.swift */, - ); - path = Window; - sourceTree = ""; - }; - 55916A0A2E8A814200ED6629 /* Models */ = { - isa = PBXGroup; - children = ( - 55916A082E8A814200ED6629 /* History.xcdatamodeld */, - 55916A092E8A814200ED6629 /* MyDocument.xcdatamodeld */, - ); - path = Models; - sourceTree = ""; - }; - 55944CDA2E5C8E5B00FB1F8C /* Tests */ = { - isa = PBXGroup; - children = ( - 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */, - 557C18212E7E8C0D00381A3A /* Signing.local.xcconfig */, - 557C18222E7E8C0D00381A3A /* Versions.local.xcconfig */, - 5580A6E32E5D7B19008D3D3A /* NoSandbox.entitlements */, - 557C18172E7E6E0100381A3A /* Resources */, - 55DF3DD12E924BB10044CC0C /* Sources */, - 553033AC2E5DBABF0016F101 /* VisualDiffer.xctestplan */, - ); - path = Tests; - sourceTree = ""; - }; - 55B1CE2D2E9934F800426FEC /* Tests */ = { - isa = PBXGroup; - children = ( - 55A4DF312E9A49A700ED11BB /* EndOfLineTests.swift */, - 55F9E79B2EDD6221001218C7 /* WhitespacesTests.swift */, - 55B1CE2C2E9934F800426FEC /* DiffResultTests.swift */, - ); - path = Tests; - sourceTree = ""; - }; - 55B1CE2F2E9934F800426FEC /* DiffResult */ = { - isa = PBXGroup; - children = ( - 55B1CE2D2E9934F800426FEC /* Tests */, - 55B1CE2E2E9934F800426FEC /* DiffResultBaseTests.swift */, - ); - path = DiffResult; - sourceTree = ""; - }; - 55B1CE302E9934F800426FEC /* FileCompare */ = { - isa = PBXGroup; - children = ( - 55B1CE2F2E9934F800426FEC /* DiffResult */, - ); - path = FileCompare; - sourceTree = ""; - }; - 55C1CBFC2E8BB6AD004FE322 /* visdiff */ = { - isa = PBXGroup; - children = ( - 55C1CBF32E8BB6AD004FE322 /* AppConfig.xcconfig */, - 55C1CBF52E8BB6AD004FE322 /* Signing.local.xcconfig */, - 55C1CBF82E8BB6AD004FE322 /* Versions.local.xcconfig */, - 55C1CC062E8BBAD1004FE322 /* DocumentWaiter.swift */, - 55C1CBF42E8BB6AD004FE322 /* main.swift */, - 55C1CBF92E8BB6AD004FE322 /* visdiff.1 */, - 55C1CBFA2E8BB6AD004FE322 /* visdiff.entitlements */, - 55C1CBFB2E8BB6AD004FE322 /* visdiff-Info.plist */, - ); - path = visdiff; - sourceTree = ""; - }; - 55DF3DAB2E924BB10044CC0C /* BaseTests */ = { - isa = PBXGroup; - children = ( - 55B1CE2A2E9934E900426FEC /* DateBuilder.swift */, - 55DF3DA92E924BB10044CC0C /* BaseTests.swift */, - 55DF3DAA2E924BB10044CC0C /* BaseTests+AssertCompareItem.swift */, - ); - path = BaseTests; - sourceTree = ""; - }; - 55DF3DAF2E924BB10044CC0C /* Mock */ = { - isa = PBXGroup; - children = ( - 55DF3DAC2E924BB10044CC0C /* MockFileOperationManagerDelegate.swift */, - 55DF3DAD2E924BB10044CC0C /* MockFolderReaderDelegate.swift */, - 5595902A2EABB15F00B5AB0B /* MockItemComparatorDelegate.swift */, - ); - path = Mock; - sourceTree = ""; - }; - 55DF3DB72E924BB10044CC0C /* MetaData */ = { - isa = PBXGroup; - children = ( - 55DF3DB02E924BB10044CC0C /* LabelsCopyTests.swift */, - 55DF3DB12E924BB10044CC0C /* LabelsCreateTests.swift */, - 55DF3DB22E924BB10044CC0C /* LabelsMoveTests.swift */, - 55DF3DB32E924BB10044CC0C /* TagsCopyTests.swift */, - 55DF3DB42E924BB10044CC0C /* TagsCreateTests.swift */, - 55DF3DB52E924BB10044CC0C /* TagsDeleteTests.swift */, - 55DF3DB62E924BB10044CC0C /* TagsMoveTests.swift */, - ); - path = MetaData; - sourceTree = ""; - }; - 55DF3DC32E924BB10044CC0C /* Tests */ = { - isa = PBXGroup; - children = ( - 55DF3DB82E924BB10044CC0C /* AlignmentTests.swift */, - 55DF3DB92E924BB10044CC0C /* BufferedInputStreamTests.swift */, - 55DF3DBA2E924BB10044CC0C /* ComparatorTests.swift */, - 55DF3DBB2E924BB10044CC0C /* CopyFilesTests.swift */, - 55DF3DBC2E924BB10044CC0C /* DeleteFileTests.swift */, - 55DF3DBD2E924BB10044CC0C /* DisplayTests.swift */, - 55DF3DBE2E924BB10044CC0C /* EmptyFolderColorTests.swift */, - 55DF3DBF2E924BB10044CC0C /* FiltersTests.swift */, - 55DF3DB72E924BB10044CC0C /* MetaData */, - 55DF3DC02E924BB10044CC0C /* MoveFilesTests.swift */, - 55DF3DC12E924BB10044CC0C /* RenameFilesTests.swift */, - 55E381C02EEFFCF500CB2F00 /* SymbolicLinkTests.swift */, - 55DF3DC22E924BB10044CC0C /* TouchFilesTests.swift */, - ); - path = Tests; - sourceTree = ""; - }; - 55DF3DC72E924BB10044CC0C /* FileSystem */ = { - isa = PBXGroup; - children = ( - 55DF3DAF2E924BB10044CC0C /* Mock */, - 55DF3DC32E924BB10044CC0C /* Tests */, - 55DF3DC42E924BB10044CC0C /* BaseTests+AssertFileSystem.swift */, - 55DF3DC52E924BB10044CC0C /* BaseTests+FileSystemOperations.swift */, - 55DF3DC62E924BB10044CC0C /* CaseSensitiveBaseTest.swift */, - ); - path = FileSystem; - sourceTree = ""; - }; - 55DF3DC92E924BB10044CC0C /* Path */ = { - isa = PBXGroup; - children = ( - 55DF3DC82E924BB10044CC0C /* LeafPathTests.swift */, - ); - path = Path; - sourceTree = ""; - }; - 55DF3DCC2E924BB10044CC0C /* Tests */ = { - isa = PBXGroup; - children = ( - 55DF3DCA2E924BB10044CC0C /* FoldersWindowControllerTests.swift */, - 55DF3DCB2E924BB10044CC0C /* RefreshInfoTests.swift */, - ); - path = Tests; - sourceTree = ""; - }; - 55DF3DCD2E924BB10044CC0C /* UI */ = { - isa = PBXGroup; - children = ( - 55DF3DCC2E924BB10044CC0C /* Tests */, - ); - path = UI; - sourceTree = ""; - }; - 55DF3DCF2E924BB10044CC0C /* Utils */ = { - isa = PBXGroup; - children = ( - 55DF3DCE2E924BB10044CC0C /* StringUtils.swift */, - ); - path = Utils; - sourceTree = ""; - }; - 55DF3DD12E924BB10044CC0C /* Sources */ = { - isa = PBXGroup; - children = ( - 55DF3DAB2E924BB10044CC0C /* BaseTests */, - 55B1CE302E9934F800426FEC /* FileCompare */, - 55DF3DC72E924BB10044CC0C /* FileSystem */, - 55DF3DC92E924BB10044CC0C /* Path */, - 55DF3DCD2E924BB10044CC0C /* UI */, - 55DF3DCF2E924BB10044CC0C /* Utils */, - ); - path = Sources; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 55944CC62E5C8D9F00FB1F8C /* VisualDifferTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 55944CCD2E5C8D9F00FB1F8C /* Build configuration list for PBXNativeTarget "VisualDifferTests" */; - buildPhases = ( - 55944CC32E5C8D9F00FB1F8C /* Sources */, - 55944CC42E5C8D9F00FB1F8C /* Frameworks */, - 55944CC52E5C8D9F00FB1F8C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 55944CCC2E5C8D9F00FB1F8C /* PBXTargetDependency */, - ); - name = VisualDifferTests; - packageProductDependencies = ( - ); - productName = VisualDifferTests; - productReference = 55944CC72E5C8D9F00FB1F8C /* VisualDifferTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 55C1CBE12E8BB6A2004FE322 /* visdiff */ = { - isa = PBXNativeTarget; - buildConfigurationList = 55C1CBE62E8BB6A2004FE322 /* Build configuration list for PBXNativeTarget "visdiff" */; - buildPhases = ( - 55C1CBDE2E8BB6A2004FE322 /* Sources */, - 55C1CBDF2E8BB6A2004FE322 /* Frameworks */, - 55C1CBE02E8BB6A2004FE322 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = visdiff; - packageProductDependencies = ( - ); - productName = visdiff; - productReference = 55C1CBE22E8BB6A2004FE322 /* visdiff */; - productType = "com.apple.product-type.tool"; - }; - 8D15AC270486D014006FF6A4 /* VisualDiffer */ = { - isa = PBXNativeTarget; - buildConfigurationList = 26FC0AA50875C8B900E6366F /* Build configuration list for PBXNativeTarget "VisualDiffer" */; - buildPhases = ( - 8D15AC2B0486D014006FF6A4 /* Resources */, - 5504B6FF255FCBBA00CDE04A /* Copy Visdiff to Helpers */, - 8D15AC300486D014006FF6A4 /* Sources */, - 8D15AC330486D014006FF6A4 /* Frameworks */, - 55943C852EE43E3800DE513B /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 55C1CC002E8BB749004FE322 /* PBXTargetDependency */, - ); - name = VisualDiffer; - productInstallPath = "$(HOME)/Applications"; - productName = QuickDirtyDiff; - productReference = 8D15AC370486D014006FF6A4 /* VisualDiffer.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - CLASSPREFIX = VD; - LastSwiftUpdateCheck = 2600; - LastTestingUpgradeCheck = 0600; - LastUpgradeCheck = 2600; - ORGANIZATIONNAME = visualdiffer.com; - TargetAttributes = { - 55944CC62E5C8D9F00FB1F8C = { - CreatedOnToolsVersion = 16.4; - LastSwiftMigration = 1640; - TestTargetID = 8D15AC270486D014006FF6A4; - }; - 55C1CBE12E8BB6A2004FE322 = { - CreatedOnToolsVersion = 26.0.1; - LastSwiftMigration = 2600; - }; - 8D15AC270486D014006FF6A4 = { - LastSwiftMigration = 1630; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 26FC0AA90875C8B900E6366F /* Build configuration list for PBXProject "VisualDiffer" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 1; - knownRegions = ( - en, - Base, - ); - mainGroup = 2A37F4AAFDCFA73011CA2CEA /* QuickDirtyDiff */; - packageReferences = ( - 55C165602EE444050082A24C /* XCRemoteSwiftPackageReference "Sparkle" */, - ); - projectDirPath = ""; - projectRoot = ""; - targets = ( - 8D15AC270486D014006FF6A4 /* VisualDiffer */, - 55C1CBE12E8BB6A2004FE322 /* visdiff */, - 55944CC62E5C8D9F00FB1F8C /* VisualDifferTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 55944CC52E5C8D9F00FB1F8C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 557C18182E7E6E0100381A3A /* big_file_5000_lines.txt in Resources */, - 557C18232E7E8C0D00381A3A /* Signing.local.xcconfig in Resources */, - 557C18242E7E8C0D00381A3A /* Versions.local.xcconfig in Resources */, - 557C18252E7E8C0D00381A3A /* AppConfig.xcconfig in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 8D15AC2B0486D014006FF6A4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8D15AC2C0486D014006FF6A4 /* Credits.rtf in Resources */, - 8D15AC2F0486D014006FF6A4 /* InfoPlist.strings in Resources */, - 2F7446AB0DB6BCF400F9684A /* MainMenu.xib in Resources */, - 55157D7512898F8800826FF8 /* folder-000-open.png in Resources */, - 5532516D2765F06B0092D65F /* mask-full.png in Resources */, - 55157D7612898F8800826FF8 /* folder-000.png in Resources */, - 55157D7712898F8800826FF8 /* folder-001-open.png in Resources */, - 55157D7812898F8800826FF8 /* folder-001.png in Resources */, - 55157D7912898F8800826FF8 /* folder-010-open.png in Resources */, - 55157D7A12898F8800826FF8 /* folder-010.png in Resources */, - 557315B12551307800CF4372 /* Images.xcassets in Resources */, - 5532516E2765F06B0092D65F /* mask-middle.png in Resources */, - 55157D7B12898F8800826FF8 /* folder-011-open.png in Resources */, - 55157D7C12898F8800826FF8 /* folder-011.png in Resources */, - 55157D7D12898F8800826FF8 /* folder-100-open.png in Resources */, - 55157D7E12898F8800826FF8 /* folder-100.png in Resources */, - 55157D7F12898F8800826FF8 /* folder-101-open.png in Resources */, - 55157D8012898F8800826FF8 /* folder-101.png in Resources */, - 55157D8112898F8800826FF8 /* folder-110-open.png in Resources */, - 55157D8212898F8800826FF8 /* folder-110.png in Resources */, - 55157D8312898F8800826FF8 /* folder-111-open.png in Resources */, - 55157D8412898F8800826FF8 /* folder-111.png in Resources */, - 555392621889732900D26258 /* deleteRed.png in Resources */, - 55F6B0661AEA99680096E55E /* Localizable.strings in Resources */, - 5560823F12AD0EF8003DC79D /* folder-999-open.png in Resources */, - 5560824012AD0EF8003DC79D /* folder-999.png in Resources */, - 55E3A5661826552A002A890B /* lockedbadge.png in Resources */, - 55E1029A12C9E17300432C61 /* empty.png in Resources */, - 557EC74212CC7A8700B43F57 /* rewind.png in Resources */, - 5550B5931A21CD1F009BC4A4 /* aliasbadge@2x.png in Resources */, - 5532516C2765F06B0092D65F /* mask-back.png in Resources */, - 55E31E011BC572F3001ACE4D /* top.png in Resources */, - 55ECD8621476B03400D8BEB4 /* prefs_text.png in Resources */, - 559614B114B0471E0075ED34 /* aliasbadge.png in Resources */, - 559EA70E14FA1D8F00BC9B82 /* VisualDifferHelp in Resources */, - 55E31E061BC57373001ACE4D /* bottom@2x.png in Resources */, - 55E3A5671826552A002A890B /* lockedbadge@2x.png in Resources */, - 5532516F2765F06B0092D65F /* mask-back-white.png in Resources */, - 555392631889732900D26258 /* deleteRed@2x.png in Resources */, - 559E927E1514A63100B76349 /* dropzone.png in Resources */, - 5590B2FF15B0449F00B0E047 /* prefs_paths.png in Resources */, - 551DEDE1160477CA0014DF88 /* prefs_folder.png in Resources */, - 553251702765F06B0092D65F /* mask-front.png in Resources */, - 5542D19616317DAC00F801AA /* vd.iconset in Resources */, - 553D10D9163BD0BC008D3F65 /* prefs_confirmations.png in Resources */, - 55B6760018956D90000D5A0D /* prefs_keyboard.png in Resources */, - 55F6B0631AEA93E30096E55E /* Localizable.stringsdict in Resources */, - 55B6760118956D90000D5A0D /* prefs_keyboard@2x.png in Resources */, - 554E0EB2163EFD51001418EA /* prefs_confirmations@2x.png in Resources */, - 554E0EB3163EFD51001418EA /* prefs_paths@2x.png in Resources */, - 554E0EB4163EFD51001418EA /* prefs_text@2x.png in Resources */, - 5550B5951A21D2F3009BC4A4 /* empty@2x.png in Resources */, - 554E0EBD163EFD6A001418EA /* dropzone@2x.png in Resources */, - 55E31E021BC572F3001ACE4D /* top@2x.png in Resources */, - 554E0EBE163EFD6A001418EA /* rewind@2x.png in Resources */, - 551BF0DA1643B91B009CC9D1 /* folder-000-open@2x.png in Resources */, - 551BF0DB1643B91B009CC9D1 /* folder-000@2x.png in Resources */, - 551BF0DC1643B91B009CC9D1 /* folder-001-open@2x.png in Resources */, - 551BF0DD1643B91B009CC9D1 /* folder-001@2x.png in Resources */, - 5537434E2E937B7F00AB56D0 /* VDDefaults.plist in Resources */, - 5537434F2E937B7F00AB56D0 /* colors.json in Resources */, - 553743502E937B7F00AB56D0 /* colorsDark.json in Resources */, - 553743512E937B7F00AB56D0 /* VisualDiffer.sdef in Resources */, - 551BF0DE1643B91B009CC9D1 /* folder-010-open@2x.png in Resources */, - 551BF0DF1643B91B009CC9D1 /* folder-010@2x.png in Resources */, - 551BF0E01643B91B009CC9D1 /* folder-011-open@2x.png in Resources */, - 551BF0E11643B91B009CC9D1 /* folder-011@2x.png in Resources */, - 551BF0E21643B91B009CC9D1 /* folder-100-open@2x.png in Resources */, - 551BF0E31643B91B009CC9D1 /* folder-100@2x.png in Resources */, - 551BF0E41643B91B009CC9D1 /* folder-101-open@2x.png in Resources */, - 551BF0E51643B91B009CC9D1 /* folder-101@2x.png in Resources */, - 551BF0E61643B91B009CC9D1 /* folder-110-open@2x.png in Resources */, - 551BF0E71643B91B009CC9D1 /* folder-110@2x.png in Resources */, - 551BF0E81643B91B009CC9D1 /* folder-111-open@2x.png in Resources */, - 55FD7E28180C484500CF473C /* prefs_folder@2x.png in Resources */, - 551BF0E91643B91B009CC9D1 /* folder-111@2x.png in Resources */, - 551BF0EA1643B91B009CC9D1 /* folder-999-open@2x.png in Resources */, - 551BF0EB1643B91B009CC9D1 /* folder-999@2x.png in Resources */, - 55E31E051BC57373001ACE4D /* bottom.png in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 55944CC32E5C8D9F00FB1F8C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 55DF3DD22E924BB10044CC0C /* BaseTests+FileSystemOperations.swift in Sources */, - 55DF3DD32E924BB10044CC0C /* FiltersTests.swift in Sources */, - 55DF3DD42E924BB10044CC0C /* LeafPathTests.swift in Sources */, - 55DF3DD52E924BB10044CC0C /* DeleteFileTests.swift in Sources */, - 55DF3DD62E924BB10044CC0C /* TagsCopyTests.swift in Sources */, - 55DF3DD82E924BB10044CC0C /* CopyFilesTests.swift in Sources */, - 55DF3DD92E924BB10044CC0C /* StringUtils.swift in Sources */, - 55DF3DDB2E924BB10044CC0C /* BufferedInputStreamTests.swift in Sources */, - 55DF3DDC2E924BB10044CC0C /* TagsMoveTests.swift in Sources */, - 55DF3DDD2E924BB10044CC0C /* CaseSensitiveBaseTest.swift in Sources */, - 55DF3DDE2E924BB10044CC0C /* BaseTests+AssertCompareItem.swift in Sources */, - 55A4DF322E9A49B000ED11BB /* EndOfLineTests.swift in Sources */, - 55DF3DDF2E924BB10044CC0C /* AlignmentTests.swift in Sources */, - 55E381C12EEFFD0500CB2F00 /* SymbolicLinkTests.swift in Sources */, - 55B1CE312E9934F800426FEC /* DiffResultBaseTests.swift in Sources */, - 55B1CE322E9934F800426FEC /* DiffResultTests.swift in Sources */, - 55DF3DE02E924BB10044CC0C /* RefreshInfoTests.swift in Sources */, - 55DF3DE12E924BB10044CC0C /* EmptyFolderColorTests.swift in Sources */, - 55DF3DE22E924BB10044CC0C /* BaseTests.swift in Sources */, - 55DF3DE32E924BB10044CC0C /* FoldersWindowControllerTests.swift in Sources */, - 55DF3DE42E924BB10044CC0C /* TagsDeleteTests.swift in Sources */, - 55DF3DE52E924BB10044CC0C /* ComparatorTests.swift in Sources */, - 55B1CE2B2E9934E900426FEC /* DateBuilder.swift in Sources */, - 55DF3DE62E924BB10044CC0C /* TouchFilesTests.swift in Sources */, - 55DF3DE72E924BB10044CC0C /* DisplayTests.swift in Sources */, - 55DF3DE82E924BB10044CC0C /* BaseTests+AssertFileSystem.swift in Sources */, - 55DF3DE92E924BB10044CC0C /* LabelsCreateTests.swift in Sources */, - 55DF3DEA2E924BB10044CC0C /* MockFileOperationManagerDelegate.swift in Sources */, - 55DF3DEB2E924BB10044CC0C /* LabelsMoveTests.swift in Sources */, - 5595902B2EABB15F00B5AB0B /* MockItemComparatorDelegate.swift in Sources */, - 55F9E79C2EDD6221001218C7 /* WhitespacesTests.swift in Sources */, - 55DF3DEC2E924BB10044CC0C /* MoveFilesTests.swift in Sources */, - 55DF3DED2E924BB10044CC0C /* TagsCreateTests.swift in Sources */, - 55DF3DEE2E924BB10044CC0C /* LabelsCopyTests.swift in Sources */, - 55DF3DEF2E924BB10044CC0C /* MockFolderReaderDelegate.swift in Sources */, - 55DF3DF02E924BB10044CC0C /* RenameFilesTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 55C1CBDE2E8BB6A2004FE322 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 55C1CBFD2E8BB6AD004FE322 /* main.swift in Sources */, - 55C1CC072E8BBD85004FE322 /* DocumentWaiter.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 8D15AC300486D014006FF6A4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 55916A102E8A814200ED6629 /* MyDocument.xcdatamodeld in Sources */, - 553741792E9372DF00AB56D0 /* NSAppearance+DarkMode.swift in Sources */, - 5537417A2E9372DF00AB56D0 /* QLPreviewPanel.swift in Sources */, - 55FE894A2F054B260003864A /* FlagSet.swift in Sources */, - 5537417B2E9372DF00AB56D0 /* TrustedPathsPreferencesPanel.swift in Sources */, - 5537417C2E9372DF00AB56D0 /* TouchPickersStackView.swift in Sources */, - 5537417D2E9372DF00AB56D0 /* BasePreferences.swift in Sources */, - 5537417E2E9372DF00AB56D0 /* DualPaneSplitViewDelegate.swift in Sources */, - 5537417F2E9372DF00AB56D0 /* FoldersOutlineView+Menu.swift in Sources */, - 553741802E9372DF00AB56D0 /* AlignRule.swift in Sources */, - 553741812E9372DF00AB56D0 /* VisualizationBox.swift in Sources */, - 553741822E9372DF00AB56D0 /* CommonPrefs+FileCompare.swift in Sources */, - 553741832E9372DF00AB56D0 /* TablePanelView.swift in Sources */, - 553741852E9372DF00AB56D0 /* NSOpenPanel+Application.swift in Sources */, - 553741862E9372DF00AB56D0 /* ConfirmReplace.swift in Sources */, - 553741872E9372DF00AB56D0 /* SessionDiff.ItemType+Paths.swift in Sources */, - 553741882E9372DF00AB56D0 /* FileSizeFormatter.swift in Sources */, - 553741892E9372DF00AB56D0 /* TOPTimestampPredicateEditorRowTemplate.m in Sources */, - 5537418A2E9372DF00AB56D0 /* DocumentWindow.swift in Sources */, - 5537418B2E9372DF00AB56D0 /* CommonPrefs+Name.swift in Sources */, - 5537418C2E9372DF00AB56D0 /* ImageNames.swift in Sources */, - 5537418D2E9372DF00AB56D0 /* FilePathTextField.swift in Sources */, - 5537418E2E9372DF00AB56D0 /* CopyCompareItem.swift in Sources */, - 5537418F2E9372DF00AB56D0 /* ColoredFoldersManager.swift in Sources */, - 553741902E9372DF00AB56D0 /* FilesTableView+Menu.swift in Sources */, - 553741912E9372DF00AB56D0 /* NSBox+Helper.swift in Sources */, - 553741922E9372DF00AB56D0 /* FontPreferencesPanel.swift in Sources */, - 558DEAD02EA2642100F05A10 /* NSWindow+Editing.swift in Sources */, - 553741932E9372DF00AB56D0 /* DisplayOptions.swift in Sources */, - 553741942E9372DF00AB56D0 /* FilesWindowController+Menu.swift in Sources */, - 553741952E9372DF00AB56D0 /* OptionSet+Toggle.swift in Sources */, - 553741962E9372DF00AB56D0 /* URL+Finder.swift in Sources */, - 553741982E9372DF00AB56D0 /* CommonPrefs+FolderCompare.swift in Sources */, - 553741992E9372DF00AB56D0 /* FoldersWindowController+PathControlDelegate.swift in Sources */, - 551DE3B82ED439AF0067AB18 /* PreferencesBoxDataSource+Default.swift in Sources */, - 55D352E72EFD0C5B00C944D5 /* FileSystemTestHelper.swift in Sources */, - 5537419A2E9372DF00AB56D0 /* FontBox.swift in Sources */, - 5537419B2E9372DF00AB56D0 /* FilesTableViewFindTextDelegate.swift in Sources */, - 5537419C2E9372DF00AB56D0 /* DeleteFileOperationExecutor.swift in Sources */, - 5537419D2E9372DF00AB56D0 /* DualPaneSplitView.swift in Sources */, - 5537419E2E9372DF00AB56D0 /* StandardButtons.swift in Sources */, - 5537419F2E9372DF00AB56D0 /* ConfirmationsFoldersBox.swift in Sources */, - 553741A02E9372DF00AB56D0 /* DifferenceNavigatorBox.swift in Sources */, - 55BC33402EDAC96F004035A0 /* TiledImageView.swift in Sources */, - 553741A12E9372DF00AB56D0 /* FolderReaderDelegate.swift in Sources */, - 553741A22E9372DF00AB56D0 /* UDiffScriptBuilder.m in Sources */, - 553741A32E9372DF00AB56D0 /* String+RegularExpression.swift in Sources */, - 553741A42E9372DF00AB56D0 /* FileSystemController.swift in Sources */, - 553741A52E9372DF00AB56D0 /* ComparatorOptions+Helper.swift in Sources */, - 553741A62E9372DF00AB56D0 /* AppearanceBox.swift in Sources */, - 553741A72E9372DF00AB56D0 /* FolderComparisonBox.swift in Sources */, - 553741A82E9372DF00AB56D0 /* FoldersWindowController+Document.swift in Sources */, - 553741A92E9372DF00AB56D0 /* FilterConfig.swift in Sources */, - 553741AA2E9372DF00AB56D0 /* IconUtils.swift in Sources */, - 553741AB2E9372DF00AB56D0 /* ScopeBarItem.swift in Sources */, - 553741AC2E9372DF00AB56D0 /* NSTextView+Style.swift in Sources */, - 553741AD2E9372DF00AB56D0 /* FoldersOutlineView.swift in Sources */, - 559186412E9B821B00A30620 /* DiffSummary.swift in Sources */, - 553741AE2E9372DF00AB56D0 /* PreferredEditorBox.swift in Sources */, - 553741AF2E9372DF00AB56D0 /* MoveCompareItem.swift in Sources */, - 553741B12E9372DF00AB56D0 /* TextFieldSelectionHolder.swift in Sources */, - 553741B22E9372DF00AB56D0 /* TouchFileOperationExecutor.swift in Sources */, - 553741B32E9372DF00AB56D0 /* NSTextFieldCell+Helper.swift in Sources */, - 553741B42E9372DF00AB56D0 /* NSApplication.ModalResponse+ReplaceFile.swift in Sources */, - 553741B52E9372DF00AB56D0 /* DiffOpenerDelegate.swift in Sources */, - 553741B62E9372DF00AB56D0 /* MGScopeBar.m in Sources */, - 553741B72E9372DF00AB56D0 /* PreferencesCheckbox.swift in Sources */, - 553741B82E9372DF00AB56D0 /* FileManager+Attributes.swift in Sources */, - 553741B92E9372DF00AB56D0 /* MainThreadFolderReaderDelegateBridge.swift in Sources */, - 55F9E79A2EDD5DBD001218C7 /* DiffResult.Options.swift in Sources */, - 553741BA2E9372DF00AB56D0 /* VisibleWhitespaces.swift in Sources */, - 553741BB2E9372DF00AB56D0 /* DisplayPositionable.swift in Sources */, - 553741BC2E9372DF00AB56D0 /* AlignRuleWindow.swift in Sources */, - 553741BD2E9372DF00AB56D0 /* DeleteCompareItem.swift in Sources */, - 553741BE2E9372DF00AB56D0 /* DiffChangeType+Color.swift in Sources */, - 553741BF2E9372DF00AB56D0 /* DescriptionOutlineNode.swift in Sources */, - 553741C02E9372DF00AB56D0 /* FilesTableView.swift in Sources */, - 553741C12E9372DF00AB56D0 /* String+Occcurrences.swift in Sources */, - 553741C22E9372DF00AB56D0 /* FoldersWindowController+QLPreviewPanel.swift in Sources */, - 553741C32E9372DF00AB56D0 /* FoldersOutlineView+DifferenceNavigator.swift in Sources */, - 553741C42E9372DF00AB56D0 /* JumpToLineWindow.swift in Sources */, - 553741C52E9372DF00AB56D0 /* EncodingError.swift in Sources */, - 553741C62E9372DF00AB56D0 /* FileExtraOptions.swift in Sources */, - 553741C72E9372DF00AB56D0 /* Array+MoveIndexes.swift in Sources */, - 553741C82E9372DF00AB56D0 /* BigFileFileOperationManager.m in Sources */, - 553741C92E9372DF00AB56D0 /* FoldersWindowController+BaseFolder.swift in Sources */, - 553741CB2E9372DF00AB56D0 /* FoldersWindowController+UICreation.swift in Sources */, - 553741CC2E9372DF00AB56D0 /* FilesWindowController+UICreation.swift in Sources */, - 557BBB0E2ED1BBB00016CBD4 /* DiffLineComponent.swift in Sources */, - 553741CD2E9372DF00AB56D0 /* NSTableView+Row.swift in Sources */, - 553741CE2E9372DF00AB56D0 /* FindText.swift in Sources */, - 553741CF2E9372DF00AB56D0 /* FoldersTraversalBox.swift in Sources */, - 553741D02E9372DF00AB56D0 /* FileError.swift in Sources */, - 55450B192ED47B700048619F /* FileSessionPreferencesWindow+PreferencesBoxDataSource.swift in Sources */, - 553741D12E9372DF00AB56D0 /* OpenEditorError.swift in Sources */, - 551DE39F2ED422820067AB18 /* FilesWindowController+SessionPreferences.swift in Sources */, - 553741D22E9372DF00AB56D0 /* GeneralPreferencesPanel.swift in Sources */, - 553741D32E9372DF00AB56D0 /* CompareItem+DifferenceNavigator.swift in Sources */, - 558DEAD22EA26C5B00F05A10 /* Logger+App.swift in Sources */, - 551DE3AA2ED428B40067AB18 /* FilePreferences.swift in Sources */, - 553741D42E9372DF00AB56D0 /* PreferencesBox.swift in Sources */, - 553741D52E9372DF00AB56D0 /* NSAlert+ReplaceFile.swift in Sources */, - 553741D72E9372DF00AB56D0 /* URL+SymLink.swift in Sources */, - 553741D82E9372DF00AB56D0 /* RecentDocumentPopupMenu.swift in Sources */, - 553741D92E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineViewContextMenu.swift in Sources */, - 553741DA2E9372DF00AB56D0 /* FileAttributes.swift in Sources */, - 553741DB2E9372DF00AB56D0 /* SyncFileController.swift in Sources */, - 553741DC2E9372DF00AB56D0 /* ConfirmationsFilesBox.swift in Sources */, - 553741DD2E9372DF00AB56D0 /* CompareItem+Accessors.swift in Sources */, - 553741DE2E9372DF00AB56D0 /* FilesWindowController+JumpLine.swift in Sources */, - 5595901F2EABA8A100B5AB0B /* CompareChangeType.swift in Sources */, - 553741DF2E9372DF00AB56D0 /* CommonPrefs+ConsoleLog.swift in Sources */, - 553741E02E9372DF00AB56D0 /* NSToolbar+Create.swift in Sources */, - 553741E12E9372DF00AB56D0 /* String+Helper.swift in Sources */, - 553741E32E9372DF00AB56D0 /* LineNumberTableRowView.swift in Sources */, - 553741E42E9372DF00AB56D0 /* FolderReader.swift in Sources */, - 553741E52E9372DF00AB56D0 /* DateFormatter+Helper.swift in Sources */, - 551DE3A62ED4237B0067AB18 /* FileSessionPreferencesWindow.swift in Sources */, - 553741E62E9372DF00AB56D0 /* FoldersWindowController+Navigation.swift in Sources */, - 553741E72E9372DF00AB56D0 /* HistoryEntity.swift in Sources */, - 553741E82E9372DF00AB56D0 /* CompareItem+Clone.swift in Sources */, - 553741E92E9372DF00AB56D0 /* URL+Path.swift in Sources */, - 55BC33432EDACB09004035A0 /* FilesWindowController+RowHeightDataSource.swift in Sources */, - 55BC33442EDACB09004035A0 /* FilesWindowController+SplitViewDelegate.swift in Sources */, - 55BC333E2EDAC69C004035A0 /* RowHeightCalculator.swift in Sources */, - 553741EA2E9372DF00AB56D0 /* PathChooser.swift in Sources */, - 553741EB2E9372DF00AB56D0 /* VDDocumentController.swift in Sources */, - 553741EC2E9372DF00AB56D0 /* CopyFileOperationExecutor.swift in Sources */, - 553741ED2E9372DF00AB56D0 /* FoldersOutlineView+Columns.swift in Sources */, - 553741EE2E9372DF00AB56D0 /* NSTextField+CenterVertically.swift in Sources */, - 554B4A452E9B7B8F0023AAF0 /* EndOfLine.swift in Sources */, - 554B4A462E9B7B8F0023AAF0 /* DiffSide.swift in Sources */, - 554B4A472E9B7B8F0023AAF0 /* DiffSection.swift in Sources */, - 554B4A482E9B7B8F0023AAF0 /* DiffResult+Dump.swift in Sources */, - 554B4A4A2E9B7B8F0023AAF0 /* DiffResult+Section.swift in Sources */, - 554B4A4B2E9B7B8F0023AAF0 /* DiffResult.swift in Sources */, - 554B4A4D2E9B7B8F0023AAF0 /* DiffResult+Lines.swift in Sources */, - 554B4A4E2E9B7B8F0023AAF0 /* DiffLine.swift in Sources */, - 553741EF2E9372DF00AB56D0 /* BufferedInputStream.swift in Sources */, - 553741F02E9372DF00AB56D0 /* ComparisonStandardUserDataSource.swift in Sources */, - 553741F12E9372DF00AB56D0 /* TextPreferencesPanel.swift in Sources */, - 553741F22E9372DF00AB56D0 /* AlignPopupButtonCell.swift in Sources */, - 553741F32E9372DF00AB56D0 /* FolderSelectionInfo+ViewerActionValidator.swift in Sources */, - 553741F42E9372DF00AB56D0 /* PopUpButtonUrl.swift in Sources */, - 553741F52E9372DF00AB56D0 /* OpenEditor.swift in Sources */, - 553741F62E9372DF00AB56D0 /* NSMenu+File.swift in Sources */, - 553741F72E9372DF00AB56D0 /* FilesWindowController+FileInfoBarDelegate.swift in Sources */, - 553741F82E9372DF00AB56D0 /* KeyboardPreferencesPanel.swift in Sources */, - 553741F92E9372DF00AB56D0 /* CompareItem+Metadata.swift in Sources */, - 553741FA2E9372DF00AB56D0 /* TextFieldVerticalCentered.swift in Sources */, - 553741FB2E9372DF00AB56D0 /* KeyEquivalent.swift in Sources */, - 553741FC2E9372DF00AB56D0 /* CompareItem+VisibleItem.swift in Sources */, - 553741FD2E9372DF00AB56D0 /* FoldersWindowController+SessionPreferences.swift in Sources */, - 553741FE2E9372DF00AB56D0 /* CompareItemTableCellView.swift in Sources */, - 553741FF2E9372DF00AB56D0 /* ProgressBarView.swift in Sources */, - 553742002E9372DF00AB56D0 /* FolderManagerError.swift in Sources */, - 553742012E9372DF00AB56D0 /* FolderSelectionInfo.swift in Sources */, - 553742022E9372DF00AB56D0 /* SynchroScrollView.swift in Sources */, - 553742032E9372DF00AB56D0 /* FilesWindowController+Save.swift in Sources */, - 553742042E9372DF00AB56D0 /* FoldersWindowController+ConsoleViewDelegate.swift in Sources */, - 553742052E9372DF00AB56D0 /* PreferencesBoxDataSource.swift in Sources */, - 553742062E9372DF00AB56D0 /* TOPFileSizePredicateEditorRowTemplate.m in Sources */, - 553742072E9372DF00AB56D0 /* FolderReader+Detached.swift in Sources */, - 553742082E9372DF00AB56D0 /* ViewLinkable.swift in Sources */, - 553742092E9372DF00AB56D0 /* SessionPreferencesFiltersBox.swift in Sources */, - 55D71FD62EE584B7007E0FA0 /* AppUpdater.swift in Sources */, - 5537420A2E9372DF00AB56D0 /* FilesWindowController+Common.swift in Sources */, - 5537420B2E9372DF00AB56D0 /* FilesWindowController+NSToolbarDelegate.swift in Sources */, - 5537420C2E9372DF00AB56D0 /* FilesWindowController+TableView.swift in Sources */, - 5537420D2E9372DF00AB56D0 /* NSProgressIndicator+Helper.swift in Sources */, - 5537420E2E9372DF00AB56D0 /* ComparatorOptions.swift in Sources */, - 5537420F2E9372DF00AB56D0 /* PathTimestamps.swift in Sources */, - 553742102E9372DF00AB56D0 /* FilesWindowController+Clipboard.swift in Sources */, - 553742112E9372DF00AB56D0 /* RSVerticallyCenteredTextFieldCell.swift in Sources */, - 553742122E9372DF00AB56D0 /* CompareItem+Description.swift in Sources */, - 553742132E9372DF00AB56D0 /* SessionPreferencesWindow.swift in Sources */, - 553742142E9372DF00AB56D0 /* UserDefinedRulesBox.swift in Sources */, - 553742162E9372DF00AB56D0 /* ProgressIndicatorController.swift in Sources */, - 553742172E9372DF00AB56D0 /* FolderPanelView.swift in Sources */, - 553742182E9372DF00AB56D0 /* ComparatorOptions+Description.swift in Sources */, - 553742192E9372DF00AB56D0 /* FoldersOutlineView+Clipboard.swift in Sources */, - 5536ADBA2EF169820019EFBF /* SessionDiff+ExtraData.swift in Sources */, - 5537421A2E9372DF00AB56D0 /* DatePickersStackView.swift in Sources */, - 5537421B2E9372DF00AB56D0 /* UnifiedDiff.m in Sources */, - 5537421C2E9372DF00AB56D0 /* SyncOutlineView.swift in Sources */, - 5537421D2E9372DF00AB56D0 /* CompareItem+Comparison.swift in Sources */, - 5537421E2E9372DF00AB56D0 /* NSError+Format.swift in Sources */, - 5537421F2E9372DF00AB56D0 /* RefreshInfo.swift in Sources */, - 553742202E9372DF00AB56D0 /* FileDropView+Helper.swift in Sources */, - 553742212E9372DF00AB56D0 /* SyncFileOperationExecutor.swift in Sources */, - 553742222E9372DF00AB56D0 /* CompareItem+FilterConfig.swift in Sources */, - 553742232E9372DF00AB56D0 /* NSStackView+PreferencesPanel.swift in Sources */, - 553742242E9372DF00AB56D0 /* CustomValidationToolbarItem.swift in Sources */, - 553742252E9372DF00AB56D0 /* VisibleItem+Sort.swift in Sources */, - 553742262E9372DF00AB56D0 /* LineNumberTableCellView.swift in Sources */, - 553742272E9372DF00AB56D0 /* FilesWindowController+Slider.swift in Sources */, - 553742282E9372DF00AB56D0 /* VDDocument.swift in Sources */, - 553742292E9372DF00AB56D0 /* FilesWindowController+Read.swift in Sources */, - 5537422A2E9372DF00AB56D0 /* PowerAssertion.swift in Sources */, - 5537422C2E9372DF00AB56D0 /* FilesWindowController+UISetup.swift in Sources */, - 551DE3B42ED42AD90067AB18 /* FilePreferencesComparisonPanel.swift in Sources */, - 5537422D2E9372DF00AB56D0 /* CompareUtil.swift in Sources */, - 5537422E2E9372DF00AB56D0 /* DisplayFiltersScopeBar.swift in Sources */, - 5537422F2E9372DF00AB56D0 /* HistorySessionManager.swift in Sources */, - 553742302E9372DF00AB56D0 /* FilePanelView.swift in Sources */, - 553742312E9372DF00AB56D0 /* FilesWindowController.swift in Sources */, - 553742322E9372DF00AB56D0 /* FilesWindowController+MenuDelegate.swift in Sources */, - 553742332E9372DF00AB56D0 /* DifferenceCounters.swift in Sources */, - 553742342E9372DF00AB56D0 /* ReplaceFileAttributeKey.swift in Sources */, - 553742352E9372DF00AB56D0 /* FoldersWindowController+Select.swift in Sources */, - 553742362E9372DF00AB56D0 /* FoldersWindowController+Comparison.swift in Sources */, - 553742372E9372DF00AB56D0 /* PathView.swift in Sources */, - 553742382E9372DF00AB56D0 /* DiffLine+Color.swift in Sources */, - 553742392E9372DF00AB56D0 /* FileDropView.swift in Sources */, - 5537423A2E9372DF00AB56D0 /* PreferredEditorPopupCell.swift in Sources */, - 5537423B2E9372DF00AB56D0 /* FilePanelView+File.swift in Sources */, - 5537423C2E9372DF00AB56D0 /* KeyboardDocumentBox.swift in Sources */, - 5537423D2E9372DF00AB56D0 /* FoldersOutlineView+VisibleItem.swift in Sources */, - 5537423E2E9372DF00AB56D0 /* FileNameCaseBox.swift in Sources */, - 5537423F2E9372DF00AB56D0 /* URL+Metadata.swift in Sources */, - 553742402E9372DF00AB56D0 /* FoldersWindowController+Menu.swift in Sources */, - 553742412E9372DF00AB56D0 /* OperationSummaryView.swift in Sources */, - 55450B172ED4790B0048619F /* TextPreferencesPanel+PreferencesBoxDataSource.swift in Sources */, - 551DE3B62ED42B4E0067AB18 /* FileComparisonBox.swift in Sources */, - 553742422E9372DF00AB56D0 /* AppDelegate.swift in Sources */, - 559590262EABB06100B5AB0B /* ItemComparator+AlignRegularExpression.swift in Sources */, - 559590272EABB06100B5AB0B /* ItemComparator+Align.swift in Sources */, - 559590282EABB06100B5AB0B /* ItemComparator+Compare.swift in Sources */, - 559590292EABB06100B5AB0B /* ItemComparator.swift in Sources */, - 553742432E9372DF00AB56D0 /* ExpressionBox+Menu.swift in Sources */, - 553742442E9372DF00AB56D0 /* FilesWindowController+PathControlDelegate.swift in Sources */, - 553742452E9372DF00AB56D0 /* VisibleItem.swift in Sources */, - 553742462E9372DF00AB56D0 /* DiffChangeType.swift in Sources */, - 553742472E9372DF00AB56D0 /* NotificationCenter+Helper.swift in Sources */, - 553742482E9372DF00AB56D0 /* NSImage+Tint.swift in Sources */, - 558DEAC62EA263DE00F05A10 /* CFStringEncoding+StringEncoding.swift in Sources */, - 553742492E9372DF00AB56D0 /* NSButton+Helper.swift in Sources */, - 5537424A2E9372DF00AB56D0 /* FoldersWindowController.swift in Sources */, - 5537424B2E9372DF00AB56D0 /* VisibleItem+Find.swift in Sources */, - 5537424C2E9372DF00AB56D0 /* String+Highlights.swift in Sources */, - 5537424D2E9372DF00AB56D0 /* SyncItemsInfo.swift in Sources */, - 5537424E2E9372DF00AB56D0 /* FileOperationManager+FileOperationManagerAction.swift in Sources */, - 5537424F2E9372DF00AB56D0 /* NoodleCustomImageRep.swift in Sources */, - 553742502E9372DF00AB56D0 /* FoldersWindowController+FileSystemControllerDelegate.swift in Sources */, - 553742512E9372DF00AB56D0 /* FilesWindowController+NSWindowDelegate.swift in Sources */, - 553742522E9372DF00AB56D0 /* FilesWindowController+Navigate.swift in Sources */, - 553742532E9372DF00AB56D0 /* DiffCountersItem.swift in Sources */, - 553742542E9372DF00AB56D0 /* SaveFileAccessoryView.swift in Sources */, - 553742552E9372DF00AB56D0 /* FileThumbnailView.swift in Sources */, - 553742562E9372DF00AB56D0 /* FilesTableView+EditorData.swift in Sources */, - 553742572E9372DF00AB56D0 /* ComparatorPopUpButtonCell.swift in Sources */, - 553742582E9372DF00AB56D0 /* IntegerFormatter.swift in Sources */, - 553742592E9372DF00AB56D0 /* SessionDiff+ResolvePath.swift in Sources */, - 5537425A2E9372DF00AB56D0 /* CompareItem+NSTableCellView.swift in Sources */, - 559590212EABAFC200B5AB0B /* SessionDiff+ItemComparator.swift in Sources */, - 5537425B2E9372DF00AB56D0 /* DocumentError.swift in Sources */, - 5537425C2E9372DF00AB56D0 /* FolderSelectionInfo+FileSystemActionValidator.swift in Sources */, - 5595901D2EABA7FE00B5AB0B /* CompareSummary.swift in Sources */, - 5537425D2E9372DF00AB56D0 /* FoldersWindowController+Exclude.swift in Sources */, - 5537425E2E9372DF00AB56D0 /* Preferences.swift in Sources */, - 5537425F2E9372DF00AB56D0 /* OutlineViewItemDelegate.swift in Sources */, - 553742612E9372DF00AB56D0 /* NSImage+SymbolCompat.swift in Sources */, - 553742622E9372DF00AB56D0 /* TableViewContextMenuDelegate.swift in Sources */, - 553742632E9372DF00AB56D0 /* NSPredicate+Objc.m in Sources */, - 553742642E9372DF00AB56D0 /* FileSummaryView.swift in Sources */, - 553742652E9372DF00AB56D0 /* CompareItemTableRowView.swift in Sources */, - 553742662E9372DF00AB56D0 /* FolderSelectionInfo+FolderActionValidator.swift in Sources */, - 553742672E9372DF00AB56D0 /* CommonPrefs+Helper.swift in Sources */, - 553742682E9372DF00AB56D0 /* FilePathTableCellView.swift in Sources */, - 553742692E9372DF00AB56D0 /* AttributedMenuItem.swift in Sources */, - 5537426A2E9372DF00AB56D0 /* URL+StructuredContent.swift in Sources */, - 5537426B2E9372DF00AB56D0 /* SessionPreferencesComparisonPanel.swift in Sources */, - 5537426C2E9372DF00AB56D0 /* FolderCompareInfoWindow.swift in Sources */, - 5537426D2E9372DF00AB56D0 /* NSToolbarItem+Create.swift in Sources */, - 5537426E2E9372DF00AB56D0 /* MoveFileOperationExecutor.swift in Sources */, - 5537426F2E9372DF00AB56D0 /* URL+FileManager.swift in Sources */, - 553742712E9372DF00AB56D0 /* ConfirmationsDocumentsBox.swift in Sources */, - 553742722E9372DF00AB56D0 /* ExpressionBox.swift in Sources */, - 553742732E9372DF00AB56D0 /* CompareItem+LeafPath.swift in Sources */, - 553742742E9372DF00AB56D0 /* SessionDiff.swift in Sources */, - 553742762E9372DF00AB56D0 /* TimeToleranceView.swift in Sources */, - 553742782E9372DF00AB56D0 /* FoldersOutlineView+Enumerate.swift in Sources */, - 553742792E9372DF00AB56D0 /* HistoryController.swift in Sources */, - 5537427A2E9372DF00AB56D0 /* ConsoleToolbarView.swift in Sources */, - 5537427B2E9372DF00AB56D0 /* HistorySearchField.swift in Sources */, - 5537427C2E9372DF00AB56D0 /* URL+ResourceFork.swift in Sources */, - 5537427D2E9372DF00AB56D0 /* LinkButton.swift in Sources */, - 5537427E2E9372DF00AB56D0 /* ReplaceInfoView.swift in Sources */, - 5537427F2E9372DF00AB56D0 /* NSAlert+DirtyFiles.swift in Sources */, - 553742802E9372DF00AB56D0 /* CommonPrefs.swift in Sources */, - 553742812E9372DF00AB56D0 /* FilesWindowController+LinesDetail.swift in Sources */, - 553742822E9372DF00AB56D0 /* FileOperationManager+Util.swift in Sources */, - 553742832E9372DF00AB56D0 /* ConsoleView.swift in Sources */, - 553742842E9372DF00AB56D0 /* CommonPrefs+DifferenceNavigator.swift in Sources */, - 553742852E9372DF00AB56D0 /* FileOperationExecutor.swift in Sources */, - 553742862E9372DF00AB56D0 /* AlignmentPanel.swift in Sources */, - 553742872E9372DF00AB56D0 /* FoldersOutlineView+ExternalApp.swift in Sources */, - 553742882E9372DF00AB56D0 /* PreferencesPanelDataSource.swift in Sources */, - 553742892E9372DF00AB56D0 /* DiffCountersTextFieldCell.swift in Sources */, - 5537428A2E9372DF00AB56D0 /* FoldersWindowController+FoldersOutlineView.swift in Sources */, - 5537428B2E9372DF00AB56D0 /* TableViewCommon.swift in Sources */, - 5537428C2E9372DF00AB56D0 /* CompareItem+Path.swift in Sources */, - 5537428D2E9372DF00AB56D0 /* FolderPreferencesPanel.swift in Sources */, - 5537428E2E9372DF00AB56D0 /* FileOperationManager.swift in Sources */, - 5537428F2E9372DF00AB56D0 /* StandardUserPreferencesBoxDataSource.swift in Sources */, - 553742902E9372DF00AB56D0 /* SecureBookmark.swift in Sources */, - 553742912E9372DF00AB56D0 /* FilesScopeBar.swift in Sources */, - 553742922E9372DF00AB56D0 /* OpenEditorAttribute.swift in Sources */, - 553742932E9372DF00AB56D0 /* WindowCancelOperation.swift in Sources */, - 553742942E9372DF00AB56D0 /* VisibleItem+QLPreviewItem.swift in Sources */, - 553742952E9372DF00AB56D0 /* FilesWindowController+FilesTableViewContextMenu.swift in Sources */, - 553742962E9372DF00AB56D0 /* FolderSelectionInfo+CompareActionValidator.swift in Sources */, - 553742972E9372DF00AB56D0 /* MainThreadComparatorDelegateBridge.swift in Sources */, - 553742982E9372DF00AB56D0 /* DiffCountersItem+DiffResult.swift in Sources */, - 553742992E9372DF00AB56D0 /* FileOperationManagerDelegateImpl.swift in Sources */, - 5537429A2E9372DF00AB56D0 /* SessionDiff+AlignRule.swift in Sources */, - 5537429B2E9372DF00AB56D0 /* FoldersWindowController+MenuDelegate.swift in Sources */, - 5537429C2E9372DF00AB56D0 /* MGRecessedPopUpButtonCell.m in Sources */, - 5537429D2E9372DF00AB56D0 /* FolderSelectionInfo+FilterActionValidator.swift in Sources */, - 5537429E2E9372DF00AB56D0 /* OpenDiffCommand.swift in Sources */, - 5537429F2E9372DF00AB56D0 /* RenameCompareItem.swift in Sources */, - 553742A02E9372DF00AB56D0 /* DisplayOptions+Helper.swift in Sources */, - 553742A12E9372DF00AB56D0 /* SessionPreferencesWindow+Data.swift in Sources */, - 553742A22E9372DF00AB56D0 /* FolderReader+Log.swift in Sources */, - 553742A32E9372DF00AB56D0 /* NSArrayController+Paths.swift in Sources */, - 553742A42E9372DF00AB56D0 /* NSPredicateEditor+Fix.swift in Sources */, - 553742A52E9372DF00AB56D0 /* FoldersWindowController+NSToolbarDelegate.swift in Sources */, - 553742A62E9372DF00AB56D0 /* NSAlert+Helper.swift in Sources */, - 553742A72E9372DF00AB56D0 /* FoldersWindowController+NSWindowDelegate.swift in Sources */, - 553742A82E9372DF00AB56D0 /* FilesWindowController+FilesScopeBar.swift in Sources */, - 553742A92E9372DF00AB56D0 /* NSManagedObjectContext+Helpers.swift in Sources */, - 553742AA2E9372DF00AB56D0 /* String+Path.swift in Sources */, - 553742AB2E9372DF00AB56D0 /* TimeInterval+Helper.swift in Sources */, - 553742AC2E9372DF00AB56D0 /* ColorScheme.swift in Sources */, - 553742AD2E9372DF00AB56D0 /* FilesWindowController+Document.swift in Sources */, - 553742AE2E9372DF00AB56D0 /* PathControl.swift in Sources */, - 553742AF2E9372DF00AB56D0 /* TouchController.swift in Sources */, - 553742B02E9372DF00AB56D0 /* CompletionIndicator.swift in Sources */, - 553742B12E9372DF00AB56D0 /* FoldersWindowController+UISetup.swift in Sources */, - 553742B22E9372DF00AB56D0 /* NSTableView+Font.swift in Sources */, - 553742B32E9372DF00AB56D0 /* ActionBarView.swift in Sources */, - 553742B42E9372DF00AB56D0 /* TouchCompareItem.swift in Sources */, - 553742B52E9372DF00AB56D0 /* HistoryFetchedResultsControllerDelegate.swift in Sources */, - 553742B62E9372DF00AB56D0 /* CommonPrefs+Font.swift in Sources */, - 553742B72E9372DF00AB56D0 /* NSWorkspace+Finder.swift in Sources */, - 553742B82E9372DF00AB56D0 /* FoldersOutlineViewFindTextDelegate.swift in Sources */, - 553742B92E9372DF00AB56D0 /* AlignTestResultBox.swift in Sources */, - 553742BA2E9372DF00AB56D0 /* VisibleItem+Log.swift in Sources */, - 553742BB2E9372DF00AB56D0 /* HistoryTableView.swift in Sources */, - 553742BC2E9372DF00AB56D0 /* SessionDiff+NSSortDescriptor.swift in Sources */, - 553742BD2E9372DF00AB56D0 /* NSPopUpButton+Helper.swift in Sources */, - 553742BE2E9372DF00AB56D0 /* NSColor+Hex.swift in Sources */, - 553742BF2E9372DF00AB56D0 /* FileInfoBar.swift in Sources */, - 553742C02E9372DF00AB56D0 /* TitlePredicateEditorRowTemplate.m in Sources */, - 553742C12E9372DF00AB56D0 /* ErrorsView.swift in Sources */, - 553742C22E9372DF00AB56D0 /* IconUtils+CompareItemIconUtils.swift in Sources */, - 553742C32E9372DF00AB56D0 /* SessionTypeError.swift in Sources */, - 553742C42E9372DF00AB56D0 /* FoldersOutlineView+FileCount.swift in Sources */, - 553742C52E9372DF00AB56D0 /* SessionPreferencesFiltersPanel.swift in Sources */, - 553742C62E9372DF00AB56D0 /* FoldersWindowController+DisplayFiltersScopeBarDelegate.swift in Sources */, - 553742C72E9372DF00AB56D0 /* DiffCountersItem+CompareItem.swift in Sources */, - 553742C82E9372DF00AB56D0 /* DisplayFiltersPopUpButtonCell.swift in Sources */, - 553742C92E9372DF00AB56D0 /* NSEvent+VirtualKeys.swift in Sources */, - 553742CA2E9372DF00AB56D0 /* DescriptionOutlineNode+FolderCompareInfo.swift in Sources */, - 553742CB2E9372DF00AB56D0 /* WindowOSD.swift in Sources */, - 553742CC2E9372DF00AB56D0 /* NSPasteboard+Helper.swift in Sources */, - 553742CD2E9372DF00AB56D0 /* ConfirmationsPreferencesPanel.swift in Sources */, - 558DEACB2EA263F500F05A10 /* EncodingManager.swift in Sources */, - 558DEACC2EA263F500F05A10 /* SelectEncodingsPanel.swift in Sources */, - 558DEACD2EA263F500F05A10 /* EncodingPopUpButtonCell.swift in Sources */, - 553742CE2E9372DF00AB56D0 /* CompareItem.swift in Sources */, - 553742CF2E9372DF00AB56D0 /* NSTextField+Helper.swift in Sources */, - 553742D02E9372DF00AB56D0 /* FolderViewBox.swift in Sources */, - 553742D12E9372DF00AB56D0 /* SessionDiff+Types.swift in Sources */, - 553742D22E9372DF00AB56D0 /* FiltersPredicateEditor.swift in Sources */, - 553742D32E9372DF00AB56D0 /* HistoryEntityTableCellView.swift in Sources */, - 55916A112E8A814200ED6629 /* History.xcdatamodeld in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 55944CCC2E5C8D9F00FB1F8C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 8D15AC270486D014006FF6A4 /* VisualDiffer */; - targetProxy = 55944CCB2E5C8D9F00FB1F8C /* PBXContainerItemProxy */; - }; - 55C1CC002E8BB749004FE322 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 55C1CBE12E8BB6A2004FE322 /* visdiff */; - targetProxy = 55C1CBFF2E8BB749004FE322 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 089C165FFE840EACC02AAC07 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 55B2120A2490AB3B001717DD /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - 2A37F4B9FDCFA73011CA2CEA /* Credits.rtf */ = { - isa = PBXVariantGroup; - children = ( - 55B212092490AB3B001717DD /* en */, - ); - name = Credits.rtf; - sourceTree = ""; - }; - 2F7446A70DB6BCF400F9684A /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 55E3EF3524C2D06800F7A95A /* Base */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; - 55F6B0611AEA93E30096E55E /* Localizable.stringsdict */ = { - isa = PBXVariantGroup; - children = ( - 55B2120C2490AB3B001717DD /* en */, - ); - name = Localizable.stringsdict; - sourceTree = ""; - }; - 55F6B0641AEA99680096E55E /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 55B2120B2490AB3B001717DD /* en */, - ); - name = Localizable.strings; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 26FC0AA60875C8B900E6366F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55F013FC27A90C46003EA2D4 /* AppConfig.xcconfig */; - buildSettings = { - AUTOMATION_APPLE_EVENTS = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEFINES_MODULE = YES; - ENABLE_MODULE_VERIFIER = YES; - ENABLE_TESTING_SEARCH_PATHS = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = VisualDiffer_Prefix.pch; - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - INSTALL_PATH = /Applications; - MACOSX_DEPLOYMENT_TARGET = 13.5; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++14"; - OTHER_CFLAGS = "-DDEBUG"; - OTHER_LDFLAGS = ""; - PRODUCT_NAME = VisualDiffer; - PROVISIONING_PROFILE = ""; - "SWIFT_ACTIVE_COMPILATION_CONDITIONS[arch=*]" = DEBUG; - SWIFT_OBJC_BRIDGING_HEADER = "VisualDiffer-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 6.0; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - 26FC0AA70875C8B900E6366F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55F013FC27A90C46003EA2D4 /* AppConfig.xcconfig */; - buildSettings = { - AUTOMATION_APPLE_EVENTS = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - COMBINE_HIDPI_IMAGES = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - ENABLE_MODULE_VERIFIER = YES; - ENABLE_TESTING_SEARCH_PATHS = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = VisualDiffer_Prefix.pch; - GCC_PREPROCESSOR_DEFINITIONS = ( - NDEBUG, - NS_BLOCK_ASSERTIONS, - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - INSTALL_PATH = /Applications; - MACOSX_DEPLOYMENT_TARGET = 13.5; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++14"; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - PRODUCT_NAME = VisualDiffer; - PROVISIONING_PROFILE = ""; - SWIFT_OBJC_BRIDGING_HEADER = "VisualDiffer-Bridging-Header.h"; - SWIFT_VERSION = 6.0; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; - 26FC0AAA0875C8B900E6366F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ""; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = ( - "-DDEBUG", - "-Wall", - "-Wextra", - "-Wno-unused-parameter", - "-Wno-missing-field-initializers", - ); - PROVISIONING_PROFILE = "59d2fc6b-4fa7-4828-8895-d3d003484da9"; - SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - }; - name = Debug; - }; - 26FC0AAB0875C8B900E6366F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ""; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - OTHER_CFLAGS = ( - "-Wall", - "-Wextra", - "-Wno-unused-parameter", - "-Wno-missing-field-initializers", - ); - PROVISIONING_PROFILE = "59d2fc6b-4fa7-4828-8895-d3d003484da9"; - SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_COMPILATION_MODE = wholemodule; - }; - name = Release; - }; - 558893662E7FF93B003B7117 /* DebugNoSandbox */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ""; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = ( - "-DDEBUG", - "-Wall", - "-Wextra", - "-Wno-unused-parameter", - "-Wno-missing-field-initializers", - ); - PROVISIONING_PROFILE = "59d2fc6b-4fa7-4828-8895-d3d003484da9"; - SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - }; - name = DebugNoSandbox; - }; - 558893672E7FF93B003B7117 /* DebugNoSandbox */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55F013FC27A90C46003EA2D4 /* AppConfig.xcconfig */; - buildSettings = { - AUTOMATION_APPLE_EVENTS = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEFINES_MODULE = YES; - ENABLE_MODULE_VERIFIER = YES; - ENABLE_TESTING_SEARCH_PATHS = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = VisualDiffer_Prefix.pch; - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - INSTALL_PATH = /Applications; - MACOSX_DEPLOYMENT_TARGET = 13.5; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++14"; - OTHER_CFLAGS = "-DDEBUG"; - OTHER_LDFLAGS = ""; - PRODUCT_NAME = VisualDiffer; - PROVISIONING_PROFILE = ""; - "SWIFT_ACTIVE_COMPILATION_CONDITIONS[arch=*]" = DEBUG; - SWIFT_OBJC_BRIDGING_HEADER = "VisualDiffer-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 6.0; - WRAPPER_EXTENSION = app; - }; - name = DebugNoSandbox; - }; - 558893692E7FF93B003B7117 /* DebugNoSandbox */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_USER_SELECTED_FILES = readwrite; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GENERATE_INFOPLIST_FILE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VisualDiffer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VisualDiffer"; - }; - name = DebugNoSandbox; - }; - 55944CCE2E5C8D9F00FB1F8C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_USER_SELECTED_FILES = readwrite; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GENERATE_INFOPLIST_FILE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VisualDiffer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VisualDiffer"; - }; - name = Debug; - }; - 55944CD02E5C8D9F00FB1F8C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_USER_SELECTED_FILES = readwrite; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GENERATE_INFOPLIST_FILE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VisualDiffer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VisualDiffer"; - }; - name = Release; - }; - 55C1CBE72E8BB6A2004FE322 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55C1CBF32E8BB6AD004FE322 /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 15.7; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 55C1CBE82E8BB6A2004FE322 /* DebugNoSandbox */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55C1CBF32E8BB6AD004FE322 /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 15.7; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - }; - name = DebugNoSandbox; - }; - 55C1CBE92E8BB6A2004FE322 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55C1CBF32E8BB6AD004FE322 /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 15.7; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 55D71FDA2EE58630007E0FA0 /* Sparkle */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ""; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; - OTHER_CFLAGS = ( - "-Wall", - "-Wextra", - "-Wno-unused-parameter", - "-Wno-missing-field-initializers", - ); - PROVISIONING_PROFILE = "59d2fc6b-4fa7-4828-8895-d3d003484da9"; - SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_COMPILATION_MODE = wholemodule; - }; - name = Sparkle; - }; - 55D71FDB2EE58630007E0FA0 /* Sparkle */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55D71FD72EE584FF007E0FA0 /* AppConfig-Sparkle.xcconfig */; - buildSettings = { - AUTOMATION_APPLE_EVENTS = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - COMBINE_HIDPI_IMAGES = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - ENABLE_MODULE_VERIFIER = YES; - ENABLE_TESTING_SEARCH_PATHS = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_MODEL_TUNING = G5; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = VisualDiffer_Prefix.pch; - GCC_PREPROCESSOR_DEFINITIONS = ( - NDEBUG, - NS_BLOCK_ASSERTIONS, - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - INSTALL_PATH = /Applications; - MACOSX_DEPLOYMENT_TARGET = 13.5; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++14"; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - PRODUCT_NAME = VisualDiffer; - PROVISIONING_PROFILE = ""; - SWIFT_OBJC_BRIDGING_HEADER = "VisualDiffer-Bridging-Header.h"; - SWIFT_VERSION = 6.0; - WRAPPER_EXTENSION = app; - }; - name = Sparkle; - }; - 55D71FDC2EE58630007E0FA0 /* Sparkle */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 55C1CBF32E8BB6AD004FE322 /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 15.7; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - }; - name = Sparkle; - }; - 55D71FDD2EE58630007E0FA0 /* Sparkle */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 557C18202E7E8C0D00381A3A /* AppConfig.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_USER_SELECTED_FILES = readwrite; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GENERATE_INFOPLIST_FILE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 14.6; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VisualDiffer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VisualDiffer"; - }; - name = Sparkle; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 26FC0AA50875C8B900E6366F /* Build configuration list for PBXNativeTarget "VisualDiffer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 26FC0AA60875C8B900E6366F /* Debug */, - 558893672E7FF93B003B7117 /* DebugNoSandbox */, - 26FC0AA70875C8B900E6366F /* Release */, - 55D71FDB2EE58630007E0FA0 /* Sparkle */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 26FC0AA90875C8B900E6366F /* Build configuration list for PBXProject "VisualDiffer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 26FC0AAA0875C8B900E6366F /* Debug */, - 558893662E7FF93B003B7117 /* DebugNoSandbox */, - 26FC0AAB0875C8B900E6366F /* Release */, - 55D71FDA2EE58630007E0FA0 /* Sparkle */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 55944CCD2E5C8D9F00FB1F8C /* Build configuration list for PBXNativeTarget "VisualDifferTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 55944CCE2E5C8D9F00FB1F8C /* Debug */, - 558893692E7FF93B003B7117 /* DebugNoSandbox */, - 55944CD02E5C8D9F00FB1F8C /* Release */, - 55D71FDD2EE58630007E0FA0 /* Sparkle */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 55C1CBE62E8BB6A2004FE322 /* Build configuration list for PBXNativeTarget "visdiff" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 55C1CBE72E8BB6A2004FE322 /* Debug */, - 55C1CBE82E8BB6A2004FE322 /* DebugNoSandbox */, - 55C1CBE92E8BB6A2004FE322 /* Release */, - 55D71FDC2EE58630007E0FA0 /* Sparkle */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 55C165602EE444050082A24C /* XCRemoteSwiftPackageReference "Sparkle" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/sparkle-project/Sparkle"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.8.1; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 55C165612EE444050082A24C /* Sparkle */ = { - isa = XCSwiftPackageProductDependency; - package = 55C165602EE444050082A24C /* XCRemoteSwiftPackageReference "Sparkle" */; - productName = Sparkle; - }; -/* End XCSwiftPackageProductDependency section */ - -/* Begin XCVersionGroup section */ - 55916A082E8A814200ED6629 /* History.xcdatamodeld */ = { - isa = XCVersionGroup; - children = ( - 55916A0B2E8A814200ED6629 /* History.xcdatamodel */, - ); - currentVersion = 55916A0B2E8A814200ED6629 /* History.xcdatamodel */; - path = History.xcdatamodeld; - sourceTree = ""; - versionGroupType = wrapper.xcdatamodel; - }; - 55916A092E8A814200ED6629 /* MyDocument.xcdatamodeld */ = { - isa = XCVersionGroup; - children = ( - 55916A0C2E8A814200ED6629 /* MyDocument1.0.xcdatamodel */, - 55916A0D2E8A814200ED6629 /* MyDocument1.1.xcdatamodel */, - 55916A0E2E8A814200ED6629 /* MyDocument1.2.xcdatamodel */, - 55916A0F2E8A814200ED6629 /* MyDocument1.3.xcdatamodel */, - 553A58692EF2758800685D02 /* MyDocument1.4.xcdatamodel */, - ); - currentVersion = 553A58692EF2758800685D02 /* MyDocument1.4.xcdatamodel */; - path = MyDocument.xcdatamodeld; - sourceTree = ""; - versionGroupType = wrapper.xcdatamodel; - }; -/* End XCVersionGroup section */ - }; - rootObject = 2A37F4A9FDCFA73011CA2CEA /* Project object */; -} diff --git a/VisualDiffer.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/VisualDiffer.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/VisualDiffer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/VisualDiffer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/VisualDiffer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/VisualDiffer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/VisualDiffer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VisualDiffer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index f55129a..0000000 --- a/VisualDiffer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "e721da7f9826abdffcb6185e886155efa2514bd6234475f1afa893e29eb258d6", - "pins" : [ - { - "identity" : "sparkle", - "kind" : "remoteSourceControl", - "location" : "https://github.com/sparkle-project/Sparkle", - "state" : { - "revision" : "5581748cef2bae787496fe6d61139aebe0a451f6", - "version" : "2.8.1" - } - } - ], - "version" : 3 -} diff --git a/VisualDiffer.xcodeproj/xcshareddata/xcschemes/Sparkle.xcscheme b/VisualDiffer.xcodeproj/xcshareddata/xcschemes/Sparkle.xcscheme deleted file mode 100644 index 01bf4fd..0000000 --- a/VisualDiffer.xcodeproj/xcshareddata/xcschemes/Sparkle.xcscheme +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/VisualDiffer.xcodeproj/xcshareddata/xcschemes/VisualDiffer.xcscheme b/VisualDiffer.xcodeproj/xcshareddata/xcschemes/VisualDiffer.xcscheme deleted file mode 100644 index 6250eb7..0000000 --- a/VisualDiffer.xcodeproj/xcshareddata/xcschemes/VisualDiffer.xcscheme +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/VisualDiffer.xctestplan b/VisualDiffer.xctestplan deleted file mode 100644 index 9b711d3..0000000 --- a/VisualDiffer.xctestplan +++ /dev/null @@ -1,25 +0,0 @@ -{ - "configurations" : [ - { - "id" : "A77105F3-FD86-46B3-BF25-87D6010789C8", - "name" : "Configuration 1", - "options" : { - - } - } - ], - "defaultOptions" : { - - }, - "testTargets" : [ - { - "parallelizable" : false, - "target" : { - "containerPath" : "container:VisualDiffer.xcodeproj", - "identifier" : "55944CC62E5C8D9F00FB1F8C", - "name" : "VisualDifferTests" - } - } - ], - "version" : 2 -} diff --git a/VisualDiffer.xcworkspace/contents.xcworkspacedata b/VisualDiffer.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index a82d0be..0000000 --- a/VisualDiffer.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/VisualDiffer.xcworkspace/xcshareddata/IDETemplateMacros.plist b/VisualDiffer.xcworkspace/xcshareddata/IDETemplateMacros.plist deleted file mode 100644 index 6d3a1d1..0000000 --- a/VisualDiffer.xcworkspace/xcshareddata/IDETemplateMacros.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - FILEHEADER - -// ___FILENAME___ -// ___PROJECTNAME___ -// -// Created by ___FULLUSERNAME___ on ___DATE___. -// Copyright (c) ___YEAR___ ___ORGANIZATIONNAME___ -// - - \ No newline at end of file diff --git a/VisualDiffer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/VisualDiffer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/VisualDiffer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/VisualDiffer.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VisualDiffer.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index f55129a..0000000 --- a/VisualDiffer.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "e721da7f9826abdffcb6185e886155efa2514bd6234475f1afa893e29eb258d6", - "pins" : [ - { - "identity" : "sparkle", - "kind" : "remoteSourceControl", - "location" : "https://github.com/sparkle-project/Sparkle", - "state" : { - "revision" : "5581748cef2bae787496fe6d61139aebe0a451f6", - "version" : "2.8.1" - } - } - ], - "version" : 3 -} diff --git a/VisualDifferHelp/Contents/Info.plist b/VisualDifferHelp/Contents/Info.plist deleted file mode 100644 index 374f0e8..0000000 --- a/VisualDifferHelp/Contents/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en-US - CFBundleIdentifier - com.visualdiffer.help - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - VisualDifferHelp - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - hbwr - CFBundleVersion - 1.0 - HPDBookAccessPath - VisualDiffer.html - HPDBookIconPath - common/images/appicon16.png - HPDBookKBProduct - visualdiffer1 - HPDBookIndexPath - VisualDiffer.helpindex - HPDBookKBURL - https://wiki.visualdiffer.com - HPDBookTitle - VisualDiffer Help - HPDBookType - 3 - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/VisualDiffer.html b/VisualDifferHelp/Contents/Resources/English.lproj/VisualDiffer.html deleted file mode 100644 index 1db3f25..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/VisualDiffer.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - VisualDiffer Help - - - - - - - - - -

-
VisualDiffer Help
- - -
-

Folders Colors Legend

- Colors used to represent folder content comparison results -

File Colors Legend

- Colors used to represent lines differences results -

File Filters

- Filter used to exclude files from comparison -

Filenames Alignment Rules

- Align filenames with user defined rules -

Third Party Applications Integration

- Integrate with svn and git clients -

OSX Finder

- Add context menu to OSX Finder -
-
- -
- - - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/alignRules.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/alignRules.html deleted file mode 100644 index 02b6772..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/alignRules.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - File Names Alignment Rules - - - - - - - - - - - -
-
-

File Names Alignment Rules

-

Change the default file names alignment method

-
-
- -
-
-

Before being compared two files they must be aligned, the alignment rule can be configured

- -

Align by Name Case Sensitivity

- -

The default alignment method compares file name strings, if they are match case then they will be aligned.
-Suppose you have the scenario shown below

- - - - - - - - - - - - - - -
LeftRight
winter.jpg 
 WINTER.JPG
summer.jpgsummer.jpg

By default winter.jpg and WINTER.JPG will be not aligned because their strings don't match uppercase and lowercase characters.

- -

This result sometimes isn't what you expect, maybe you want to ignore uppercase/lowercase differences.
-This can be achieved selecting 'Ignore File Name Case' from popup menu.

- - - - - - - - - - - -
LeftRight
winter.jpgWINTER.JPG
summer.jpgsummer.jpg

Align by HFS+ Filesystem Case

- -

Normally the HFS+ disks are formatted ignoring the file name case, so if we have winter.jpg and WINTER.JPG they cannot be created on same directory (because they have the same name).

- -

When the HFS+ partitions are formatted with case sensitive support the file names winter.jpg and WINTER.JPG are two different file names and can be created on same directory.

- -

It is possible to let VisualDiffer determine the file name alignment case algorithm looking at HFS partition case, so three scenarios are possible

- -
  1. left and right are both case insensitive
  2. -
  3. left and right are both case sensitive
  4. -
  5. left/right is case sensitive and right/left is case insensitive
  6. -

If the last scenario is true, the alignment will try to be smart, first it searches if a match case is available (winter.jpg with winter.jpg) then it tries to align with the most similar name.

- -

Align by User Defined Rules (required OSX Lion or above)

- -

There are scenarios where it is necessary to align files having different names.

- -

The most simple scenario has files with same name but different extension as shown below

- - - - - - - - - - - - - - - - - -
LeftRight
001.jpg 
 001.raw
002.jpg 
 002.RAW

Suppose you want to align ignoring the file extension to produce the result shown below

- - - - - - - - -
LeftRight
001.jpg001.raw
002.jpg002.RAW

This can be achieved using VisualDiffer 'user defined alignment rules'.

- -

Managing Alignment Rules

- -

You can create, edit or delete rules from Session Preferences Dialog

- -

- image -

- -

More rules can be assigned to a VisualDiffer session comparison, they are evaluated from top to bottom.

- -

Creating a Rule

- -

When you add a new rule (or edit an existing one) you access to the dialog shown below

- -

- image -

- -

A rule has

- -
  • a left regular expression
  • -
  • a right pattern expression
  • -

- If you are not familiar with regular expressions please refer to ICU documentation. -

- -

Left Regular Expression

- -

The left regexp is used to match a filename on the left side of VisualDiffer Folder View, we want to find all jpg files so we create a group (.*) followed by .jpg extension.

- -

Right Pattern Expression

- -

Notice the right expression isn't a regular expression, it should contain some special patterns used to access to regular expression groups (if any).

- -

The right expression is used to match a file name on right side of VisualDiffer Folder View, we want to match .raw files having the same name of file on left side.
-So we use the $1 to get the first group present on regexp (in this example there is only one group but you can have nine groups).

- -

Now if you save the rule and then run the comparison you obtain the result shown below

- - - - - - - - - - - - - - -
LeftRight
001.jpg001.raw
002.jpg 
 002.RAW

But 002.jpg and 002.RAW files are not aligned because 002.RAW extension is uppercase, this can be easily fixed ignoring the case on right expression as shown in figure shown below

- -

- image -

- -

Test Rule

- -

It is possible to verify immediately if the expressions work as expected using the 'Test Rule', just type a file name and the 'Result' field will be filled accordingly.

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/comparisonMethods.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/comparisonMethods.html deleted file mode 100644 index 9d0f9b1..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/comparisonMethods.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - Comparison Method - - - - - - - - - - - -
-
-

Comparison Method

-

The folders comparison method, special files and metadata

-
-
- -
-
-

Comparison Method

- -

It is possible to choose which method to use to compare two folders.
-The method to use is strictly related to user needs, comparing source code (files large only few kilobytes) should use a content comparison but to find which movies (files larger than a gigabytes) are not present on right side is faster compare by file size or file timestamp.

- -

Every file is treated as binary and it is compared byte by byte, only 'Compare file content ignoring line ending differences' compares text.

- -

The complete list of supported comparison methods

- - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
Compare file sizes -
Very quick
Two file are identical if they have the same file size
Compare file timestamps -
Very quick
Two file are identical if they have the same timestamp. -
It is used the Unix last modified date for comparison. -
If file A timestamp is less than file B it is marked as older and it will be shown with different color. -
It is possible to set a span in seconds to consider two files with same timestamp, see below
Compare file timestamps and sizes -
Very quick
Compare the files timestamp, only if it is different then compare the size.
Compare file content only -
Slow on large files
Compare files as binary, byte by byte
Compare file timestamp, size and content -
Slow on large files
Compare the file content, only if content is different compare the timestamp and only if timestamp differs compare size
Compare file content ignoring line ending differences -
Slow on large files
Compare file as plain text, read line by line and compare lines. -
The line ending character is ignored so a DOS file (with lines separated by CR+LF) matches an Unix file (with lines separated by LF) if, ignoring the newlines, the content is identical

Compare Finder Metadata

- -

It is possible to compare OSX Finder Metadata.

- -

When the metadata comparison mismatches, for example left file's label is red and right file's label is blue, the other comparison methods (size, timestamp, content) are not evaluated.

- -

The complete list of supported metadata

- - - - - - - - - - - -
MetadataDescription
LabelCompare all labels assigned to a file
Tags -
Available in OSX 10.9 or above
Compare all tags assigned to a file

Folders Traversal

- -

User can choose to not traverse special files like symbolic links and packages

- - - - - - - - - - - - - - -
FileDescription
Follow Symbolic LinksIf checked the symbolic links will be traversed
Skip PackagesIf checked special OSX files (so called bundle or packages) -
like Applications and Frameworks will not be traversed
Check Resource ForksCheck if files are resource forks, this can slow down the comparison.
-When this option is on if the file is a resource fork its size is determined from its structured data. -

Timestamp Tolerance

- -

The timestamp comparison considers two files matching when the time (including the seconds) is identical but sometime it would be useful to have a range of tolerance for example when the difference is between 5 seconds.

- -

You can set this tolerance entering a positive integer number into the text field

- -

Ignore differences of X seconds or less

- -

Choose Files to View according to differences found

- -

After the comparison complete you can choose which files to view

- -

Definition: Orphan indicates a file present only on one side

- -

The complete list of show options

- - - - - - - - - - - - - - - - - - - - -
ChoiceDescription
Show AllNo matters which comparison result, show files
Only MismatchesShow only files with some mismatch (size, timestamp or content)
Only MatchesShow only identical files
No OrphansShow files present both on left and right side
Only OrphansShow files present only on left or only on right

Choose Folders to View

- -

It is possible to hide empty and orphans folders. -Folders can be empty because they don't contains any file on disk or because all files/folders inside them are filtered.
-Orphans folders are present only on one side (left or right)

- -

Show Filtered Files

- -

When a filter founds matches the elements are not visible but in case it's necessary to shown them you can click on the Filtered button

- -

Some files are excluded by defaults, the complete list is shown below.
-Default filters can be changed or totally deleted from Session Preferences window

- - - - - - - - - - - -
DescriptionFile
Backup and system files.DS_Store, *~
Control Version System files CVS, .svn, .git, .hg", .bzr
-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/fileFilters.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/fileFilters.html deleted file mode 100644 index 4cca78c..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/fileFilters.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - File Filters - - - - - - - - - - - -
-
-

File Filters

-

Filters can be used to exclude files from comparison, copy and delete actions

-
-
- -
-
-
- - - - - - -
-
-

Contents

-
- -
-

Files can be excluded from comparison specifying filenames, relative paths, file sizes and file modification dates.

- -

Filename Filters

- -

Available filters for filenames are shown below

- - - - - - - - - - - - - - - - - - - - - - - -
Comparator FilterDescription
ContainsFilename contains the specified string
Begins withFilename begins with the specified string
Ends withFilename ends with the specified string
IsFilename is equal to the specified string
Is notFilename is not equal to the specified string
Is likeFilename equals the specified string -
? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters

Path Filters

- -

It is possible to filter by specifying a path relative to root, this allows to exclude specific paths
-Example

- -

Left root path: /Users/dave/sources/
-Folder content

- -
-/Users/dave/sources/
-  project_1
-    src/
-  project_2
-    src/
-    index.txt
-
- - -

To exclude only the folder src under project_1 you can specify project_1/src as path filter.
-The path is relative to root /Users/dave/sources/, you can use both folders and files as path filter.
-The context menu item 'Exclude' automatically excludes folders as path.

- -

Pay attention to not add extra characters, the following paths will never found
project_1//src   invalid, double slash
project_1/src/   invalid, trailing slash

- -

File Size Filters

- -

It is possible to specify files (not folders) to exclude based on their size expressed in bytes, KB, MB and GB

- -

File Modification Date Filters

- -

It is possible to specify files (not folders) to exclude based on their last modification date and time

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/fileLegend.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/fileLegend.html deleted file mode 100644 index 9bd1469..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/fileLegend.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - File Colors Legend - - - - - - - - - - - -
-
-

File Colors Legend

-

Colors used in File Differ View

-
-
- -
-
-

Lines colors in the File Differ View are based on differences found

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ColorDescription
Text content on left and right is the same
Text content present on the left differs from text content present on the right
Text content is present only on right, on left the line is missing
Text content is present only on left, on right the line is missing
Text content has been copied from the other side, this mark the document as edited and can be saved
- - - - -
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/finder.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/finder.html deleted file mode 100644 index 7ca76ca..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/finder.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - OSX Finder Integration - - - - - - - - - - - -
-
-

OSX Finder Integration

-

Launch VisualDiffer directly from OSX Finder

-
-
- -
-
-

You can run VisualDiffer directly from OSX Finder, selecting two files or two folders

- -

You don't need to run VisualDiffer then select items to compare, if you are on Finder do that from it

- -
-

The OSX Finder integration requires a script not present on standard installation, you must download it from visualdiffer github's repository

-

You can download the script here

-
- - -

Compare with VisualDiffer

- -

Add to Finder context menu (under Services) a new menu item to allow files and folders comparison.

- -

How to Install

- -
  • On Lion or above double click on "Compare with VisualDiffer.workflow" file
  • -
  • On Snow Leopard you must copy manually the file Compare with VisualDiffer.workflow inside the directory /Users/your account -name/Library/Services. If the directory Services doesn't exist you must create it
  • -

More details about installation are available here

- -

How to Use

- -
  • When elements to compare are both visible on Finder window simply select the two files or folders and choose "Compare with VisualDiffer" -from service menu
  • -
  • When elements to compare are on different folders - -
    1. select first element from Finder and click on the service menu item
    2. -
    3. browse to second element and again click on the service menu item
    4. -
    5. the comparison will start
    6. -
  • -

If you have more Finder windows visible first element can be selected using menu item from first window and second from another Finder window

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/foldersLegend.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/foldersLegend.html deleted file mode 100644 index 67d6278..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/foldersLegend.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - Folder Colors Legend - - - - - - - - - - - -
-
-

Folder Colors Legend

-

Folders icons assume colors based on their content or filter status

-
-
- -
-
-

Folders are shown using different colors to quickly visualize the files comparison results

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FolderDescription
Matched folder (all contained files are same on other side)
Folder contains only older files than other side
Folder contains only modified files
Folder contains only modified files and files older than other side
Folder contains only files not present on other side (so called orphan folder)
Folder contains files not present on other side and files older that other side
Folder contains modified files and files not present on the other side
Folder matches file filters or is empty and the 'Empty folders' setting is set to on
- - - - -
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/confirmations.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/confirmations.html deleted file mode 100644 index f7e58e1..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/confirmations.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - Confirmations Preferences - - - - - - - - - - - -
-
-

Confirmations Preferences

-

Application Preferences - Confirmations

-
-
- -
-
-

Confirmation and Warning Messages for Folders

- -

Include Filtered Items By Default

- -

When a copy/move operation begins the filtered files are not included, it is possible to change this behavior turning on this option.

- -

If the summary dialog doesn't appear, this option can be changed without accessing to preference dialog simply holding down the ALT key while clicking on toolbar button or context menu item

- -

Confirmation for Documents

- -

As any application in the world, closing a window with settings changed (comparison method, filters, alignment rules and so on) will show a confirmation dialog.

- -

This prompt can be annoying when user does many quick comparisons and doesn't want to save any session, if this is your case the prompt can be suppressed turning on this option.

- -

As usual the user can always save the session selecting Save or Save As from the File menu.

- -

WARNING if this option is on and you spend a lot of time to define the filters or the alignment rules and then you close the window/application any settings will be lost, use this option with care.

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/folder.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/folder.html deleted file mode 100644 index 3dfbda7..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/folder.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - Folder Preferences - - - - - - - - - - - -
-
-

Folder Preferences

-

Application Preferences - Folder

-
-
- -
-
-

Folders Traversal

- -

User can choose to not traverse special files like symbolic links and packages

- - - - - - - - - - - - - - -
FileDescription
Follow Symbolic LinksIf checked the symbolic links will be traversed
Skip PackagesIf checked special OSX files (so called bundle or packages) -
like Applications and Frameworks will not be traversed
Check Resource ForksCheck if files are resource forks, this can slow down the comparison.
-When this option is on if the file is a resource fork its size is determined from its structured data. -
-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/fonts.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/fonts.html deleted file mode 100644 index 8183289..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/fonts.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Fonts Preferences - - - - - - - - - - - -
-
-

Fonts Preferences

-

Application Preferences - Fonts

-
-
- -
-
-

Fonts

- -

User can select the fonts to use in Folders Differ View and File Differ View

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/general.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/general.html deleted file mode 100644 index 470b047..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/general.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - General Preferences - - - - - - - - - - - -
-
-

General Preferences

-

Application Preferences - General

-
-
- -
-
-

Comparison Method

- -

It is possible to choose which method to use to compare two folders.
-The method to use is strictly related to user needs, comparing source code (files large only few kilobytes) should use a content comparison but to find which movies (files larger than a gigabytes) are not present on right side is faster compare by file size or file timestamp.

- -

Every file is treated as binary and it is compared byte by byte, only 'Compare file content ignoring line ending differences' compares text.

- -

The complete list of supported comparison methods

- - - - - - - - - - - - - - - - - - - - - - - -
MethodDescription
Compare file sizes -
Very quick
Two file are identical if they have the same file size
Compare file timestamps -
Very quick
Two file are identical if they have the same timestamp. -
It is used the Unix last modified date for comparison. -
If file A timestamp is less than file B it is marked as older and it will be shown with different color. -
It is possible to set a span in seconds to consider two files with same timestamp, see below
Compare file timestamps and sizes -
Very quick
Compare the files timestamp, only if it is different then compare the size.
Compare file content only -
Slow on large files
Compare files as binary, byte by byte
Compare file timestamp, size and content -
Slow on large files
Compare the file content, only if content is different compare the timestamp and only if timestamp differs compare size
Compare file content ignoring line ending differences -
Slow on large files
Compare file as plain text, read line by line and compare lines. -
The line ending character is ignored so a DOS file (with lines separated by CR+LF) matches an Unix file (with lines separated by LF) if, ignoring the newlines, the content is identical

Compare Finder Metadata

- -

It is possible to compare OSX Finder Metadata.

- -

When the metadata comparison mismatches, for example left file's label is red and right file's label is blue, the other comparison methods (size, timestamp, content) are not evaluated.

- -

The complete list of supported metadata

- - - - - - - - - - - -
MetadataDescription
LabelCompare all labels assigned to a file
Tags -
Available in OSX 10.9 or above
Compare all tags assigned to a file

Choose Files to View according to differences found

- -

After the comparison complete you can choose which files to view

- -

Definition: Orphan indicates a file present only on one side

- -

The complete list of show options

- - - - - - - - - - - - - - - - - - - - -
ChoiceDescription
Show AllNo matters which comparison result, show files
Only MismatchesShow only files with some mismatch (size, timestamp or content)
Only MatchesShow only identical files
No OrphansShow files present both on left and right side
Only OrphansShow files present only on left or only on right

Choose Folders to View

- -

It is possible to hide empty and orphans folders. -Folders can be empty because they don't contains any file on disk or because all files/folders inside them are filtered.
-Orphans folders are present only on one side (left or right)

- -

Show Filtered Files

- -

When a filter founds matches the elements are not visible but in case it's necessary to shown them you can click on the Filtered button

- -

Some files are excluded by defaults, the complete list is shown below.
-Default filters can be changed or totally deleted from Session Preferences window

- - - - - - - - - - - -
DescriptionFile
Backup and system files.DS_Store, *~
Control Version System files CVS, .svn, .git, .hg", .bzr

Preferred Viewer/Editor

- -

It is possible to open a file with an external application, the application can be chosen from the context menu.
-When user double clicks a file, the default system application (if any) will be launched.
-The default system application can be overridden (only inside VisualDiffer) specifying the predefined one on "Preferred Viewer/Editor"

- -

- The default system application is not changed. -

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/prefsKeyboard.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/prefsKeyboard.html deleted file mode 100644 index 9266c3e..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/prefsKeyboard.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Keyboard Shortcuts - - - - - - - - - - - -
-
-

Keyboard Shortcuts

-

Application Preferences - Keyboard

-
-
- -
-
-

ESC key closes documents/application

- -

Together with standard shortcuts to close document windows (Cmd-W) and application (Cmd-Q) it's possible to use the ESC key to close current window, when pressed on the last open window the application quits.

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/text.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/text.html deleted file mode 100644 index b1f9f10..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/preferences/text.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - Text Preferences - - - - - - - - - - - -
-
-

Text Preferences

-

Application Preferences - Text

-
-
- -
-
-
- - - - - - -
-
-

Contents

-
- -
-

Show additional difference indicator

- -

Show a symbol near the lines to indicate the type of difference

- - - - - - - - - - - - - - -
SymbolDescription
!Left and right lines contain are different
-Line is present on left but not on right side
+Line is present on right but not on left side

- image -

- -

Tab Width

- -

Tab characters are replaces with spaces, it is possible to specify the number of spaces for tab

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/trustedPaths.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/trustedPaths.html deleted file mode 100644 index ca11511..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/trustedPaths.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - Trusted Paths - - - - - - - - - - - -
-
-

Trusted Paths

-

How to handle paths with Apple sandbox

-
-
- -
-
-

Why we need trusted paths

- -

Starting from OSX Lion applications run in the so called sandbox that is an isolated and secure environment.
-Applications accessing to file system (reading file, listing a folder) need explicitly that user gives permission to application.
-User gives his permission to access to a file (or folder) selecting it from a file panel or dragging path to application.
-VisualDiffer prompts a file panel when it can't access to a file due to permission error, after user selection the path is stored as trusted so next time it isn't necessary to re-prompt.

- -

Using VisualDiffer as external diff tool with sandbox

- -

VisualDiffer can be used to compare files using other applications (Dreamweaver, SourceTree and so on), for example to compare a remote file version with the local one.

- -

Applications like Dreamweaver save to temporary file the remote version and then pass the generated full path to VisualDiffer but this path isn't trusted so VisualDiffer shows the file panel every time.

- -

This behaviour generates a very frustrating user experience because the user must select the file from the panel again and again.

- -

Use Trusted Paths

- -

Trusted paths resolve this problem, applications save files always on their own specific temporary folders, for example SourceTree can generate the following path

- -
-   /var/folders/mr/5dfd6w717cj5j374q24fdxsh0000gn/T/2PhpDQ_UnifiedDiff.m
-
-
- -

but trusting it works only once because the path component 5dfd6w717cj5j374q24fdxsh0000gn/T/ is randomly generated and changes every time.

- -

We can trust /var/folders that is the root used by SourceTree to saves temporary files.

- -

Trusting /var/folders ensures every path generated inside it (at any deeper subfolder level) doesn't require any additional user permission and the file panel prompts no longer appears

- -

Where configure Trusted Paths

- -

You can add (drag&drop is supported) and/or remove paths manually from the VisualDiffer Preference panel.

- -

The paths colored in red no longer exist.

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/English.lproj/topics/unixshell.html b/VisualDifferHelp/Contents/Resources/English.lproj/topics/unixshell.html deleted file mode 100644 index c76db03..0000000 --- a/VisualDifferHelp/Contents/Resources/English.lproj/topics/unixshell.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - Unix Shell Scripting Support - - - - - - - - - - - -
-
-

Unix Shell Scripting Support

-

Launching the UI from the Unix command line

-
-
- -
-
-

Using visdiff

- -

Inside /Applications/VisualDiffer.app/Contents/Resources folder it is present the executable file visdiff that integrates with VisualDiffer.app

- -

You can launch visdiff to visually show files or folders comparison using the following syntax

- -
-   visdiff <left file or folder> <right file or folder>
-
-
- -

Wait for document close

- -

If you need to wait the user closes the diff document window (not necessary to quit application) associated to visdiff command you can pass the switch --wait

- -

For example

- -
-
-      $ visdiff ~/original.txt ~/modified.txt --wait
-
-   
-
- - -

Creating a symbolic link

- -

VisualDiffer.app doesn't install the terminal application but manually it is possible to create the link to the shell command.

- -

It requires root access, from a terminal prompt type the command shown below

- -
-
-      $ sudo ln -s /Applications/VisualDiffer.app/Contents/Resources/visdiff /usr/bin/visdiff
-
-   
-
- - -

Note: VisualDiffer can't install command line tool from its User Interface to comply with Apple's submission guidelines

- -

OSX sandbox, temporary files and annoying file open panel prompts

- -

VisualDiffer was sandboxed starting from version 1.4.2 and many users found using it very annoying because any comparison prompts to pick folders/files.

- -

This problem was fixed on version 1.4.3 introducing the so called "Trusted Paths", please refer to Trusted Paths for further details

-
-
- - - diff --git a/VisualDifferHelp/Contents/Resources/common/css/pygment_trac.css b/VisualDifferHelp/Contents/Resources/common/css/pygment_trac.css deleted file mode 100644 index e65cedf..0000000 --- a/VisualDifferHelp/Contents/Resources/common/css/pygment_trac.css +++ /dev/null @@ -1,70 +0,0 @@ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f0f3f3; } -.highlight .c { color: #0099FF; font-style: italic } /* Comment */ -.highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ -.highlight .k { color: #006699; font-weight: bold } /* Keyword */ -.highlight .o { color: #555555 } /* Operator */ -.highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #009999 } /* Comment.Preproc */ -.highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ -.highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ -.highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ -.highlight .go { color: #AAAAAA } /* Generic.Output */ -.highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #99CC66 } /* Generic.Traceback */ -.highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #006699 } /* Keyword.Pseudo */ -.highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ -.highlight .m { color: #FF6600 } /* Literal.Number */ -.highlight .s { color: #CC3300 } /* Literal.String */ -.highlight .na { color: #330099 } /* Name.Attribute */ -.highlight .nb { color: #336666 } /* Name.Builtin */ -.highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ -.highlight .no { color: #336600 } /* Name.Constant */ -.highlight .nd { color: #9999FF } /* Name.Decorator */ -.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #CC00FF } /* Name.Function */ -.highlight .nl { color: #9999FF } /* Name.Label */ -.highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #003333 } /* Name.Variable */ -.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #FF6600 } /* Literal.Number.Float */ -.highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ -.highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ -.highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ -.highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ -.highlight .sc { color: #CC3300 } /* Literal.String.Char */ -.highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #CC3300 } /* Literal.String.Double */ -.highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ -.highlight .si { color: #AA0000 } /* Literal.String.Interpol */ -.highlight .sx { color: #CC3300 } /* Literal.String.Other */ -.highlight .sr { color: #33AAAA } /* Literal.String.Regex */ -.highlight .s1 { color: #CC3300 } /* Literal.String.Single */ -.highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ -.highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #003333 } /* Name.Variable.Class */ -.highlight .vg { color: #003333 } /* Name.Variable.Global */ -.highlight .vi { color: #003333 } /* Name.Variable.Instance */ -.highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ - -.type-csharp .highlight .k { color: #0000FF } -.type-csharp .highlight .kt { color: #0000FF } -.type-csharp .highlight .nf { color: #000000; font-weight: normal } -.type-csharp .highlight .nc { color: #2B91AF } -.type-csharp .highlight .nn { color: #000000 } -.type-csharp .highlight .s { color: #A31515 } -.type-csharp .highlight .sc { color: #A31515 } diff --git a/VisualDifferHelp/Contents/Resources/common/css/stylesheet.css b/VisualDifferHelp/Contents/Resources/common/css/stylesheet.css deleted file mode 100644 index 2227baa..0000000 --- a/VisualDifferHelp/Contents/Resources/common/css/stylesheet.css +++ /dev/null @@ -1,463 +0,0 @@ -/******************************************************************************* -Slate Theme for Github Pages -by Jason Costello, @jsncostello -*******************************************************************************/ - -@import url(pygment_trac.css); - -/******************************************************************************* -MeyerWeb Reset -*******************************************************************************/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font: inherit; - vertical-align: baseline; -} - -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} - -ol, ul { - list-style: none; -} - -blockquote, q { -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -a:focus { - outline: none; -} - -/******************************************************************************* -Theme Styles -*******************************************************************************/ - -body { - box-sizing: border-box; - color:#373737; - background: #212121; - font-size: 16px; - font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; - line-height: 1.5; - -webkit-font-smoothing: antialiased; -} - -body#small { - background: #F2F2F2; -} - -h1, h2, h3, h4, h5, h6 { - margin: 10px 0; - font-weight: 700; - color:#222222; - font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; - letter-spacing: -1px; -} - -h1 { - font-size: 18px; - font-weight: 700; -} - -h2 { - padding-bottom: 10px; - font-size: 16px; - background: url('../images/bg_hr.png') repeat-x bottom; -} - -h3 { - font-size: 12px; -} - -h4 { - font-size: 10px; -} - -h5 { - font-size: 9px; -} - -h6 { - font-size: 8px; -} - -p { - margin: 10px 0 15px 0; -} - -footer p { - color: #f2f2f2; - text-align: right; -} - -a { - text-decoration: none; - color: #007edf; - text-shadow: none; - - transition: color 0.5s ease; - transition: text-shadow 0.5s ease; - -webkit-transition: color 0.5s ease; - -webkit-transition: text-shadow 0.5s ease; - -moz-transition: color 0.5s ease; - -moz-transition: text-shadow 0.5s ease; - -o-transition: color 0.5s ease; - -o-transition: text-shadow 0.5s ease; - -ms-transition: color 0.5s ease; - -ms-transition: text-shadow 0.5s ease; -} - -#main_content a:hover { - color: #0069ba; - text-shadow: #0090ff 0px 0px 2px; -} - -footer a:hover { - color: #43adff; - text-shadow: #0090ff 0px 0px 2px; -} - -em { - font-style: italic; -} - -strong { - font-weight: bold; -} - -img { - position: relative; - margin: 0 auto; - max-width: 739px; - padding: 5px; - margin: 10px 0 10px 0; - border: 1px solid #ebebeb; - - box-shadow: 0 0 5px #ebebeb; - -webkit-box-shadow: 0 0 5px #ebebeb; - -moz-box-shadow: 0 0 5px #ebebeb; - -o-box-shadow: 0 0 5px #ebebeb; - -ms-box-shadow: 0 0 5px #ebebeb; -} - -pre, code { - width: 100%; - color: #222; - background-color: #fff; - - font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; - font-size: 14px; - - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - - - -} - -pre { - width: 100%; - padding: 10px; - box-shadow: 0 0 10px rgba(0,0,0,.1); - overflow: auto; -} - -code { - padding: 3px; - margin: 0 3px; - box-shadow: 0 0 10px rgba(0,0,0,.1); -} - -pre code { - display: block; - box-shadow: none; -} - -blockquote { - color: #666; - margin-bottom: 20px; - padding: 0 0 0 20px; - border-left: 3px solid #bbb; -} - -ul, ol, dl { - margin-bottom: 15px -} - -ul li { - list-style: inside; - padding-left: 20px; -} - -ol li { - list-style: decimal inside; - padding-left: 20px; -} - -dl dt { - font-weight: bold; -} - -dl dd { - padding-left: 20px; - font-style: italic; -} - -dl p { - padding-left: 20px; - font-style: italic; -} - -hr { - height: 1px; - margin-bottom: 5px; - border: none; - background: url('../images/bg_hr.png') repeat-x center; -} - -table { - border: 1px solid #373737; - margin-bottom: 20px; - text-align: left; - } - -th { - font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; - padding: 10px; - background: #373737; - color: #fff; - text-align: center;; - } - -td { - padding: 10px; - border: 1px solid #373737; - } - -form { - background: #f2f2f2; - padding: 20px; -} - -/******************************************************************************* -Full-Width Styles -*******************************************************************************/ - -.outer { - width: 100%; -} - -.inner { - position: relative; - max-width: 960px; - padding: 0 10px; - margin: 0 auto; -} - -#header_wrap { - background: #212121; - background: -moz-linear-gradient(top, #373737, #212121); - background: -webkit-linear-gradient(top, #373737, #212121); - background: -ms-linear-gradient(top, #373737, #212121); - background: -o-linear-gradient(top, #373737, #212121); - background: linear-gradient(top, #373737, #212121); -} - -#header_wrap .inner { - padding: 10px 0 0 0; -} - -#project_title { - margin: 0; - color: #fff; - font-size: 42px; - font-weight: 700; - text-shadow: #111 0px 0px 10px; - background: url("../images/logo.png") no-repeat scroll 0 0 transparent; - background-size: 64px 64px; -} - -#project_title > a { - color: #FFF; - margin-left: 70px; -} - -#project_tagline { - color: #FFB90F; - font-size: 24px; - font-weight: 300; - background: none; - text-shadow: #111 0px 0px 10px; - margin: 0; - padding-bottom: 4px; -} - -#main_content_wrap { - background: #f2f2f2; - border-bottom: 1px solid #111; - padding-bottom: 5px; -} - -#main_content_wrap { - border-bottom: 0; -} - -#main_content { - padding-top: 10px; -} - -#footer_wrap { - background: #212121; -} - - -#project_title_small { - margin: 0 0 0 10px; - color: #fff; - font-size: 24px; - font-weight: 700; - text-shadow: #111 0px 0px 10px; - background: url("../images/logo.png") no-repeat scroll 0 0 transparent; - background-size: 32px 32px; -} - -#project_title_small > span { - color: #FFF; - margin-left: 40px; -} - -#project_tagline_small { - color: #FFB90F; - font-size: 18px; - font-weight: 300; - background: none; - text-shadow: #111 0px 0px 10px; - margin: 0 0 0 10px; - padding: 0 0 10px; -} - -acronym { - border: 1px dotted; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - color: #007EDF; - padding: 1px; -} - -/******************************************************************************* -* Topics using in Apple Help index page -*******************************************************************************/ - -.topics-logo { - text-align: center; - float: left; - margin-right: 40px; -} - -.topics-container { - margin: 30px auto 50px; - width: 450px; -} - -.topics { - border-left: 1px solid silver; - float: left; - padding-left: 10px; - width: 250px; -} - -.topics-list { - list-style-type: none; -} - -.topics-appname { - color: #475461; - background-color: #F9F9F9; - border: 1px solid #BFBFBF; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - text-align: center; - font-size: 18px; - font-weight: bold; - margin-bottom: 10px; -} - -/******************************************************************************* -Small Device Styles -*******************************************************************************/ - -@media screen and (max-width: 480px) { - body { - font-size:14px; - } - - #downloads { - display: none; - } - - .inner { - min-width: 320px; - max-width: 480px; - } - - #project_title { - font-size: 32px; - } - - h1 { - font-size: 28px; - } - - h2 { - font-size: 24px; - } - - h3 { - font-size: 21px; - } - - h4 { - font-size: 18px; - } - - h5 { - font-size: 14px; - } - - h6 { - font-size: 12px; - } - - code, pre { - min-width: 320px; - max-width: 480px; - font-size: 11px; - } - -} diff --git a/VisualDifferHelp/Contents/Resources/common/css/toc.css b/VisualDifferHelp/Contents/Resources/common/css/toc.css deleted file mode 100644 index 5807de5..0000000 --- a/VisualDifferHelp/Contents/Resources/common/css/toc.css +++ /dev/null @@ -1,40 +0,0 @@ - -#toc, .toc, .mw-warning { - background-color: #F9F9F9; - border: 1px solid #AAAAAA; - font-size: 95%; - padding: 5px; -} -#toc h2, .toc h2 { - border: medium none; - display: inline; - font-size: 100%; - font-weight: bold; - padding: 0; -} -#toc #toctitle, .toc #toctitle, #toc .toctitle, .toc .toctitle { - text-align: center; -} -#toc ul, .toc ul { - list-style-image: none; - list-style-type: none; - margin-left: 0; - margin-bottom: 0; - padding-left: 0; - text-align: left; -} -#toc ul ul, .toc ul ul { - margin: 0 0 0 2em; -} -#toc .toctoggle, .toc .toctoggle { - font-size: 94%; -} - -#toc ul li { - list-style-type: none; - padding-left: 0; -} - -#toc-container { - margin-bottom: 10px; -} \ No newline at end of file diff --git a/VisualDifferHelp/Contents/Resources/common/css/vd.css b/VisualDifferHelp/Contents/Resources/common/css/vd.css deleted file mode 100644 index 25b2473..0000000 --- a/VisualDifferHelp/Contents/Resources/common/css/vd.css +++ /dev/null @@ -1,50 +0,0 @@ -table.folder-legend tr td:nth-child(1) { - text-align: center; -} - -table.folder-legend tr td:nth-child(2) { - vertical-align: middle; -} - -table.folder-legend tr td { - padding: 0 10px; -} - -img.folder-legend { - max-width: 16px; - max-height: 16px; -} - -#main_content a[href ^="https://"], .link-https { - background: url('../images/lock-icon.png') center right no-repeat; - padding-right: 16px; -} - -#main_content a[href ^="http://"], .link-external { - background: url('../images/external-icon.png') center right no-repeat; - padding-right: 16px; -} - -table.file-legend tr td { - padding: 0 10px; -} - -td.file-legend-same { - background-color: rgb(0, 0, 0); -} - -td.file-legend-different { - background-color: rgb(168, 232, 166); -} - -td.file-legend-missing-left { - background-color: rgb(220, 229, 245); -} - -td.file-legend-missing-right { - background-color: rgb(255, 225, 225); -} - -td.file-legend-merged { - background-color: rgb(255, 204, 0); -} diff --git a/VisualDifferHelp/Contents/Resources/common/images/appicon16.png b/VisualDifferHelp/Contents/Resources/common/images/appicon16.png deleted file mode 100644 index b527998..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/appicon16.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/bg_hr.png b/VisualDifferHelp/Contents/Resources/common/images/bg_hr.png deleted file mode 100644 index 7973bd6..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/bg_hr.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/external-icon.png b/VisualDifferHelp/Contents/Resources/common/images/external-icon.png deleted file mode 100644 index ed7a55e..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/external-icon.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000-open.png deleted file mode 100644 index 024948e..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000-open@2x.png deleted file mode 100644 index 0fdc807..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000.png deleted file mode 100644 index 9c54edc..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000@2x.png deleted file mode 100644 index 8fb02ff..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-000@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001-open.png deleted file mode 100644 index dd5564a..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001-open@2x.png deleted file mode 100644 index 923da1e..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001.png deleted file mode 100644 index 685aaec..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001@2x.png deleted file mode 100644 index 836b556..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-001@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010-open.png deleted file mode 100644 index 2ab5c93..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010-open@2x.png deleted file mode 100644 index 72c9fcf..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010.png deleted file mode 100644 index fd52395..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010@2x.png deleted file mode 100644 index 74718a1..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-010@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011-open.png deleted file mode 100644 index 7e9707f..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011-open@2x.png deleted file mode 100644 index 6e13fd6..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011.png deleted file mode 100644 index 435f426..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011@2x.png deleted file mode 100644 index 29f76c1..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-011@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100-open.png deleted file mode 100644 index b22e51a..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100-open@2x.png deleted file mode 100644 index 4e52c98..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100.png deleted file mode 100644 index 708fe84..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100@2x.png deleted file mode 100644 index c7722a9..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-100@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101-open.png deleted file mode 100644 index f886134..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101-open@2x.png deleted file mode 100644 index 26bfc7b..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101.png deleted file mode 100644 index 6bd7bf0..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101@2x.png deleted file mode 100644 index 563c90a..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-101@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110-open.png deleted file mode 100644 index 69bedb8..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110-open@2x.png deleted file mode 100644 index bcffb58..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110.png deleted file mode 100644 index 12d7e12..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110@2x.png deleted file mode 100644 index dc58706..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-110@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111-open.png deleted file mode 100644 index 69bedb8..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111-open@2x.png deleted file mode 100644 index bcffb58..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111.png deleted file mode 100644 index 12d7e12..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111@2x.png deleted file mode 100644 index dc58706..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-111@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999-open.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999-open.png deleted file mode 100644 index da7c8f3..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999-open.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999-open@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999-open@2x.png deleted file mode 100644 index c8bbe77..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999-open@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999.png deleted file mode 100644 index e242577..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999@2x.png b/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999@2x.png deleted file mode 100644 index ef31494..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/folder/folder-999@2x.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/lock-icon.png b/VisualDifferHelp/Contents/Resources/common/images/lock-icon.png deleted file mode 100644 index 01339ea..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/lock-icon.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/logo.png b/VisualDifferHelp/Contents/Resources/common/images/logo.png deleted file mode 100644 index cfb56e2..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/logo.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/screenshots/alignRule.png b/VisualDifferHelp/Contents/Resources/common/images/screenshots/alignRule.png deleted file mode 100644 index 129a538..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/screenshots/alignRule.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/screenshots/alignRuleIgnoreCase.png b/VisualDifferHelp/Contents/Resources/common/images/screenshots/alignRuleIgnoreCase.png deleted file mode 100644 index 59a62cc..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/screenshots/alignRuleIgnoreCase.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/screenshots/differenceIndicator.png b/VisualDifferHelp/Contents/Resources/common/images/screenshots/differenceIndicator.png deleted file mode 100644 index 943de33..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/screenshots/differenceIndicator.png and /dev/null differ diff --git a/VisualDifferHelp/Contents/Resources/common/images/screenshots/sessionPrefAlignment.png b/VisualDifferHelp/Contents/Resources/common/images/screenshots/sessionPrefAlignment.png deleted file mode 100644 index 6e8e53e..0000000 Binary files a/VisualDifferHelp/Contents/Resources/common/images/screenshots/sessionPrefAlignment.png and /dev/null differ diff --git a/VisualDiffer_Prefix.pch b/VisualDiffer_Prefix.pch deleted file mode 100644 index e0d8658..0000000 --- a/VisualDiffer_Prefix.pch +++ /dev/null @@ -1,9 +0,0 @@ -// -// Prefix header for all source files of the 'VisualDiffer' target in the 'VisualDiffer' project -// - -#ifdef __OBJC__ - #import - #import - #import -#endif diff --git a/Warnings.xcconfig b/Warnings.xcconfig deleted file mode 100644 index c1a18eb..0000000 --- a/Warnings.xcconfig +++ /dev/null @@ -1,51 +0,0 @@ -// -// MoreWarnings.xcconfig -// -// Created by Steven Fisher: -// http://tewha.net/2010/11/better-xcode-warnings-through-xcconfig-files/ -// See also: -// http://boredzo.org/blog/archives/2009-11-07/warnings -// - -GCC_WARN_CHECK_SWITCH_STATEMENTS = YES -GCC_WARN_SHADOW = YES -GCC_WARN_64_TO_32_BIT_CONVERSION = YES -CLANG_WARN_ENUM_CONVERSION = YES -CLANG_WARN_INT_CONVERSION = YES -CLANG_WARN_CONSTANT_CONVERSION = YES -CLANG_WARN_BOOL_CONVERSION = YES; -CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; -GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES -GCC_WARN_ABOUT_RETURN_TYPE = YES -GCC_WARN_MISSING_PARENTHESES = YES -GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES -GCC_WARN_ABOUT_MISSING_NEWLINE = YES -GCC_WARN_SIGN_COMPARE = YES -GCC_WARN_UNDECLARED_SELECTOR = YES -GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; -GCC_WARN_UNUSED_FUNCTION = YES -GCC_WARN_UNUSED_LABEL = YES -GCC_WARN_UNUSED_VALUE = YES -GCC_WARN_UNUSED_VARIABLE = YES -GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES -GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES -GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES -GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES -GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = YES -GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES -CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES -//CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES -CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES -CLANG_WARN_ASSIGN_ENUM = YES -CLANG_WARN_EMPTY_BODY = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES -OTHER_CFLAGS = -Wall -Wextra -Wno-unused-parameter -Wformat=2 -Wunreachable-code -//GCC_TREAT_WARNINGS_AS_ERRORS = YES -RUN_CLANG_STATIC_ANALYZER = YES - -// -Wpartial-availability is an alias of -Wunguarded-availability https://reviews.llvm.org/D23003 -WARNING_CFLAGS = -Wunguarded-availability diff --git a/appcast.xml b/appcast.xml new file mode 100644 index 0000000..b26e930 --- /dev/null +++ b/appcast.xml @@ -0,0 +1,43 @@ + + + + VisualDiffer + VisualDiffer + en + + Version 2.1.2 + + v2.1.2 + +

This is a minor release with bug fixes.

+ +

🚀 Features

+ +
    +
  • None
  • +
+ +

🐛 Bug Fixes

+ +
    +
  • General: Corrected message formatting for title case and sentence case
  • +
  • Folder Compare: Fixed sync dialog not showing items
  • +
  • File Compare: Word-wrap truncates text when slider is not at beginning
  • +
+ + ]]> +
+ Wed, 24 December 2025 00:00:00 +0100 + 2.1.2 + 2.1.2 + 10.13 + +
+
+
\ No newline at end of file diff --git a/archive.plist b/archive.plist deleted file mode 100644 index ac7523f..0000000 --- a/archive.plist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - destination - export - method - app-store - uploadBitcode - - compileBitcode - - uploadSymbols - - signingStyle - automatic - teamID - MLU48HP7P3 - - manageAppVersionAndBuildNumber - - - diff --git a/archive_exporter.conf b/archive_exporter.conf deleted file mode 100644 index 4743dca..0000000 --- a/archive_exporter.conf +++ /dev/null @@ -1,3 +0,0 @@ -ROOT_PATH=~/trash/archive_export/visualdiffer -APP_NAME=VisualDiffer -GDRIVE_PATH=shared_code:code/visualdiffer diff --git a/assets/Images.xcassets/Button/Contents.json b/assets/Images.xcassets/Button/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/assets/Images.xcassets/Button/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Button/browse.imageset/Contents.json b/assets/Images.xcassets/Button/browse.imageset/Contents.json deleted file mode 100644 index ecdf26c..0000000 --- a/assets/Images.xcassets/Button/browse.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "browse-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "browse-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "browse-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "browse-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "browse-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "browse-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "browse-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "browse-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "browse-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-any.png b/assets/Images.xcassets/Button/browse.imageset/browse-any.png deleted file mode 100644 index 0630603..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-any@2x.png b/assets/Images.xcassets/Button/browse.imageset/browse-any@2x.png deleted file mode 100644 index d5e9b3a..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-any@3x.png b/assets/Images.xcassets/Button/browse.imageset/browse-any@3x.png deleted file mode 100644 index f481552..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-dark.png b/assets/Images.xcassets/Button/browse.imageset/browse-dark.png deleted file mode 100644 index cb939d2..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-dark@2x.png b/assets/Images.xcassets/Button/browse.imageset/browse-dark@2x.png deleted file mode 100644 index 3ce6950..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-dark@3x.png b/assets/Images.xcassets/Button/browse.imageset/browse-dark@3x.png deleted file mode 100644 index dad5420..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-light.png b/assets/Images.xcassets/Button/browse.imageset/browse-light.png deleted file mode 100644 index 0630603..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-light@2x.png b/assets/Images.xcassets/Button/browse.imageset/browse-light@2x.png deleted file mode 100644 index d5e9b3a..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/browse.imageset/browse-light@3x.png b/assets/Images.xcassets/Button/browse.imageset/browse-light@3x.png deleted file mode 100644 index f481552..0000000 Binary files a/assets/Images.xcassets/Button/browse.imageset/browse-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/Contents.json b/assets/Images.xcassets/Button/save.imageset/Contents.json deleted file mode 100644 index 75e7052..0000000 --- a/assets/Images.xcassets/Button/save.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "save-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "save-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "save-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "save-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "save-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "save-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "save-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "save-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "save-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Button/save.imageset/save-any.png b/assets/Images.xcassets/Button/save.imageset/save-any.png deleted file mode 100644 index ccc8274..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-any@2x.png b/assets/Images.xcassets/Button/save.imageset/save-any@2x.png deleted file mode 100644 index b28b433..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-any@3x.png b/assets/Images.xcassets/Button/save.imageset/save-any@3x.png deleted file mode 100644 index 3c7a870..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-dark.png b/assets/Images.xcassets/Button/save.imageset/save-dark.png deleted file mode 100644 index a2d8f59..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-dark@2x.png b/assets/Images.xcassets/Button/save.imageset/save-dark@2x.png deleted file mode 100644 index 822f66a..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-dark@3x.png b/assets/Images.xcassets/Button/save.imageset/save-dark@3x.png deleted file mode 100644 index a972398..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-light.png b/assets/Images.xcassets/Button/save.imageset/save-light.png deleted file mode 100644 index ccc8274..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-light@2x.png b/assets/Images.xcassets/Button/save.imageset/save-light@2x.png deleted file mode 100644 index b28b433..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/save.imageset/save-light@3x.png b/assets/Images.xcassets/Button/save.imageset/save-light@3x.png deleted file mode 100644 index 3c7a870..0000000 Binary files a/assets/Images.xcassets/Button/save.imageset/save-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/Contents.json b/assets/Images.xcassets/Button/stop.imageset/Contents.json deleted file mode 100644 index 86b4fe2..0000000 --- a/assets/Images.xcassets/Button/stop.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "stop-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "stop-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "stop-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "stop-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "stop-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "stop-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "stop-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "stop-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "stop-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-any.png b/assets/Images.xcassets/Button/stop.imageset/stop-any.png deleted file mode 100644 index 22dcd4a..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-any@2x.png b/assets/Images.xcassets/Button/stop.imageset/stop-any@2x.png deleted file mode 100644 index e2706cb..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-any@3x.png b/assets/Images.xcassets/Button/stop.imageset/stop-any@3x.png deleted file mode 100644 index 045e751..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-dark.png b/assets/Images.xcassets/Button/stop.imageset/stop-dark.png deleted file mode 100644 index 4f8c82e..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-dark@2x.png b/assets/Images.xcassets/Button/stop.imageset/stop-dark@2x.png deleted file mode 100644 index 6f2328f..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-dark@3x.png b/assets/Images.xcassets/Button/stop.imageset/stop-dark@3x.png deleted file mode 100644 index 3dbf5c1..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-light.png b/assets/Images.xcassets/Button/stop.imageset/stop-light.png deleted file mode 100644 index 22dcd4a..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-light@2x.png b/assets/Images.xcassets/Button/stop.imageset/stop-light@2x.png deleted file mode 100644 index e2706cb..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Button/stop.imageset/stop-light@3x.png b/assets/Images.xcassets/Button/stop.imageset/stop-light@3x.png deleted file mode 100644 index 045e751..0000000 Binary files a/assets/Images.xcassets/Button/stop.imageset/stop-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Contents.json b/assets/Images.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/assets/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/Contents.json b/assets/Images.xcassets/Toolbar/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/assets/Images.xcassets/Toolbar/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/Contents.json b/assets/Images.xcassets/Toolbar/collapse.imageset/Contents.json deleted file mode 100644 index 36dcfde..0000000 --- a/assets/Images.xcassets/Toolbar/collapse.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "collapse-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "collapse-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "collapse-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "collapse-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "collapse-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "collapse-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "collapse-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "collapse-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "collapse-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any.png deleted file mode 100644 index c3f1f85..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any@2x.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any@2x.png deleted file mode 100644 index 9b30c80..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any@3x.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any@3x.png deleted file mode 100644 index 142f085..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark.png deleted file mode 100644 index 55767e5..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark@2x.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark@2x.png deleted file mode 100644 index 4376008..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark@3x.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark@3x.png deleted file mode 100644 index 10fc8d6..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light.png deleted file mode 100644 index c3f1f85..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light@2x.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light@2x.png deleted file mode 100644 index 9b30c80..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light@3x.png b/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light@3x.png deleted file mode 100644 index 142f085..0000000 Binary files a/assets/Images.xcassets/Toolbar/collapse.imageset/collapse-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/Contents.json b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/Contents.json deleted file mode 100644 index 063dc9e..0000000 --- a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "comparisonMethod-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "comparisonMethod-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "comparisonMethod-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "comparisonMethod-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "comparisonMethod-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "comparisonMethod-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "comparisonMethod-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "comparisonMethod-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "comparisonMethod-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any.png deleted file mode 100644 index 9f63148..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any@2x.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any@2x.png deleted file mode 100644 index 05d5939..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any@3x.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any@3x.png deleted file mode 100644 index ef83f4c..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark.png deleted file mode 100644 index c2d0daa..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark@2x.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark@2x.png deleted file mode 100644 index e289701..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark@3x.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark@3x.png deleted file mode 100644 index 3c9ce88..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light.png deleted file mode 100644 index 9f63148..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light@2x.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light@2x.png deleted file mode 100644 index 05d5939..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light@3x.png b/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light@3x.png deleted file mode 100644 index ef83f4c..0000000 Binary files a/assets/Images.xcassets/Toolbar/comparisonMethod.imageset/comparisonMethod-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/Contents.json b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/Contents.json deleted file mode 100644 index 2063518..0000000 --- a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "copyLinesl-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyLinesl-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyLinesl-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "copyLinesl-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyLinesl-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyLinesl-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "copyLinesl-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyLinesl-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyLinesl-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any.png deleted file mode 100644 index aad0f66..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any@2x.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any@2x.png deleted file mode 100644 index 21a2e13..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any@3x.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any@3x.png deleted file mode 100644 index 0d7c82b..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark.png deleted file mode 100644 index 4bde550..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark@2x.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark@2x.png deleted file mode 100644 index cc00ff3..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark@3x.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark@3x.png deleted file mode 100644 index a3642b3..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light.png deleted file mode 100644 index aad0f66..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light@2x.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light@2x.png deleted file mode 100644 index 21a2e13..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light@3x.png b/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light@3x.png deleted file mode 100644 index 0d7c82b..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesl.imageset/copyLinesl-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/Contents.json b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/Contents.json deleted file mode 100644 index 828575c..0000000 --- a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "copyLinesr-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyLinesr-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyLinesr-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "copyLinesr-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyLinesr-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyLinesr-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "copyLinesr-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyLinesr-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyLinesr-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any.png deleted file mode 100644 index f94b213..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any@2x.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any@2x.png deleted file mode 100644 index 365a016..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any@3x.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any@3x.png deleted file mode 100644 index 4a75c00..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark.png deleted file mode 100644 index 51684a2..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark@2x.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark@2x.png deleted file mode 100644 index 15a6af4..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark@3x.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark@3x.png deleted file mode 100644 index 8b5463a..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light.png deleted file mode 100644 index f94b213..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light@2x.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light@2x.png deleted file mode 100644 index 365a016..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light@3x.png b/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light@3x.png deleted file mode 100644 index 4a75c00..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyLinesr.imageset/copyLinesr-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/Contents.json b/assets/Images.xcassets/Toolbar/copyl.imageset/Contents.json deleted file mode 100644 index 63321cb..0000000 --- a/assets/Images.xcassets/Toolbar/copyl.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "copyl-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyl-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyl-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "copyl-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyl-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyl-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "copyl-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyl-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyl-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any.png deleted file mode 100644 index 215a250..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any@2x.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any@2x.png deleted file mode 100644 index 9a122c6..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any@3x.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any@3x.png deleted file mode 100644 index d6a74b3..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark.png deleted file mode 100644 index 519dc37..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark@2x.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark@2x.png deleted file mode 100644 index 8e03d77..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark@3x.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark@3x.png deleted file mode 100644 index 731d61d..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light.png deleted file mode 100644 index 215a250..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light@2x.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light@2x.png deleted file mode 100644 index 9a122c6..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light@3x.png b/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light@3x.png deleted file mode 100644 index d6a74b3..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyl.imageset/copyl-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/Contents.json b/assets/Images.xcassets/Toolbar/copyr.imageset/Contents.json deleted file mode 100644 index 4b98091..0000000 --- a/assets/Images.xcassets/Toolbar/copyr.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "copyr-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyr-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyr-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "copyr-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyr-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyr-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "copyr-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "copyr-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "copyr-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any.png deleted file mode 100644 index b854d25..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any@2x.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any@2x.png deleted file mode 100644 index 2251cd7..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any@3x.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any@3x.png deleted file mode 100644 index 8d15248..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark.png deleted file mode 100644 index 609b7cc..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark@2x.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark@2x.png deleted file mode 100644 index 6d81001..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark@3x.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark@3x.png deleted file mode 100644 index f2e5413..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light.png deleted file mode 100644 index b854d25..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light@2x.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light@2x.png deleted file mode 100644 index 2251cd7..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light@3x.png b/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light@3x.png deleted file mode 100644 index 8d15248..0000000 Binary files a/assets/Images.xcassets/Toolbar/copyr.imageset/copyr-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/Contents.json b/assets/Images.xcassets/Toolbar/datetime.imageset/Contents.json deleted file mode 100644 index bb6795c..0000000 --- a/assets/Images.xcassets/Toolbar/datetime.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "datetime-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "datetime-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "datetime-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "datetime-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "datetime-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "datetime-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "datetime-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "datetime-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "datetime-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any.png deleted file mode 100644 index 4013781..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any@2x.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any@2x.png deleted file mode 100644 index 0ed837b..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any@3x.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any@3x.png deleted file mode 100644 index c52c82f..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark.png deleted file mode 100644 index 4a559b2..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark@2x.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark@2x.png deleted file mode 100644 index 1b95a67..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark@3x.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark@3x.png deleted file mode 100644 index 469c802..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light.png deleted file mode 100644 index 4013781..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light@2x.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light@2x.png deleted file mode 100644 index 0ed837b..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light@3x.png b/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light@3x.png deleted file mode 100644 index c52c82f..0000000 Binary files a/assets/Images.xcassets/Toolbar/datetime.imageset/datetime-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/Contents.json b/assets/Images.xcassets/Toolbar/delete.imageset/Contents.json deleted file mode 100644 index d320439..0000000 --- a/assets/Images.xcassets/Toolbar/delete.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "delete-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "delete-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "delete-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "delete-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "delete-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "delete-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "delete-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "delete-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "delete-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-any.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-any.png deleted file mode 100644 index 78f22fd..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-any@2x.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-any@2x.png deleted file mode 100644 index 36a4522..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-any@3x.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-any@3x.png deleted file mode 100644 index 4512bd1..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark.png deleted file mode 100644 index 2ce08f9..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark@2x.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark@2x.png deleted file mode 100644 index 517d80c..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark@3x.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark@3x.png deleted file mode 100644 index 6039570..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-light.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-light.png deleted file mode 100644 index 78f22fd..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-light@2x.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-light@2x.png deleted file mode 100644 index 36a4522..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/delete.imageset/delete-light@3x.png b/assets/Images.xcassets/Toolbar/delete.imageset/delete-light@3x.png deleted file mode 100644 index 4512bd1..0000000 Binary files a/assets/Images.xcassets/Toolbar/delete.imageset/delete-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/Contents.json b/assets/Images.xcassets/Toolbar/expand.imageset/Contents.json deleted file mode 100644 index 121a856..0000000 --- a/assets/Images.xcassets/Toolbar/expand.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "expand-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "expand-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "expand-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "expand-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "expand-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "expand-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "expand-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "expand-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "expand-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-any.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-any.png deleted file mode 100644 index 51b9686..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-any@2x.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-any@2x.png deleted file mode 100644 index 8e7d08c..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-any@3x.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-any@3x.png deleted file mode 100644 index 03d32a7..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark.png deleted file mode 100644 index 832cb39..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark@2x.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark@2x.png deleted file mode 100644 index ecbdaa5..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark@3x.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark@3x.png deleted file mode 100644 index bc95b3c..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-light.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-light.png deleted file mode 100644 index 51b9686..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-light@2x.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-light@2x.png deleted file mode 100644 index 8e7d08c..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/expand.imageset/expand-light@3x.png b/assets/Images.xcassets/Toolbar/expand.imageset/expand-light@3x.png deleted file mode 100644 index 03d32a7..0000000 Binary files a/assets/Images.xcassets/Toolbar/expand.imageset/expand-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/Contents.json b/assets/Images.xcassets/Toolbar/file_next.imageset/Contents.json deleted file mode 100644 index f867f1d..0000000 --- a/assets/Images.xcassets/Toolbar/file_next.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "file_next-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "file_next-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "file_next-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "file_next-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "file_next-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "file_next-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "file_next-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "file_next-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "file_next-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any.png deleted file mode 100644 index 5302edf..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any@2x.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any@2x.png deleted file mode 100644 index 5fc1d7e..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any@3x.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any@3x.png deleted file mode 100644 index de63330..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark.png deleted file mode 100644 index 4bf8119..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark@2x.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark@2x.png deleted file mode 100644 index 493d81e..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark@3x.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark@3x.png deleted file mode 100644 index 52c81d4..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light.png deleted file mode 100644 index 5302edf..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light@2x.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light@2x.png deleted file mode 100644 index 5fc1d7e..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light@3x.png b/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light@3x.png deleted file mode 100644 index de63330..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_next.imageset/file_next-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/Contents.json b/assets/Images.xcassets/Toolbar/file_prev.imageset/Contents.json deleted file mode 100644 index 67623ba..0000000 --- a/assets/Images.xcassets/Toolbar/file_prev.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "file_prev-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "file_prev-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "file_prev-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "file_prev-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "file_prev-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "file_prev-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "file_prev-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "file_prev-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "file_prev-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any.png deleted file mode 100644 index d71e54d..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any@2x.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any@2x.png deleted file mode 100644 index 77de674..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any@3x.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any@3x.png deleted file mode 100644 index 805ecef..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark.png deleted file mode 100644 index 77605be..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark@2x.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark@2x.png deleted file mode 100644 index 0f91c99..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark@3x.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark@3x.png deleted file mode 100644 index b5a12ae..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light.png deleted file mode 100644 index d71e54d..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light@2x.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light@2x.png deleted file mode 100644 index 77de674..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light@3x.png b/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light@3x.png deleted file mode 100644 index 805ecef..0000000 Binary files a/assets/Images.xcassets/Toolbar/file_prev.imageset/file_prev-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/Contents.json b/assets/Images.xcassets/Toolbar/filter.imageset/Contents.json deleted file mode 100644 index 830c3cb..0000000 --- a/assets/Images.xcassets/Toolbar/filter.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "filter-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "filter-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "filter-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "filter-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "filter-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "filter-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "filter-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "filter-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "filter-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-any.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-any.png deleted file mode 100644 index 6b9301a..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-any@2x.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-any@2x.png deleted file mode 100644 index 2308311..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-any@3x.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-any@3x.png deleted file mode 100644 index ed58fbf..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark.png deleted file mode 100644 index eb96553..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark@2x.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark@2x.png deleted file mode 100644 index ca04050..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark@3x.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark@3x.png deleted file mode 100644 index a6f2b48..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-light.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-light.png deleted file mode 100644 index 6b9301a..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-light@2x.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-light@2x.png deleted file mode 100644 index 2308311..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/filter.imageset/filter-light@3x.png b/assets/Images.xcassets/Toolbar/filter.imageset/filter-light@3x.png deleted file mode 100644 index ed58fbf..0000000 Binary files a/assets/Images.xcassets/Toolbar/filter.imageset/filter-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/Contents.json b/assets/Images.xcassets/Toolbar/finder.imageset/Contents.json deleted file mode 100644 index bb352e4..0000000 --- a/assets/Images.xcassets/Toolbar/finder.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "finder-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "finder-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "finder-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "finder-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "finder-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "finder-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "finder-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "finder-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "finder-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-any.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-any.png deleted file mode 100644 index d953481..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-any@2x.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-any@2x.png deleted file mode 100644 index 9b741f3..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-any@3x.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-any@3x.png deleted file mode 100644 index 7e29f87..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark.png deleted file mode 100644 index 0a528ab..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark@2x.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark@2x.png deleted file mode 100644 index 50c4ffc..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark@3x.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark@3x.png deleted file mode 100644 index 33a4a8c..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-light.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-light.png deleted file mode 100644 index d953481..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-light@2x.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-light@2x.png deleted file mode 100644 index 9b741f3..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/finder.imageset/finder-light@3x.png b/assets/Images.xcassets/Toolbar/finder.imageset/finder-light@3x.png deleted file mode 100644 index 7e29f87..0000000 Binary files a/assets/Images.xcassets/Toolbar/finder.imageset/finder-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/Contents.json b/assets/Images.xcassets/Toolbar/movel.imageset/Contents.json deleted file mode 100644 index 4334df9..0000000 --- a/assets/Images.xcassets/Toolbar/movel.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "movel-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "movel-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "movel-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "movel-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "movel-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "movel-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "movel-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "movel-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "movel-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-any.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-any.png deleted file mode 100644 index 1f74131..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-any@2x.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-any@2x.png deleted file mode 100644 index a802e05..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-any@3x.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-any@3x.png deleted file mode 100644 index 91f68d5..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark.png deleted file mode 100644 index bc5231d..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark@2x.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark@2x.png deleted file mode 100644 index 2845de7..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark@3x.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark@3x.png deleted file mode 100644 index f409df9..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-light.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-light.png deleted file mode 100644 index 1f74131..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-light@2x.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-light@2x.png deleted file mode 100644 index a802e05..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/movel.imageset/movel-light@3x.png b/assets/Images.xcassets/Toolbar/movel.imageset/movel-light@3x.png deleted file mode 100644 index 91f68d5..0000000 Binary files a/assets/Images.xcassets/Toolbar/movel.imageset/movel-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/Contents.json b/assets/Images.xcassets/Toolbar/mover.imageset/Contents.json deleted file mode 100644 index 7063a37..0000000 --- a/assets/Images.xcassets/Toolbar/mover.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "mover-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "mover-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "mover-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "mover-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "mover-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "mover-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "mover-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "mover-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "mover-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-any.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-any.png deleted file mode 100644 index 6242b11..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-any@2x.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-any@2x.png deleted file mode 100644 index 5f3eeb7..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-any@3x.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-any@3x.png deleted file mode 100644 index 9061ffa..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark.png deleted file mode 100644 index a63dfd7..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark@2x.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark@2x.png deleted file mode 100644 index 5ef2f1b..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark@3x.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark@3x.png deleted file mode 100644 index ae566a5..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-light.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-light.png deleted file mode 100644 index 6242b11..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-light@2x.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-light@2x.png deleted file mode 100644 index 5f3eeb7..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/mover.imageset/mover-light@3x.png b/assets/Images.xcassets/Toolbar/mover.imageset/mover-light@3x.png deleted file mode 100644 index 9061ffa..0000000 Binary files a/assets/Images.xcassets/Toolbar/mover.imageset/mover-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/Contents.json b/assets/Images.xcassets/Toolbar/next.imageset/Contents.json deleted file mode 100644 index e922f0b..0000000 --- a/assets/Images.xcassets/Toolbar/next.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "next-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "next-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "next-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "next-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "next-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "next-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "next-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "next-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "next-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-any.png b/assets/Images.xcassets/Toolbar/next.imageset/next-any.png deleted file mode 100644 index 2ac83e7..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-any@2x.png b/assets/Images.xcassets/Toolbar/next.imageset/next-any@2x.png deleted file mode 100644 index 008180c..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-any@3x.png b/assets/Images.xcassets/Toolbar/next.imageset/next-any@3x.png deleted file mode 100644 index c4234a1..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-dark.png b/assets/Images.xcassets/Toolbar/next.imageset/next-dark.png deleted file mode 100644 index 2ed5a11..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-dark@2x.png b/assets/Images.xcassets/Toolbar/next.imageset/next-dark@2x.png deleted file mode 100644 index 6794373..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-dark@3x.png b/assets/Images.xcassets/Toolbar/next.imageset/next-dark@3x.png deleted file mode 100644 index 7f09491..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-light.png b/assets/Images.xcassets/Toolbar/next.imageset/next-light.png deleted file mode 100644 index 2ac83e7..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-light@2x.png b/assets/Images.xcassets/Toolbar/next.imageset/next-light@2x.png deleted file mode 100644 index 008180c..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/next.imageset/next-light@3x.png b/assets/Images.xcassets/Toolbar/next.imageset/next-light@3x.png deleted file mode 100644 index c4234a1..0000000 Binary files a/assets/Images.xcassets/Toolbar/next.imageset/next-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/Contents.json b/assets/Images.xcassets/Toolbar/openWith.imageset/Contents.json deleted file mode 100644 index 8085a75..0000000 --- a/assets/Images.xcassets/Toolbar/openWith.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "openWith-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "openWith-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "openWith-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "openWith-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "openWith-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "openWith-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "openWith-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "openWith-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "openWith-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any.png deleted file mode 100644 index c44ba0a..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any@2x.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any@2x.png deleted file mode 100644 index 12a7ab3..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any@3x.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any@3x.png deleted file mode 100644 index cb53ce5..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark.png deleted file mode 100644 index cd5ffc9..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark@2x.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark@2x.png deleted file mode 100644 index 2c7aa73..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark@3x.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark@3x.png deleted file mode 100644 index 97bc25a..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light.png deleted file mode 100644 index c44ba0a..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light@2x.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light@2x.png deleted file mode 100644 index 12a7ab3..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light@3x.png b/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light@3x.png deleted file mode 100644 index cb53ce5..0000000 Binary files a/assets/Images.xcassets/Toolbar/openWith.imageset/openWith-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/Contents.json b/assets/Images.xcassets/Toolbar/prev.imageset/Contents.json deleted file mode 100644 index 769a86b..0000000 --- a/assets/Images.xcassets/Toolbar/prev.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "prev-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "prev-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "prev-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "prev-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "prev-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "prev-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "prev-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "prev-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "prev-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-any.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-any.png deleted file mode 100644 index 99bfb97..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-any@2x.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-any@2x.png deleted file mode 100644 index 7dd2044..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-any@3x.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-any@3x.png deleted file mode 100644 index b70d032..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark.png deleted file mode 100644 index 268f748..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark@2x.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark@2x.png deleted file mode 100644 index 2f330d6..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark@3x.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark@3x.png deleted file mode 100644 index 3a95b68..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-light.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-light.png deleted file mode 100644 index 99bfb97..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-light@2x.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-light@2x.png deleted file mode 100644 index 7dd2044..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/prev.imageset/prev-light@3x.png b/assets/Images.xcassets/Toolbar/prev.imageset/prev-light@3x.png deleted file mode 100644 index b70d032..0000000 Binary files a/assets/Images.xcassets/Toolbar/prev.imageset/prev-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/Contents.json b/assets/Images.xcassets/Toolbar/refresh.imageset/Contents.json deleted file mode 100644 index 761603e..0000000 --- a/assets/Images.xcassets/Toolbar/refresh.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "refresh-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "refresh-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "refresh-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "refresh-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "refresh-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "refresh-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "refresh-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "refresh-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "refresh-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any.png deleted file mode 100644 index 4c309d1..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any@2x.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any@2x.png deleted file mode 100644 index 1b815bd..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any@3x.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any@3x.png deleted file mode 100644 index 6d61931..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark.png deleted file mode 100644 index 120d07b..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark@2x.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark@2x.png deleted file mode 100644 index 7dfbdeb..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark@3x.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark@3x.png deleted file mode 100644 index 1bea91f..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light.png deleted file mode 100644 index 4c309d1..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light@2x.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light@2x.png deleted file mode 100644 index 1b815bd..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light@3x.png b/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light@3x.png deleted file mode 100644 index 6d61931..0000000 Binary files a/assets/Images.xcassets/Toolbar/refresh.imageset/refresh-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/Contents.json b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/Contents.json deleted file mode 100644 index 26acce4..0000000 --- a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "sessionPreferences-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "sessionPreferences-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "sessionPreferences-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "sessionPreferences-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "sessionPreferences-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "sessionPreferences-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "sessionPreferences-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "sessionPreferences-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "sessionPreferences-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any.png deleted file mode 100644 index ad2f248..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any@2x.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any@2x.png deleted file mode 100644 index 38e8e9d..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any@3x.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any@3x.png deleted file mode 100644 index ff80914..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark.png deleted file mode 100644 index 9ef8074..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark@2x.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark@2x.png deleted file mode 100644 index 519d8ce..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark@3x.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark@3x.png deleted file mode 100644 index e4a8024..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light.png deleted file mode 100644 index ad2f248..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light@2x.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light@2x.png deleted file mode 100644 index 38e8e9d..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light@3x.png b/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light@3x.png deleted file mode 100644 index ff80914..0000000 Binary files a/assets/Images.xcassets/Toolbar/sessionPreferences.imageset/sessionPreferences-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/Contents.json b/assets/Images.xcassets/Toolbar/syncBoth.imageset/Contents.json deleted file mode 100644 index b1bd88e..0000000 --- a/assets/Images.xcassets/Toolbar/syncBoth.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "syncBoth-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncBoth-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncBoth-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "syncBoth-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncBoth-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncBoth-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "syncBoth-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncBoth-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncBoth-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any.png deleted file mode 100644 index 3ad9afa..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any@2x.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any@2x.png deleted file mode 100644 index c47f278..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any@3x.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any@3x.png deleted file mode 100644 index 08c9f06..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark.png deleted file mode 100644 index 2076b06..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark@2x.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark@2x.png deleted file mode 100644 index e076c17..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark@3x.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark@3x.png deleted file mode 100644 index 2bfcbe3..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light.png deleted file mode 100644 index 3ad9afa..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light@2x.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light@2x.png deleted file mode 100644 index c47f278..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light@3x.png b/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light@3x.png deleted file mode 100644 index 08c9f06..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncBoth.imageset/syncBoth-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/Contents.json b/assets/Images.xcassets/Toolbar/syncl.imageset/Contents.json deleted file mode 100644 index 587d588..0000000 --- a/assets/Images.xcassets/Toolbar/syncl.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "syncl-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncl-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncl-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "syncl-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncl-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncl-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "syncl-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncl-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncl-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any.png deleted file mode 100644 index 21e80aa..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any@2x.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any@2x.png deleted file mode 100644 index 3e6a193..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any@3x.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any@3x.png deleted file mode 100644 index 64b52a9..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark.png deleted file mode 100644 index 63c73c2..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark@2x.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark@2x.png deleted file mode 100644 index 0ae1dd1..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark@3x.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark@3x.png deleted file mode 100644 index 260165e..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light.png deleted file mode 100644 index 21e80aa..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light@2x.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light@2x.png deleted file mode 100644 index 3e6a193..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light@3x.png b/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light@3x.png deleted file mode 100644 index 64b52a9..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncl.imageset/syncl-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/Contents.json b/assets/Images.xcassets/Toolbar/syncr.imageset/Contents.json deleted file mode 100644 index 0bbd600..0000000 --- a/assets/Images.xcassets/Toolbar/syncr.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "syncr-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncr-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncr-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "syncr-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncr-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncr-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "syncr-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "syncr-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "syncr-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any.png deleted file mode 100644 index 9db0dff..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any@2x.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any@2x.png deleted file mode 100644 index 710cece..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any@3x.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any@3x.png deleted file mode 100644 index 06b3f3a..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark.png deleted file mode 100644 index 3cb4d55..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark@2x.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark@2x.png deleted file mode 100644 index 4469aa7..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark@3x.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark@3x.png deleted file mode 100644 index acf6a2d..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light.png deleted file mode 100644 index 9db0dff..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light@2x.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light@2x.png deleted file mode 100644 index 710cece..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light@3x.png b/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light@3x.png deleted file mode 100644 index 06b3f3a..0000000 Binary files a/assets/Images.xcassets/Toolbar/syncr.imageset/syncr-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/Contents.json b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/Contents.json deleted file mode 100644 index 6a3a5bb..0000000 --- a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "wordWrapOff-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "wordWrapOff-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "wordWrapOff-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "wordWrapOff-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "wordWrapOff-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "wordWrapOff-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "wordWrapOff-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "wordWrapOff-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "wordWrapOff-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any.png deleted file mode 100644 index 8b685f1..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any@2x.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any@2x.png deleted file mode 100644 index 688994e..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any@3x.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any@3x.png deleted file mode 100644 index 4dc1bb5..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark.png deleted file mode 100644 index 798e4fa..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark@2x.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark@2x.png deleted file mode 100644 index ab496a6..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark@3x.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark@3x.png deleted file mode 100644 index f58b962..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light.png deleted file mode 100644 index 8b685f1..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light@2x.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light@2x.png deleted file mode 100644 index 688994e..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light@3x.png b/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light@3x.png deleted file mode 100644 index 4dc1bb5..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOff.imageset/wordWrapOff-light@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/Contents.json b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/Contents.json deleted file mode 100644 index 69bcc67..0000000 --- a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/Contents.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "images" : [ - { - "filename" : "wordWrapOn-any.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "wordWrapOn-light.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "wordWrapOn-dark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "wordWrapOn-any@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "wordWrapOn-light@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "wordWrapOn-dark@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "wordWrapOn-any@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "light" - } - ], - "filename" : "wordWrapOn-light@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "wordWrapOn-dark@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any.png deleted file mode 100644 index 330c53a..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any@2x.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any@2x.png deleted file mode 100644 index 3bc191d..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any@3x.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any@3x.png deleted file mode 100644 index 6db593e..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-any@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark.png deleted file mode 100644 index 0571e91..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark@2x.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark@2x.png deleted file mode 100644 index c59fe55..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark@3x.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark@3x.png deleted file mode 100644 index 8306d8d..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-dark@3x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light.png deleted file mode 100644 index 330c53a..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light@2x.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light@2x.png deleted file mode 100644 index 3bc191d..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light@2x.png and /dev/null differ diff --git a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light@3x.png b/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light@3x.png deleted file mode 100644 index 6db593e..0000000 Binary files a/assets/Images.xcassets/Toolbar/wordWrapOn.imageset/wordWrapOn-light@3x.png and /dev/null differ diff --git a/en.lproj/Credits.rtf b/en.lproj/Credits.rtf deleted file mode 100644 index 247ef9b..0000000 --- a/en.lproj/Credits.rtf +++ /dev/null @@ -1,34 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1347\cocoasubrtf570 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\vieww9000\viewh8400\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 - -\f0\b\fs24 \cf0 Software Design and Engineering: -\b0 \ - Davide Ficano\ -\ - -\b Application Icon: -\b0 \ - {\field{\*\fldinst{HYPERLINK "http://vonwhites.co"}}{\fldrslt Pablo J. Malacara}}\ - \ - -\b Toolbar Icons:\ - -\b0 Webdesigner Depot\ - Gnome Project\ - Marco Martin -\b \ - -\b0 \ - -\b MGScopeBar: -\b0 \ - {\field{\*\fldinst{HYPERLINK "http://mattgemmell.com/2008/10/28/mgscopebar"}}{\fldrslt Matt Gemmell}}\ -\ - -\b Human Interface Design: -\b0 \ - Davide Ficano\ -} \ No newline at end of file diff --git a/en.lproj/InfoPlist.strings b/en.lproj/InfoPlist.strings deleted file mode 100644 index 2d92fac..0000000 --- a/en.lproj/InfoPlist.strings +++ /dev/null @@ -1 +0,0 @@ -/* Localized versions of Info.plist keys */ diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings deleted file mode 100644 index 3c92728..0000000 Binary files a/en.lproj/Localizable.strings and /dev/null differ diff --git a/en.lproj/Localizable.stringsdict b/en.lproj/Localizable.stringsdict deleted file mode 100644 index 4addb8f..0000000 --- a/en.lproj/Localizable.stringsdict +++ /dev/null @@ -1,359 +0,0 @@ - - - - - %ld %lu orphans - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld %#@direction@ orphan - other - %ld %#@direction@ orphans - - - direction - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - lu - zero - left - one - right - - - - %ld %lu newer - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld %#@direction@ newer - other - %ld %#@direction@ newer - - - direction - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - lu - zero - left - one - right - - - - %ld different - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - No differences - one - %ld different - other - %ld different - - - - %ld changed - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - %ld changed - one - %ld changed - other - %ld changed - - - - %ld same - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - %ld same - one - %ld same - other - %ld same - - - - %ld different sections - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - No differences found - one - %ld different section - other - %ld different sections - - - - Copy %ld files (%@) to %lu - - NSStringLocalizedFormatKey - Copy %#@value@ (%@) to %#@direction@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld file - other - %ld files - - - direction - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - lu - zero - left - one - right - - - - Create %ld empty folders on %lu - - NSStringLocalizedFormatKey - Create %#@value@ on %#@direction@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld empty folder - other - %ld empty folders - - - direction - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - lu - zero - left - one - right - - - - %ld files - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - No files - one - %ld file - other - %ld files - - - - %ld folders - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - No folders - one - %ld folder - other - %ld folders - - - - %ld files, %@ - - NSStringLocalizedFormatKey - %#@value@, %@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld file - other - %ld files - - - - Selected %ld files, %@ - - NSStringLocalizedFormatKey - Selected %#@value@, %@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld file - other - %ld files - - - - %ld hours - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld hour - other - %ld hours - - - - %ld minutes - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld minute - other - %ld minutes - - - - %ld seconds - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - one - %ld second - other - %ld seconds - - - - %ld mismatching labels - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - No label - one - %ld mismatching label - other - %ld mismatching labels - - - - %ld mismatching tags - - NSStringLocalizedFormatKey - %#@value@ - value - - NSStringFormatSpecTypeKey - NSStringPluralRuleType - NSStringFormatValueTypeKey - ld - zero - No tag - one - %ld mismatching tag - other - %ld mismatching tags - - - - - diff --git a/fastlane/.env.default b/fastlane/.env.default deleted file mode 100644 index 72e3919..0000000 --- a/fastlane/.env.default +++ /dev/null @@ -1,32 +0,0 @@ -# === General === -APP_NAME="VisualDiffer" -APP_IDENTIFIER="com.visualdiffer" -SCHEME="VisualDiffer" -TARGET="VisualDiffer" -PROJECT_PATH="VisualDiffer.xcodeproj" -TEAM_ID= -APPLE_ID= -RELEASE_NOTE_PATH=notes - -# === Build === -BUILD_PATH="build" -ARCHIVE_PATH="$BUILD_PATH/${APP_NAME}.xcarchive" -EXPORT_PATH="$BUILD_PATH/export" - -# === Notarization === -NOTARIZE_ENABLED=false - -NOTARIZE_API_KEY_PATH=".p8 path" -NOTARIZE_API_KEY_ID= -NOTARIZE_API_ISSUER_ID= - -# === GitHub === -GITHUB_ENABLED=false -GITHUB_REPO="visualdiffer/${APP_NAME}" - -GITHUB_API_TOKEN= - -# === Sparkle === -SPARKLE_ENABLED=false -# Used only if SPARKLE_ENABLED == true -SPARKLE_SCHEME= \ No newline at end of file diff --git a/fastlane/Appfile b/fastlane/Appfile deleted file mode 100644 index 2fde50d..0000000 --- a/fastlane/Appfile +++ /dev/null @@ -1,5 +0,0 @@ -# For more information about the Appfile, see: -# https://docs.fastlane.tools/advanced/#appfile -apple_id ENV["APPLE_ID"] -team_id ENV["TEAM_ID"] -app_identifier ENV["APP_IDENTIFIER"] diff --git a/fastlane/Fastfile b/fastlane/Fastfile deleted file mode 100644 index 85b361d..0000000 --- a/fastlane/Fastfile +++ /dev/null @@ -1,132 +0,0 @@ -fastlane_version "2.229.0" - -default_platform(:mac) - -require 'kramdown' -require 'time' -require 'tmpdir' - -import "Sparkle" -import "Linter" - -platform :mac do - before_all do - - ENV["LC_ALL"] = "en_US.UTF-8" - ENV["LANG"] = "en_US.UTF-8" - ENV["PROJECT_ROOT"] = File.expand_path("..", __dir__) - - # derived variables - ENV["APP_PATH"] = File.join(ENV["PROJECT_ROOT"], ENV['EXPORT_PATH'], "#{ENV['APP_NAME']}.app") - ENV["APP_VERSION"] = get_version_number(xcodeproj: ENV["PROJECT_PATH"], target: ENV["TARGET"]) - ENV["ZIP_NAME"] = "#{ENV['APP_NAME']}-#{ENV["APP_VERSION"]}.zip" - ENV["ZIP_PATH"] = File.join(ENV["PROJECT_ROOT"], ENV["EXPORT_PATH"], ENV['ZIP_NAME']) - - UI.message("🏗️ Environment loaded: #{ENV['FASTLANE_ENVIRONMENT'] || 'default'}") - end - - desc "Create release, build project and create the zip file" - lane :release do - lint_app - format_app( - check: true - ) - build_archive_app - verify_codesign - - notarize_app if ENV["NOTARIZE_ENABLED"] == "true" - zip_app - github if ENV["GITHUB_ENABLED"] == "true" - sparkle if ENV["SPARKLE_ENABLED"] == "true" - UI.success("🎉 Release completed") - end - - private_lane :build_archive_app do - UI.header("🧱 Build & Archive") - clean_build_artifacts - gym( - project: ENV["PROJECT_PATH"], - scheme: find_scheme, - archive_path: ENV["ARCHIVE_PATH"], - export_method: "developer-id", - export_options: { - signingStyle: "manual", - signingCertificate: "Developer ID Application", - teamID: ENV["TEAM_ID"] - }, - output_directory: ENV["EXPORT_PATH"] - ) - end - - private_lane :verify_codesign do - UI.header("🔏 Verify sign") - result = sh("codesign --verify --deep --strict --verbose=2 '#{ENV["APP_PATH"]}'", log: true) - if result.include?('satisfies its Designated Requirement') - UI.success("✅ Sign is valid") - else - UI.user_error!("Cosign output does not contain 'satisfies its Designated Requirement'") - end - end - - private_lane :zip_app do - UI.header("📦 Build ZIP") - sh("ditto -c -k --sequesterRsrc --keepParent #{ENV["APP_PATH"]} #{ENV["ZIP_PATH"]}") - UI.message("✅ ZIP created: #{ENV["ZIP_PATH"]}") - end - - private_lane :notarize_app do - UI.header("🧾 Notarization") - - key_filepath = File.expand_path(ENV['NOTARIZE_API_KEY_PATH']) - - api_key = app_store_connect_api_key( - key_id: ENV['NOTARIZE_API_KEY_ID'], - issuer_id: ENV['NOTARIZE_API_ISSUER_ID'], - key_filepath: key_filepath - ) - - notarize( - package: ENV["APP_PATH"], - api_key: api_key, - use_notarytool: true, - try_early_stapling: true - ) - UI.success("✅ App notarized and stapled") - end - - desc "Create and upload release to GitHub" - lane :github do - UI.header("🚀 GitHub distribution") - note_file_name = File.join(ENV["BUILD_PATH"], ENV["RELEASE_NOTE_PATH"], ENV["APP_VERSION"]) - notes_full_path = File.join(ENV["PROJECT_ROOT"], "#{note_file_name}.md") - UI.user_error!("Unable to find #{note_file_name}") unless File.exist?(notes_full_path) - notes = File.read(notes_full_path) - - result = set_github_release( - repository_name: ENV["GITHUB_REPO"], - api_token: ENV["GITHUB_API_TOKEN"], - tag_name: "v#{ENV["APP_VERSION"]}", - name: "#{ENV['APP_NAME']} v#{ENV["APP_VERSION"]}", - description: notes, - is_prerelease: (ENV["APP_VERSION"] =~ /(alpha|beta|rc)/i).to_i > 0, - upload_assets: [ENV["ZIP_PATH"]] - ) - - UI.success("✅ GitHub release completed") - end - - def find_scheme - if ENV["SPARKLE_ENABLED"] == "true" - scheme = ENV["SPARKLE_SCHEME"] - - UI.user_error!("Scheme for sparkle not defined") if scheme.nil? || scheme.empty? - return scheme - end - - scheme = ENV["SCHEME"] - UI.user_error!("Default Scheme not defined") if scheme.nil? || scheme.empty? - - return scheme - end - -end diff --git a/fastlane/Linter b/fastlane/Linter deleted file mode 100644 index 5b54d33..0000000 --- a/fastlane/Linter +++ /dev/null @@ -1,36 +0,0 @@ -desc "Lint source code with SwiftLint" -lane :lint_app do - config = File.join(ENV["PROJECT_ROOT"], '.swiftlint.yml') - config_local = File.join(ENV["PROJECT_ROOT"], '.swiftlint.local.yml') - - if File.exist?(config_local) - config = config_local - end - - swiftlint( - mode: :lint, - config_file: config, - quiet: true, - raise_if_swiftlint_error: true, - ) -end - -desc "Format source code with SwiftFormat (pass check: false to fix code)" -lane :format_app do |options| - check = options.fetch(:check, true) - - root_path = File.join(ENV["PROJECT_ROOT"]) - config = File.join(ENV["PROJECT_ROOT"], '.swiftformat') - error_result = nil - - - command = "swiftformat --config #{config} #{root_path}" - command << " --lint" if check - - sh( - command, - error_callback: ->(result) { error_result = result } - ) - - UI.user_error!("Found files to format") if error_result -end diff --git a/fastlane/Sparkle b/fastlane/Sparkle deleted file mode 100644 index 6e86e26..0000000 --- a/fastlane/Sparkle +++ /dev/null @@ -1,49 +0,0 @@ -lane :sparkle do - sign_update_signature = sh("#{ENV["SPARKLE_BIN_HOME"]}/sign_update #{ENV["ZIP_PATH"]} -f #{ENV["SPARKLE_PRIVATE_KEY_PATH"]}") - zip_url = ENV["SPARKLE_ZIP_ROOT_URL"] - .gsub("$app_version$", ENV["APP_VERSION"]) - .gsub("$zip_name$", ENV["ZIP_NAME"]) - note_file_name = File.join(ENV["BUILD_PATH"], ENV["RELEASE_NOTE_PATH"], ENV["APP_VERSION"]) - notes_full_path = File.join(ENV["PROJECT_ROOT"], "#{note_file_name}.md") - relnotes_html = Kramdown::Document.new(File.read(notes_full_path)).to_html - - properties = { - 'app_version' => ENV["APP_VERSION"], - 'relnotes_html' => relnotes_html, - 'pub_date' => Time.now.strftime("%a, %-d %B %Y 00:00:00 %z"), - 'zip_url' => zip_url, - 'sign_update_signature' => sign_update_signature, - } - appcast_text = File.read(File.expand_path(ENV['SPARKLE_APPCAST_TEMPLATE_PATH'])) - - properties.each_pair { |k, v| appcast_text.gsub!("$#{k}$", v) } - - output_appcast_path = File.expand_path(ENV["SPARKLE_APPCAST_UPDATED_PATH"]) - File.write(output_appcast_path, appcast_text) - - upload_appcast -end - -private_lane :upload_appcast do - output_appcast_path = File.expand_path(ENV["SPARKLE_APPCAST_UPDATED_PATH"]) - - Dir.mktmpdir do |temp_dir| - sh("git worktree add -B gh-pages #{temp_dir} origin/gh-pages") - sh("cp #{output_appcast_path} #{temp_dir}/") - - Dir.chdir(temp_dir) do - repo_clean = sh("git status --porcelain").empty? - - if repo_clean - UI.success("✅ No appcast modified, working tree clean.") - else - sh("git add appcast.xml") - sh("git commit -m 'Update appcast for #{ENV["APP_VERSION"]}'") - sh("git push origin gh-pages") - end - end - end - # use prune because git worktree remove <> deletes the directory - # and Dir.mktmpdir fails removing it itself - sh("git worktree prune") -end diff --git a/images/aliasbadge.png b/images/aliasbadge.png deleted file mode 100644 index f54f735..0000000 Binary files a/images/aliasbadge.png and /dev/null differ diff --git a/images/aliasbadge@2x.png b/images/aliasbadge@2x.png deleted file mode 100644 index ef72754..0000000 Binary files a/images/aliasbadge@2x.png and /dev/null differ diff --git a/images/bottom.png b/images/bottom.png deleted file mode 100644 index 8cd6da6..0000000 Binary files a/images/bottom.png and /dev/null differ diff --git a/images/bottom@2x.png b/images/bottom@2x.png deleted file mode 100644 index 7a6c2d9..0000000 Binary files a/images/bottom@2x.png and /dev/null differ diff --git a/images/dropzone.png b/images/dropzone.png deleted file mode 100644 index 9514d84..0000000 Binary files a/images/dropzone.png and /dev/null differ diff --git a/images/dropzone@2x.png b/images/dropzone@2x.png deleted file mode 100644 index 30703c5..0000000 Binary files a/images/dropzone@2x.png and /dev/null differ diff --git a/images/empty.png b/images/empty.png deleted file mode 100644 index d8be470..0000000 Binary files a/images/empty.png and /dev/null differ diff --git a/images/empty@2x.png b/images/empty@2x.png deleted file mode 100644 index d8be470..0000000 Binary files a/images/empty@2x.png and /dev/null differ diff --git a/images/folder/folder-000-open.png b/images/folder/folder-000-open.png deleted file mode 100644 index c651494..0000000 Binary files a/images/folder/folder-000-open.png and /dev/null differ diff --git a/images/folder/folder-000-open@2x.png b/images/folder/folder-000-open@2x.png deleted file mode 100644 index 655deb0..0000000 Binary files a/images/folder/folder-000-open@2x.png and /dev/null differ diff --git a/images/folder/folder-000.png b/images/folder/folder-000.png deleted file mode 100644 index 16c0cf7..0000000 Binary files a/images/folder/folder-000.png and /dev/null differ diff --git a/images/folder/folder-000@2x.png b/images/folder/folder-000@2x.png deleted file mode 100644 index ab96ac3..0000000 Binary files a/images/folder/folder-000@2x.png and /dev/null differ diff --git a/images/folder/folder-001-open.png b/images/folder/folder-001-open.png deleted file mode 100644 index caf6348..0000000 Binary files a/images/folder/folder-001-open.png and /dev/null differ diff --git a/images/folder/folder-001-open@2x.png b/images/folder/folder-001-open@2x.png deleted file mode 100644 index 4a72978..0000000 Binary files a/images/folder/folder-001-open@2x.png and /dev/null differ diff --git a/images/folder/folder-001.png b/images/folder/folder-001.png deleted file mode 100644 index 29f048c..0000000 Binary files a/images/folder/folder-001.png and /dev/null differ diff --git a/images/folder/folder-001@2x.png b/images/folder/folder-001@2x.png deleted file mode 100644 index 6efc215..0000000 Binary files a/images/folder/folder-001@2x.png and /dev/null differ diff --git a/images/folder/folder-010-open.png b/images/folder/folder-010-open.png deleted file mode 100644 index 2674478..0000000 Binary files a/images/folder/folder-010-open.png and /dev/null differ diff --git a/images/folder/folder-010-open@2x.png b/images/folder/folder-010-open@2x.png deleted file mode 100644 index 50df42e..0000000 Binary files a/images/folder/folder-010-open@2x.png and /dev/null differ diff --git a/images/folder/folder-010.png b/images/folder/folder-010.png deleted file mode 100644 index 815289b..0000000 Binary files a/images/folder/folder-010.png and /dev/null differ diff --git a/images/folder/folder-010@2x.png b/images/folder/folder-010@2x.png deleted file mode 100644 index cc52c38..0000000 Binary files a/images/folder/folder-010@2x.png and /dev/null differ diff --git a/images/folder/folder-011-open.png b/images/folder/folder-011-open.png deleted file mode 100644 index 644870d..0000000 Binary files a/images/folder/folder-011-open.png and /dev/null differ diff --git a/images/folder/folder-011-open@2x.png b/images/folder/folder-011-open@2x.png deleted file mode 100644 index adc196d..0000000 Binary files a/images/folder/folder-011-open@2x.png and /dev/null differ diff --git a/images/folder/folder-011.png b/images/folder/folder-011.png deleted file mode 100644 index 9f2d2f3..0000000 Binary files a/images/folder/folder-011.png and /dev/null differ diff --git a/images/folder/folder-011@2x.png b/images/folder/folder-011@2x.png deleted file mode 100644 index c5f5966..0000000 Binary files a/images/folder/folder-011@2x.png and /dev/null differ diff --git a/images/folder/folder-100-open.png b/images/folder/folder-100-open.png deleted file mode 100644 index 00af370..0000000 Binary files a/images/folder/folder-100-open.png and /dev/null differ diff --git a/images/folder/folder-100-open@2x.png b/images/folder/folder-100-open@2x.png deleted file mode 100644 index 3ae072b..0000000 Binary files a/images/folder/folder-100-open@2x.png and /dev/null differ diff --git a/images/folder/folder-100.png b/images/folder/folder-100.png deleted file mode 100644 index d7a088a..0000000 Binary files a/images/folder/folder-100.png and /dev/null differ diff --git a/images/folder/folder-100@2x.png b/images/folder/folder-100@2x.png deleted file mode 100644 index 83a0013..0000000 Binary files a/images/folder/folder-100@2x.png and /dev/null differ diff --git a/images/folder/folder-101-open.png b/images/folder/folder-101-open.png deleted file mode 100644 index b9368d6..0000000 Binary files a/images/folder/folder-101-open.png and /dev/null differ diff --git a/images/folder/folder-101-open@2x.png b/images/folder/folder-101-open@2x.png deleted file mode 100644 index fa055dd..0000000 Binary files a/images/folder/folder-101-open@2x.png and /dev/null differ diff --git a/images/folder/folder-101.png b/images/folder/folder-101.png deleted file mode 100644 index 69aeb20..0000000 Binary files a/images/folder/folder-101.png and /dev/null differ diff --git a/images/folder/folder-101@2x.png b/images/folder/folder-101@2x.png deleted file mode 100644 index a8b1502..0000000 Binary files a/images/folder/folder-101@2x.png and /dev/null differ diff --git a/images/folder/folder-110-open.png b/images/folder/folder-110-open.png deleted file mode 100644 index a43e213..0000000 Binary files a/images/folder/folder-110-open.png and /dev/null differ diff --git a/images/folder/folder-110-open@2x.png b/images/folder/folder-110-open@2x.png deleted file mode 100644 index 0855a06..0000000 Binary files a/images/folder/folder-110-open@2x.png and /dev/null differ diff --git a/images/folder/folder-110.png b/images/folder/folder-110.png deleted file mode 100644 index ce7280b..0000000 Binary files a/images/folder/folder-110.png and /dev/null differ diff --git a/images/folder/folder-110@2x.png b/images/folder/folder-110@2x.png deleted file mode 100644 index b123085..0000000 Binary files a/images/folder/folder-110@2x.png and /dev/null differ diff --git a/images/folder/folder-111-open.png b/images/folder/folder-111-open.png deleted file mode 100644 index a43e213..0000000 Binary files a/images/folder/folder-111-open.png and /dev/null differ diff --git a/images/folder/folder-111-open@2x.png b/images/folder/folder-111-open@2x.png deleted file mode 100644 index 0855a06..0000000 Binary files a/images/folder/folder-111-open@2x.png and /dev/null differ diff --git a/images/folder/folder-111.png b/images/folder/folder-111.png deleted file mode 100644 index ce7280b..0000000 Binary files a/images/folder/folder-111.png and /dev/null differ diff --git a/images/folder/folder-111@2x.png b/images/folder/folder-111@2x.png deleted file mode 100644 index 704d8f6..0000000 Binary files a/images/folder/folder-111@2x.png and /dev/null differ diff --git a/images/folder/folder-999-open.png b/images/folder/folder-999-open.png deleted file mode 100644 index cd1697e..0000000 Binary files a/images/folder/folder-999-open.png and /dev/null differ diff --git a/images/folder/folder-999-open@2x.png b/images/folder/folder-999-open@2x.png deleted file mode 100644 index 947c1c6..0000000 Binary files a/images/folder/folder-999-open@2x.png and /dev/null differ diff --git a/images/folder/folder-999.png b/images/folder/folder-999.png deleted file mode 100644 index 1952fc3..0000000 Binary files a/images/folder/folder-999.png and /dev/null differ diff --git a/images/folder/folder-999@2x.png b/images/folder/folder-999@2x.png deleted file mode 100644 index 12a7fbc..0000000 Binary files a/images/folder/folder-999@2x.png and /dev/null differ diff --git a/images/folder/mask-back-white.png b/images/folder/mask-back-white.png deleted file mode 100644 index 894cd4b..0000000 Binary files a/images/folder/mask-back-white.png and /dev/null differ diff --git a/images/folder/mask-back.png b/images/folder/mask-back.png deleted file mode 100644 index 719656d..0000000 Binary files a/images/folder/mask-back.png and /dev/null differ diff --git a/images/folder/mask-front.png b/images/folder/mask-front.png deleted file mode 100644 index 94e44a6..0000000 Binary files a/images/folder/mask-front.png and /dev/null differ diff --git a/images/folder/mask-full.png b/images/folder/mask-full.png deleted file mode 100644 index 88679ea..0000000 Binary files a/images/folder/mask-full.png and /dev/null differ diff --git a/images/folder/mask-middle.png b/images/folder/mask-middle.png deleted file mode 100644 index 40b29a1..0000000 Binary files a/images/folder/mask-middle.png and /dev/null differ diff --git a/images/lockedbadge.png b/images/lockedbadge.png deleted file mode 100644 index 840922a..0000000 Binary files a/images/lockedbadge.png and /dev/null differ diff --git a/images/lockedbadge@2x.png b/images/lockedbadge@2x.png deleted file mode 100644 index 33a9de2..0000000 Binary files a/images/lockedbadge@2x.png and /dev/null differ diff --git a/images/rewind.png b/images/rewind.png deleted file mode 100644 index b1b7ada..0000000 Binary files a/images/rewind.png and /dev/null differ diff --git a/images/rewind@2x.png b/images/rewind@2x.png deleted file mode 100644 index 5ef79e7..0000000 Binary files a/images/rewind@2x.png and /dev/null differ diff --git a/images/toolbar/deleteRed.png b/images/toolbar/deleteRed.png deleted file mode 100644 index 3324a41..0000000 Binary files a/images/toolbar/deleteRed.png and /dev/null differ diff --git a/images/toolbar/deleteRed@2x.png b/images/toolbar/deleteRed@2x.png deleted file mode 100644 index 2a7351e..0000000 Binary files a/images/toolbar/deleteRed@2x.png and /dev/null differ diff --git a/images/toolbar/prefs_confirmations.png b/images/toolbar/prefs_confirmations.png deleted file mode 100644 index b2f7064..0000000 Binary files a/images/toolbar/prefs_confirmations.png and /dev/null differ diff --git a/images/toolbar/prefs_confirmations@2x.png b/images/toolbar/prefs_confirmations@2x.png deleted file mode 100644 index 51fcb2e..0000000 Binary files a/images/toolbar/prefs_confirmations@2x.png and /dev/null differ diff --git a/images/toolbar/prefs_folder.png b/images/toolbar/prefs_folder.png deleted file mode 100644 index 4a72978..0000000 Binary files a/images/toolbar/prefs_folder.png and /dev/null differ diff --git a/images/toolbar/prefs_folder@2x.png b/images/toolbar/prefs_folder@2x.png deleted file mode 100644 index 6238bd8..0000000 Binary files a/images/toolbar/prefs_folder@2x.png and /dev/null differ diff --git a/images/toolbar/prefs_keyboard.png b/images/toolbar/prefs_keyboard.png deleted file mode 100644 index 6760eac..0000000 Binary files a/images/toolbar/prefs_keyboard.png and /dev/null differ diff --git a/images/toolbar/prefs_keyboard@2x.png b/images/toolbar/prefs_keyboard@2x.png deleted file mode 100644 index 1f2bf72..0000000 Binary files a/images/toolbar/prefs_keyboard@2x.png and /dev/null differ diff --git a/images/toolbar/prefs_paths.png b/images/toolbar/prefs_paths.png deleted file mode 100644 index d5ff492..0000000 Binary files a/images/toolbar/prefs_paths.png and /dev/null differ diff --git a/images/toolbar/prefs_paths@2x.png b/images/toolbar/prefs_paths@2x.png deleted file mode 100644 index 5f9a53d..0000000 Binary files a/images/toolbar/prefs_paths@2x.png and /dev/null differ diff --git a/images/toolbar/prefs_text.png b/images/toolbar/prefs_text.png deleted file mode 100644 index 5eecac5..0000000 Binary files a/images/toolbar/prefs_text.png and /dev/null differ diff --git a/images/toolbar/prefs_text@2x.png b/images/toolbar/prefs_text@2x.png deleted file mode 100644 index 67eabc8..0000000 Binary files a/images/toolbar/prefs_text@2x.png and /dev/null differ diff --git a/images/top.png b/images/top.png deleted file mode 100644 index 9cd4c86..0000000 Binary files a/images/top.png and /dev/null differ diff --git a/images/top@2x.png b/images/top@2x.png deleted file mode 100644 index 6da29f0..0000000 Binary files a/images/top@2x.png and /dev/null differ diff --git a/images/vd.iconset/icon_128x128.png b/images/vd.iconset/icon_128x128.png deleted file mode 100644 index cfb56e2..0000000 Binary files a/images/vd.iconset/icon_128x128.png and /dev/null differ diff --git a/images/vd.iconset/icon_128x128@2x.png b/images/vd.iconset/icon_128x128@2x.png deleted file mode 100644 index 9e3407e..0000000 Binary files a/images/vd.iconset/icon_128x128@2x.png and /dev/null differ diff --git a/images/vd.iconset/icon_16x16.png b/images/vd.iconset/icon_16x16.png deleted file mode 100644 index b527998..0000000 Binary files a/images/vd.iconset/icon_16x16.png and /dev/null differ diff --git a/images/vd.iconset/icon_16x16@2x.png b/images/vd.iconset/icon_16x16@2x.png deleted file mode 100644 index 624bead..0000000 Binary files a/images/vd.iconset/icon_16x16@2x.png and /dev/null differ diff --git a/images/vd.iconset/icon_256x256.png b/images/vd.iconset/icon_256x256.png deleted file mode 100644 index 396da49..0000000 Binary files a/images/vd.iconset/icon_256x256.png and /dev/null differ diff --git a/images/vd.iconset/icon_256x256@2x.png b/images/vd.iconset/icon_256x256@2x.png deleted file mode 100644 index 9496ad9..0000000 Binary files a/images/vd.iconset/icon_256x256@2x.png and /dev/null differ diff --git a/images/vd.iconset/icon_32x32.png b/images/vd.iconset/icon_32x32.png deleted file mode 100644 index 75d6a63..0000000 Binary files a/images/vd.iconset/icon_32x32.png and /dev/null differ diff --git a/images/vd.iconset/icon_32x32@2x.png b/images/vd.iconset/icon_32x32@2x.png deleted file mode 100644 index 95ccbbe..0000000 Binary files a/images/vd.iconset/icon_32x32@2x.png and /dev/null differ diff --git a/images/vd.iconset/icon_512x512.png b/images/vd.iconset/icon_512x512.png deleted file mode 100644 index 6bc3b6a..0000000 Binary files a/images/vd.iconset/icon_512x512.png and /dev/null differ diff --git a/images/vd.iconset/icon_512x512@2x.png b/images/vd.iconset/icon_512x512@2x.png deleted file mode 100644 index 0dabbbb..0000000 Binary files a/images/vd.iconset/icon_512x512@2x.png and /dev/null differ diff --git a/scripts/changelog.sh b/scripts/changelog.sh deleted file mode 100755 index d8c55f9..0000000 --- a/scripts/changelog.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -XC_CONFIG_FILE="Versions.local.xcconfig" -CONTEXT_JSON="changelog-context.local.json" - -get_key_from_xcconfig() { - local xcconfig_file="$1" - local key="${2:-APP_VERSION}" - - if [[ ! -f "$xcconfig_file" ]]; then - echo "Error: file not found: $xcconfig_file" >&2 - return 1 - fi - - local value - value="$(sed -nE "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*(.*)\$/\1/p" "$xcconfig_file")" - value="${value%%[[:space:]]*}" - - if [[ -z "$value" ]]; then - echo "Error: $key not found in $xcconfig_file" >&2 - return 1 - fi - - echo "$value" -} - -update_json_property() { - local json_file="$1" - local property="$2" - local prop_value="$3" - - if [[ ! -f "$json_file" ]]; then - echo "Error: file not found: $json_file" >&2 - return 1 - fi - - sed -i '' -E \ - 's/^([[:space:]]*"'$property'":[[:space:]]*")[^"]*(")/\1'"$prop_value"'\2/' \ - "$json_file" -} - -rename_json_property() { - local json_file="$1" - local old_name="$2" - local new_name="$3" - - if [[ ! -f "$json_file" ]]; then - echo "Error: file not found: $json_file" >&2 - return 1 - fi - - sed -i '' -E \ - 's/^([[:space:]]*")'$old_name'(":)/\1'"$new_name"'\2/' \ - "$json_file" -} - - -version="$(get_key_from_xcconfig $XC_CONFIG_FILE)" -update_json_property $CONTEXT_JSON "appVersion" "v$version" - -if [ "$1" == "all" ]; then - rename_json_property $CONTEXT_JSON "excludeTypes" "_excludeTypes" -else - rename_json_property $CONTEXT_JSON "_excludeTypes" "excludeTypes" -fi - -npx conventional-changelog -p visualdiffer -c $CONTEXT_JSON diff --git a/scripts/helpIndexer.sh b/scripts/helpIndexer.sh deleted file mode 100755 index 0d7f400..0000000 --- a/scripts/helpIndexer.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -HELP_LOCALE_ROOT=VisualDifferHelp/Contents/Resources -INDEXER_NAME=VisualDiffer.helpindex - -for locale_path in $TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/$HELP_LOCALE_ROOT/*.lproj ; -do - hiutil -Caf $locale_path/$INDEXER_NAME $locale_path -done diff --git a/scripts/lint.sh b/scripts/lint.sh deleted file mode 100755 index fdb93e7..0000000 --- a/scripts/lint.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# Resolve symlinks, too -DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" -PARENT_DIR="$DIR/.." - -if [ "$1" == "p" ] -then - (cd "$PARENT_DIR" && periphery scan ) -else - cd "$PARENT_DIR" - - SWIFT_CONFIG=.swiftlint.yml - SWIFT_CONFIG_LOCAL=.swiftlint.local.yml - - - if [ -e "$SWIFT_CONFIG_LOCAL" ]; then - SWIFT_CONFIG=$SWIFT_CONFIG_LOCAL - fi - - swiftformat . && swiftlint --quiet --config $SWIFT_CONFIG - echo using $SWIFT_CONFIG -fi diff --git a/scripts/xt2swift.rb b/scripts/xt2swift.rb deleted file mode 100755 index 45e451a..0000000 --- a/scripts/xt2swift.rb +++ /dev/null @@ -1,436 +0,0 @@ -#!/usr/bin/env ruby - -require 'FileUtils' - - -folderReaderStr = ' - let comparatorDelegate = MockFolderStatusComparatorDelegate() - let comparator = FolderStatusComparator( - delegate: comparatorDelegate, - bufferSize: 8192, - ) - let filterConfig = FilterConfig( - showFilteredFiles: , - hideEmptyFolders: , - followSymLinks: , - skipPackages: , - traverseFilteredFolders: false, - predicate: defaultPredicate, - fileInfoOptions: 0, - displayOptions: .showAll) - let folderReaderDelegate = MockFolderReaderDelegate(isRunning: true) - let folderReader = FolderReader(with: folderReaderDelegate, - comparator: comparator, - filterConfig: filterConfig, - refreshInfo: RefreshInfo(initState: true)) -' - -# Array di coppie [regex, replacement] da applicare -regexps_tests = [ - [/\[self addTags:@?(.*?) fullPath:(.*?)\];/, 'add(tags: \1, fullPath:\2)'], - [/:(VDComparatorType.*)/, Proc.new { |m| "flags: [#{m[0].gsub('|', ', ')}]," }], - [/^.*comparator\.(.*CaseSensitive)\s*=\s*(.*?);/, Proc.new { |m| renamedToIS(m, 0, 'is\1: \2,') }], - [/folderReader\.(.*?)\s*=\s*(.*?);/, '\1: \2,'], - [/[;@]/, ''], - [/^{$/, ''], # remove { only for function definition with { on newline as unique character - [/(\d+)L/, '\1'], - [/(.):([^\s.])/, '\1: \2'], - # [/- \(void\)(test[^(\s{)]*)/, '@Test func \1() throws '], - [/-\s*\(void\)test([^(\s{)]*)/, Proc.new { |m| downcaseFirstLetter(m, 0, '@Test func \1() throws {') }], - [/^\s+FolderStatus\* rootL, \*rootR\n/, ''], - [/^\s+NSError\* error = nil\n/, ''], - [/^\s+VisibleItem\* vi\n/, ''], - [/rootL = folderReader.leftRoot/, 'let rootL = folderReader.leftRoot!'], - [/rootR = folderReader.rightRoot/, '// let rootR = folderReader.rightRoot!'], - [/vi = rootL.visibleItem/, 'let vi = rootL.visibleItem!'], - - [/VDMockFolderReaderDelegate\* delegate = \[\[VDMockFolderReaderDelegate alloc\] initWithRunningState:\s*(.*)\]/, ''], - - [/(FolderStatus\*|VisibleItem\*)/, 'let'], - [/StatusAssert/, 'assertStatus'], - [/\[(.*)\.subfolders objectAtIndex:\s*(\d+)\]/, '\1.subfolders[\2]'], - - [/^(\s+)\{/, '\1do {'], - [/ArrayCountAssert/, 'assertArrayCount'], - [/\[(childVI\d+) item\]/i, '\1.item'], - [/\bYES\b/, 'true'], - [/\bNO\b/, 'false'], - [/\bCreateFolder\b/, 'try createFolder'], - [/\bCreateFile\b/, 'try createFile'], - [/\bCreateDataFile\b/, 'try createDataFile'], - [/\bSetFileTimestamp\b/, 'try setFileTimestamp'], - [/\bSetFileTimestamp\b/, 'try setFileTimestamp'], - [/\bSetFileCreationTime\b/, 'try setFileCreationTime'], - [/\bCreateSymlink\b/, ' try createSymlink'], - [/\bAppendFolder\b/, ' appendFolder'], - [/\bStatusFolderLabels\b/, 'assertFolderLabels'], - [/\bStatusMismatchingLabels\b/, 'assertMismatchingLabels'], - [/\bStatusFolderTags\b/, 'assertFolderTags'], - [/\bStatusMismatchingTags\b/, 'assertMismatchingTags'], - [/ResourceFileLabels/, 'assertResourceFileLabels'], - [/\[self add(.*?Number):(.*) fullPath:(.*?)\]/, Proc.new { |m| downcaseFirstLetter(m, 0, 'try add(\1: \2, fullPath:\3)') }], - [/let operationElement/, 'var operationElement: FolderStatus'], - - [/\[fm removeItemAtPath:\s*appendFolder\(@?(.*?)\) error:.*\]/, 'try removeItem(\1)'], - - [/\bXCTAssertTrue\b/, '#expect'], - [/\bSymlinkAssert\b/, 'try assertSymlink'], - [/\bTimestampsAssert\b/, 'try assertTimestamps'], - [/VDMakeRefreshInfo\((.*?)\)/, 'RefreshInfo(initState: \1)'], - [/VDFolderReader\* folderReader = \[\[VDFolderReader alloc\] initWithDelegate:\s*delegate\]/, folderReaderStr], - [/folderReader.comparator = \[FolderStatusComparator comparatorWithFlags/, "let comparator = FolderStatusComparator(\nflags "], - [/FolderStatusComparator\* comparator = \[FolderStatusComparator comparatorWithFlags/, "let comparator = FolderStatusComparator(\nflags"], - [/displayFilters: (.*?),/, 'displayOptions: \1)'], - [/.*VDComparatorType\)\(([^)]+)\)/, Proc.new { |m| "flags: [#{m[0].gsub('|', ', ')}]," }], - [/^(\s+)rightRoot: nil$/, '\1rightRoot: nil,'], - [/^(\s+)leftPath:\s*(appendFolder.*)/, '\1leftPath: \2,'], - [/^(\s+)rightPath:\s*(appendFolder.*?)\]/, '\1rightPath: \2)'], - [/bufferSize:\s*(\d+)\]/, 'bufferSize: \1,' "\n)"], - [/linkedItem\./, 'linkedItem!.'], - - [/%ld found %ld", (.*?), (.*?)\)/, '\\(\1) found \\(\2)")'], - [/\[folderReader readFoldersWithLeftRoot:\s*nil/, 'folderReader.start(withLeftRoot: nil,'], - # [ /(?:.*) (.*) = (.*)/, 'let \1 = \2'], - [/MockFileOperationDelegate\* mockDelegate = \[\[MockFileOperationDelegate alloc\] initWithReplaceFlag:\s*(.*)\];?/, 'let fileOperationDelegate = MockFileOperationManagerDelegate(replaceAll: \1)'], - [/StatusMismatchingTags/, 'assertUtils.mismatchingTags'], - [/StatusFolderTags/, 'assertUtils.folderTags'], - - [/VD_ASSERT_ONLY_SETUP/, '// VD_ASSERT_ONLY_SETUP'], - [/MockFolderManagerDelegate\* mockDelegate/, ' let mockDelegate'], - [/VDLocalFileManager\* fsu = \[VDLocalFileManager localWithIncludesFiltered: includesFiltered/, - " - let fileOperationManager = FileOperationManager(filterConfig: filterConfig, - comparator: comparator, - delegate: fileOperationDelegate) - let fileOperation = DeleteFolderStatus(operationManager: fileOperationManager) - let fileOperation = CopyFolderStatus(operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000) - let fileOperation = MoveFolderStatus(operationManager: fileOperationManager, - bigFileSizeThreshold: 100_000) - let fileOperation = RenameFolderStatus(operationManager: fileOperationManager) - let fileOperation = TouchFolderStatus(operationManager: fileOperationManager) - -"], - - - [/\[fsu (.*)FolderStatus:\s*(.*)/, 'fileOperation.\1(\2,'], - [/BOOL includesFiltered = (.*)/, 'let includesFiltered = \1'], - - [/\[(.*) (add.*):\s*(.*)\]/, '\1.\2(\3)'], - [/NSString\*/, 'String'], - [/NSDictionary\*/, '[DKey: DValue]'], - [/NSArray\*/, '[ArrType]'], - [/VDFileManager\*/, 'FileManager'], - [/BOOL/, 'Bool'], - [/FSFileCountHolder\*?/, 'FileCountHolder'], - [/MockFileOperationDelegate/, 'MockFolderManagerDelegate'], - [/FSFileCountHolderInit/, 'FileCountHolder'], - [/#pragma mark/, '// MARK:'], - - [/\bVDComparisonStatus([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\bVDComparatorType([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\bVDDisplayFilter([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - - # [/(?:VDComparisonStatus|VDDisplayFilter|VDComparatorType)([a-zA-Z]*)/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - -] - -regexps_methods = [ - [ /^-\s*\(IBAction\)(.*):\s*\(id\)\s*sender\s*{/, '@objc func \1(_ sender: AnyObject) {'], - - # method declared on single line - # [ /- \((.*?)\)(.*?):\((.*)\)(.*);/, 'func \2(\4: \3) -> \1'], - - # function no arguments - [ /-\s*\(void\)\s*(.*?)\s*{/, 'func \1() {'], - [ /-\s*\(([a-zA-Z]+)\s*\*?\)\s*([a-zA-Z]+)\s*{/, 'func \2() -> \1 {'], - - # static getter - [/^\+\s*\(([a-zA-Z]*) \*\)\s*([a-zA-Z]*)\s*{/, 'static func \2() -> \1 {'], - # static setter - # [/^\+\s*\(([a-zA-Z]*)\)\s*([a-zA-Z]*)\s*:\((.*?)\)(.*?)\s*{/, 'static func \2(_ \4: \3) {'], - - # function declaration 3 arguments single line (group are more than 9 and ruby can't recognize \10 so we use named groups) - [/^-\s*\(void\)((?.*?)):\(((?.*?))\)((?.*?))\s+.*?:\(((?.*?))\)((?.*?)) .*?:\(((?.*?))\)((?.*?)) {/, - 'func \k(_ \k: \k, \k: \k, \k: \k) {'], - - [/^-\s*\((?(.*?))\)((?.*?)):\(((?.*?))\)((?.*?))\s+.*?:\(((?.*?))\)((?.*?)) .*?:\(((?.*?))\)((?.*?)) {/, - 'func \k(_ \k: \k, \k: \k, \k: \k) -> \k {'], - - # static function declaration 3 arguments single line - [/^\+\s*\((?(.*?))\)((?.*?)):\(((?.*?))\)((?.*?))\s+.*?:\(((?.*?))\)((?.*?)) .*?:\(((?.*?))\)((?.*?)) {/, - 'static func \k(_ \k: \k, \k: \k, \k: \k) -> \k {'], - - # function declaration 2 argument single line - [/^-\s*\((void)\)(.*?):\((.*?)\)(.*?)\s+.*?:\((.*?)\)(.*?)(.*?) {/, 'func \2(_ \4: \3, \7: \5) {'], - [/^-\s*\((.*?)\)(.*?):\((.*?)\)(.*?)\s+.*?:\((.*?)\)(.*?)(.*?) {/, 'func \2(_ \4: \3, \7: \5) -> \1 {'], - - # static function declaration 2 argument single line - [/^\+\s*\((void)\)(.*?):\((.*?)\)(.*?)\s+.*?:\((.*?)\)(.*?)(.*?) {/, 'static func \2(_ \4: \3, \7: \5) {'], - [/^\+\s*\((.*?)\)(.*?):\((.*?)\)(.*?)\s+.*?:\((.*?)\)(.*?)(.*?) {/, 'static func \2(_ \4: \3, \7: \5) -> \1 {'], - - # function declaration convert only first line - [/^-\s*\(([a-zA-Z]+)\s*\*?\)([a-zA-Z]+):\(([a-zA-Z]+)\s*\*?\)([a-zA-Z]+)\s*{$/, 'func \2(_ \4: \3) -> \1 {'], - [/^\+\s*\(([a-zA-Z]+)\s*\*?\)([a-zA-Z]+):\(([a-zA-Z]+)\s*\*?\)([a-zA-Z]+)\s*{$/, 'static func \2(_ \4: \3) -> \1 {'], - - # function call 3 parameters - [/\[(self)\s+([a-zA-Z]+):([a-zA-Z\._]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\]/, '\2(\3, \4: \5, \6: \7)'], - [/\[([a-zA-Z]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\]/, '\1.\2(\3, \4: \5, \6: \7)'], - - # function call 2 parameters - [/\[(self)\s+([a-zA-Z]+):([a-zA-Z\._]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\]/, '\2(\3, \4: \5)'], - [/\[([a-zA-Z]+)\s+([a-zA-Z_]+):([a-zA-Z\._]+)\s+([a-zA-Z]+):([a-zA-Z\._]+)\]/, '\1.\2(\3, \4: \5)'], - - # convert property - # [/@property\s+\((.*?)\)\s+(.*?)\*?\s+(.*?);/, 'var \3: \2 // dafi: \1'], - - # private variable declaration - [/ (.*)\* ([a-zA-Z]*);/, ' private var \2: \1'], - - # variable assignment (remove type) - [/(^\s+)[a-zA-Z\*]+ ([a-zA-Z]+?) =\s+/, '\1let \2 = '], - - # # method declared on multiple lines - # [ /- \((.*?)\)(.*):\((.*)\)(.*)/, 'func \2(\4: \3) // -> \1'], - # [/^\s(.*?):\((.*)\)(.*)/, '\1 \3: \2,'], - - [ /^\s+([a-zA-Z]+):\(([a-zA-Z\*]+)\)([a-zA-Z]+)$/, Proc.new { |m| remove_identical_argument_name(m, ",") }], - [ /^\s+([a-zA-Z]+):\(([a-zA-Z\*]+)\)([a-zA-Z]+) {$/, Proc.new { |m| remove_identical_argument_name(m, ") {") }], - - [ /@interface (.*)\(\) {/, 'class \1 {'], - [ /@implementation (.*)$/, 'class \1 {'], - - [ /@selector\(([a-zA-Z]*):\)/, '#selector(\1)'], - - [ /for \([a-zA-Z]*\* ([a-zA-Z]*) in (.*)\) {/, 'for \1 in \2 {'], - - [/NSFileManager\.defaultManager\(\)/, 'FileManager.default'], - [/\[NSFileManager defaultManager\]/, 'FileManager.default'], - - [/[;]/, ''], - # [/@"/, '"'], - - [/\[(.*)\.subfolders objectAtIndex:\s*(\d+)\]/, '\1.subfolders[\2]'], - - # [ /(?:VDComparisonStatus|VDDisplayFilter|VDComparatorType)([a-zA-Z]*)/, Proc.new { |m| create_optionset(m) }], - - # optionset - [ /(?:NSButtonType|NSControlStateValue|NSBezelStyle|NSTextAlignment|NSUserInterfaceLayoutOrientation|NSLayoutAttribute|NSLineBreak|NSImageScale)([a-zA-Z]*)/, - Proc.new { |m| downcaseFirstLetter(m, 0) }], - [/(?:NSWindowStyleMask|NSBackingStore|NSTitlebarSeparatorStyle|NSControlSize)([a-zA-Z]+)/, Proc.new { |m| downcaseFirstLetter(m, 0) }], - [/(?:NSDragOperation|NSEventModifierFlag)([a-zA-Z]+)/, Proc.new { |m| downcaseFirstLetter(m, 0) }], - - [/\bYES\b/, 'true'], - [/\bNO\b/, 'false'], - - [/VDMakeRefreshInfo\((.*?)\)/, 'RefreshInfo(initState: \1)'], - [/FolderStatusComparator\* comparator = \[FolderStatusComparator comparatorWithFlags/, 'let comparator = FolderStatusComparator(flags:'], - [/displayFilters:/, 'displayFlags:'], - [/bufferSize:\s*(\d+)\]/, 'bufferSize: \1)'], - - # [/\[folderReader readFoldersWithLeftRoot:\s*nil/, 'folderReader.readFolders(withLeftRoot: nil,'], - - [/addItemWithTitle:(.*)/, 'addItem(withTitle: \1'], - [/\[(.*) (add.*):\s*(.*)\]/, '\1.\2(\3)'], - # [/FSFileCountHolderAdd\((.*), \&(.*)\);?/, '\1 += \2'], - # [/FSFileCountHolderSub\((.*), \&(.*)\);?/, '\1 -= \2'], - - [/initWithFrame:\(NSRect\)frame/, 'init(frame frameRect: NSRect)'], - [/self = \[super initWithFrame:frameRect\]/, 'super.init(frame: frameRect)'], - [/self = \[super initWithFrame:frame\]/, 'super.init(frame: frameRect)'], - # convert [[xxx alloc ] initWithFrame] - [/\[\[(.*?) alloc\] initWithFrame:(.*?)\];/, '\1(frame: \2)'], - # convert [[xxx alloc ] init] - [/\[\[([a-zA-Z]+) alloc\] init(.*?):(.*?)\]/, '\1(\2: \3)'], - [/\[\[([a-zA-Z]+) alloc\] init\]/, '\1()'], - - [/if \(self\) \{/, '{'], - - # [/\[(.*Anchor) (constraint)(.*)\]/, '\1.\2(\3)'], - [/\[(.*Anchor) (constraint)(.*)\]/, Proc.new { |m| downcaseFirstLetter(m, 2, '\1.\2(\3)') }], - [ /Anchor constant/, 'Anchor, constant'], - - [/\.(restorable|bordered|bezeled|editable|selectable|buttonBordered|hidden|enabled)/, Proc.new { |m| renamedToIS(m, 0) }], - [/\(With(Frame|Title|Identifier):/, Proc.new { |m| downcaseFirstLetter(m, 0, '(\1:') }], - [/\bVDDisplayPosition(Left|Right)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\bVDSelectionType(.*?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\bVDComparisonStatus([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\[NSLayoutConstraint activateConstraints:/, 'NSLayoutConstraint.activate('], - [/equalToAnchor/, 'equalTo'], - - [/\.buttonType = (.*)/, '.setButtonType(\1)'], - - [/\bNSString\b/, 'String'], - [/(NSImage|NSButton|FolderStatus|NSTableView|NSEvent)\s*\*/, '\1 '], - # [ /NSDictionary\*/, '[DKey: DValue]'], - [/NSArray\*/, '[ArrType]'], - [/NSNotification/, 'Notification'], - [/\bNSURL\b/, 'URL'], - [/\bNSInteger\b/, 'Int'], - [/\bNSUInteger\b/, 'UInt'], - [/\bNSImageOnly\b/, '.imageOnly'], - [/\bseparatorItem\b/, 'separator'], - [/\bNSTableColumnAutoresizingMask\b/, '.autoresizingMask'], - [/\bNSTableColumnUserResizingMask\b/, '.userResizingMask'], - - [/\bdefaultCenter\b/, 'default'], - [/NSUserDefaults(.)standardUserDefaults/, 'UserDefaults\1standard'], - [/VDFileManager\*/, 'FileManager'], - [/BOOL/, 'Bool'], - [/\bis(Folder|File)Object\b/, 'is\1'], - [/FSFileCountHolder\*?/, 'FileCountHolder'], - [/sharedDocumentController/, 'shared'], - [/#pragma mark/, '// MARK: -'], - - [/\[(.*) isEqualToString:(.*?)\]/, '\1 == \2'], - [/NSMakeRect\((.+?), (.+?), (.+?), (.+?)\)/, 'NSRect(x: \1, y: \2, width: \3, height: \4)'], - [/\bNSZeroRect\b/, '.zero'], - [/NSMakeSize\((.*?), (.*?)\)/, 'NSSize(width: \1, height: \2)'], - [/NSMakeRange\((.+?), (.+?)\)/, 'NSRange(location: \1, length: \2)'], - - [/NSLocalizedString\((.*?), nil\)/, 'NSLocalizedString(\1, comment: "")'], - - # font - [/\[NSFont (.*)OfSize:(NSFont..*)\]/, 'NSFont.\1(ofSize: \2)'], - - # self is removed - [/\[self ([a-zA-Z]*?)\]/, '\1()'], - [/\[([a-zA-Z\.]*?) ([a-zA-Z]*?)\]/, '\1.\2()'], - - [/frameAutosaveName = "(.*?)"/, 'setFrameAutosaveName("\1")'], - - [ /\[NSImage imageNamed:(.*?)\]/, 'NSImage(named: \1)'], - [ /- \(instancetype\)/, ''], - - [/\bif\b\s*\((.*?)\)/, 'if \1'], - [/\bid\b/, 'Any'], - - # for menu definition - [/ action:(.*) keyEquivalent\(/, ', action:\1, keyEquivalent:'], - [/\[NSMenuItem\.alloc\(\) initWithTitle/, 'NSMenuItem(title'], - [/keyEquivalent:"(.*)"\]/, 'keyEquivalent:"\1")'], - [/\[([a-zA-Z]*) setAlternate:(.*)\]/, '\1.isAlternate = \2'], - [/\[([a-zA-Z]*) setKeyEquivalentModifierMask:\((.*)\)\]/, '\1.keyEquivalentModifierMask = [\2]'], - - [/\bVDComparisonStatus([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\bVDComparatorType([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - [/\bVDDisplayFilter([a-zA-Z]+?)\b/, Proc.new { |m| downcaseFirstLetter(m, 0, '.\1') }], - -] - -def renamedToIS(m, index, format_string) - if m.empty? - "" - else - upper = m[index][0].upcase + m[index][1..] - if format_string - duplicated = m.dup - duplicated[index] = upper - replaced = format_string - duplicated.each_with_index { |el, i| replaced.gsub!("\\#{i + 1}", el) } - replaced - else - ".is#{upper}" - end - end -end - -def downcaseFirstLetter(m, index, format_string = nil) - # puts "running on #{m} index #{index} format #{format_string}" - if m.empty? - "" - else - dc = m[index][0].downcase + m[index][1..] - if format_string - duplicated = m.dup - duplicated[index] = dc - replaced = format_string - duplicated.each_with_index { |el, i| replaced.gsub!("\\#{i + 1}", el) } - replaced - else - '.' + dc - end - end -end - -def remove_identical_argument_name(m, separator) - if m.empty? - "" - else - if m[0] == m[2] - "#{m[0]}: #{m[1]}#{separator}" - else - "#{m[0]} #{m[2]}: #{m[1]}#{separator}" - end - end -end - -# Funzione per applicare tutte le regex e le sostituzioni a una riga -def apply_regexps(line, regexps) - regexps.each do |regexp, replacement| - do_replace(line, regexp, replacement) - end - line -end - -def do_replace(line, regexp, replacement) - if replacement.is_a?(String) - # puts "line #{line} replacement #{replacement}" - line.gsub!(regexp, replacement) - elsif replacement.respond_to?(:call) - line.gsub!(regexp) do |m| - replacement.call(Regexp.last_match.captures) - end - end -end - - - - -# line = '[self addTags:@[@"Red"] fullPath:AppendFolder(@"l/Parent/FolderWithTags")];' -# # repl = [/\[self addTags:(\[.*\]) fullPath:(.*?)\]/, 'add(tags: \1, fullPath:\2)'] -# repl = [/\[self addTags:@?(.*?) fullPath:AppendFolder((.*?))];/, 'add(tags: \1, fullPath:\2)'] - -# do_replace(line, repl[0], repl[1]) - -# puts "line is: #{line}" - -# exit - - -# Controlla se è stato passato un file di input -if ARGV.empty? - puts "Per favore, fornisci il nome di un file." - exit 1 -end - -# Apri il file di ingresso -filename = ARGV[0] -reexp_used = if ARGV[1] == 'tests' - STDERR.puts "Using tests regexp" - regexps_tests - else - STDERR.puts "Using appl regexp" - regexps_methods - end - -outdir = 'converted' -FileUtils.mkdir outdir unless File.exist?(outdir) -outname = File.join(outdir, "./#{File.basename(filename, ".*")}.conv") - -begin - File.open(outname, 'w') do |out| - File.open(filename, 'r') do |file| - file.each_line do |line| - # Applica le espressioni regolari e le sostituzioni a ogni riga - modified_line = apply_regexps(line, reexp_used) - # Scrive la riga modificata su stdout - out.print modified_line - end - end - end - system("subl #{outname}") -rescue Errno::ENOENT - puts "Errore: Il file #{filename} non esiste." - exit 1 -end diff --git a/visdiff/AppConfig.xcconfig b/visdiff/AppConfig.xcconfig deleted file mode 100644 index 18865e5..0000000 --- a/visdiff/AppConfig.xcconfig +++ /dev/null @@ -1,28 +0,0 @@ -// -// Versions.xcconfig -// VisualDiffer -// -// Created by davide ficano on 01/02/22. -// Copyright (c) 2022 visualdiffer.com -// - -// The directory containing the project -// must be used to prefix the value for CODE_SIGN_ENTITLEMENTS in Signing.local.xcconfig -VISDIFF_ROOT = visdiff - -PRODUCT_BUNDLE_IDENTIFIER = com.visualdiffer.visdiff -PRODUCT_NAME = $(TARGET_NAME) -CREATE_INFOPLIST_SECTION_IN_BINARY = YES -SKIP_INSTALL = YES -REGISTER_APP_GROUPS = NO - -INFOPLIST_FILE = $(VISDIFF_ROOT)/visdiff-Info.plist - -ENABLE_APP_SANDBOX[config=Debug] = YES -ENABLE_APP_SANDBOX[config=Release] = YES -ENABLE_APP_SANDBOX[config=DebugNoSandbox] = YES - -AUTOMATION_APPLE_EVENTS = YES - -#include "Signing.local.xcconfig" -#include "Versions.local.xcconfig" diff --git a/visdiff/DocumentWaiter.swift b/visdiff/DocumentWaiter.swift deleted file mode 100644 index d39c686..0000000 --- a/visdiff/DocumentWaiter.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// DocumentWaiter.swift -// VisualDiffer -// -// Created by davide ficano on 30/08/11. -// Copyright (c) 2011 visualdiffer.com -// - -import ScriptingBridge - -let documentClosedNotification = NSNotification.Name("VDDocumentClosedNotification") - -enum ComparisonLaunchError: Error, LocalizedError { - case launch(description: String) - case script(error: any Error) - - var errorDescription: String? { - switch self { - case let .launch(description): - description - case let .script(error): - ((error as NSError).userInfo["ErrorString"] as? String) ?? error.localizedDescription - } - } -} - -class DocumentWaiter: NSObject, SBApplicationDelegate { - var leftPath: URL - var rightPath: URL - var uuid: String? - - private var waitClose: Bool - private var error: (any Error)? - - init( - leftPath: URL, - rightPath: URL, - waitClose: Bool - ) { - self.leftPath = leftPath - self.rightPath = rightPath - self.waitClose = waitClose - - super.init() - - DistributedNotificationCenter.default.addObserver( - self, - selector: #selector(exitFromApp), - name: documentClosedNotification, - object: nil - ) - } - - @objc func exitFromApp(_ notification: NSNotification) { - let uuidNotification = notification.object as? String - - if uuid == uuidNotification { - CFRunLoopStop(CFRunLoopGetCurrent()) - } - } - - func openDocument() throws { - guard let app = SBApplication(bundleIdentifier: "com.visualdiffer") else { - throw ComparisonLaunchError.launch(description: "Unable to find VisualDiffer application") - } - app.delegate = self - app.activate() - - // in Swift is more simple to use the dynamic approach - uuid = app.perform( - NSSelectorFromString("openDiffLeftPath:rightPath:"), - with: leftPath.path(percentEncoded: false), - with: rightPath.path(percentEncoded: false) - )?.takeRetainedValue() as? String // swiftlint:disable:this multiline_function_chains - - if let error { - throw ComparisonLaunchError.script(error: error) - } - - #if DEBUG - if uuid == nil { - print("UUID is null. Note to myself. If application is launched from XCode this doesn't work. Close the app and launch it from dock") - } - #endif - if uuid != nil { - if waitClose { - CFRunLoopRun() - } - } - } - - func eventDidFail(_: UnsafePointer, withError error: any Error) -> Any? { - self.error = error - return nil - } -} diff --git a/visdiff/README.md b/visdiff/README.md deleted file mode 100644 index 41fbcd0..0000000 --- a/visdiff/README.md +++ /dev/null @@ -1,17 +0,0 @@ -Visdiff -=== - - -#### Problem: The plist build macros like `PRODUCT_BUNDLE_IDENTIFIER` are not expanded - -The command line tool visdiff is sandboxed using `.plist` + `.entitlements` files - -The plist file must be embedded into binary and before XCode 7 this was achieved adding to the linker `OTHER_LDFLAGS` (under "Other Linker Flags") the value - - -sectcreate __TEXT __info_plist visdiff/Info.plist - -but this 'overwrites' the expansion done by the compiler - -#### Solution - -Remove the linker flag and turn on the flag `Build Settings` -> `Packaging` -> `Create info.plit Section in Binary` \ No newline at end of file diff --git a/visdiff/main.swift b/visdiff/main.swift deleted file mode 100644 index 026a2e3..0000000 --- a/visdiff/main.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// main.swift -// visdiff -// -// Created by davide ficano on 31/07/11. -// Copyright (c) 2011 visualdiffer.com -// - -import Foundation - -let version = Bundle.main.infoDictionary?["CFBundleVersion"] ?? "n/a" - -enum Flags: String { - case version = "--version" - case wait = "--wait" - - func isEqual(_ lhs: String) -> Bool { - rawValue.caseInsensitiveCompare(lhs) == .orderedSame - } -} - -func showHelp() { - print("Usage: leftFileOrFolder rightFileOrFolder \(Flags.version.rawValue) \(Flags.wait.rawValue)\nNotes: left and right must be both files or both folders\n") -} - -func showVersion() { - print(version) -} - -func main() -> Int32 { - var waitClose = false - - // remove executable path - let argv = ProcessInfo.processInfo.arguments - let argc = argv.count - - if argc == 2, Flags.version.isEqual(argv[1]) { - showVersion() - return 1 - } else if argc < 3 { - showHelp() - return 1 - } - - let l = URL(filePath: argv[1]).absoluteURL - let r = URL(filePath: argv[2]).absoluteURL - - for arg in argv.dropFirst(3) { - if Flags.version.isEqual(arg) { - showVersion() - } else if Flags.wait.isEqual(arg) { - waitClose = true - } - } - - do { - try DocumentWaiter(leftPath: l, rightPath: r, waitClose: waitClose).openDocument() - } catch { - print(error.localizedDescription) - return 1 - } - - return 0 -} - -exit(main()) diff --git a/visdiff/visdiff-Info.plist b/visdiff/visdiff-Info.plist deleted file mode 100644 index 19dfe4a..0000000 --- a/visdiff/visdiff-Info.plist +++ /dev/null @@ -1,18 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundleVersion - $(APP_VERSION) - NSAppleEventsUsageDescription - Communicate with the app to open comparison documents - - diff --git a/visdiff/visdiff.1 b/visdiff/visdiff.1 deleted file mode 100644 index 63c9b03..0000000 --- a/visdiff/visdiff.1 +++ /dev/null @@ -1,79 +0,0 @@ -.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. -.\"See Also: -.\"man mdoc.samples for a complete listing of options -.\"man mdoc for the short list of editing options -.\"/usr/share/misc/mdoc.template -.Dd 31/07/11 \" DATE -.Dt visdiff 1 \" Program name and manual section number -.Os Darwin -.Sh NAME \" Section Header - required - don't modify -.Nm visdiff, -.\" The following lines are read in generating the apropos(man -k) database. Use only key -.\" words here as the database is built based on the words here and in the .ND line. -.Nm Other_name_for_same_program(), -.Nm Yet another name for the same program. -.\" Use .Nm macro to designate other names for the documented program. -.Nd This line parsed for whatis database. -.Sh SYNOPSIS \" Section Header - required - don't modify -.Nm -.Op Fl abcd \" [-abcd] -.Op Fl a Ar path \" [-a path] -.Op Ar file \" [file] -.Op Ar \" [file ...] -.Ar arg0 \" Underlined argument - use .Ar anywhere to underline -arg2 ... \" Arguments -.Sh DESCRIPTION \" Section Header - required - don't modify -Use the .Nm macro to refer to your program throughout the man page like such: -.Nm -Underlining is accomplished with the .Ar macro like this: -.Ar underlined text . -.Pp \" Inserts a space -A list of items with descriptions: -.Bl -tag -width -indent \" Begins a tagged list -.It item a \" Each item preceded by .It macro -Description of item a -.It item b -Description of item b -.El \" Ends the list -.Pp -A list of flags and their descriptions: -.Bl -tag -width -indent \" Differs from above in tag removed -.It Fl a \"-a flag as a list item -Description of -a flag -.It Fl b -Description of -b flag -.El \" Ends the list -.Pp -.\" .Sh ENVIRONMENT \" May not be needed -.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 -.\" .It Ev ENV_VAR_1 -.\" Description of ENV_VAR_1 -.\" .It Ev ENV_VAR_2 -.\" Description of ENV_VAR_2 -.\" .El -.Sh FILES \" File used or created by the topic of the man page -.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact -.It Pa /usr/share/file_name -FILE_1 description -.It Pa /Users/joeuser/Library/really_long_file_name -FILE_2 description -.El \" Ends the list -.\" .Sh DIAGNOSTICS \" May not be needed -.\" .Bl -diag -.\" .It Diagnostic Tag -.\" Diagnostic informtion here. -.\" .It Diagnostic Tag -.\" Diagnostic informtion here. -.\" .El -.Sh SEE ALSO -.\" List links in ascending order by section, alphabetically within a section. -.\" Please do not reference files that do not exist without filing a bug report -.Xr a 1 , -.Xr b 1 , -.Xr c 1 , -.Xr a 2 , -.Xr b 2 , -.Xr a 3 , -.Xr b 3 -.\" .Sh BUGS \" Document known, unremedied bugs -.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/visdiff/visdiff.entitlements b/visdiff/visdiff.entitlements deleted file mode 100644 index 8ade55c..0000000 --- a/visdiff/visdiff.entitlements +++ /dev/null @@ -1,17 +0,0 @@ - - - - - com.apple.security.application-groups - - $(TeamIdentifierPrefix)com.appgroup.visualdiffer - - com.apple.security.scripting-targets - - com.visualdiffer - - com.visualdiffer.core - - - -