From 72b9543c3c9d7117687b703f3a979135cf4d3c14 Mon Sep 17 00:00:00 2001 From: "Alexander A. Kropotin" Date: Sat, 21 Aug 2021 20:37:11 +0500 Subject: [PATCH 1/4] geature(CreateLinkAction.swift, PasteLinkAction.swift) implemet relative links --- commons/CreateLinkAction.swift | 25 ++++++++++++++++++- commons/PasteLinkAction.swift | 24 +++++++++++++++++- .../ru.lproj/Localizable.strings | 2 +- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/commons/CreateLinkAction.swift b/commons/CreateLinkAction.swift index 7f6dc76..f302ee3 100644 --- a/commons/CreateLinkAction.swift +++ b/commons/CreateLinkAction.swift @@ -28,8 +28,31 @@ public class CreateLinkAction: QuickSymlinkAction { for path in target { let targetPath = self.getTargetPath(path, to: path.deletingLastPathComponent()); + + var pathFragments = path.pathComponents; + var targetPathFragments = targetPath?.deletingLastPathComponent().pathComponents; + + var destinationPath = URL.init(string: "./")!; + + for targetPathFragment in targetPathFragments! { + if (!pathFragments.contains(targetPathFragment)) { + break; + } + + pathFragments.remove(at: 0); + targetPathFragments?.remove(at: 0); + } + + for _ in targetPathFragments! { + destinationPath.appendPathComponent("../"); + } + + for pathFragment in pathFragments { + destinationPath.appendPathComponent(pathFragment); + } + do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: path); + try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: destinationPath); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/commons/PasteLinkAction.swift b/commons/PasteLinkAction.swift index b174aa1..e071489 100644 --- a/commons/PasteLinkAction.swift +++ b/commons/PasteLinkAction.swift @@ -37,8 +37,30 @@ public class PasteLinkAction: QuickSymlinkAction { let pathUrl = URL(fileURLWithPath: path); let targetPath = self.getTargetPath(pathUrl, to: target); + var pathFragments = pathUrl.pathComponents; + var targetPathFragments = targetPath?.deletingLastPathComponent().pathComponents; + + var destinationPath = URL.init(string: "./")!; + + for targetPathFragment in targetPathFragments! { + if (!pathFragments.contains(targetPathFragment)) { + break; + } + + pathFragments.remove(at: 0); + targetPathFragments?.remove(at: 0); + } + + for _ in targetPathFragments! { + destinationPath.appendPathComponent("../"); + } + + for pathFragment in pathFragments { + destinationPath.appendPathComponent(pathFragment); + } + do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: URL(fileURLWithPath: path)); + try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: destinationPath); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/quick-symlink-extension/ru.lproj/Localizable.strings b/quick-symlink-extension/ru.lproj/Localizable.strings index 361f830..dec8822 100644 --- a/quick-symlink-extension/ru.lproj/Localizable.strings +++ b/quick-symlink-extension/ru.lproj/Localizable.strings @@ -7,7 +7,7 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Быстрый симлинк"; +"EXTENTION_NAME" = "Быстрый симлинк22"; /* Class = "NSMenuItem"; */ "EXTENTION_TOOL_TIP" = "Создать символьные ссылки для выбранных файлов и папок"; From 61593996fe4b4df6b50c637b178f7d37a9116ad6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Kropotin" Date: Sun, 22 Aug 2021 14:29:53 +0500 Subject: [PATCH 2/4] feature(Path.swift, ResourcePath.swift) define Path entity --- commons/FileSystem/Path.swift | 20 +++++ commons/FileSystem/ResourcePath.swift | 81 +++++++++++++++++++ .../CopyPathAction.swift | 0 .../CreateLinkAction.swift | 25 +----- .../PasteLinkAction.swift | 24 +----- .../QuickSymlinkAction.swift | 0 .../ReplaceWithLinkAction.swift | 2 +- .../ru.lproj/Localizable.strings | 2 +- quick-symlink.xcodeproj/project.pbxproj | 38 +++++++-- 9 files changed, 138 insertions(+), 54 deletions(-) create mode 100644 commons/FileSystem/Path.swift create mode 100644 commons/FileSystem/ResourcePath.swift rename commons/{ => QuickSymlinkActions}/CopyPathAction.swift (100%) rename commons/{ => QuickSymlinkActions}/CreateLinkAction.swift (55%) rename commons/{ => QuickSymlinkActions}/PasteLinkAction.swift (63%) rename commons/{ => QuickSymlinkActions}/QuickSymlinkAction.swift (100%) rename commons/{ => QuickSymlinkActions}/ReplaceWithLinkAction.swift (91%) diff --git a/commons/FileSystem/Path.swift b/commons/FileSystem/Path.swift new file mode 100644 index 0000000..cc1f269 --- /dev/null +++ b/commons/FileSystem/Path.swift @@ -0,0 +1,20 @@ +// +// Path.swift +// quick-symlink +// +// Created by Alexander A. Kropotin on 22/08/2021. +// Copyright © 2021 Alexander A. Kropotin. All rights reserved. +// + +import Foundation + +public protocol Path { + + func relativize(to other: Path!) -> Path!; + + func getPathFragments() -> [String]!; + + func toUrl() -> URL?; + + func toUriString() -> String?; +} diff --git a/commons/FileSystem/ResourcePath.swift b/commons/FileSystem/ResourcePath.swift new file mode 100644 index 0000000..2417bf7 --- /dev/null +++ b/commons/FileSystem/ResourcePath.swift @@ -0,0 +1,81 @@ +// +// ResourcePath.swift +// quick-symlink +// +// Created by Alexander A. Kropotin on 22/08/2021. +// Copyright © 2021 Alexander A. Kropotin. All rights reserved. +// + +import Foundation + +public class ResourcePath: Path { + + public static func of(url: URL!) -> Path! { + return ResourcePath.of(fragments: url.pathComponents) + } + + public static func of(fragments: [String]!) -> Path! { + return ResourcePath.init(of: fragments); + } + + private var uriFragments: [String]!; + + public init(of fragments: [String]) { + self.uriFragments = fragments; + } + + public func getPathFragments() -> [String]! { + return self.uriFragments; + } + + public func relativize(to other: Path!) -> Path! { + var pathFragments = self.uriFragments; + var targetPathFragments = other.getPathFragments(); + + var destinationPath = URL.init(string: "./")!; + + for targetPathFragment in targetPathFragments! { + if (!(pathFragments?.contains(targetPathFragment))!) { + break; + } + + pathFragments?.remove(at: 0); + targetPathFragments?.remove(at: 0); + } + + for _ in targetPathFragments! { + destinationPath.appendPathComponent("../"); + } + + for pathFragment in pathFragments! { + destinationPath.appendPathComponent(pathFragment); + } + + return ResourcePath.of(url: destinationPath); + } + + public func toUrl() -> URL? { + if self.uriFragments.count == 0 { + return nil; + } + + var uri = URL.init(string: self.uriFragments.first!)!; + for fragment in self.uriFragments.dropFirst().dropLast() { + uri.appendPathComponent(fragment); + uri.appendPathComponent("/"); + } + uri.appendPathComponent(self.uriFragments.last!); + + return uri; + } + + public func toUriString() -> String? { + if self.uriFragments.count == 0 { + return nil; + } + + return self.toUrl()!.absoluteString; + } + + +} diff --git a/commons/CopyPathAction.swift b/commons/QuickSymlinkActions/CopyPathAction.swift similarity index 100% rename from commons/CopyPathAction.swift rename to commons/QuickSymlinkActions/CopyPathAction.swift diff --git a/commons/CreateLinkAction.swift b/commons/QuickSymlinkActions/CreateLinkAction.swift similarity index 55% rename from commons/CreateLinkAction.swift rename to commons/QuickSymlinkActions/CreateLinkAction.swift index f302ee3..ac97835 100644 --- a/commons/CreateLinkAction.swift +++ b/commons/QuickSymlinkActions/CreateLinkAction.swift @@ -28,31 +28,8 @@ public class CreateLinkAction: QuickSymlinkAction { for path in target { let targetPath = self.getTargetPath(path, to: path.deletingLastPathComponent()); - - var pathFragments = path.pathComponents; - var targetPathFragments = targetPath?.deletingLastPathComponent().pathComponents; - - var destinationPath = URL.init(string: "./")!; - - for targetPathFragment in targetPathFragments! { - if (!pathFragments.contains(targetPathFragment)) { - break; - } - - pathFragments.remove(at: 0); - targetPathFragments?.remove(at: 0); - } - - for _ in targetPathFragments! { - destinationPath.appendPathComponent("../"); - } - - for pathFragment in pathFragments { - destinationPath.appendPathComponent(pathFragment); - } - do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: destinationPath); + try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: path).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/commons/PasteLinkAction.swift b/commons/QuickSymlinkActions/PasteLinkAction.swift similarity index 63% rename from commons/PasteLinkAction.swift rename to commons/QuickSymlinkActions/PasteLinkAction.swift index e071489..991fbbe 100644 --- a/commons/PasteLinkAction.swift +++ b/commons/QuickSymlinkActions/PasteLinkAction.swift @@ -37,30 +37,8 @@ public class PasteLinkAction: QuickSymlinkAction { let pathUrl = URL(fileURLWithPath: path); let targetPath = self.getTargetPath(pathUrl, to: target); - var pathFragments = pathUrl.pathComponents; - var targetPathFragments = targetPath?.deletingLastPathComponent().pathComponents; - - var destinationPath = URL.init(string: "./")!; - - for targetPathFragment in targetPathFragments! { - if (!pathFragments.contains(targetPathFragment)) { - break; - } - - pathFragments.remove(at: 0); - targetPathFragments?.remove(at: 0); - } - - for _ in targetPathFragments! { - destinationPath.appendPathComponent("../"); - } - - for pathFragment in pathFragments { - destinationPath.appendPathComponent(pathFragment); - } - do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: destinationPath); + try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: pathUrl).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/commons/QuickSymlinkAction.swift b/commons/QuickSymlinkActions/QuickSymlinkAction.swift similarity index 100% rename from commons/QuickSymlinkAction.swift rename to commons/QuickSymlinkActions/QuickSymlinkAction.swift diff --git a/commons/ReplaceWithLinkAction.swift b/commons/QuickSymlinkActions/ReplaceWithLinkAction.swift similarity index 91% rename from commons/ReplaceWithLinkAction.swift rename to commons/QuickSymlinkActions/ReplaceWithLinkAction.swift index 07c988f..d2a9c1b 100644 --- a/commons/ReplaceWithLinkAction.swift +++ b/commons/QuickSymlinkActions/ReplaceWithLinkAction.swift @@ -39,7 +39,7 @@ public class ReplaceWithLinkAction: QuickSymlinkAction { do { try FileManager.default.moveItem(at: pathUrl, to: targetPath!); - try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: targetPath!); + try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: ResourcePath.of(url: targetPath).relativize(to: ResourcePath.of(url: pathUrl.deletingLastPathComponent())).toUrl()!); } catch let error as NSError { NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); } diff --git a/quick-symlink-extension/ru.lproj/Localizable.strings b/quick-symlink-extension/ru.lproj/Localizable.strings index dec8822..99d36ab 100644 --- a/quick-symlink-extension/ru.lproj/Localizable.strings +++ b/quick-symlink-extension/ru.lproj/Localizable.strings @@ -7,7 +7,7 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Быстрый симлинк22"; +"EXTENTION_NAME" = "Быстрый симлинк2"; /* Class = "NSMenuItem"; */ "EXTENTION_TOOL_TIP" = "Создать символьные ссылки для выбранных файлов и папок"; diff --git a/quick-symlink.xcodeproj/project.pbxproj b/quick-symlink.xcodeproj/project.pbxproj index 7e1ab47..d1a60af 100644 --- a/quick-symlink.xcodeproj/project.pbxproj +++ b/quick-symlink.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + A307B41726D21E39002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; + A307B41826D21E39002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; + A307B41A26D22116002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A307B41B26D22116002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; A30B9AAB265CA63300ACAA63 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A30B9AAA265CA63300ACAA63 /* AppDelegate.swift */; }; A30B9AAD265CA63300ACAA63 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A30B9AAC265CA63300ACAA63 /* ViewController.swift */; }; A30B9AAF265CA63300ACAA63 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A30B9AAE265CA63300ACAA63 /* Assets.xcassets */; }; @@ -52,6 +56,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A307B41626D21E39002EEF58 /* Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Path.swift; sourceTree = ""; }; + A307B41926D22116002EEF58 /* ResourcePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePath.swift; sourceTree = ""; }; A30B9AA7265CA63300ACAA63 /* quick-symlink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "quick-symlink.app"; sourceTree = BUILT_PRODUCTS_DIR; }; A30B9AAA265CA63300ACAA63 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A30B9AAC265CA63300ACAA63 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -97,6 +103,27 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A307B41426D21DBE002EEF58 /* QuickSymlinkActions */ = { + isa = PBXGroup; + children = ( + A316477626B7B403001DD969 /* CreateLinkAction.swift */, + A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */, + A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */, + A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */, + A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */, + ); + path = QuickSymlinkActions; + sourceTree = ""; + }; + A307B41526D21E23002EEF58 /* FileSystem */ = { + isa = PBXGroup; + children = ( + A307B41626D21E39002EEF58 /* Path.swift */, + A307B41926D22116002EEF58 /* ResourcePath.swift */, + ); + path = FileSystem; + sourceTree = ""; + }; A30B9A9E265CA63300ACAA63 = { isa = PBXGroup; children = ( @@ -145,11 +172,8 @@ A345C9BD26A0B12C004FBF0F /* commons */ = { isa = PBXGroup; children = ( - A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */, - A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */, - A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */, - A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */, - A316477626B7B403001DD969 /* CreateLinkAction.swift */, + A307B41526D21E23002EEF58 /* FileSystem */, + A307B41426D21DBE002EEF58 /* QuickSymlinkActions */, ); path = commons; sourceTree = ""; @@ -265,8 +289,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A307B41A26D22116002EEF58 /* ResourcePath.swift in Sources */, A345C9C826A0B552004FBF0F /* ReplaceWithLinkAction.swift in Sources */, A316477726B7B403001DD969 /* CreateLinkAction.swift in Sources */, + A307B41726D21E39002EEF58 /* Path.swift in Sources */, A30B9AAD265CA63300ACAA63 /* ViewController.swift in Sources */, A345C9C226A0B30F004FBF0F /* CopyPathAction.swift in Sources */, A345C9BF26A0B200004FBF0F /* QuickSymlinkAction.swift in Sources */, @@ -280,6 +306,8 @@ buildActionMask = 2147483647; files = ( A30B9AC1265CA68900ACAA63 /* FinderSync.swift in Sources */, + A307B41826D21E39002EEF58 /* Path.swift in Sources */, + A307B41B26D22116002EEF58 /* ResourcePath.swift in Sources */, A30D4A4B26A0C14400BA775B /* PasteLinkAction.swift in Sources */, A30D4A4C26A0C14400BA775B /* ReplaceWithLinkAction.swift in Sources */, A316477826B7B403001DD969 /* CreateLinkAction.swift in Sources */, From a8c7c78e6c6c0066d56a62b1a40cbdbc1ca6c3d3 Mon Sep 17 00:00:00 2001 From: "Alexander A. Kropotin" Date: Sun, 22 Aug 2021 19:46:59 +0500 Subject: [PATCH 3/4] chore(ResourcePathTest.swift) add the ResourcePath test cases --- commons/FileSystem/ResourcePath.swift | 3 +- quick-symlink-tests/Info.plist | 22 ++++ quick-symlink-tests/ResourcePathTest.swift | 49 +++++++++ quick-symlink.xcodeproj/project.pbxproj | 118 +++++++++++++++++++++ 4 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 quick-symlink-tests/Info.plist create mode 100644 quick-symlink-tests/ResourcePathTest.swift diff --git a/commons/FileSystem/ResourcePath.swift b/commons/FileSystem/ResourcePath.swift index 2417bf7..fa4fb6c 100644 --- a/commons/FileSystem/ResourcePath.swift +++ b/commons/FileSystem/ResourcePath.swift @@ -33,7 +33,6 @@ public class ResourcePath: Path { var targetPathFragments = other.getPathFragments(); var destinationPath = URL.init(string: "./")!; - for targetPathFragment in targetPathFragments! { if (!(pathFragments?.contains(targetPathFragment))!) { break; @@ -51,6 +50,8 @@ public class ResourcePath: Path { destinationPath.appendPathComponent(pathFragment); } + //pathFragments!.append(contentsOf: targetPathFragments!); + return ResourcePath.of(url: destinationPath); } diff --git a/quick-symlink-tests/Info.plist b/quick-symlink-tests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/quick-symlink-tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/quick-symlink-tests/ResourcePathTest.swift b/quick-symlink-tests/ResourcePathTest.swift new file mode 100644 index 0000000..40f4b7e --- /dev/null +++ b/quick-symlink-tests/ResourcePathTest.swift @@ -0,0 +1,49 @@ +// +// ResourcePathTest.swift +// quick-symlink-tests +// +// Created by Alexander A. Kropotin on 22/08/2021. +// Copyright © 2021 Alexander A. Kropotin. All rights reserved. +// + +import XCTest + +class ResourcePathTest: XCTestCase { + + func test_toURL_methodExecution_returnedURLIsEqualToInitialURL() { + let uri: URL = URL.init(string: "/a/b/c/d")!; + let path: Path = ResourcePath.of(url: uri); + + XCTAssert(path.toUrl() == uri, "The Path URL is not equal to URL"); + } + + func test_relativize_whenCurrentDirectoryIsNestedToOtherDirectory_thenReturnPathStartingFromOtherDirectory() { + let currentUri: URL = URL.init(string: "/a/b/c/d")!; + let otherUri: URL = URL.init(string: "/a/b")!; + + let relativePath: Path = ResourcePath.of(url: currentUri) + .relativize(to: ResourcePath.of(url: otherUri)); + + XCTAssert(relativePath.toUriString() == "./c/d", "The relative path is wrong"); + } + + func test_relativize_whenOtherDirectoryIsNestedToCurrentDirectory_thenReturnPathWithOnlyJumpsAboveOtherDirectory() { + let currentUri: URL = URL.init(string: "/a/b")!; + let otherUri: URL = URL.init(string: "/a/b/c/d")!; + + let relativePath: Path = ResourcePath.of(url: currentUri) + .relativize(to: ResourcePath.of(url: otherUri)); + + XCTAssert(relativePath.toUriString() == "./../..", "The relative path is wrong"); + } + + func test_relativize_whenCurrentDirectoryAndOtherDirectoryAreNestedToGeneralDirectoryy_thenReturnPathWithJumpsAboveOtherDirectoryAndPartOfCurrentDirectory() { + let currentUri: URL = URL.init(string: "/a/b/c1/d1")!; + let otherUri: URL = URL.init(string: "/a/b/c2/d2")!; + + let relativePath: Path = ResourcePath.of(url: currentUri) + .relativize(to: ResourcePath.of(url: otherUri)); + + XCTAssert(relativePath.toUriString() == "./../../c1/d1", "The relative path is wrong"); + } +} diff --git a/quick-symlink.xcodeproj/project.pbxproj b/quick-symlink.xcodeproj/project.pbxproj index d1a60af..1de801c 100644 --- a/quick-symlink.xcodeproj/project.pbxproj +++ b/quick-symlink.xcodeproj/project.pbxproj @@ -11,6 +11,9 @@ A307B41826D21E39002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; A307B41A26D22116002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; A307B41B26D22116002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A307B42F26D2563B002EEF58 /* ResourcePathTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B42E26D2563B002EEF58 /* ResourcePathTest.swift */; }; + A307B43026D257C5002EEF58 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A307B43126D257CA002EEF58 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; A30B9AAB265CA63300ACAA63 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A30B9AAA265CA63300ACAA63 /* AppDelegate.swift */; }; A30B9AAD265CA63300ACAA63 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A30B9AAC265CA63300ACAA63 /* ViewController.swift */; }; A30B9AAF265CA63300ACAA63 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A30B9AAE265CA63300ACAA63 /* Assets.xcassets */; }; @@ -32,6 +35,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + A307B42926D255FB002EEF58 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A30B9A9F265CA63300ACAA63 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A30B9AA6265CA63300ACAA63; + remoteInfo = "quick-symlink"; + }; A30B9AC4265CA68900ACAA63 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A30B9A9F265CA63300ACAA63 /* Project object */; @@ -58,6 +68,9 @@ /* Begin PBXFileReference section */ A307B41626D21E39002EEF58 /* Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Path.swift; sourceTree = ""; }; A307B41926D22116002EEF58 /* ResourcePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePath.swift; sourceTree = ""; }; + A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "quick-symlink-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + A307B42826D255FB002EEF58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A307B42E26D2563B002EEF58 /* ResourcePathTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePathTest.swift; sourceTree = ""; }; A30B9AA7265CA63300ACAA63 /* quick-symlink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "quick-symlink.app"; sourceTree = BUILT_PRODUCTS_DIR; }; A30B9AAA265CA63300ACAA63 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A30B9AAC265CA63300ACAA63 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -86,6 +99,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + A307B42126D255FB002EEF58 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A30B9AA4265CA63300ACAA63 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -124,6 +144,15 @@ path = FileSystem; sourceTree = ""; }; + A307B42526D255FB002EEF58 /* quick-symlink-tests */ = { + isa = PBXGroup; + children = ( + A307B42826D255FB002EEF58 /* Info.plist */, + A307B42E26D2563B002EEF58 /* ResourcePathTest.swift */, + ); + path = "quick-symlink-tests"; + sourceTree = ""; + }; A30B9A9E265CA63300ACAA63 = { isa = PBXGroup; children = ( @@ -131,6 +160,7 @@ A3D93EB726A09B5A004E068D /* Localizable.strings */, A30B9AA9265CA63300ACAA63 /* quick-symlink */, A30B9ABF265CA68900ACAA63 /* quick-symlink-extension */, + A307B42526D255FB002EEF58 /* quick-symlink-tests */, A30B9AA8265CA63300ACAA63 /* Products */, ); sourceTree = ""; @@ -140,6 +170,7 @@ children = ( A30B9AA7265CA63300ACAA63 /* quick-symlink.app */, A30B9ABE265CA68900ACAA63 /* quick-symlink-extension.appex */, + A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */, ); name = Products; sourceTree = ""; @@ -181,6 +212,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + A307B42326D255FB002EEF58 /* quick-symlink-tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A307B42B26D255FB002EEF58 /* Build configuration list for PBXNativeTarget "quick-symlink-tests" */; + buildPhases = ( + A307B42026D255FB002EEF58 /* Sources */, + A307B42126D255FB002EEF58 /* Frameworks */, + A307B42226D255FB002EEF58 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A307B42A26D255FB002EEF58 /* PBXTargetDependency */, + ); + name = "quick-symlink-tests"; + productName = "quick-symlink-tests"; + productReference = A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; A30B9AA6265CA63300ACAA63 /* quick-symlink */ = { isa = PBXNativeTarget; buildConfigurationList = A30B9AB7265CA63300ACAA63 /* Build configuration list for PBXNativeTarget "quick-symlink" */; @@ -227,6 +276,11 @@ LastUpgradeCheck = 0920; ORGANIZATIONNAME = "Alexander A. Kropotin"; TargetAttributes = { + A307B42326D255FB002EEF58 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + TestTargetID = A30B9AA6265CA63300ACAA63; + }; A30B9AA6265CA63300ACAA63 = { CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; @@ -259,11 +313,19 @@ targets = ( A30B9AA6265CA63300ACAA63 /* quick-symlink */, A30B9ABD265CA68900ACAA63 /* quick-symlink-extension */, + A307B42326D255FB002EEF58 /* quick-symlink-tests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + A307B42226D255FB002EEF58 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; A30B9AA5265CA63300ACAA63 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -285,6 +347,16 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + A307B42026D255FB002EEF58 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A307B43026D257C5002EEF58 /* ResourcePath.swift in Sources */, + A307B42F26D2563B002EEF58 /* ResourcePathTest.swift in Sources */, + A307B43126D257CA002EEF58 /* Path.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A30B9AA3265CA63300ACAA63 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -319,6 +391,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + A307B42A26D255FB002EEF58 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A30B9AA6265CA63300ACAA63 /* quick-symlink */; + targetProxy = A307B42926D255FB002EEF58 /* PBXContainerItemProxy */; + }; A30B9AC5265CA68900ACAA63 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A30B9ABD265CA68900ACAA63 /* quick-symlink-extension */; @@ -353,6 +430,38 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + A307B42C26D255FB002EEF58 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "quick-symlink-tests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/quick-symlink.app/Contents/MacOS/quick-symlink"; + }; + name = Debug; + }; + A307B42D26D255FB002EEF58 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "quick-symlink-tests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink-tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/quick-symlink.app/Contents/MacOS/quick-symlink"; + }; + name = Release; + }; A30B9AB5265CA63300ACAA63 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -530,6 +639,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + A307B42B26D255FB002EEF58 /* Build configuration list for PBXNativeTarget "quick-symlink-tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A307B42C26D255FB002EEF58 /* Debug */, + A307B42D26D255FB002EEF58 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; A30B9AA2265CA63300ACAA63 /* Build configuration list for PBXProject "quick-symlink" */ = { isa = XCConfigurationList; buildConfigurations = ( From cd54055eaff313a52d609060c80450e02274436c Mon Sep 17 00:00:00 2001 From: "Alexander A. Kropotin" Date: Sun, 22 Aug 2021 19:56:40 +0500 Subject: [PATCH 4/4] chore(CHANGELOG.md, README.md, TODO.md) actualize description --- CHANGELOG.md | 11 +++++++++++ README.md | 2 +- TODO.md | 5 +++-- quick-symlink-extension/ru.lproj/Localizable.strings | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64713e8..b158d0f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Refactor application. +## [0.7.0] - 2021-08-22 + +### Added + +- Added new classes `Path` for working with path's and creating relative path from specified directory. +- Add unit-tests cases for the `Path` class. + +### Changed + +- Change soft link creation using relative instead absolute path. + ## [0.6.0] - 2021-08-02 ### Changed diff --git a/README.md b/README.md index a224cac..e09474a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The Quick Symlink is a `Finder extension` which provides a `contextual menu item` for the symbolic links creation on macOS. -[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_07,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_20,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date) +[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date) [![license](https://img.shields.io/badge/license-MIT-informational?style=flat-square)](LICENSE) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg?style=flat-square)](CODE_OF_CONDUCT.md) diff --git a/TODO.md b/TODO.md index 3aee519..a3ffbc5 100755 --- a/TODO.md +++ b/TODO.md @@ -5,8 +5,9 @@ - [x] Develop the `Finder extension` which allows to create a symlinks for selected folders and files via contextual menu. - [x] Add the new menu item for replacing selected folders and files with symlinks. - [x] Add the new menu item for creating symlink in a parent directory (parent for target objects). -- [ ] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume) -- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu. +- [x] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume) +- [ ] Refactor code in `commons/*` part and add unit-tests +- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu - [ ] Develop the action panel for created symbolic links and hard lonks (in the app window): - [ ] a) (if broken) process to browse finder for 'Find/fix missing target' - [ ] b) Modify existing paths and symbolic link features diff --git a/quick-symlink-extension/ru.lproj/Localizable.strings b/quick-symlink-extension/ru.lproj/Localizable.strings index 99d36ab..361f830 100644 --- a/quick-symlink-extension/ru.lproj/Localizable.strings +++ b/quick-symlink-extension/ru.lproj/Localizable.strings @@ -7,7 +7,7 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Быстрый симлинк2"; +"EXTENTION_NAME" = "Быстрый симлинк"; /* Class = "NSMenuItem"; */ "EXTENTION_TOOL_TIP" = "Создать символьные ссылки для выбранных файлов и папок";