diff --git a/NEWREADME.md b/NEWREADME.md
index 8744cd1..b2cc97e 100644
--- a/NEWREADME.md
+++ b/NEWREADME.md
@@ -134,7 +134,6 @@ You can see in which language an app is written. Currently there are following l
### Development
#### Git
-
- [Cashew](https://github.com/simplerocket-llc/OpenCashew) - Cashew macOS Github Issue Tracker. ![c_icon] ![objective_c_icon]
- [GPM](https://github.com/mtgto/GPM) - macOS application for easily operating GitHub Projects. ![swift_icon]
- [GitSync](https://github.com/eonist/GitSync) - Minimalistic Git client for Mac. ![swift_icon] Screenshots
@@ -149,16 +148,13 @@ You can see in which language an app is written. Currently there are following l
- [Xit](https://github.com/Uncommon/Xit) - Xit is a graphical tool for working with git repositories. ![swift_icon] Screenshots
#### JSON Parsing
-
- [JSONExport](https://github.com/Ahmed-Ali/JSONExport) - Desktop application for macOS which enables you to export JSON objects as model classes with their associated constructors, utility methods, setters and getters in your favorite language. ![swift_icon] Screenshots
- [j2s](https://github.com/zadr/j2s) - macOS app to convert JSON objects into Swift structs (currently targets Swift 4 and Codable). ![swift_icon]
#### Other
-
- [vegvisir](https://github.com/ant4g0nist/vegvisir) - Browser based GUI for **LLDB** Debugger. ![javascript_icon] Screenshots
#### Web Development
-
- [Insomnia](https://github.com/getinsomnia/insomnia) - Insomnia is a cross-platform REST client, built on top of Electron. ![javascript_icon] Screenshots
- [KubeMonitor](https://github.com/DanSanche/KubeMonitor) - KubeMonitor is a macOS app that displays information about your active Kubernetes cluster in your menu bar. ![swift_icon] Screenshots
- [Lantern](https://github.com/BurntCaramel/Lantern) - Dedicated Mac app for website auditing and crawling. ![swift_icon]
@@ -169,7 +165,6 @@ You can see in which language an app is written. Currently there are following l
- [stts](https://github.com/inket/stts) - macOS app for monitoring the status of cloud services. ![swift_icon] Screenshots
#### iOS / macOS
-
- [Alloy](https://github.com/alexlee002/alloy) - Simple toolkit that makes your ios-macos-development apps development more easier. ![objective_c_icon]
- [AppBox](https://github.com/vineetchoudhary/AppBox-iOSAppsWirelessInstallation) - Tool for iOS developers to build and deploy Development, Ad-Hoc and In-house (Enterprise) applications directly to the devices from your Dropbox account. ![objective_c_icon]
- [AppIcons](https://github.com/kuyawa/AppIcons) - Tool for generating icons in all sizes as required by macOS and iOS apps. ![swift_icon] Screenshots
@@ -204,26 +199,21 @@ You can see in which language an app is written. Currently there are following l
### Editors
#### CSV
-
- [TableTool](https://github.com/jakob/TableTool) - simple CSV editor for the macOS. ![objective_c_icon]
#### JSON
-
- [JSON-Splora](https://github.com/wellsjo/JSON-Splora) - GUI for editing, visualizing, and manipulating JSON data. ![javascript_icon]
#### Markdown
-
- [Gingko](https://github.com/gingko/client) - Tree-structured markdown editor for macOS, Windows, and Linux. ![elm_icon] Screenshots
- [MacDown](https://github.com/MacDownApp/macdown) - Markdown editor for macOS. ![objective_c_icon]
- [Mark Text](https://github.com/marktext/marktext) - Realtime preview markdown editor for macOS Windows and Linux. ![javascript_icon]
- [Twig](https://github.com/lukakerr/twig) - A modern MacOS markdown editor. ![swift_icon] Screenshots
#### TeX
-
- [Qilin Editor](https://github.com/qilin-editor/qilin-app) - Text editor for exact sciences with built-in KaTeX/AsciiMath support. ![javascript_icon]
#### Text
-
- [CotEditor](https://github.com/coteditor/CotEditor) - Lightweight Plain-Text Editor for macOS. ![swift_icon] Screenshots
- [MacVim](https://github.com/macvim-dev/macvim) - Text editor for macOS. ![c_icon]
- [Noto](https://github.com/brunophilipe/noto) - Plain text editor for macOS with customizable themes. ![swift_icon] Screenshots
diff --git a/ReadmeGenerator.swift b/ReadmeGenerator.swift
new file mode 100644
index 0000000..2fff504
--- /dev/null
+++ b/ReadmeGenerator.swift
@@ -0,0 +1,309 @@
+//
+// ReadmeGenerator.swift
+// awesome-mac-os-apps-helper
+//
+// Created by Serhii Londar on 11/12/18.
+//
+
+import Foundation
+
+let header = """
+
+
+
+
+# Awesome macOS open source applications
+
+
+
+
+
+
+## Support
+Hey friend! Help me out for a couple of :beers:!
+
+
+List of awesome open source applications for macOS. This list contains a lot of native, and cross-platform apps. The main goal of this repository is to find free open source apps and start contributing. Feel free to [contribute](CONTRIBUTING.md) to the list, any suggestions are welcome!
+
+You can see in which language an app is written. Currently there are following languages:
+
+- ![c_icon] - C language.
+- ![cpp_icon] - C++ language.
+- ![c_sharp_icon] - C# language.
+- ![clojure_icon] - Clojure language.
+- ![coffee_script_icon] - CoffeeScript language.
+- ![css_icon] - CSS language.
+- ![elm_icon] - Elm language.
+- ![haskell_icon] - Haskell language.
+- ![javascript_icon] - JavaScript language.
+- ![lua_icon] - Lua language.
+- ![objective_c_icon] - Objective-C language.
+- ![python_icon] - Python language.
+- ![ruby_icon] - Ruby language.
+- ![rust_icon] - Rust language.
+- ![swift_icon] - Swift language.
+- ![type_script_icon] - TypeScript language.
+
+
+## Contents
+- [Audio](#audio)
+- [Backup](#backup)
+- [Browser](#browser)
+- [Chat](#chat)
+- [Cryptocurrency](#cryptocurrency)
+- [Database](#database)
+- [Development](#development)
+ - [Git](#git)
+ - [iOS / macOS](#ios--macos)
+ - [JSON Parsing](#json-parsing)
+ - [Web development](#web-development)
+ - [Other](#other)
+- [Downloader](#downloader)
+- [Editors](#editors)
+ - [CSV](#csv)
+ - [JSON](#json)
+ - [Markdown](#markdown)
+ - [TeX](#tex)
+ - [Text](#text)
+- [Extensions](#extensions)
+- [Finder](#finder)
+- [Games](#games)
+- [Graphics](#graphics)
+- [IDE](#ide)
+- [Images](#images)
+- [Keyboard](#keyboard)
+- [Mail](#mail)
+- [Menubar](#menubar)
+- [Music](#music)
+- [News](#news)
+- [Notes](#notes)
+- [Other](#other-1)
+- [Podcast](#podcast)
+- [Productivity](#productivity)
+- [Screensaver](#screensaver)
+- [Security](#security)
+- [Sharing Files](#sharing-files)
+- [Social Networking](#social-networking)
+- [Streaming](#streaming)
+- [System](#system)
+- [Terminal](#terminal)
+- [Utilities](#utilities)
+- [VPN & Proxy](#vpn--proxy)
+- [Video](#video)
+- [Wallpaper](#wallpaper)
+- [Window Management](#window-management)
+
+## Applications
+
+"""
+
+let footer = """
+
+## Contributors
+
+Thanks to all the people who contribute:
+
+
+
+[app_store]: ./icons/app_store-16.png 'App Store.'
+[c_icon]: ./icons/c-16.png 'C language.'
+[cpp_icon]: ./icons/cpp-16.png 'C++ language.'
+[c_sharp_icon]: ./icons/csharp-16.png 'C# Language'
+[clojure_icon]: ./icons/clojure-16.png 'Clojure Language'
+[coffee_script_icon]: ./icons/coffeescript-16.png 'CoffeeScript language.'
+[css_icon]: ./icons/css-16.png 'CSS language.'
+[elm_icon]: ./icons/elm-16.png 'Elm Language'
+[haskell_icon]: ./icons/haskell-16.png 'Haskell language.'
+[java_icon]: ./icons/java-16.png 'Java language.'
+[javascript_icon]: ./icons/javascript-16.png 'JavaScript language.'
+[lua_icon]: ./icons/Lua-16.png 'Lua language.'
+[objective_c_icon]: ./icons/objective-c-16.png 'Objective-C language.'
+[python_icon]: ./icons/python-16.png 'Python language.'
+[ruby_icon]: ./icons/ruby-16.png 'Ruby language.'
+[rust_icon]: ./icons/rust-16.png 'Rust language.'
+[swift_icon]: ./icons/swift-16.png 'Swift language.'
+[type_script_icon]: ./icons/typescript-16.png 'TypeScript language.'
+"""
+class JSONApplications: Codable {
+ let applications: [JSONApplication]
+
+ enum CodingKeys: String, CodingKey {
+ case applications
+ }
+
+ init(applications: [JSONApplication]) {
+ self.applications = applications
+ }
+
+ required public init(from decoder: Decoder) throws {
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ applications = try values.decodeIfPresent([JSONApplication].self, forKey: .applications) ?? []
+ }
+}
+
+class JSONApplication: Codable {
+ var title: String
+ var repoURL: String
+ var shortDescription: String
+ var languages: [String]
+ var screenshots: [String]
+ var category: String
+
+ enum CodingKeys: String, CodingKey {
+ case title
+ case repoURL = "repo_url"
+ case shortDescription = "short_description"
+ case languages
+ case screenshots
+ case category
+ }
+
+ init(title: String, repoURL: String, shortDescription: String, languages: [String], screenshots: [String], category: String) {
+ self.title = title
+ self.repoURL = repoURL
+ self.shortDescription = shortDescription
+ self.languages = languages
+ self.screenshots = screenshots
+ self.category = category
+ }
+}
+
+class Categories: Codable {
+ let categories: [Category]
+
+ init(categories: [Category]) {
+ self.categories = categories
+ }
+
+ required public init(from decoder: Decoder) throws {
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ categories = try values.decodeIfPresent([Category].self, forKey: .categories) ?? []
+ }
+}
+
+class Category: Codable {
+ let title, id, description: String
+ let parent: String?
+
+ init(title: String, id: String, description: String, parent: String?) {
+ self.title = title
+ self.id = id
+ self.description = description
+ self.parent = parent
+ }
+}
+
+class ReadmeGenerator {
+ var readmeString = String.empty
+
+ func generateReadme() {
+ print("Start")
+ guard let applicationsData = try? Data(contentsOf: URL(fileURLWithPath: FilePaths.applications.rawValue)) else { return }
+ guard let categoriesData = try? Data(contentsOf: URL(fileURLWithPath: FilePaths.categories.rawValue)) else { return }
+ let jsonDecoder = JSONDecoder()
+ guard let applicationsObject = try? jsonDecoder.decode(JSONApplications.self, from: applicationsData) else { return }
+ guard let categoriesObject = try? jsonDecoder.decode(Categories.self, from: categoriesData) else { return }
+
+ var categories = categoriesObject.categories
+ let subcategories = categories.filter({ $0.parent != nil && !$0.parent!.isEmpty })
+ var applications = applicationsObject.applications
+
+ for subcategory in subcategories {
+ if let index = categories.lastIndex(where: { $0.parent != subcategory.id }) {
+ categories.remove(at: index)
+ }
+ }
+
+ categories = categories.sorted(by: { $0.title < $1.title })
+
+ applications = applications.sorted(by: { $0.category < $1.category })
+
+ readmeString.append(header)
+
+ for category in categories {
+ readmeString.append(String.enter + String.section + String.space + category.title + String.enter)
+ var categoryApplications = applications.filter({ $0.category == category.id })
+ categoryApplications = categoryApplications.sorted(by: { $0.title < $1.title })
+
+ for application in categoryApplications {
+ readmeString.append(application.markdownDescription())
+ readmeString.append(String.enter)
+ }
+
+ var subcategories = subcategories.filter({ $0.parent == category.id })
+ guard subcategories.count > 0 else { continue }
+ subcategories = subcategories.sorted(by: { $0.title < $1.title })
+ for subcategory in subcategories {
+ readmeString.append(String.enter + String.subsection + String.space + subcategory.title + String.enter)
+ var categoryApplications = applications.filter({ $0.category == subcategory.id })
+ categoryApplications = categoryApplications.sorted(by: { $0.title < $1.title })
+
+ for application in categoryApplications {
+ readmeString.append(application.markdownDescription())
+ readmeString.append(String.enter)
+ }
+ }
+ }
+ readmeString.append(footer)
+ print(readmeString)
+ try? readmeString.data(using: .utf8)?.write(to: URL(fileURLWithPath: FilePaths.newReadme.rawValue))
+ print("Finish")
+ }
+}
+
+extension String {
+ static let empty = ""
+ static let space = " "
+ static let enter = "\n"
+ static let section = "###"
+ static let subsection = "####"
+}
+
+extension JSONApplication {
+ func markdownDescription() -> String {
+ var markdownDescription = String.empty
+ var languages: String = String.empty
+ for lang in self.languages {
+ languages.append("![\(lang)] ")
+ }
+
+ markdownDescription.append("- [\(self.title)](\(self.repoURL)) - \(self.shortDescription) \(languages)")
+
+ if self.screenshots.count > 0 {
+ var screenshotsString = String.empty
+ screenshotsString += (String.space + Constants.detailsBeginString + String.space)
+ self.screenshots.forEach({
+ screenshotsString += (String.space + (NSString(format: Constants.srcLinePattern as NSString, $0 as CVarArg) as String) + String.space)
+ })
+ screenshotsString += (String.space + Constants.detailsEndString + String.space)
+ markdownDescription.append(screenshotsString)
+ }
+ return markdownDescription
+ }
+}
+
+enum FilePaths: String {
+ case readme = "./README.md"
+ case newReadme = "./NEWREADME.md"
+ case applications = "./applications.json"
+ case categories = "./categories.json"
+}
+
+struct Constants {
+ static let detailsBeginString = " Screenshots
"
+ static let detailsEndString = "
"
+ static let srcLinePattern = "
"
+
+ static let startProcessString = "### Database"
+ static let endProcessString = "### Development"
+
+ static func regex(for type: String) -> String {
+ return "\\((.+\\.\(type))"
+ }
+ static func regex1(for type: String) -> String {
+ return "\\\"(.+\\.\(type))"
+ }
+}
+
+
+ReadmeGenerator().generateReadme()