1
0

APIDocFetcher.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. //
  2. // APIFetcher.swift
  3. // C Code Develop
  4. //
  5. // Created by xcbosa on 2022/6/20.
  6. // Copyright © 2022 xcbosa. All rights reserved.
  7. //
  8. import Foundation
  9. public typealias ProvideRootMarkdownBlock = () -> [APIDocFileCollectionModel]
  10. public protocol APIDocMarkdownGenerable: AnyObject {
  11. func generateMarkdown() -> String
  12. var navigationItemTitle: String { get }
  13. var shareURL: URL? { get }
  14. var collectionPath: String { get }
  15. var linkPathPrefix: String { get set }
  16. var language: ElementDefs.SupportedLanguage { get set }
  17. var provideRootMarkdownBlock: ProvideRootMarkdownBlock? { get set }
  18. }
  19. extension APIDocMarkdownGenerable {
  20. public func getCollectionName() -> String {
  21. switch collectionPath {
  22. case "stdc": return "APIDoc.StandardCHeaderName".apiDocLocalized(language)
  23. case "cdenvc": return "APIDoc.ExtendedCHeaderName".apiDocLocalized(language)
  24. default: return collectionPath
  25. }
  26. }
  27. }
  28. public struct FunctionParamDef {
  29. public var name: String
  30. public var type: String
  31. public var description: String
  32. }
  33. public extension APIDocMarkdownGenerable {
  34. var cKeyWordsExtended: [String] {
  35. [
  36. "break", "case", "char", "continue", "default", "do", "double", "else", "extern", "false", "FALSE", "float", "for", "goto", "if", "int", "long", "register", "const", "return", "short", "signed", "sizeof", "struct", "null", "static", "switch", "true", "TRUE", "typedef", "unsigned", "void", "while", "include", "define", "builtin", "unsigned int", "unsigned long", "long long", "unsigned char", "unsigned short", "__Macro", "const int", "const long", "const float", "const double", "Never", "const void", "const char", "const short", "const unsigned char", "const unsigned short", "const unsigned int", "const unsigned long", "union"
  37. ]
  38. }
  39. func buildLinkPath(withMemberName name: String?, inFile file: String, inCollection collection: String) -> String {
  40. if let name = name {
  41. return "\(linkPathPrefix)/\(collection)/\(file)/\(name)"
  42. } else {
  43. return "\(linkPathPrefix)/\(collection)/\(file)"
  44. }
  45. }
  46. func color(token str: String, drawFunctionName: Bool, drawTypeName: Bool, drawStructName: Bool, drawVariableName: Bool) -> String {
  47. var pstr = str
  48. while pstr.first == "*" { pstr.removeFirst() }
  49. while pstr.last == "*" { pstr.removeLast() }
  50. let colorTable = CLangColorTable.getColorTable()
  51. if cKeyWordsExtended.contains(pstr) {
  52. return "<font color=\"\(colorTable.getKeywordColor().hexString)\">\(str)</font>"
  53. }
  54. var files = [APIDocFileCollectionModel]()
  55. if let rootArray = provideRootMarkdownBlock?() {
  56. files = rootArray
  57. } else {
  58. print("[APIDoc] provideRootMarkdownBlock = nil, Using \"Current\"")
  59. if let self = self as? APIDocFileModel {
  60. files.append(APIDocFileCollectionModel([self], withCollectionTitle: "Current", andCollectionStoragePath: "Current"))
  61. }
  62. }
  63. for it in files {
  64. for doc in it.files {
  65. if doc.fileName.hasSuffix(".h") {
  66. let items = doc.items
  67. if drawFunctionName,
  68. items.filter({ $0.type == .functionDefintion }).contains(where: { $0.name == pstr }) {
  69. return "<font color=\"\(colorTable.getMethodContentColor().hexString)\"><a href='\(buildLinkPath(withMemberName: pstr, inFile: doc.fileName, inCollection: it.collectionStoragePath))' style='color:\(colorTable.getMethodContentColor().hexString)'>\(str)</a></font>"
  70. }
  71. if drawTypeName {
  72. if pstr.hasPrefix("struct ") {
  73. if items.filter({ $0.type == .structDefinition }).contains(where: { $0.name == pstr.split(separator: " ").last?.description ?? "" }) {
  74. return "<font color=\"\(colorTable.getStructContentColor().hexString)\"><a href='\(buildLinkPath(withMemberName: pstr.split(separator: " ").last?.description ?? "", inFile: doc.fileName, inCollection: it.collectionStoragePath))' style='color:\(colorTable.getStructContentColor().hexString)'>\(str)</a></font>"
  75. }
  76. }
  77. if items.filter({ $0.type == .typeDefinition }).contains(where: { $0.name == pstr }) {
  78. return "<font color=\"\(colorTable.getTypeContentColor().hexString)\"><a href='\(buildLinkPath(withMemberName: pstr, inFile: doc.fileName, inCollection: it.collectionStoragePath))' style='color:\(colorTable.getTypeContentColor().hexString)'>\(str)</a></font>"
  79. }
  80. }
  81. if drawStructName,
  82. items.filter({ $0.type == .structDefinition }).contains(where: { $0.name == pstr }) {
  83. return "<font color=\"\(colorTable.getStructContentColor().hexString)\"><a href='\(buildLinkPath(withMemberName: pstr, inFile: doc.fileName, inCollection: it.collectionStoragePath))' style='color:\(colorTable.getStructContentColor().hexString)'>\(str)</a></font>"
  84. }
  85. if drawVariableName,
  86. items.filter({ $0.type == .variableDefinition }).contains(where: { $0.name == pstr }) {
  87. return "<font color=\"\(colorTable.getUserDefinedContentColor().hexString)\"><a href='\(buildLinkPath(withMemberName: pstr, inFile: doc.fileName, inCollection: it.collectionStoragePath))' style='color:\(colorTable.getUserDefinedContentColor().hexString)'>\(str)</a></font>"
  88. }
  89. }
  90. }
  91. }
  92. return str
  93. }
  94. func drawFunc(_ token: String) -> String { color(token: token, drawFunctionName: true, drawTypeName: false, drawStructName: false, drawVariableName: false) }
  95. func drawType(_ token: String) -> String { color(token: token, drawFunctionName: false, drawTypeName: true, drawStructName: false, drawVariableName: false) }
  96. func drawStruct(_ token: String) -> String { color(token: token, drawFunctionName: false, drawTypeName: false, drawStructName: true, drawVariableName: false) }
  97. func drawVariable(_ token: String) -> String { color(token: token, drawFunctionName: false, drawTypeName: false, drawStructName: false, drawVariableName: true) }
  98. func getFuncParamDef(_ def: ElementDefs) -> [FunctionParamDef] {
  99. let paramArray = def.functionArgs.split(separator: ",").filter({ !$0.isEmpty })
  100. var defs = [FunctionParamDef]()
  101. var unnameValIndex = 0
  102. let peekUnnameIndex = { () -> String in
  103. unnameValIndex += 1
  104. return "arg\(unnameValIndex)"
  105. }
  106. for it in paramArray {
  107. let paramDef = it.trimmingCharacters(in: .whitespacesAndNewlines)
  108. let paramDefArray = paramDef.split(separator: " ").filter({ !$0.isEmpty })
  109. var paramType = ""
  110. var paramName = ""
  111. for id in 0..<paramDefArray.count {
  112. let it = paramDefArray[id]
  113. if id != paramDefArray.count - 1 {
  114. paramType.append(it + " ")
  115. } else {
  116. paramName = it.description
  117. }
  118. }
  119. paramType = paramType.trimmingCharacters(in: .whitespacesAndNewlines)
  120. paramName = paramName.trimmingCharacters(in: .whitespacesAndNewlines)
  121. if paramType.isEmpty {
  122. paramType = paramName
  123. paramName = peekUnnameIndex()
  124. }
  125. while paramType.last == "*" {
  126. paramType.removeLast()
  127. paramName = "*" + paramName
  128. }
  129. var realParamName = paramName
  130. while realParamName.first == "*" { realParamName.removeFirst() }
  131. let comment = def.getLocalizedComment(withKey: realParamName, language: language)
  132. defs.append(FunctionParamDef(name: paramName, type: paramType, description: comment))
  133. }
  134. return defs
  135. }
  136. func drawParamTable(_ str: String) -> String {
  137. let paramArray = str.split(separator: ",").filter({ !$0.isEmpty })
  138. var strbuf = ""
  139. var unnameValIndex = 0
  140. let peekUnnameIndex = { () -> String in
  141. unnameValIndex += 1
  142. return "arg\(unnameValIndex)"
  143. }
  144. for it in paramArray {
  145. let paramDef = it.trimmingCharacters(in: .whitespacesAndNewlines)
  146. let paramDefArray = paramDef.split(separator: " ").filter({ !$0.isEmpty })
  147. var paramType = ""
  148. var paramName = ""
  149. for id in 0..<paramDefArray.count {
  150. let it = paramDefArray[id]
  151. if id != paramDefArray.count - 1 {
  152. paramType.append(it + " ")
  153. } else {
  154. paramName = it.description
  155. }
  156. }
  157. paramType = paramType.trimmingCharacters(in: .whitespacesAndNewlines)
  158. paramName = paramName.trimmingCharacters(in: .whitespacesAndNewlines)
  159. if paramType.isEmpty {
  160. paramType = paramName
  161. paramName = peekUnnameIndex()
  162. }
  163. while paramType.last == "*" {
  164. paramType.removeLast()
  165. paramName = "*" + paramName
  166. }
  167. var realParamName = paramName
  168. var starStr = ""
  169. while realParamName.first == "*" { starStr.append(realParamName.removeFirst()) }
  170. if realParamName.isEmpty {
  171. paramName = starStr + peekUnnameIndex()
  172. }
  173. strbuf.append(drawType(paramType))
  174. strbuf.append(" \(paramName), ")
  175. }
  176. strbuf = strbuf.trimmingCharacters(in: .whitespacesAndNewlines)
  177. if strbuf.last == "," { strbuf.removeLast() }
  178. return strbuf
  179. }
  180. func drawDefinition(forElement element: ElementDefs) -> String {
  181. let colorTable = CLangColorTable.getColorTable()
  182. switch element.type {
  183. case .functionDefintion:
  184. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\">\(drawType(element.functionReturn)) \(drawFunc(element.name))(\(drawParamTable(element.functionArgs)));</font>".replacingOccurrences(of: "*", with: "\\*")
  185. case .structDefinition:
  186. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\"><font color=\"\(colorTable.getStructContentColor().hexString)\">struct</font> { ... } \(drawStruct(element.name));</font>".replacingOccurrences(of: "*", with: "\\*")
  187. case .typeDefinition:
  188. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\">\(color(token: "typedef", drawFunctionName: false, drawTypeName: false, drawStructName: false, drawVariableName: false)) \(drawType(element.typeImplementation)) \(drawType(element.name));</font>".replacingOccurrences(of: "*", with: "\\*")
  189. case .variableDefinition:
  190. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\">\(drawType(element.variableType)) \(drawVariable(element.name));</font>".replacingOccurrences(of: "*", with: "\\*")
  191. case .anyToken:
  192. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\">\(element.name)</font>".replacingOccurrences(of: "*", with: "\\*")
  193. case .functionPointer:
  194. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\">\(drawType(element.functionReturn)) (\(element.functionPointerFlag) \(drawType(element.name)))(\(drawParamTable(element.functionArgs)));</font>"
  195. case .specialReserved:
  196. return "<font face=\"menlo\" color=\"\(colorTable.getKeywordColor().hexString)\">\(element.name)</font>".replacingOccurrences(of: "*", with: "\\*")
  197. case .unionDefinition:
  198. return "<font face=\"menlo\" color=\"\(colorTable.getDocDefaultColor().hexString)\"><font color=\"\(colorTable.getStructContentColor().hexString)\">union</font> { ... } \(drawStruct(element.name));</font>".replacingOccurrences(of: "*", with: "\\*")
  199. }
  200. }
  201. }
  202. public class APIDocMemberModel: APIDocMarkdownGenerable {
  203. public var language: ElementDefs.SupportedLanguage = .unspecified
  204. public var provideRootMarkdownBlock: ProvideRootMarkdownBlock?
  205. public var member: ElementDefs
  206. public var collectionPath: String
  207. public var fileName: String
  208. public var linkPathPrefix: String = "https://docs.forgetive.org"
  209. public var shareURL: URL? {
  210. URL(string: "\(linkPathPrefix)/\(collectionPath)/\(fileName)/\(member.name)")
  211. }
  212. public var navigationItemTitle: String { member.name }
  213. public init(_ item: ElementDefs, inFile file: String, inCollection collection: String) {
  214. member = item
  215. fileName = file
  216. collectionPath = collection
  217. }
  218. public func generateMarkdown() -> String {
  219. let colorTable = CLangColorTable.getColorTable()
  220. let headerColorHex = colorTable.getTypeContentColor().hexString
  221. var markdown = ""
  222. markdown.append("#### \("APIDoc.\(member.type.rawValue)".apiDocLocalized(language)) (\("APIDoc.BelongsTo".apiDocLocalized(language)) \(getCollectionName()) / <font color=\"\(headerColorHex)\"><a href=\"\(buildLinkPath(withMemberName: nil, inFile: fileName, inCollection: collectionPath))\" style=\"color: \(headerColorHex)\">\(fileName)</a></font>) \n")
  223. markdown.append("# \(member.name) \n")
  224. markdown.append("\(drawDefinition(forElement: member)) \n")
  225. markdown.append("### \(member.getLocalizedComment(withKey: nil, language: language)) \n")
  226. let filePath = Bundle.main.resourcePath! + "/CodeAnalyserFile/article/\(fileName).md"
  227. if ["cdenvc", "stdc"].contains(collectionPath), FileManager.default.fileExists(atPath: filePath) {
  228. markdown.append("### <font color=\"\(CLangColorTable.getColorTable().getTypeContentColor().hexString)\"><a href=\"\(buildLinkPath(withMemberName: "__article__", inFile: fileName, inCollection: collectionPath))\" style=\"color: \(CLangColorTable.getColorTable().getTypeContentColor().hexString)\">\("APIDoc.Article".apiDocLocalized(language))</a></font> \n")
  229. }
  230. var wantDrawBottomLine = true
  231. if member.type == .functionDefintion || member.type == .functionPointer {
  232. let paramDef = getFuncParamDef(member)
  233. if !paramDef.isEmpty {
  234. if wantDrawBottomLine {
  235. markdown.append(" \n-------- \n")
  236. wantDrawBottomLine = false
  237. }
  238. markdown.append("### \("APIDoc.ParamTable".apiDocLocalized(language)) \n")
  239. for it in paramDef {
  240. markdown.append("&nbsp;&nbsp;\(drawType(it.type)) <font color=\"\(colorTable.getUserDefinedContentColor().hexString)\">\(it.name)</font> \n")
  241. if !it.description.isEmpty {
  242. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(it.description) \n \n")
  243. }
  244. }
  245. wantDrawBottomLine = true
  246. }
  247. }
  248. if member.type == .typeDefinition || member.type == .structDefinition {
  249. let currentObjectName = member.type == .typeDefinition ? member.name : "struct \(member.name)"
  250. let collections = provideRootMarkdownBlock?() ?? []
  251. var relatedTypes = [ElementDefs]()
  252. var relatedFunctions = [ElementDefs]()
  253. var relatedVariables = [ElementDefs]()
  254. for collection in collections {
  255. for file in collection.files {
  256. if file.fileName.hasSuffix(".h") {
  257. for member in file.items {
  258. switch member.type {
  259. case .typeDefinition:
  260. if member.typeImplementation == currentObjectName {
  261. relatedTypes.append(member)
  262. }
  263. break
  264. case .functionDefintion:
  265. if member.functionReturn == currentObjectName {
  266. relatedFunctions.append(member)
  267. break
  268. }
  269. let paramDefs = getFuncParamDef(member)
  270. if paramDefs.contains(where: { $0.type == currentObjectName }) {
  271. relatedFunctions.append(member)
  272. }
  273. break
  274. case .variableDefinition:
  275. if member.variableType == currentObjectName {
  276. relatedVariables.append(member)
  277. }
  278. break
  279. default:
  280. break
  281. }
  282. }
  283. }
  284. }
  285. }
  286. if relatedTypes.count + relatedVariables.count + relatedFunctions.count > 0 {
  287. if wantDrawBottomLine {
  288. markdown.append(" \n-------- \n")
  289. wantDrawBottomLine = false
  290. }
  291. markdown.append("### \("APIDoc.Related".apiDocLocalized(language)) \n")
  292. for it in relatedTypes {
  293. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  294. let comment = it.getLocalizedComment(withKey: nil, language: language)
  295. if !comment.isEmpty {
  296. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  297. } else {
  298. markdown.append("\n")
  299. }
  300. }
  301. for it in relatedFunctions {
  302. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  303. let comment = it.getLocalizedComment(withKey: nil, language: language)
  304. if !comment.isEmpty {
  305. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  306. } else {
  307. markdown.append("\n")
  308. }
  309. }
  310. for it in relatedVariables {
  311. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  312. let comment = it.getLocalizedComment(withKey: nil, language: language)
  313. if !comment.isEmpty {
  314. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  315. } else {
  316. markdown.append("\n")
  317. }
  318. }
  319. wantDrawBottomLine = true
  320. }
  321. }
  322. return markdown
  323. }
  324. }
  325. public class ArticleDocModel: APIDocMarkdownGenerable {
  326. public var navigationItemTitle: String { "" }
  327. public var shareURL: URL? { URL(string: "\(linkPathPrefix)/\(collectionPath)/\(filePath)/__article__") }
  328. public var collectionPath: String
  329. public var linkPathPrefix: String = "https://docs.forgetive.org"
  330. public var language: ElementDefs.SupportedLanguage = .unspecified
  331. public var provideRootMarkdownBlock: ProvideRootMarkdownBlock?
  332. public var filePath: String
  333. public init(collectionPath: String, filePath: String) {
  334. self.filePath = filePath
  335. self.collectionPath = collectionPath
  336. }
  337. public func generateMarkdown() -> String {
  338. var markdown = ""
  339. markdown.append("#### \("APIDoc.Article.Title".apiDocLocalized(language)) (\("APIDoc.BelongsTo".apiDocLocalized(language)) \(getCollectionName()) / <font color=\"\(CLangColorTable.getColorTable().getTypeContentColor().hexString)\"><a href=\"\(buildLinkPath(withMemberName: nil, inFile: filePath, inCollection: collectionPath))\" style=\"color: \(CLangColorTable.getColorTable().getTypeContentColor().hexString)\">\(filePath)</a></font>) \n")
  340. let pathToFile = Bundle.main.resourcePath! + "/CodeAnalyserFile/article/\(filePath).md"
  341. markdown.append((try? String(contentsOfFile: pathToFile)) ?? "")
  342. return markdown
  343. }
  344. }
  345. public class APIDocFileModel: APIDocMarkdownGenerable {
  346. public var language: ElementDefs.SupportedLanguage = .unspecified
  347. public var fileName: String
  348. public var collectionPath: String
  349. public var firstComment: ElementDefs
  350. public var items: [ElementDefs]
  351. public var provideRootMarkdownBlock: ProvideRootMarkdownBlock?
  352. public var linkPathPrefix: String = "https://docs.forgetive.org"
  353. public var shareURL: URL? {
  354. URL(string: "\(linkPathPrefix)/\(collectionPath)/\(fileName)")
  355. }
  356. public var navigationItemTitle: String { fileName }
  357. public convenience init() {
  358. self.init([], firstComment: ElementDefs(withAnyToken: "", comment: ""), withFileName: "", inCollection: "")
  359. }
  360. public init(_ items: [ElementDefs], firstComment: ElementDefs, withFileName file: String, inCollection collectionPath: String) {
  361. self.items = items
  362. self.fileName = file
  363. self.firstComment = firstComment
  364. self.collectionPath = collectionPath
  365. }
  366. public func generateMarkdown() -> String {
  367. var markdown = ""
  368. if fileName.hasSuffix(".h") {
  369. markdown.append("#### \("APIDoc.Header".apiDocLocalized(language)) \n")
  370. }
  371. if fileName.hasSuffix(".c") {
  372. markdown.append("#### \("APIDoc.Source".apiDocLocalized(language)) \n")
  373. }
  374. markdown.append("# \(fileName) \n")
  375. if fileName.hasSuffix(".c") {
  376. markdown.append("<font color='#777777'>\("APIDoc.Source.Warn".apiDocLocalized(language))</font> \n")
  377. }
  378. markdown.append("### \(firstComment.getLocalizedComment(withKey: "filesummary", language: language)) \n")
  379. let fileDescription = firstComment.getLocalizedComment(withKey: "filedescription", language: language)
  380. if !fileDescription.isEmpty {
  381. markdown.append("\(fileDescription) \n")
  382. }
  383. let filePath = Bundle.main.resourcePath! + "/CodeAnalyserFile/article/\(fileName).md"
  384. if ["cdenvc", "stdc"].contains(collectionPath), FileManager.default.fileExists(atPath: filePath) {
  385. markdown.append("### <font color=\"\(CLangColorTable.getColorTable().getTypeContentColor().hexString)\"><a href=\"\(buildLinkPath(withMemberName: "__article__", inFile: fileName, inCollection: collectionPath))\" style=\"color: \(CLangColorTable.getColorTable().getTypeContentColor().hexString)\">\("APIDoc.Article".apiDocLocalized(language))</a></font> \n")
  386. }
  387. markdown.append("-------- \n")
  388. let typeItems = items.filter { $0.type == .typeDefinition }
  389. let funcPtrItems = items.filter { $0.type == .functionPointer }
  390. let structItems = items.filter { $0.type == .structDefinition }
  391. let functionItems = items.filter { $0.type == .functionDefintion }
  392. let variableItems = items.filter { $0.type == .variableDefinition }
  393. var wantDrawBottomLine = false
  394. if !typeItems.isEmpty {
  395. markdown.append("### \("APIDoc.Type".apiDocLocalized(language)) \n")
  396. for it in typeItems {
  397. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  398. let comment = it.getLocalizedComment(withKey: nil, language: language)
  399. if !comment.isEmpty {
  400. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  401. } else {
  402. markdown.append("\n")
  403. }
  404. }
  405. wantDrawBottomLine = true
  406. }
  407. if !funcPtrItems.isEmpty {
  408. markdown.append("### \("APIDoc.FunctionPointer".apiDocLocalized(language)) \n")
  409. for it in funcPtrItems {
  410. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  411. let comment = it.getLocalizedComment(withKey: nil, language: language)
  412. if !comment.isEmpty {
  413. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  414. } else {
  415. markdown.append("\n")
  416. }
  417. }
  418. wantDrawBottomLine = true
  419. }
  420. if !structItems.isEmpty {
  421. if wantDrawBottomLine {
  422. markdown.append(" \n-------- \n")
  423. wantDrawBottomLine = false
  424. }
  425. markdown.append("### \("APIDoc.Struct".apiDocLocalized(language)) \n")
  426. for it in structItems {
  427. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  428. let comment = it.getLocalizedComment(withKey: nil, language: language)
  429. if !comment.isEmpty {
  430. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  431. } else {
  432. markdown.append("\n")
  433. }
  434. }
  435. wantDrawBottomLine = true
  436. }
  437. if !functionItems.isEmpty {
  438. if wantDrawBottomLine {
  439. markdown.append(" \n-------- \n")
  440. wantDrawBottomLine = false
  441. }
  442. markdown.append("### \("APIDoc.Function".apiDocLocalized(language)) \n")
  443. for it in functionItems {
  444. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  445. let comment = it.getLocalizedComment(withKey: nil, language: language)
  446. if !comment.isEmpty {
  447. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  448. } else {
  449. markdown.append("\n")
  450. }
  451. }
  452. wantDrawBottomLine = true
  453. }
  454. if !variableItems.isEmpty {
  455. if wantDrawBottomLine {
  456. markdown.append(" \n-------- \n")
  457. wantDrawBottomLine = false
  458. }
  459. markdown.append("### \("APIDoc.Variable".apiDocLocalized(language)) \n")
  460. for it in variableItems {
  461. markdown.append("&nbsp;&nbsp;\(drawDefinition(forElement: it)) \n")
  462. let comment = it.getLocalizedComment(withKey: nil, language: language)
  463. if !comment.isEmpty {
  464. markdown.append("&nbsp;&nbsp;&nbsp;&nbsp;\(comment) \n\n")
  465. } else {
  466. markdown.append("\n")
  467. }
  468. }
  469. wantDrawBottomLine = true
  470. }
  471. return markdown
  472. }
  473. }
  474. public class APIDocFileCollectionModel {
  475. public var collectionTitle: String
  476. public var collectionStoragePath: String
  477. public var files: [APIDocFileModel]
  478. public convenience init() {
  479. self.init([], withCollectionTitle: "", andCollectionStoragePath: "")
  480. }
  481. public init(_ files: [APIDocFileModel], withCollectionTitle name: String, andCollectionStoragePath path: String) {
  482. self.collectionStoragePath = path
  483. self.files = files
  484. self.collectionTitle = name
  485. }
  486. }
  487. public class APIDocFetcher {
  488. public static let cdEnvCHeaders = ["autotree.h", "ccd.h", "debug.h", "http.h", "ccdui.h", "ccduicomp.h"]
  489. public class func fetch(_ code: String, _ name: String, inCollection collectionPath: String) -> APIDocFileModel {
  490. let doc = APIDocFileModel()
  491. doc.fileName = name
  492. doc.collectionPath = collectionPath
  493. let engine = CodeAnalysisEngine()
  494. var includeSet = [String]()
  495. engine.writeElementList = true
  496. engine.analysis(code, name, nil, &includeSet, false)
  497. doc.items = engine.elementList
  498. doc.firstComment = engine.getFirstComment(code)
  499. return doc
  500. }
  501. public class func fetchAll(withGivenProjectContext project: CCDProject?) -> [APIDocFileCollectionModel] {
  502. let standardCollection = APIDocFileCollectionModel([], withCollectionTitle: "APIDoc.StandardCHeaderName".apiDocLocalized(), andCollectionStoragePath: "stdc")
  503. let cdenvcCollection = APIDocFileCollectionModel([], withCollectionTitle: "APIDoc.ExtendedCHeaderName".apiDocLocalized(), andCollectionStoragePath: "cdenvc")
  504. var collections = [standardCollection, cdenvcCollection]
  505. for it in (try? FileManager.default.contentsOfDirectory(atPath: (Bundle.main.resourcePath ?? "") + "/CodeAnalyserFile"))?.filter({ !$0.hasSuffix("_intrinsic.h") }) ?? [] {
  506. let isFolder = UnsafeMutablePointer<ObjCBool>.allocate(capacity: 1)
  507. FileManager.default.fileExists(atPath: (Bundle.main.resourcePath ?? "") + "/CodeAnalyserFile/" + it, isDirectory: isFolder)
  508. if !isFolder.pointee.boolValue {
  509. let fileName = it
  510. let fileCode = (try? String(contentsOfFile: (Bundle.main.resourcePath ?? "") + "/CodeAnalyserFile/" + it)) ?? ""
  511. let apiDoc = fetch(fileCode, fileName, inCollection: cdEnvCHeaders.contains(fileName) ? "cdenvc" : "stdc")
  512. if cdEnvCHeaders.contains(fileName) {
  513. cdenvcCollection.files.append(apiDoc)
  514. } else {
  515. standardCollection.files.append(apiDoc)
  516. }
  517. }
  518. }
  519. if let project = project {
  520. let currentProjectCollection = APIDocFileCollectionModel([], withCollectionTitle: "\(project.projectName ) \("APIDoc.Current".apiDocLocalized())", andCollectionStoragePath: project.projectName )
  521. for it in project.files.files {
  522. let ifn = it.fileName ?? ""
  523. let ifv = it.fileCode ?? ""
  524. if ifn.hasSuffix(".h") || ifn.hasSuffix(".c") {
  525. currentProjectCollection.files.append(fetch(ifv, ifn, inCollection: project.projectName ))
  526. }
  527. }
  528. collections.append(currentProjectCollection)
  529. }
  530. for it in (try? FileManager.default.contentsOfDirectory(atPath: AppDelegate.documentsDirectory())) ?? [] {
  531. if it == (project?.projectName ?? "") { continue }
  532. let project = CCDProject(openWithName: it)
  533. let currentProjectCollection = APIDocFileCollectionModel([], withCollectionTitle: it, andCollectionStoragePath: it)
  534. let collectionPath = it
  535. if let project = project {
  536. for it in project.files.files {
  537. let ifn = it.fileName ?? ""
  538. let ifv = it.fileCode ?? ""
  539. if ifn.hasSuffix(".h") || ifn.hasSuffix(".c") {
  540. currentProjectCollection.files.append(fetch(ifv, ifn, inCollection: collectionPath))
  541. }
  542. }
  543. }
  544. collections.append(currentProjectCollection)
  545. }
  546. return collections
  547. }
  548. }