git.iko.soy/SourceDocsSwift
on 50b0899 help sign in
9 files changed+1−561
since splitsnapshots
unifiedsplit
.gitignore−78 README.md−39 SourceDocsSwift.xcodeproj/project.pbxproj−18 SourceDocsSwift.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/xcschememanagement.plist−8 SourceDocsSwift/Shell.swift−41 SourceDocsSwift/SourceKittenResponse.swift−123 SourceDocsSwift/TeXer.swift−101 SourceDocsSwift/XcodeBuildTargetResponse.swift−25 SourceDocsSwift/main.swift+1−128
.gitignoredeleted−78
@@ -1,78 +0,0 @@
1−# Xcode
2−#
3−# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4−
5−## Build generated
6−build/
7−DerivedData/
8−
9−## Various settings
10−*.pbxuser
11−!default.pbxuser
12−*.mode1v3
13−!default.mode1v3
14−*.mode2v3
15−!default.mode2v3
16−*.perspectivev3
17−!default.perspectivev3
18−xcuserdata/
19−
20−## Other
21−*.moved-aside
22−*.xccheckout
23−*.xcscmblueprint
24−
25−## Obj-C/Swift specific
26−*.hmap
27−*.ipa
28−*.dSYM.zip
29−*.dSYM
30−
31−## Playgrounds
32−timeline.xctimeline
33−playground.xcworkspace
34−
35−# Swift Package Manager
36−#
37−# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38−# Packages/
39−# Package.pins
40−# Package.resolved
41−.build/
42−
43−# CocoaPods
44−#
45−# We recommend against adding the Pods directory to your .gitignore. However
46−# you should judge for yourself, the pros and cons are mentioned at:
47−# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48−#
49−# Pods/
50−#
51−# Add this line if you want to avoid checking in source code from the Xcode workspace
52−# *.xcworkspace
53−
54−# Carthage
55−#
56−# Add this line if you want to avoid checking in source code from Carthage dependencies.
57−# Carthage/Checkouts
58−
59−Carthage/Build
60−
61−# fastlane
62−#
63−# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
64−# screenshots whenever they are needed.
65−# For more information about the recommended setup visit:
66−# https://docs.fastlane.tools/best-practices/source-control/#source-control
67−
68−fastlane/report.xml
69−fastlane/Preview.html
70−fastlane/screenshots/**/*.png
71−fastlane/test_output
72−
73−# Code Injection
74−#
75−# After new code Injection tools there's a generated folder /iOSInjectionProject
76−# https://github.com/johnno1962/injectionforxcode
77−
78−iOSInjectionProject/
README.mddeleted−39
@@ -1,39 +0,0 @@
1−# SourceDocsSwift
2−
3−Генернирует LaTeX-таблицы всех классов, структур и их полей и методов из проекта Xcode для вставки в техническую документацию.
4−
5−## Установка
6−
7−```bash
8−brew install ilyakooo0/tap/sourceDocsSwift
9−```
10−
11−## Использование
12−
13−```bash
14−sourceDocsSwift -i path/to/XcodePrjectDirectory/ -o path/to/output/doc.tex -s latex -l 1
15−```
16−
17−```bash
18−sourceDocsSwift -s tech -l 2
19−```
20−
21−По умолчанию ищет проект в текущей папке и пишет результат в эту же папку в файл `doc.tex`.
22−
23−В LaTeX документ можно вставить просто положив рядом с щсновным `.tex` документом и вставив в нужное место:
24−
25−```LaTeX
26−\include{doc}
27−```
28−
29−Так же требуется пакет `longtable`. Если в вашем шаблоне этого еще нигде нет, то где-нибудь в преамбуле надо вставить:
30−
31−```LaTeX
32−\usepackage{longtable}
33−```
34−### Параметры
35−
36−- `[-s | --style] [latex | tech]` -- стиль команды самого внешнего уровня
37−- `[-l | --level] [1 | 2 | 3]` -- отступ в уровнях
38−- `[-i | --input] path/to/projectDirectory` -- путь к папке с проектом
39−- `[-o | --output] path/to/output.tex` -- путь к фалу, в который писать результат
SourceDocsSwift.xcodeproj/project.pbxproj−18
@@ -7,11 +7,7 @@
77 objects = {
88
99/* Begin PBXBuildFile section */
10− 094C8925212F6FAF00993F4A /* XcodeBuildTargetResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094C8924212F6FAF00993F4A /* XcodeBuildTargetResponse.swift */; };
11− 094C8927212F6FE900993F4A /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094C8926212F6FE900993F4A /* Shell.swift */; };
1210 09D361FA212DD5490010CC83 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D361F9212DD5490010CC83 /* main.swift */; };
13− 09D36201212DD58E0010CC83 /* SourceKittenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D36200212DD58E0010CC83 /* SourceKittenResponse.swift */; };
14− 09D36203212E13820010CC83 /* TeXer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D36202212E13820010CC83 /* TeXer.swift */; };
1511/* End PBXBuildFile section */
1612
1713/* Begin PBXCopyFilesBuildPhase section */
@@ -27,13 +23,8 @@
2723/* End PBXCopyFilesBuildPhase section */
2824
2925/* Begin PBXFileReference section */
30− 094C8924212F6FAF00993F4A /* XcodeBuildTargetResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeBuildTargetResponse.swift; sourceTree = "<group>"; };
31− 094C8926212F6FE900993F4A /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
32− 09B928F221304ED6005A0427 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
3326 09D361F6212DD5480010CC83 /* SourceDocsSwift */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SourceDocsSwift; sourceTree = BUILT_PRODUCTS_DIR; };
3427 09D361F9212DD5490010CC83 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
35− 09D36200212DD58E0010CC83 /* SourceKittenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceKittenResponse.swift; sourceTree = "<group>"; };
36− 09D36202212E13820010CC83 /* TeXer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeXer.swift; sourceTree = "<group>"; };
3728/* End PBXFileReference section */
3829
3930/* Begin PBXFrameworksBuildPhase section */
@@ -50,7 +41,6 @@
5041 09D361ED212DD5480010CC83 = {
5142 isa = PBXGroup;
5243 children = (
53− 09B928F221304ED6005A0427 /* README.md */,
5444 09D361F8212DD5480010CC83 /* SourceDocsSwift */,
5545 09D361F7212DD5480010CC83 /* Products */,
5646 );
@@ -68,10 +58,6 @@
6858 isa = PBXGroup;
6959 children = (
7060 09D361F9212DD5490010CC83 /* main.swift */,
71− 094C8926212F6FE900993F4A /* Shell.swift */,
72− 094C8924212F6FAF00993F4A /* XcodeBuildTargetResponse.swift */,
73− 09D36202212E13820010CC83 /* TeXer.swift */,
74− 09D36200212DD58E0010CC83 /* SourceKittenResponse.swift */,
7561 );
7662 path = SourceDocsSwift;
7763 sourceTree = "<group>";
@@ -133,10 +119,6 @@
133119 isa = PBXSourcesBuildPhase;
134120 buildActionMask = 2147483647;
135121 files = (
136− 09D36201212DD58E0010CC83 /* SourceKittenResponse.swift in Sources */,
137− 09D36203212E13820010CC83 /* TeXer.swift in Sources */,
138− 094C8927212F6FE900993F4A /* Shell.swift in Sources */,
139− 094C8925212F6FAF00993F4A /* XcodeBuildTargetResponse.swift in Sources */,
140122 09D361FA212DD5490010CC83 /* main.swift in Sources */,
141123 );
142124 runOnlyForDeploymentPostprocessing = 0;
SourceDocsSwift.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/xcschememanagement.plist−8
@@ -10,13 +10,5 @@
1010 <integer>0</integer>
1111 </dict>
1212 </dict>
13− <key>SuppressBuildableAutocreation</key>
14− <dict>
15− <key>09D361F5212DD5480010CC83</key>
16− <dict>
17− <key>primary</key>
18− <true/>
19− </dict>
20− </dict>
2113</dict>
2214</plist>
SourceDocsSwift/Shell.swiftdeleted−41
@@ -1,41 +0,0 @@
1−//
2−// Shell.swift
3−// SourceDocsSwift
4−//
5−// Created by Ilya Kos on 8/24/18.
6−// Copyright © 2018 Ilya Kos. All rights reserved.
7−//
8−
9−import Foundation
10−
11−struct Shell {
12− static func launch(executable: String, arguments: [String], launchDirectory: String?) -> Data? {
13− let process = Process()
14− if let launchDirectory = launchDirectory {
15− process.currentDirectoryPath = launchDirectory
16− }
17− process.arguments = arguments
18− if !FileManager.default.isExecutableFile(atPath: executable) {
19− return nil
20− }
21− let executableURL = URL(fileURLWithPath: executable)
22− process.executableURL = executableURL
23−
24− let pipe = Pipe()
25−
26− var out = Data()
27−
28− NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: pipe.fileHandleForReading, queue: nil) { (_) in
29− out.append(pipe.fileHandleForReading.readDataToEndOfFile())
30− }
31−
32− process.standardOutput = pipe
33−
34− pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
35−
36− process.launch()
37− process.waitUntilExit()
38−
39− return out
40− }
41−}
SourceDocsSwift/SourceKittenResponse.swiftdeleted−123
@@ -1,123 +0,0 @@
1−//
2−// SourceKittenResponse.swift
3−// SourceDocsSwift
4−//
5−// Created by Ilya Kos on 8/22/18.
6−// Copyright © 2018 Ilya Kos. All rights reserved.
7−//
8−
9−import Foundation
10−
11−struct SourceKittenResponse: Decodable {
12−
13− private enum CodingKeys: String, CodingKey {
14− case objects = "key.substructure"
15− }
16−
17− var objects: [Object]
18−}
19−
20−struct Object: Decodable {
21− let type: ObjectType
22− let name: String
23− /// This is a documented field
24− let declaration: String
25− let documentation: String?
26− let methods: [Subobject]
27− let fields: [Subobject]
28−
29− private enum CodingKeys: String, CodingKey {
30− case name = "key.name"
31− case declaration = "key.parsed_declaration"
32− case documenatation = "key.doc.comment"
33− case type = "key.kind"
34− case subobjects = "key.substructure"
35− }
36−
37− init(from decoder: Decoder) throws {
38− let container = try decoder.container(keyedBy: CodingKeys.self)
39− let typeString = try container.decodeIfPresent(String.self, forKey: .type)
40− if typeString == "source.lang.swift.decl.struct" {
41− type = .struct
42− } else if typeString == "source.lang.swift.decl.class" {
43− type = .class
44− } else {
45− type = .other
46− name = ""
47− declaration = ""
48− documentation = nil
49− methods = []
50− fields = []
51− return
52− }
53− name = try container.decode(String.self, forKey: .name).replacingOccurrences(of: "_", with: "\\_")
54− declaration = try container.decode(String.self, forKey: .declaration).replacingOccurrences(of: "_", with: "\\_")
55− documentation = try container.decodeIfPresent(String.self, forKey: .documenatation)?.replacingOccurrences(of: "_", with: "\\_")
56− let subobjects = try container.decode([Subobject].self, forKey: .subobjects)
57− methods = subobjects.filter {$0.type == .method}
58− fields = subobjects.filter {$0.type == .field}
59− }
60−}
61−
62−enum ObjectType {
63− case `class`
64− case `struct`
65− case other
66−}
67−
68−struct Subobject: Decodable {
69− let type: SubobjectType
70− let name: String
71− let declaration: String
72− let documentation: String?
73− let typeName: String
74−
75− private enum CodingKeys: String, CodingKey {
76− case name = "key.name"
77− case declaration = "key.parsed_declaration"
78− case documenatation = "key.doc.comment"
79− case type = "key.kind"
80− case typeName = "key.typename"
81− }
82−
83− private static let methodKeys: Set<String> = [
84− "source.lang.swift.decl.function.method.static",
85− "source.lang.swift.decl.function.method.instance"
86− ]
87−
88− private static let fieldKeys: Set<String> = [
89− "source.lang.swift.decl.var.instance",
90− "source.lang.swift.decl.var.static"
91− ]
92−
93− init(from decoder: Decoder) throws {
94− let container = try decoder.container(keyedBy: CodingKeys.self)
95− let stringType = try container.decode(String.self, forKey: .type)
96− if Subobject.methodKeys.contains(stringType) {
97− type = .method
98− } else if Subobject.fieldKeys.contains(stringType) {
99− type = .field
100− } else {
101− type = .other
102− name = ""
103− declaration = ""
104− documentation = nil
105− typeName = ""
106− return
107− }
108− name = try container.decode(String.self, forKey: .name).replacingOccurrences(of: "_", with: "\\_")
109− declaration = try container.decode(String.self, forKey: .declaration).replacingOccurrences(of: "_", with: "\\_")
110− documentation = try container.decodeIfPresent(String.self, forKey: .documenatation)?.replacingOccurrences(of: "_", with: "\\_")
111− typeName = try container.decode(String.self, forKey: .typeName).replacingOccurrences(of: "_", with: "\\_")
112− }
113−}
114−
115−enum DecodingError: Error {
116− case DecodingError
117−}
118−
119−enum SubobjectType {
120− case method
121− case field
122− case other
123−}
SourceDocsSwift/TeXer.swiftdeleted−101
@@ -1,101 +0,0 @@
1−//
2−// TeXer.swift
3−// SourceDocsSwift
4−//
5−// Created by Ilya Kos on 8/23/18.
6−// Copyright © 2018 Ilya Kos. All rights reserved.
7−//
8−
9−struct TeXer {
10− static func convert(objects: [Object], with style: TexType = .laTeX(2)) -> String {
11− return """
12− \\\(style.section(for: 1)){Описание классов и структур}
13−
14− \\begin{longtable}{| >{\\raggedright\\arraybackslash}p{0.55\\textwidth} | p{0.4\\textwidth}|}
15− \\hline
16− \\textbf{Класс или стркура} & \\textbf{Описание} \\\\ \\hline
17− \( objects.map {"\\texttt{\($0.declaration)} & {\\color{red} TODO} \\\\ \\hline"} .joined(separator: "\n") )
18− \\end{longtable}
19−
20−
21− \\\(style.section(for: 1)){Описание полей классов и структур}
22−
23− \(
24− objects.compactMap {
25− if $0.fields.count > 0 {
26− return """
27− \\\(style.section(for: 2))*{\\texttt{\($0.declaration)}}
28−
29− \\begin{longtable}{| >{\\raggedright\\arraybackslash}p{0.55\\textwidth} | p{0.4\\textwidth}|}
30− \\hline
31− \\textbf{Поле} & \\textbf{Описание} \\\\ \\hline
32− \( $0.fields.map {"\\texttt{\($0.declaration.prefix {$0 != "="})} & {\\color{red} TODO} \\\\ \\hline"} .joined(separator: "\n") )
33− \\end{longtable}
34−
35− """
36− } else {
37− return nil
38− }
39− } .joined(separator: "\n")
40− )
41−
42− \\\(style.section(for: 1)){Описание методов классов и структур}
43−
44− \(
45− objects.compactMap {
46− if $0.methods.count > 0 {
47− return """
48− \\\(style.section(for: 2))*{\\texttt{\($0.declaration)}}
49−
50− \\begin{longtable}{| >{\\raggedright\\arraybackslash}p{0.55\\textwidth} | p{0.4\\textwidth}|}
51− \\hline
52− \\textbf{Метод} & \\textbf{Описание} \\\\ \\hline
53− \( $0.methods.map {"\\texttt{\($0.declaration)} & {\\color{red} TODO} \\\\ \\hline"} .joined(separator: "\n") )
54− \\end{longtable}
55−
56− """
57− } else {
58− return nil
59− }
60− } .joined(separator: "\n")
61− )
62− """
63− }
64−
65− enum TexType {
66− case techDoc(Int)
67− case laTeX(Int)
68−
69− func section(`for` level: Int) -> String {
70− switch self {
71− case .laTeX(let i):
72− return String(repeating: "sub", count: i+level-2).appending("section")
73− case .techDoc(let i):
74− switch i+level-2 {
75− case 0:
76− return "addition"
77− default:
78− return TexType.laTeX(i).section(for: level)
79− }
80− }
81− }
82− var levelOffset: Int {
83− get {
84− switch self {
85− case .laTeX(let i):
86− return i
87− case .techDoc(let i):
88− return i
89− }
90− }
91− set {
92− switch self {
93− case .laTeX(_):
94− self = .laTeX(newValue)
95− case .techDoc(_):
96− self = .techDoc(newValue)
97− }
98− }
99− }
100− }
101−}
SourceDocsSwift/XcodeBuildTargetResponse.swiftdeleted−25
@@ -1,25 +0,0 @@
1−//
2−// XcodeBuildTargetResponse.swift
3−// SourceDocsSwift
4−//
5−// Created by Ilya Kos on 8/24/18.
6−// Copyright © 2018 Ilya Kos. All rights reserved.
7−//
8−
9−struct XcodeBuildTargetResponse: Decodable {
10− private enum OuterKeys: CodingKey {
11− case project
12− }
13−
14− private enum InnerKeys: CodingKey {
15− case targets
16− }
17−
18− let targets: [String]
19−
20− init(from decoder: Decoder) throws {
21− let outerContainer = try decoder.container(keyedBy: OuterKeys.self)
22− let innerContainer = try outerContainer.nestedContainer(keyedBy: InnerKeys.self, forKey: .project)
23− targets = try innerContainer.decode([String].self, forKey: .targets)
24− }
25−}
SourceDocsSwift/main.swift+1−128
@@ -8,132 +8,5 @@
88
99import Foundation
1010
11−let args = Array(CommandLine.arguments.dropFirst())
12−//let temp = FileManager.default.temporaryDirectory.appendingPathComponent("SourceDocsSwift/build\(Date().timeIntervalSince1970)", isDirectory: true)
11+print("Hello, World!")
1312
14−var output = "docs.tex"
15−var directory: String?
16−var style: TeXer.TexType = .laTeX(2)
17−
18−var i = 0
19−while i < args.count {
20− switch args[i] {
21− case "-o", "--output":
22− if i + 1 < args.count {
23− i += 1
24− output = args[i]
25− }
26− case "-i", "--input":
27− if i + 1 < args.count {
28− i += 1
29− directory = args[i]
30− }
31− case "-s", "--style":
32− if i + 1 < args.count {
33− i += 1
34− switch args[i].lowercased() {
35− case "latex", "xetex", "tex":
36− style = .laTeX(style.levelOffset)
37− case "tech", "techdoc", "techdocs":
38− style = .techDoc(style.levelOffset)
39− default:
40− print("""
41− That is not a valid style.
42−
43− Valid styles are:
44− latex
45− techdoc
46− """)
47− exit(1)
48− }
49− }
50− case "-l", "--level":
51− if i + 1 < args.count {
52− i += 1
53− guard let l = Int(args[i]) else {
54− print("""
55− That is not a valid int.
56− """)
57− exit(1)
58− }
59− if l < 1 {
60− print("""
61− Level must be greater or equal to 1.
62− """)
63− exit(1)
64− }
65− style.levelOffset = l
66− }
67− default:
68− break
69− }
70− i += 1
71−}
72−
73−var outputIsDirectory: ObjCBool = false
74−
75−FileManager.default.fileExists(atPath: output, isDirectory: &outputIsDirectory)
76−
77−if outputIsDirectory.boolValue {
78− output.append("/docs.tex")
79−}
80−
81−let decoder = JSONDecoder()
82−
83−guard let xcbTagetData = Shell.launch(executable: "/usr/bin/xcodebuild", arguments: ["-list", "-json"], launchDirectory: directory) else {
84− print("""
85− Make sure xcodebuild is installed
86−
87− xcode-select --install
88− """)
89− exit(1)
90−}
91−
92−guard let xcbResponse = try? decoder.decode(XcodeBuildTargetResponse.self, from: xcbTagetData) else {
93− print("Unexpected response from xcodebuild")
94− exit(1)
95−}
96−
97−let targets = xcbResponse.targets
98−
99−var files: [String: [Object]] = [:]
100−
101−for target in targets {
102−// try? FileManager.default.removeItem(at: temp)
103− guard let skResponseData = Shell.launch(executable: "/usr/local/bin/sourcekitten", arguments: ["doc", "--", "-target", target], launchDirectory: directory) else {
104− print("""
105− Make sure sourcekitten is installed
106−
107− brew install sourcekitten
108− """)
109− exit(1)
110− }
111−
112− guard let skResponse = try? decoder.decode([[String: SourceKittenResponse]].self, from: skResponseData) else {
113− print("Unexpected response from sourcekitten")
114− exit(1)
115− }
116−
117− skResponse.forEach {files.merge($0.mapValues {$0.objects.filter {$0.type != .other}}, uniquingKeysWith: {a, b in a})}
118−}
119−
120−
121−
122−
123−let tex = TeXer.convert(objects: files.flatMap {$0.value}, with: style)
124−do {
125− try tex.write(toFile: output, atomically: true, encoding: .utf8)
126−} catch {
127− print("""
128− There was an error writing the outuput file.
129− """)
130− exit(1)
131−}
132−
133−print("""
134−+-------+
135−| i l y |
136−| a k o |
137−| o o 0 |
138−+-------+
139−""")