summaryrefslogtreecommitdiff
path: root/mut
diff options
context:
space:
mode:
Diffstat (limited to 'mut')
-rw-r--r--mut/keuze/.gitignore63
-rw-r--r--mut/keuze/README.md44
-rw-r--r--mut/keuze/keuze.xcodeproj/project.pbxproj353
-rw-r--r--mut/keuze/keuze.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--mut/keuze/keuze.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist8
-rw-r--r--mut/keuze/keuze.xcodeproj/xcshareddata/xcschemes/kies.xcscheme97
-rw-r--r--mut/keuze/kies/DataSource.swift43
-rw-r--r--mut/keuze/kies/Info.plist32
-rw-r--r--mut/keuze/kies/InputField.swift67
-rw-r--r--mut/keuze/kies/Layout.swift44
-rw-r--r--mut/keuze/kies/PromptText.swift28
-rw-r--r--mut/keuze/kies/Settings.swift48
-rw-r--r--mut/keuze/kies/SwiftLCS.swift268
-rw-r--r--mut/keuze/kies/TableView.swift89
-rw-r--r--mut/keuze/kies/Window.swift51
-rw-r--r--mut/keuze/kies/constants.swift13
-rw-r--r--mut/keuze/kies/main.swift132
-rw-r--r--mut/keuze/screenshots/screenshot1.pngbin0 -> 2922865 bytes
18 files changed, 1387 insertions, 0 deletions
diff --git a/mut/keuze/.gitignore b/mut/keuze/.gitignore
new file mode 100644
index 0000000..09dfede
--- /dev/null
+++ b/mut/keuze/.gitignore
@@ -0,0 +1,63 @@
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xccheckout
+*.xcscmblueprint
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+*.dSYM.zip
+*.dSYM
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# Pods/
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://docs.fastlane.tools/best-practices/source-control/#source-control
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+
+# Code Injection
+#
+# After new code Injection tools there's a generated folder /iOSInjectionProject
+# https://github.com/johnno1962/injectionforxcode
+
+iOSInjectionProject/
diff --git a/mut/keuze/README.md b/mut/keuze/README.md
new file mode 100644
index 0000000..05fda20
--- /dev/null
+++ b/mut/keuze/README.md
@@ -0,0 +1,44 @@
+# Keuze
+Universal fuzzy selector for macOs comparable with dmenu.
+
+![example screenshot](screenshots/screenshot1.png)
+
+- Gets list of items from stdin.
+- Sorts choices as you type.
+- Sends result to stdout.
+
+## Install
+
+1. Download the latest release (or build it)
+2. Place it in your path
+
+## Basic Usage
+
+```bash
+$ alias keuze="keuze -fs 12 -fn Monaco"
+$ ls | keuze -p "list"
+```
+
+## License
+
+> Released under MIT license.
+>
+> Copyright (c) 2019 Thomas Billiet
+>
+> 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.
diff --git a/mut/keuze/keuze.xcodeproj/project.pbxproj b/mut/keuze/keuze.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..5292f7b
--- /dev/null
+++ b/mut/keuze/keuze.xcodeproj/project.pbxproj
@@ -0,0 +1,353 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 48;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 90281C6D225F763100236C8B /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90281C6C225F763100236C8B /* DataSource.swift */; };
+ 90281C7D2262925500236C8B /* SwiftLCS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90281C7C2262925500236C8B /* SwiftLCS.swift */; };
+ 90281C7F2263539A00236C8B /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90281C7E2263539A00236C8B /* Settings.swift */; };
+ 90281C8122635C7100236C8B /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90281C8022635C7100236C8B /* Layout.swift */; };
+ 90537621225B89E2003B4D25 /* constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90537620225B89E2003B4D25 /* constants.swift */; };
+ 90537627225B8EDA003B4D25 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90537626225B8EDA003B4D25 /* main.swift */; };
+ 90537629225B95DB003B4D25 /* PromptText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90537628225B95DB003B4D25 /* PromptText.swift */; };
+ 9053762B225BA282003B4D25 /* InputField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9053762A225BA282003B4D25 /* InputField.swift */; };
+ 9053762D225BEA61003B4D25 /* TableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9053762C225BEA61003B4D25 /* TableView.swift */; };
+ 90EB57CA225B83BC002E81D7 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90EB57C9225B83BC002E81D7 /* AppKit.framework */; };
+ 90EB57CC225B83C2002E81D7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90EB57CB225B83C2002E81D7 /* Foundation.framework */; };
+ 90EB57CE225B83CB002E81D7 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90EB57CD225B83CB002E81D7 /* Cocoa.framework */; };
+ 90EB57D0225B8742002E81D7 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EB57CF225B8742002E81D7 /* Window.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 90281C6C225F763100236C8B /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = "<group>"; };
+ 90281C7C2262925500236C8B /* SwiftLCS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLCS.swift; sourceTree = "<group>"; };
+ 90281C7E2263539A00236C8B /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
+ 90281C8022635C7100236C8B /* Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = "<group>"; };
+ 90537620225B89E2003B4D25 /* constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = constants.swift; sourceTree = "<group>"; };
+ 90537626225B8EDA003B4D25 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
+ 90537628225B95DB003B4D25 /* PromptText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromptText.swift; sourceTree = "<group>"; };
+ 9053762A225BA282003B4D25 /* InputField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputField.swift; sourceTree = "<group>"; };
+ 9053762C225BEA61003B4D25 /* TableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableView.swift; sourceTree = "<group>"; };
+ 90EB57C9225B83BC002E81D7 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ 90EB57CB225B83C2002E81D7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 90EB57CD225B83CB002E81D7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 90EB57CF225B8742002E81D7 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
+ EF3CE234203361F2000CAD87 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ EF3CE235203361F2000CAD87 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ EF860B222034C4D50087C40D /* head.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = head.h; sourceTree = "<group>"; };
+ EFEAA7E6203393C200FB0263 /* keuze */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = keuze; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ EFEAA7E3203393C200FB0263 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 90EB57CE225B83CB002E81D7 /* Cocoa.framework in Frameworks */,
+ 90EB57CC225B83C2002E81D7 /* Foundation.framework in Frameworks */,
+ 90EB57CA225B83BC002E81D7 /* AppKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 90EB57C8225B83BB002E81D7 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 90EB57CD225B83CB002E81D7 /* Cocoa.framework */,
+ 90EB57CB225B83C2002E81D7 /* Foundation.framework */,
+ 90EB57C9225B83BC002E81D7 /* AppKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ EF3CE220203361F2000CAD87 = {
+ isa = PBXGroup;
+ children = (
+ EF3CE22B203361F2000CAD87 /* kies */,
+ EF3CE22A203361F2000CAD87 /* Products */,
+ 90EB57C8225B83BB002E81D7 /* Frameworks */,
+ );
+ sourceTree = "<group>";
+ };
+ EF3CE22A203361F2000CAD87 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ EFEAA7E6203393C200FB0263 /* keuze */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ EF3CE22B203361F2000CAD87 /* kies */ = {
+ isa = PBXGroup;
+ children = (
+ EF3CE234203361F2000CAD87 /* Info.plist */,
+ EF3CE235203361F2000CAD87 /* main.m */,
+ EF860B222034C4D50087C40D /* head.h */,
+ 90537626225B8EDA003B4D25 /* main.swift */,
+ 90EB57CF225B8742002E81D7 /* Window.swift */,
+ 90537620225B89E2003B4D25 /* constants.swift */,
+ 90537628225B95DB003B4D25 /* PromptText.swift */,
+ 9053762A225BA282003B4D25 /* InputField.swift */,
+ 9053762C225BEA61003B4D25 /* TableView.swift */,
+ 90281C6C225F763100236C8B /* DataSource.swift */,
+ 90281C7C2262925500236C8B /* SwiftLCS.swift */,
+ 90281C7E2263539A00236C8B /* Settings.swift */,
+ 90281C8022635C7100236C8B /* Layout.swift */,
+ );
+ path = kies;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ EFEAA7E5203393C200FB0263 /* keuze */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = EFEAA7EC203393C200FB0263 /* Build configuration list for PBXNativeTarget "keuze" */;
+ buildPhases = (
+ EFEAA7E2203393C200FB0263 /* Sources */,
+ EFEAA7E3203393C200FB0263 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = keuze;
+ productName = kies;
+ productReference = EFEAA7E6203393C200FB0263 /* keuze */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ EF3CE221203361F2000CAD87 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "Thomas Billiet";
+ TargetAttributes = {
+ EFEAA7E5203393C200FB0263 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1020;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = EF3CE224203361F2000CAD87 /* Build configuration list for PBXProject "keuze" */;
+ compatibilityVersion = "Xcode 8.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = EF3CE220203361F2000CAD87;
+ productRefGroup = EF3CE22A203361F2000CAD87 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ EFEAA7E5203393C200FB0263 /* keuze */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ EFEAA7E2203393C200FB0263 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 90EB57D0225B8742002E81D7 /* Window.swift in Sources */,
+ 9053762D225BEA61003B4D25 /* TableView.swift in Sources */,
+ 90281C6D225F763100236C8B /* DataSource.swift in Sources */,
+ 90281C7D2262925500236C8B /* SwiftLCS.swift in Sources */,
+ 90537621225B89E2003B4D25 /* constants.swift in Sources */,
+ 90537629225B95DB003B4D25 /* PromptText.swift in Sources */,
+ 90281C7F2263539A00236C8B /* Settings.swift in Sources */,
+ 90281C8122635C7100236C8B /* Layout.swift in Sources */,
+ 9053762B225BA282003B4D25 /* InputField.swift in Sources */,
+ 90537627225B8EDA003B4D25 /* main.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ EF3CE238203361F2000CAD87 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ 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;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ EF3CE239203361F2000CAD87 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ 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;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ };
+ name = Release;
+ };
+ EFEAA7EA203393C200FB0263 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ INFOPLIST_FILE = "$(SRCROOT)/kies/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks @loader_path/../Frameworks";
+ LIBRARY_SEARCH_PATHS = "";
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ OTHER_LDFLAGS = (
+ "-sectcreate",
+ __TEXT,
+ __info_plist,
+ "$(SRCROOT)/kies/Info.plist",
+ "-rpath",
+ $DT_TOOLCHAIN_DIR/usr/lib/swift/macosx/,
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VALID_ARCHS = x86_64;
+ };
+ name = Debug;
+ };
+ EFEAA7EB203393C200FB0263 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ INFOPLIST_FILE = "$(SRCROOT)/kies/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks @loader_path/../Frameworks";
+ LIBRARY_SEARCH_PATHS = "";
+ MACOSX_DEPLOYMENT_TARGET = 10.13;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ VALID_ARCHS = x86_64;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ EF3CE224203361F2000CAD87 /* Build configuration list for PBXProject "keuze" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ EF3CE238203361F2000CAD87 /* Debug */,
+ EF3CE239203361F2000CAD87 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ EFEAA7EC203393C200FB0263 /* Build configuration list for PBXNativeTarget "keuze" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ EFEAA7EA203393C200FB0263 /* Debug */,
+ EFEAA7EB203393C200FB0263 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = EF3CE221203361F2000CAD87 /* Project object */;
+}
diff --git a/mut/keuze/keuze.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mut/keuze/keuze.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..ccb0550
--- /dev/null
+++ b/mut/keuze/keuze.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:/Users/thomasbilliet/Projects/kies/keuze.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/mut/keuze/keuze.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mut/keuze/keuze.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/mut/keuze/keuze.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/mut/keuze/keuze.xcodeproj/xcshareddata/xcschemes/kies.xcscheme b/mut/keuze/keuze.xcodeproj/xcshareddata/xcschemes/kies.xcscheme
new file mode 100644
index 0000000..8c95d67
--- /dev/null
+++ b/mut/keuze/keuze.xcodeproj/xcshareddata/xcschemes/kies.xcscheme
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1020"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "EFEAA7E5203393C200FB0263"
+ BuildableName = "keuze"
+ BlueprintName = "keuze"
+ ReferencedContainer = "container:keuze.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "EFEAA7E5203393C200FB0263"
+ BuildableName = "keuze"
+ BlueprintName = "keuze"
+ ReferencedContainer = "container:keuze.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "EFEAA7E5203393C200FB0263"
+ BuildableName = "keuze"
+ BlueprintName = "keuze"
+ ReferencedContainer = "container:keuze.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <CommandLineArguments>
+ <CommandLineArgument
+ argument = ""
+ isEnabled = "NO">
+ </CommandLineArgument>
+ </CommandLineArguments>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "EFEAA7E5203393C200FB0263"
+ BuildableName = "keuze"
+ BlueprintName = "keuze"
+ ReferencedContainer = "container:keuze.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/mut/keuze/kies/DataSource.swift b/mut/keuze/kies/DataSource.swift
new file mode 100644
index 0000000..23c1f8e
--- /dev/null
+++ b/mut/keuze/kies/DataSource.swift
@@ -0,0 +1,43 @@
+//
+// Sorter.swift
+// kies
+//
+// Created by Thomas Billiet on 11/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+class DataSource: NSObject, NSTableViewDataSource {
+ var items = [String]()
+ var sortedItems = [String]()
+ var matches = [String: String]()
+ var scores = [String: Float]()
+
+ func updateItems(_ items: [String]) {
+ self.items = items
+ self.sortedItems = items
+ }
+
+ func updateSort(query: String) {
+ for item in items {
+ let match = item.lowercased().longestCommonSubsequence(query.lowercased())
+ matches[item] = match
+ scores[item] = Float(match.count) / Float(query.count)
+ }
+
+ sortedItems = items.sorted(by: { (a, b) -> Bool in
+ scores[a]! > scores[b]!
+ })
+ }
+
+ func numberOfRows(in tableView: NSTableView) -> Int {
+ return sortedItems.count
+ }
+
+ func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
+ return sortedItems[row]
+ }
+
+}
diff --git a/mut/keuze/kies/Info.plist b/mut/keuze/kies/Info.plist
new file mode 100644
index 0000000..bfedb9a
--- /dev/null
+++ b/mut/keuze/kies/Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.productivity</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2019 Thomas Billiet. All rights reserved.</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/mut/keuze/kies/InputField.swift b/mut/keuze/kies/InputField.swift
new file mode 100644
index 0000000..6e8e27c
--- /dev/null
+++ b/mut/keuze/kies/InputField.swift
@@ -0,0 +1,67 @@
+//
+// KiesInputField.swift
+// kies
+//
+// Created by Thomas Billiet on 08/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+class InputField: NSTextField, NSTextFieldDelegate {
+ var appDelegate: AppDelegate!
+
+ init(appDelegate: AppDelegate) {
+ super.init(frame: layouts.inputRect)
+ self.appDelegate = appDelegate
+ stringValue = ""
+ isEditable = true
+ isSelectable = true
+ delegate = self
+ bezelStyle = .squareBezel
+ isBordered = false
+ drawsBackground = false
+ focusRingType = .none
+ target = self
+ font = settings.font
+ allowsEditingTextAttributes = false
+ isAutomaticTextCompletionEnabled = false
+ allowsDefaultTighteningForTruncation = false
+ maximumNumberOfLines = 1
+ cell?.wraps = false
+ cell?.isScrollable = true
+ autoresizingMask = [NSView.AutoresizingMask.width]
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
+ if (commandSelector == #selector(NSStandardKeyBindingResponding.cancelOperation(_:))){
+ appDelegate.cancel()
+ return true
+ }
+ if (commandSelector == #selector(NSStandardKeyBindingResponding.insertNewline(_:))){
+ appDelegate.handleSelect()
+ return true
+ }
+ if (commandSelector == #selector(NSStandardKeyBindingResponding.moveUp(_:))){
+ appDelegate.handleMoveUp()
+ return true
+ }
+ if (commandSelector == #selector(NSStandardKeyBindingResponding.moveDown(_:))){
+ appDelegate.handleMoveDown()
+ return true
+ }
+ if (commandSelector == #selector(NSStandardKeyBindingResponding.insertTab(_:))){
+ return true
+ }
+ return false
+ }
+
+ func controlTextDidChange(_ obj: Notification) {
+ appDelegate.handleInputChange(input: stringValue)
+ }
+}
diff --git a/mut/keuze/kies/Layout.swift b/mut/keuze/kies/Layout.swift
new file mode 100644
index 0000000..cf04eb0
--- /dev/null
+++ b/mut/keuze/kies/Layout.swift
@@ -0,0 +1,44 @@
+//
+// Layout.swift
+// keuze
+//
+// Created by Thomas Billiet on 14/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+
+class Layout {
+ let DIVIDER_HEIGHT: CGFloat = 9
+
+ var lineHeight: CGFloat {
+ return ceil(NSString(string: "TEST").size(withAttributes: [.font: settings.font]).height) + 0;
+ }
+
+ var windowRect: NSRect {
+ return NSMakeRect(0, 0, settings.windowWidth, settings.windowHeight)
+ }
+
+ var containerRect: NSRect {
+ return NSMakeRect(settings.windowPadding, settings.windowPadding, windowRect.width - settings.windowPadding * 2, windowRect.height - settings.windowPadding * 2)
+ }
+
+ var listRect: NSRect {
+ return NSMakeRect(containerRect.minX, containerRect.minY, containerRect.width, containerRect.height - lineHeight - DIVIDER_HEIGHT)
+ }
+
+ var dividerRect: NSRect {
+ return NSMakeRect(containerRect.minX, listRect.maxY + DIVIDER_HEIGHT / 2, containerRect.width, 1)
+ }
+
+ var promptRect: NSRect {
+ let stringWidth = ceil(NSString(string: settings.promptText!).size(withAttributes: [.font: settings.font]).width) + 4;
+ return NSMakeRect(containerRect.minX, containerRect.maxY - lineHeight, stringWidth, lineHeight)
+ }
+
+ var inputRect: NSRect {
+ return NSMakeRect(promptRect.maxX, containerRect.maxY - lineHeight, containerRect.width - promptRect.width, lineHeight)
+ }
+}
+
+let layouts = Layout()
diff --git a/mut/keuze/kies/PromptText.swift b/mut/keuze/kies/PromptText.swift
new file mode 100644
index 0000000..7cd6750
--- /dev/null
+++ b/mut/keuze/kies/PromptText.swift
@@ -0,0 +1,28 @@
+//
+// KiesPromptText.swift
+// kies
+//
+// Created by Thomas Billiet on 08/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+class PromptText: NSTextField {
+ init(text: String) {
+ super.init(frame: layouts.promptRect)
+ stringValue = text
+ isEditable = false
+ drawsBackground = false
+ isSelectable = false
+ bezelStyle = .squareBezel
+ font = settings.font
+ isBordered = false
+ autoresizingMask = [NSView.AutoresizingMask.width]
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+}
diff --git a/mut/keuze/kies/Settings.swift b/mut/keuze/kies/Settings.swift
new file mode 100644
index 0000000..ab2c3df
--- /dev/null
+++ b/mut/keuze/kies/Settings.swift
@@ -0,0 +1,48 @@
+//
+// Settings.swift
+// keuze
+//
+// Created by Thomas Billiet on 14/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+class Settings {
+
+ var promptText: String? {
+ var prompt = UserDefaults.standard.string(forKey: "p")
+ if (prompt != nil) {
+ prompt = prompt! + ":"
+ }
+ return prompt
+ }
+
+ var font: NSFont? {
+ let fn = UserDefaults.standard.string(forKey: "fn")
+ let fs = UserDefaults.standard.integer(forKey: "fs")
+ let fontSize = fs == 0 ? 14 : CGFloat(fs)
+ if (fn == nil) {
+ return NSFont.systemFont(ofSize: fontSize)
+ }
+ return NSFont(name: fn!, size: fontSize)
+ }
+
+ var windowPadding: CGFloat {
+ let val = UserDefaults.standard.integer(forKey: "pd")
+ return val == 0 ? 12 : CGFloat(val)
+ }
+
+ var windowHeight: CGFloat {
+ let val = UserDefaults.standard.integer(forKey: "h")
+ return val == 0 ? 310 : CGFloat(val)
+ }
+
+ var windowWidth: CGFloat {
+ let val = UserDefaults.standard.integer(forKey: "w")
+ return val == 0 ? 500 : CGFloat(val)
+ }
+}
+
+let settings = Settings()
diff --git a/mut/keuze/kies/SwiftLCS.swift b/mut/keuze/kies/SwiftLCS.swift
new file mode 100644
index 0000000..2587c93
--- /dev/null
+++ b/mut/keuze/kies/SwiftLCS.swift
@@ -0,0 +1,268 @@
+//
+// The MIT License (MIT)
+//
+// Copyright (c) 2015 Tommaso Madonia
+//
+// 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.
+//
+import Foundation
+
+/**
+ A generic struct that represents a diff between two collections.
+ */
+public struct Diff<Index> {
+
+ /// The indexes whose corresponding values in the old collection are in the LCS.
+ public var commonIndexes: [Index] {
+ return self._commonIndexSet.indexes
+ }
+
+ /// The indexes whose corresponding values in the new collection are not in the LCS.
+ public var addedIndexes: [Index] {
+ return self._addedIndexSet.indexes
+ }
+
+ /// The indexes whose corresponding values in the old collection are not in the LCS.
+ public var removedIndexes: [Index] {
+ return self._removedIndexSet.indexes
+ }
+
+ /// Construct the `Diff` between two given collections.
+ public init<C: Collection>(_ old: C, _ new: C) where C.Index == Index, C.Element: Equatable {
+ self = old.diff(new)
+ }
+
+ fileprivate let _commonIndexSet: DiffIndexSet<Index>
+ fileprivate let _addedIndexSet: DiffIndexSet<Index>
+ fileprivate let _removedIndexSet: DiffIndexSet<Index>
+
+ fileprivate init(commonIndexes: DiffIndexSet<Index>, addedIndexes: DiffIndexSet<Index>, removedIndexes: DiffIndexSet<Index>) {
+ self._commonIndexSet = commonIndexes
+ self._addedIndexSet = addedIndexes
+ self._removedIndexSet = removedIndexes
+ }
+
+}
+
+/**
+ An extension of `Diff`, which adds support for `IndexSet`.
+ */
+public extension Diff where Index: Strideable, Index.Stride: SignedInteger {
+
+ /// The indexes whose corresponding values in the old collection are in the LCS.
+ var commonIndexSet: IndexSet {
+ return self._commonIndexSet.indexSet
+ }
+
+ /// The indexes whose corresponding values in the new collection are not in the LCS.
+ var addedIndexSet: IndexSet {
+ return self._addedIndexSet.indexSet
+ }
+
+ /// The indexes whose corresponding values in the old collection are not in the LCS.
+ var removedIndexSet: IndexSet {
+ return self._removedIndexSet.indexSet
+ }
+
+}
+
+private struct DiffIndexSet<Index> {
+
+ let startIndex: Index
+ let indexes: [Index]
+
+ init(_ indexes: [Index], startIndex: Index) {
+ self.indexes = indexes
+ self.startIndex = startIndex
+ }
+
+}
+
+private extension DiffIndexSet where Index: Strideable, Index.Stride: SignedInteger {
+
+ var indexSet: IndexSet {
+ let indexes = self.indexes.map { Int((self.startIndex..<$0).count) }
+
+ return IndexSet(indexes)
+ }
+
+}
+
+// MARK: -
+/**
+ An extension of `Collection`, which calculates the diff between two collections.
+ */
+public extension Collection where Element: Equatable {
+
+ /**
+ Returns the diff between two collections.
+
+ - complexity: O(mn) where `m` and `n` are the lengths of the receiver and the given collection.
+ - parameter collection: The collection with which to compare the receiver.
+ - returns: The diff between the receiver and the given collection.
+ */
+ func diff(_ otherCollection: Self) -> Diff<Index> {
+ let count = self.count
+ let commonIndexes = self.longestCommonSubsequence(otherCollection, selfCount: count)
+
+ var removedIndexes: [Index] = []
+ removedIndexes.reserveCapacity(count - commonIndexes.count)
+
+ var addedIndexes: [Index] = []
+
+ var index = self.startIndex
+ var otherIndex = otherCollection.startIndex
+ var commonIndexesIterator = commonIndexes.makeIterator()
+ var commonIndex = commonIndexesIterator.next()
+ while index != self.endIndex {
+ if commonIndex == index {
+ commonIndex = commonIndexesIterator.next()
+
+ while otherIndex != otherCollection.endIndex && otherCollection[otherIndex] != self[index] {
+ addedIndexes.append(otherIndex)
+ otherIndex = otherCollection.index(after: otherIndex)
+ }
+
+ if otherIndex != otherCollection.endIndex {
+ otherIndex = otherCollection.index(after: otherIndex)
+ }
+ } else {
+ removedIndexes.append(index)
+ }
+
+ index = self.index(after: index)
+ }
+
+ while otherIndex != otherCollection.endIndex {
+ addedIndexes.append(otherIndex)
+ otherIndex = otherCollection.index(after: otherIndex)
+ }
+
+ return Diff(commonIndexes: DiffIndexSet(commonIndexes, startIndex: self.startIndex),
+ addedIndexes: DiffIndexSet(addedIndexes, startIndex: otherCollection.startIndex),
+ removedIndexes: DiffIndexSet(removedIndexes, startIndex: self.startIndex))
+ }
+
+ // MARK: Private functions
+
+ private func prefix(_ otherCollection: Self, count: Int, suffix: Int) -> (Int, [Index]) {
+ var iterator = self.makeIterator()
+ var otherIterator = otherCollection.makeIterator()
+
+ var prefix = self.startIndex
+ let endIndex = self.index(self.startIndex, offsetBy: count - suffix)
+ while let lhs = iterator.next(), let rhs = otherIterator.next(), lhs == rhs, prefix < endIndex {
+ prefix = self.index(after: prefix)
+ }
+
+ let prefixLength = self.distance(from: self.startIndex, to: prefix)
+ return (prefixLength, Array(self.indices.prefix(prefixLength)))
+ }
+
+ private func suffix(_ otherCollection: Self, count: Int) -> (Int, [Index]) {
+ var iterator = self.reversed().makeIterator()
+ var otherIterator = otherCollection.reversed().makeIterator()
+
+ var offset = count
+ var suffix = self.index(self.startIndex, offsetBy: offset)
+ while let lhs = iterator.next(), let rhs = otherIterator.next(), lhs == rhs {
+ offset &-= 1
+ suffix = self.index(self.startIndex, offsetBy: offset)
+ }
+
+ let suffixLength = self.distance(from: suffix, to: self.endIndex)
+ return (suffixLength, Array(self.indices.suffix(suffixLength)))
+ }
+
+ private func computeLCS(_ otherCollection: Self, prefixLength: Int, suffixLength: Int, count: Int) -> [Index] {
+ let rows = Int(count - prefixLength - suffixLength) + 1
+ let columns = Int(otherCollection.count - prefixLength - suffixLength) + 1
+
+ guard rows > 1 && columns > 1 else {
+ return []
+ }
+
+ var lengths = Array(repeating: 0, count: rows * columns)
+ var index = self.index(self.startIndex, offsetBy: prefixLength)
+ for i in 0..<rows &- 1 {
+ var otherIndex = otherCollection.index(otherCollection.startIndex, offsetBy: prefixLength)
+ for j in 0..<columns &- 1 {
+ if self[index] == otherCollection[otherIndex] {
+ lengths[(i &+ 1) &* columns &+ j &+ 1] = lengths[i &* columns &+ j] &+ 1
+ } else {
+ let lhs = lengths[(i &+ 1) &* columns &+ j]
+ let rhs = lengths[i &* columns &+ j &+ 1]
+ lengths[(i &+ 1) &* columns &+ j &+ 1] = Swift.max(lhs, rhs)
+ }
+
+ otherIndex = otherCollection.index(after: otherIndex)
+ }
+
+ index = self.index(after: index)
+ }
+
+ var commonIndexes: [Index] = []
+
+ var indexOffset = count - suffixLength
+ index = self.index(self.startIndex, offsetBy: indexOffset)
+ var (i, j) = (rows &- 1, columns &- 1)
+ while i != 0 && j != 0 {
+ if lengths[i &* columns &+ j] == lengths[(i &- 1) &* columns &+ j] {
+ i = i &- 1
+ indexOffset &-= 1
+ index = self.index(self.startIndex, offsetBy: indexOffset)
+ } else if lengths[i &* columns &+ j] == lengths[i &* columns &+ j &- 1] {
+ j = j &- 1
+ } else {
+ indexOffset &-= 1
+ index = self.index(self.startIndex, offsetBy: indexOffset)
+ commonIndexes.append(index)
+ (i, j) = (i &- 1, j &- 1)
+ }
+ }
+
+ return commonIndexes.reversed()
+ }
+
+ fileprivate func longestCommonSubsequence(_ otherCollection: Self, selfCount count: Int) -> [Index] {
+ let (suffix, suffixIndexes) = self.suffix(otherCollection, count: count)
+ let (prefix, prefixIndexes) = self.prefix(otherCollection, count: count, suffix: suffix)
+
+ return prefixIndexes + self.computeLCS(otherCollection, prefixLength: prefix, suffixLength: suffix, count: count) + suffixIndexes
+ }
+
+}
+
+// MARK: -
+/**
+ An extension of `RangeReplaceableCollection`, which calculates the longest common subsequence between two collections.
+ */
+public extension RangeReplaceableCollection where Element: Equatable {
+
+ /**
+ Returns the longest common subsequence between two collections.
+
+ - parameter collection: The collection with which to compare the receiver.
+ - returns: The longest common subsequence between the receiver and the given collection.
+ */
+ func longestCommonSubsequence(_ collection: Self) -> Self {
+ return Self(self.longestCommonSubsequence(collection, selfCount: self.count).map { self[$0] })
+ }
+
+}
diff --git a/mut/keuze/kies/TableView.swift b/mut/keuze/kies/TableView.swift
new file mode 100644
index 0000000..cd7de9f
--- /dev/null
+++ b/mut/keuze/kies/TableView.swift
@@ -0,0 +1,89 @@
+//
+// KiesTableView.swift
+// kies
+//
+// Created by Thomas Billiet on 08/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+class TableView: NSTableView, NSTableViewDelegate {
+
+ let column = NSTableColumn(identifier: LIST_COL_ID)
+
+ init(dataSource: DataSource) {
+ super.init(frame: .zero)
+ self.dataSource = dataSource
+ headerView = nil
+ allowsEmptySelection = false
+ allowsMultipleSelection = false
+ allowsTypeSelect = false
+ selectionHighlightStyle = .none
+ delegate = self
+ backgroundColor = .clear
+
+ column.isEditable = false
+ column.width = layouts.listRect.width
+ addTableColumn(column)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func reloadData() {
+ super.reloadData()
+ scrollToBeginningOfDocument(self)
+ selectRow(0)
+ }
+
+ func selectRow(_ row: Int) {
+ let indexSet = IndexSet(integer: row)
+ selectRowIndexes(indexSet, byExtendingSelection: false)
+ }
+
+ func selectRowAbove() {
+ let row = selectedRow - 1
+ if (row >= 0) {
+ selectRow(row)
+ scrollRowToVisible(row)
+ }
+ }
+
+ func selectRowBelow() {
+ let row = selectedRow + 1
+ if (row < numberOfRows) {
+ selectRow(row)
+ scrollRowToVisible(row)
+ }
+ }
+
+ func tableView(_ tableView: NSTableView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, row: Int) {
+ let c = cell as! NSTextFieldCell
+ let value = c.stringValue
+ c.font = settings.font
+ c.usesSingleLineMode = true
+ if (row == selectedRow) {
+ c.backgroundColor = .alternateSelectedControlColor
+ c.textColor = .alternateSelectedControlTextColor
+ c.drawsBackground = true
+ } else {
+ c.drawsBackground = false
+ c.textColor = .textColor
+ }
+ let match = (dataSource as! DataSource).matches[value]
+ if (match != nil && !match!.isEmpty) {
+ let range = (value.lowercased() as NSString).range(of: match!)
+ let attribute = NSMutableAttributedString.init(string: value)
+ attribute.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue , range: range)
+ c.attributedStringValue = attribute
+ }
+
+ }
+
+ func tableViewSelectionDidChange(_ notification: Notification) {
+ }
+
+}
diff --git a/mut/keuze/kies/Window.swift b/mut/keuze/kies/Window.swift
new file mode 100644
index 0000000..078bdad
--- /dev/null
+++ b/mut/keuze/kies/Window.swift
@@ -0,0 +1,51 @@
+//
+// NSWindowExtension.swift
+// kies
+//
+// Created by Thomas Billiet on 08/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+class Window: NSWindow {
+ override var canBecomeKey: Bool {
+ return true
+ }
+
+ override var canBecomeMain: Bool {
+ return true
+ }
+
+ init() {
+ super.init(contentRect: layouts.windowRect, styleMask: .borderless, backing: .buffered, defer: false)
+ isOpaque = true
+ canHide = false
+ isMovable = false
+ title = WINDOW_TITLE
+ level = NSWindow.Level.floating
+ hasShadow = true
+ backgroundColor = .clear
+
+ let visualEffect = NSVisualEffectView()
+ visualEffect.blendingMode = .withinWindow
+ visualEffect.state = .active
+ visualEffect.material = .appearanceBased
+ visualEffect.frame = layouts.windowRect
+ contentView = visualEffect
+ }
+
+ public func showWindow() {
+ positionWindowAtCenter()
+ makeMain()
+ makeKeyAndOrderFront(self)
+ }
+
+ public func positionWindowAtCenter(){
+ let xPos = NSWidth((self.screen?.frame)!)/2 - NSWidth(self.frame)/2
+ let yPos = NSHeight((self.screen?.frame)!)/2 - NSHeight(self.frame)/2
+ let frame = NSMakeRect(xPos, yPos, NSWidth(self.frame), NSHeight(self.frame))
+ self.setFrame(frame, display: true)
+ }
+}
diff --git a/mut/keuze/kies/constants.swift b/mut/keuze/kies/constants.swift
new file mode 100644
index 0000000..a142eac
--- /dev/null
+++ b/mut/keuze/kies/constants.swift
@@ -0,0 +1,13 @@
+//
+// Constants.swift
+// kies
+//
+// Created by Thomas Billiet on 08/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+let WINDOW_TITLE = "Kies"
+let LIST_COL_ID = NSUserInterfaceItemIdentifier(rawValue: "column");
diff --git a/mut/keuze/kies/main.swift b/mut/keuze/kies/main.swift
new file mode 100644
index 0000000..a20922d
--- /dev/null
+++ b/mut/keuze/kies/main.swift
@@ -0,0 +1,132 @@
+//
+// main.swift
+// kies
+//
+// Created by Thomas Billiet on 08/04/2019.
+// Copyright © 2019 Thomas Billiet. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+import Darwin
+
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+ var window: Window!
+ var dataSource: DataSource!
+ var promptText: PromptText!
+ var divider: NSBox!
+ var inputField: InputField!
+ var tableView: TableView!
+ var scrollView: NSScrollView!
+
+ func applicationDidBecomeActive(_ notification: Notification) {
+
+ }
+
+ func applicationDidResignActive(_ notification: Notification) {
+ cancel();
+ }
+
+ func receiveStdin() -> [String] {
+ var lines: [String] = []
+ while let line = readLine() {
+ lines.append(line)
+ }
+ return lines
+ }
+
+ func applicationDidFinishLaunching(_ notification: Notification)
+ {
+ if (settings.promptText == nil) {
+ fputs("Please provide a prompt text\n", stderr)
+ exit(1)
+ }
+ if (settings.font == nil) {
+ fputs("The provided font could not be loaded\n", stderr)
+ exit(1)
+ }
+ let items = receiveStdin()
+ if (items.count == 0) {
+ fputs("No data was received on stdin\n", stderr)
+ exit(1)
+ }
+
+ window = Window()
+ dataSource = DataSource()
+ dataSource.updateItems(items)
+
+ setupPromptText()
+ setupDivider()
+ setupInputField()
+ setupList()
+
+ window.showWindow()
+ app.activate(ignoringOtherApps: true)
+ }
+
+ func setupPromptText() {
+ promptText = PromptText(text: settings.promptText!)
+ window.contentView!.addSubview(promptText)
+ }
+
+ func setupDivider() {
+ divider = NSBox(frame: layouts.dividerRect)
+ divider.boxType = .custom
+ divider.fillColor = .lightGray
+ divider.borderWidth = 0
+ window.contentView!.addSubview(divider)
+ }
+
+ func setupInputField() {
+ inputField = InputField(appDelegate: self);
+ window.contentView!.addSubview(inputField)
+ window.makeFirstResponder(inputField)
+ }
+
+ func handleInputChange(input: String) {
+ dataSource.updateSort(query: input)
+ tableView.reloadData()
+ }
+
+ func handleMoveUp() {
+ tableView.selectRowAbove()
+ }
+
+ func handleMoveDown() {
+ tableView.selectRowBelow()
+ }
+
+ func handleSelect() {
+ let value = dataSource.sortedItems[tableView.selectedRow]
+ fputs(value, stdout)
+ cancel()
+ }
+
+ func setupList() {
+ scrollView = NSScrollView(frame: layouts.listRect)
+ tableView = TableView(dataSource: dataSource)
+
+ scrollView.horizontalScrollElasticity = .none
+ scrollView.verticalScrollElasticity = .none
+ scrollView.hasVerticalScroller = true
+ scrollView.hasHorizontalScroller = false
+ scrollView.drawsBackground = false
+ scrollView.autoresizingMask = [NSView.AutoresizingMask.width, NSView.AutoresizingMask.height]
+ scrollView.documentView = tableView
+ scrollView.automaticallyAdjustsContentInsets = false
+ window.contentView!.addSubview(scrollView)
+ }
+
+ func cancel() {
+ exit(0)
+ }
+}
+
+
+let app = NSApplication.shared
+app.setActivationPolicy(.accessory)
+let delegate = AppDelegate()
+app.delegate = delegate
+
+_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
diff --git a/mut/keuze/screenshots/screenshot1.png b/mut/keuze/screenshots/screenshot1.png
new file mode 100644
index 0000000..6b94279
--- /dev/null
+++ b/mut/keuze/screenshots/screenshot1.png
Binary files differ