Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
ololx committed Jul 15, 2021
2 parents c71ad1b + 0456f68 commit 4cfcefb
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 105 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

- Add the new menu item for replacing selected folders and files with symlinks.

## [0.4.1] - 2021-07-15

### Changed

- Implement the `Command` pattern for the `Quick Symlink` actions.

## [0.4.0] - 2021-07-15

### Added
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-important?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.4.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/latest_release_date-May_10,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-July_15,_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.4.1-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-May_10,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-July_15,_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)

Expand Down Expand Up @@ -34,9 +34,18 @@ Of course, creating symbolic links via the terminal is very easy and convenient.

## 📸 Demo

This gif demonstrates the `Quick Symlink` usage.
This GIF demonstrates how the `Quick Symlink` allows quite simple to copy files or folders and paste symlink somewhere.

<img src="https://github.com/ololx/quick-symlink/blob/assets/demo/quick-symlink-demo-1.gif?raw=true" width="700"/>
<img src="https://github.com/ololx/quick-symlink/blob/assets/demo/quick-symlink-demo-1.gif?raw=true" width="100%"/>

This GIF demonstrates how the `Quick Symlink` allows quite simple to copy files or folders, paste them somewhere, and replace them with symlinks.

<img src="https://github.com/ololx/quick-symlink/blob/assets/demo/quick-symlink-demo-replace-with-link.gif?raw=true" width="100%"/>

These GIFs demonstrate the `Quick Symlink` localization (English and Russian).

<img src="https://github.com/ololx/quick-symlink/blob/assets/demo/quick-symlink-demo-localization-1.gif?raw=true" width="100%"/>
<img src="https://github.com/ololx/quick-symlink/blob/assets/demo/quick-symlink-demo-localization-2.gif?raw=true" width="100%"/>

## 🎚 Features

Expand Down
41 changes: 41 additions & 0 deletions commons/CopyPathAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// CopyPathAction.swift
// quick-symlink
//
// Created by Alexander A. Kropotin on 15/07/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import Foundation
import FinderSync

public class CopyPathAction: QuickSymlinkAction {

private var finderController: FIFinderSyncController;

public init() {
self.finderController = FIFinderSyncController.default();
}

public func execute() {
//Get all selected path
guard let target = self.finderController.selectedItemURLs() else {
NSLog("FinderSync() failed to obtain targeted URLs: %@");

return;
}

// Append all selected paths to string
var paths = ""
for path in target {
paths.append(contentsOf: path.relativePath);
paths.append(";");
}
paths.removeLast();

//Copy path list to clipboard
let pasteboard = NSPasteboard.general;
pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil);
pasteboard.setString(paths, forType: NSPasteboard.PasteboardType.string);
}
}
45 changes: 45 additions & 0 deletions commons/PasteLinkAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// PasteSymlinkAction.swift
// quick-symlink
//
// Created by Alexander A. Kropotin on 15/07/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import Foundation
import FinderSync

public class PasteLinkAction: QuickSymlinkAction {

private var finderController: FIFinderSyncController;

public init() {
self.finderController = FIFinderSyncController.default();
}

public func execute() {
//Get selected folder path
guard let target = self.finderController.targetedURL() else {
NSLog("FinderSync() failed to obtain targeted URL: %@");

return;
}

let pathsFromClipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string) ?? "";
if pathsFromClipboard.isEmpty {
return;
}

let paths = pathsFromClipboard.components(separatedBy: ";");
for path in paths {
let pathUrl = URL(fileURLWithPath: path);
let targetPath = self.getTargetPath(pathUrl, to: target);

do {
try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: URL(fileURLWithPath: path));
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
}
}
}
40 changes: 40 additions & 0 deletions commons/QuickSymlinkAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// QuickSymlinkAction.swift
// quick-symlink
//
// Created by Alexander A. Kropotin on 15/07/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import Foundation

public protocol QuickSymlinkAction {

func execute();
}

internal extension QuickSymlinkAction {

internal func getTargetPath(_ from: URL!, to: URL!) -> URL! {
let originSourceName = from.absoluteURL.deletingPathExtension().lastPathComponent;
let fileType = from.absoluteURL.pathExtension;

var fileExtention = fileType;
if !fileType.isEmpty {
fileExtention = ".\(fileType)"
}

var fileName = "\(originSourceName)\(fileExtention)";
var counter = 1
var targetPath = to;
targetPath = targetPath?.appendingPathComponent(fileName);

while FileManager.default.fileExists(atPath: (targetPath?.path)!) {
fileName = "\(originSourceName)-\(counter)\(fileExtention)";
counter += 1;
targetPath = to.appendingPathComponent(fileName);
}

return targetPath!;
}
}
46 changes: 46 additions & 0 deletions commons/ReplaceWithLinkAction.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// ReplaceWithLinkAction.swift
// quick-symlink
//
// Created by Alexander A. Kropotin on 15/07/2021.
// Copyright © 2021 Alexander A. Kropotin. All rights reserved.
//

import Foundation
import FinderSync

public class ReplaceWithLinkAction: QuickSymlinkAction {

private var finderController: FIFinderSyncController;

public init() {
self.finderController = FIFinderSyncController.default();
}

public func execute() {
//Get selected folder path
guard let target = self.finderController.targetedURL() else {
NSLog("FinderSync() failed to obtain targeted URL: %@");

return;
}

let pathsFromClipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string) ?? "";
if pathsFromClipboard.isEmpty {
return;
}

let paths = pathsFromClipboard.components(separatedBy: ";");
for path in paths {
let pathUrl = URL(fileURLWithPath: path);
let targetPath = self.getTargetPath(pathUrl, to: target);

do {
try FileManager.default.moveItem(at: pathUrl, to: targetPath!);
try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: targetPath!);
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
}
}
}
107 changes: 7 additions & 100 deletions quick-symlink-extension/FinderSync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import FinderSync

class FinderSync: FIFinderSync {

var myFolderURL = URL(fileURLWithPath: "/");

let quickSymlinkToolbarItemImage = NSImage(named:NSImage.Name(rawValue: "quick-symlink-toolbar-item-image"));

let copyPathAction = CopyPathAction.init();
let pasteLinkAction = PasteLinkAction.init();
let replaceWithLinkAction = ReplaceWithLinkAction.init();

override init() {
super.init()

Expand Down Expand Up @@ -105,109 +107,14 @@ class FinderSync: FIFinderSync {
}

@IBAction func copyPathToClipboard(_ sender: AnyObject?) {
//Get all selected path
guard let target = FIFinderSyncController.default().selectedItemURLs() else {
NSLog("FinderSync() failed to obtain targeted URLs: %@");

return;
}

// Append all selected paths to string
var paths = ""
for path in target {
paths.append(contentsOf: path.relativePath);
paths.append(";");
}
paths.removeLast();

//Copy path list to clipboard
let pasteboard = NSPasteboard.general;
pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil);
pasteboard.setString(paths, forType: NSPasteboard.PasteboardType.string);
self.copyPathAction.execute();
}

@IBAction func replaceFileWithSymlinkFromClipboard(_ sender: AnyObject?) {
//Get selected folder path
guard let target = FIFinderSyncController.default().targetedURL() else {
NSLog("FinderSync() failed to obtain targeted URL: %@");

return;
}

let pathsFromClipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string) ?? "";
if pathsFromClipboard.isEmpty {
return;
}

let paths = pathsFromClipboard.components(separatedBy: ";");
for path in paths {
var targetPath = target
let pathUrl = URL(fileURLWithPath: path);
let originSourceName = pathUrl.absoluteURL.deletingPathExtension().lastPathComponent;
let fileType = pathUrl.absoluteURL.pathExtension;

var fileExtention = fileType;
if !fileType.isEmpty {
fileExtention = ".\(fileType)"
}

var fileName = "\(originSourceName)\(fileExtention)";
var counter = 1
targetPath = targetPath.appendingPathComponent(fileName);
while FileManager.default.fileExists(atPath: targetPath.path) {
fileName = "\(originSourceName)-\(counter)\(fileExtention)";
counter += 1;
targetPath = target.appendingPathComponent(fileName);
}

do {
try FileManager.default.moveItem(at: pathUrl, to: targetPath);
try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: targetPath);
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
}
self.replaceWithLinkAction.execute();
}

@IBAction func pastleSymlinkFromClipboard(_ sender: AnyObject?) {
//Get selected folder path
guard let target = FIFinderSyncController.default().targetedURL() else {
NSLog("FinderSync() failed to obtain targeted URL: %@");

return;
}

let pathsFromClipboard = NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.string) ?? "";
if pathsFromClipboard.isEmpty {
return;
}

let paths = pathsFromClipboard.components(separatedBy: ";");
for path in paths {
var targetPath = target
let pathUrl = URL(fileURLWithPath: path);
let originSourceName = pathUrl.absoluteURL.deletingPathExtension().lastPathComponent;
let fileType = pathUrl.absoluteURL.pathExtension;

var fileExtention = fileType;
if !fileType.isEmpty {
fileExtention = ".\(fileType)"
}

var fileName = "\(originSourceName)\(fileExtention)";
var counter = 1
targetPath = targetPath.appendingPathComponent(fileName);
while FileManager.default.fileExists(atPath: targetPath.path) {
fileName = "\(originSourceName)-\(counter)\(fileExtention)";
counter += 1;
targetPath = target.appendingPathComponent(fileName);
}

do {
try FileManager.default.createSymbolicLink(at: targetPath, withDestinationURL: URL(fileURLWithPath: path));
} catch let error as NSError {
NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString);
}
}
self.pasteLinkAction.execute();
}
}
2 changes: 1 addition & 1 deletion quick-symlink-extension/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<string>en_GB</string>
<key>CFBundleDisplayName</key>
<string>quick-symlink-extension</string>
<key>CFBundleExecutable</key>
Expand Down
Loading

0 comments on commit 4cfcefb

Please sign in to comment.