diff --git a/Example/Podfile.lock b/Example/Podfile.lock index faeced7..e8b3327 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 96c61efa17d3058e833c2604cf2b449c1d44fa8f -COCOAPODS: 1.8.4 +COCOAPODS: 1.10.0 diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index faeced7..e8b3327 100644 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 96c61efa17d3058e833c2604cf2b449c1d44fa8f -COCOAPODS: 1.8.4 +COCOAPODS: 1.10.0 diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index ef8e1c8..e6d1657 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -7,118 +7,131 @@ objects = { /* Begin PBXBuildFile section */ - 0536DAE2BFB65CC1E6D7C6649FD7EFB7 /* CGFloat+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69A9690509FF5347B2F9DFCF3E9F2C10 /* CGFloat+Formatting.swift */; }; - 0F1AC95398343A8CF891F54CC7BFAE65 /* Dictionary+TypingAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9455CBD61FFB81885A48813278F02D34 /* Dictionary+TypingAttributes.swift */; }; - 18B9CEEDC03524F19A7AEA7C02AC7CCC /* RichEditor+Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0EA0312AC6030E860747A25DC571C6 /* RichEditor+Stack.swift */; }; - 1BA7597F4A23D7C273ACB71FA65DBC79 /* RichEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD461F686F69A1BFB0A2C5813F28AC93 /* RichEditor.swift */; }; - 23BC0A53B7E82CBE56FAB5FE93F72454 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; }; - 30FA46EAE153CB6F25D6BF98983690DE /* RichEditor-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D4D2C78F451C3024F602512CCCAF1FF /* RichEditor-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 313BD9ED3E74B7BF07318C75C7E4B209 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; }; - 3CA198A87B67998867951D1AF37ADA6C /* RichEditor-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 83E51373070107D858809F1858B92F75 /* RichEditor-dummy.m */; }; - 50025A3B35CC646480609CA43E02C312 /* NSMenu+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F00A72D1E8405B7D893897CC7CA91B9 /* NSMenu+Convenience.swift */; }; - 520D7B6078E649241EF5AE352149AC1B /* NSFont+Traits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49272EE7EB4A435C69FF23CC4EE3356E /* NSFont+Traits.swift */; }; - 9BF3F9198BD94616E0C42F0699D95AFE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */; }; - A8836C9983F0524A5E4B072BE09FC94F /* Dictionary+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = A251AEAB57C4F0623E305D7D2E8532AC /* Dictionary+Convenience.swift */; }; - B047CCC24055F178CA45836F188DC624 /* URL+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB0AA13D264AE2AC3F8842D4551C3FA /* URL+Images.swift */; }; - BE01E19623EA675A006448BE /* String+BulletPoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE01E19523EA675A006448BE /* String+BulletPoints.swift */; }; - BE8DA31B23EC05BF003FD3B9 /* RichTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE8DA31A23EC05BF003FD3B9 /* RichTextView.swift */; }; - BECFB61D23E3EEE700544CD5 /* FontStyling.swift in Sources */ = {isa = PBXBuildFile; fileRef = BECFB61C23E3EECD00544CD5 /* FontStyling.swift */; }; - C45D099150C6AD0684B37FC07F26ADFF /* Pods-RichEditor_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B26BDF036DCC80E19475FB78017414B /* Pods-RichEditor_Tests-dummy.m */; }; - CB7ACBA6C79C31828BBC3E446C3019D2 /* String+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE033E35858AB3ED75D20E3EAB4DC0E0 /* String+Convenience.swift */; }; - CD62B3390B5113D97E69AA709C045E42 /* NSAttributedString+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E8EE0559C8F99B7E39836E1FE070F7 /* NSAttributedString+Convenience.swift */; }; - E928BD52A2FA5B5754CFF7FE6BF96FDE /* NSTextView+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5F00060F0CDEB413150FD7A56B6FE4B /* NSTextView+Convenience.swift */; }; - ED78BB100505E34D1356BA2EA468A3A8 /* Pods-RichEditor_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F81BA55F823E056CD3D3D28A8D5D20C1 /* Pods-RichEditor_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EE28395F3119EB766AD91F469011ED9E /* Pods-RichEditor_Example-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D3DC6B5661A9963CFAFB81A6EFF6C06 /* Pods-RichEditor_Example-dummy.m */; }; - F6CAD5FEC417DE0F2B3D1813196A40D9 /* Pods-RichEditor_Tests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F69B63BDDCA09A017AEABEE4574414 /* Pods-RichEditor_Tests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0AF7337A69A0A6F7D39CA20C3DE26C81 /* NSAttributedString+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC2B4BA8B89E845BD76D631366B1FBFE /* NSAttributedString+Convenience.swift */; }; + 15D4E2AC46F31C52C4E0CF7E22E9F20C /* NSFont+Traits.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57136E16C5B4AB9AB7A94E1D4ABB337 /* NSFont+Traits.swift */; }; + 30D9C51F34FFBC23A8FB97DCA0640231 /* RichEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF188972288B2EEB3BF8F7A42C8271C /* RichEditor.swift */; }; + 3231B4EB5A3F9E0127050B4BC50A848F /* CGFloat+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 071B9BF6D8AB167238AA11BCF5A79037 /* CGFloat+Formatting.swift */; }; + 46F18EEA7380DCAA3DFA2A2DBDB7DE47 /* Dictionary+TypingAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5CC592EB1734293957B042968A42A5 /* Dictionary+TypingAttributes.swift */; }; + 496B91CF2C718CE03A198AAD0A722F66 /* NSTextView+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85F09B296ACCCDA94F6190386F2BB35 /* NSTextView+Convenience.swift */; }; + 4C40196C7E0F65E5D455CA5772A775A3 /* URL+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = F244702E91C6E685F5BA2D6987B1FE40 /* URL+Images.swift */; }; + 5A2D4CDF5A0E51D01A5D82360D7E1CC1 /* Pods-RichEditor_Example-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D3DC6B5661A9963CFAFB81A6EFF6C06 /* Pods-RichEditor_Example-dummy.m */; }; + 5C9E64FF8A167B25946A584A1070C2BE /* RichTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9E72101047C7CD34D5282BD1C77BDC /* RichTextView.swift */; }; + 5D63D32A6748C568A673F760D9B1A095 /* RichEditor-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D09BC28AE31D4C8F80B6527BCC7455F /* RichEditor-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 79E2DF02E273DA397678B6A539A47CBF /* Pods-RichEditor_Tests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B26BDF036DCC80E19475FB78017414B /* Pods-RichEditor_Tests-dummy.m */; }; + 80E85A637E7F7A4AD8D5FCA863ABAEE2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1430F003D39D9906881A6AC724B7931 /* Cocoa.framework */; }; + 8874BA765074C42CEB58254AE2800049 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1430F003D39D9906881A6AC724B7931 /* Cocoa.framework */; }; + 8C4870DA051B560F5B0ADC9483484054 /* RichEditor+Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66AF401DD28F49C6BD30A1056C700812 /* RichEditor+Stack.swift */; }; + 90A50D1687A3A559F874FAA916AE1DD7 /* String+BulletPoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFF02483AC606AA7820349786DBF2254 /* String+BulletPoints.swift */; }; + 94CEE51779C852F26F3337870292D67E /* RichEditor-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C4DA4EBB36575E4E9B814A8FCCA85F9 /* RichEditor-dummy.m */; }; + 9A254BED5778147A73492444EBA6C921 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1430F003D39D9906881A6AC724B7931 /* Cocoa.framework */; }; + BE858C64257D2272000A363C /* RichEditor+BulletPoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE858C63257D2272000A363C /* RichEditor+BulletPoints.swift */; }; + BE858C6C257D234D000A363C /* RichEditor+Attachments.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE858C6B257D234D000A363C /* RichEditor+Attachments.swift */; }; + BE858C71257D23A9000A363C /* RichEditor+KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE858C70257D23A9000A363C /* RichEditor+KeyboardShortcuts.swift */; }; + BE9E158A257D1B600091899E /* RichEditor+Links.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9E1589257D1B600091899E /* RichEditor+Links.swift */; }; + BE9E1597257D1E2C0091899E /* RichEditor+Styling.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9E1596257D1E2C0091899E /* RichEditor+Styling.swift */; }; + BE9E15A7257D1EC70091899E /* RichEditor+Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9E15A6257D1EC70091899E /* RichEditor+Core.swift */; }; + CE4E385709B899CCFE8EA8D94F3FB51B /* Dictionary+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02E9D9D841B1BB46A544FC8E16D1384A /* Dictionary+Convenience.swift */; }; + CF5D3BE0876B3482291A53BFCF829B39 /* FontStyling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6094D6B615760AE64B312B7DD1B41D80 /* FontStyling.swift */; }; + D5461764728CE0BC0BADAF5B57A83608 /* NSMenu+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D57E615C9274FE04C6DA7EC71D719B /* NSMenu+Convenience.swift */; }; + D8033D0D61D7DF6DD501BEF6C02FC8C9 /* String+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CB10A22B4382AE30548F7C84C88077E /* String+Convenience.swift */; }; + E36FD18935FD9721274ACE40C527DB70 /* Pods-RichEditor_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F81BA55F823E056CD3D3D28A8D5D20C1 /* Pods-RichEditor_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EA841A2166492D897C0256F520A466DF /* Pods-RichEditor_Tests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F69B63BDDCA09A017AEABEE4574414 /* Pods-RichEditor_Tests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 4A437524988058980C39DD1DD50F45C9 /* PBXContainerItemProxy */ = { + B6416DC58E5EE859558D2DDBF26FCCAA /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 83FFF657196094B71F147D25444AC5E8; - remoteInfo = "Pods-RichEditor_Example"; + remoteGlobalIDString = 369E4AC0D183973D19A4522DC26B1027; + remoteInfo = RichEditor; }; - C63A103C00E4469B94FED29545A837CD /* PBXContainerItemProxy */ = { + FDBF6431CCE754159BA43DA298026204 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 369E4AC0D183973D19A4522DC26B1027; - remoteInfo = RichEditor; + remoteGlobalIDString = 83FFF657196094B71F147D25444AC5E8; + remoteInfo = "Pods-RichEditor_Example"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 04C23FA109C1860D5276E72F7F2FC876 /* RichEditor.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = RichEditor.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 02E9D9D841B1BB46A544FC8E16D1384A /* Dictionary+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "Dictionary+Convenience.swift"; sourceTree = ""; }; + 071B9BF6D8AB167238AA11BCF5A79037 /* CGFloat+Formatting.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "CGFloat+Formatting.swift"; sourceTree = ""; }; 09976142F5FA03FEFDE008740067A034 /* Pods-RichEditor_Tests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RichEditor_Tests-Info.plist"; sourceTree = ""; }; + 0C0DD6EEF4315289FCBDE14AF91C521A /* RichEditor-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RichEditor-prefix.pch"; sourceTree = ""; }; 0DA5CBB531A36D4145FFA13CCDEA7F9F /* Pods-RichEditor_Tests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RichEditor_Tests-acknowledgements.plist"; sourceTree = ""; }; - 18E8EE0559C8F99B7E39836E1FE070F7 /* NSAttributedString+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Convenience.swift"; sourceTree = ""; }; - 1DDD9677F8A6D1D1146C9D7825045650 /* RichEditor.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RichEditor.xcconfig; sourceTree = ""; }; - 20ADEE3AF901163A2015C61F70F7A86C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 19DEDE74A17AAA45396B073BCDE61F03 /* RichEditor.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = RichEditor.modulemap; sourceTree = ""; }; 2168F8B7DB4A93C65C73705E9A7D299C /* Pods-RichEditor_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RichEditor_Tests.release.xcconfig"; sourceTree = ""; }; 2684C158B6E8E6E5728AEEBAF9368BE1 /* Pods-RichEditor_Example-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-RichEditor_Example-acknowledgements.markdown"; sourceTree = ""; }; - 2EB0AA13D264AE2AC3F8842D4551C3FA /* URL+Images.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "URL+Images.swift"; sourceTree = ""; }; + 27D57E615C9274FE04C6DA7EC71D719B /* NSMenu+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSMenu+Convenience.swift"; sourceTree = ""; }; + 301D4308094E7BD12919A4575B04FBE8 /* RichEditor.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RichEditor.release.xcconfig; sourceTree = ""; }; 32B0DA79E5632EBC96B9B618F14557D1 /* Pods-RichEditor_Example-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RichEditor_Example-Info.plist"; sourceTree = ""; }; - 37303EE5B7B7587725CAEFBDB2AAAFC6 /* RichEditor.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = RichEditor.modulemap; sourceTree = ""; }; 3ECEA4E695D78C7DB4E540A8D2E8A5F0 /* Pods_RichEditor_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RichEditor_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 49272EE7EB4A435C69FF23CC4EE3356E /* NSFont+Traits.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSFont+Traits.swift"; sourceTree = ""; }; 4B26BDF036DCC80E19475FB78017414B /* Pods-RichEditor_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-RichEditor_Tests-dummy.m"; sourceTree = ""; }; 4CA10B9349BA650D24EC7F70C0FBB86A /* Pods-RichEditor_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RichEditor_Tests.debug.xcconfig"; sourceTree = ""; }; - 54263055724CEB76E9F92BEFFA4C309D /* RichEditor-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "RichEditor-Info.plist"; sourceTree = ""; }; 59F69B63BDDCA09A017AEABEE4574414 /* Pods-RichEditor_Tests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-RichEditor_Tests-umbrella.h"; sourceTree = ""; }; - 69A9690509FF5347B2F9DFCF3E9F2C10 /* CGFloat+Formatting.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "CGFloat+Formatting.swift"; sourceTree = ""; }; - 6F00A72D1E8405B7D893897CC7CA91B9 /* NSMenu+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSMenu+Convenience.swift"; sourceTree = ""; }; + 6094D6B615760AE64B312B7DD1B41D80 /* FontStyling.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FontStyling.swift; sourceTree = ""; }; + 66AF401DD28F49C6BD30A1056C700812 /* RichEditor+Stack.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RichEditor+Stack.swift"; sourceTree = ""; }; + 6E5CC592EB1734293957B042968A42A5 /* Dictionary+TypingAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "Dictionary+TypingAttributes.swift"; sourceTree = ""; }; 70C449C8BAA1BBA9DAAF4999DA9E7094 /* Pods-RichEditor_Example-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-RichEditor_Example-frameworks.sh"; sourceTree = ""; }; 785E7F5721CA363ABC49BE29448B218C /* Pods_RichEditor_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RichEditor_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 83E51373070107D858809F1858B92F75 /* RichEditor-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RichEditor-dummy.m"; sourceTree = ""; }; + 7CB10A22B4382AE30548F7C84C88077E /* String+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "String+Convenience.swift"; sourceTree = ""; }; + 7EE9B0C7AB56792B82AE6DDFBCB69278 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 8D3DC6B5661A9963CFAFB81A6EFF6C06 /* Pods-RichEditor_Example-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-RichEditor_Example-dummy.m"; sourceTree = ""; }; - 8D4D2C78F451C3024F602512CCCAF1FF /* RichEditor-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RichEditor-umbrella.h"; sourceTree = ""; }; - 9455CBD61FFB81885A48813278F02D34 /* Dictionary+TypingAttributes.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "Dictionary+TypingAttributes.swift"; sourceTree = ""; }; + 981711BDD70430BE43460591AD8A5FB6 /* RichEditor-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "RichEditor-Info.plist"; sourceTree = ""; }; 9B89DEEF181DD4430EBDE56F18C6F196 /* RichEditor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RichEditor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9C4DA4EBB36575E4E9B814A8FCCA85F9 /* RichEditor-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "RichEditor-dummy.m"; sourceTree = ""; }; + 9D09BC28AE31D4C8F80B6527BCC7455F /* RichEditor-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RichEditor-umbrella.h"; sourceTree = ""; }; 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9F170FC9A00FB0FA29EE483E899CD96F /* Pods-RichEditor_Tests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-RichEditor_Tests.modulemap"; sourceTree = ""; }; - A251AEAB57C4F0623E305D7D2E8532AC /* Dictionary+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "Dictionary+Convenience.swift"; sourceTree = ""; }; - AD0EA0312AC6030E860747A25DC571C6 /* RichEditor+Stack.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "RichEditor+Stack.swift"; sourceTree = ""; }; - BE01E19523EA675A006448BE /* String+BulletPoints.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+BulletPoints.swift"; sourceTree = ""; }; - BE8DA31A23EC05BF003FD3B9 /* RichTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RichTextView.swift; sourceTree = ""; }; - BECFB61C23E3EECD00544CD5 /* FontStyling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontStyling.swift; sourceTree = ""; }; - C2BAE0C9C417B1A9DD0B78201E8BA424 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + BCD7588E60EAAF63ECDC95F5C77F0EC3 /* RichEditor.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = RichEditor.debug.xcconfig; sourceTree = ""; }; + BE858C63257D2272000A363C /* RichEditor+BulletPoints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichEditor+BulletPoints.swift"; sourceTree = ""; }; + BE858C6B257D234D000A363C /* RichEditor+Attachments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichEditor+Attachments.swift"; sourceTree = ""; }; + BE858C70257D23A9000A363C /* RichEditor+KeyboardShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichEditor+KeyboardShortcuts.swift"; sourceTree = ""; }; + BE9E1589257D1B600091899E /* RichEditor+Links.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichEditor+Links.swift"; sourceTree = ""; }; + BE9E1596257D1E2C0091899E /* RichEditor+Styling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichEditor+Styling.swift"; sourceTree = ""; }; + BE9E15A6257D1EC70091899E /* RichEditor+Core.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RichEditor+Core.swift"; sourceTree = ""; }; + BFF188972288B2EEB3BF8F7A42C8271C /* RichEditor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RichEditor.swift; sourceTree = ""; }; + C1430F003D39D9906881A6AC724B7931 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; C699BBD208ED791C1AEFF10837E2E44D /* Pods-RichEditor_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RichEditor_Example.release.xcconfig"; sourceTree = ""; }; CA8180738F431CEC7545D3D3B12595BD /* Pods-RichEditor_Tests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-RichEditor_Tests-acknowledgements.markdown"; sourceTree = ""; }; - CE033E35858AB3ED75D20E3EAB4DC0E0 /* String+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "String+Convenience.swift"; sourceTree = ""; }; + CD61309C06A4E8637D52126E2C7DBDAD /* RichEditor.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = RichEditor.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; D18502E295F130AFDDECBBEAE3DCF70D /* Pods-RichEditor_Example.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-RichEditor_Example.modulemap"; sourceTree = ""; }; - D395F611462715F382FF0D95A6EC9866 /* RichEditor-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "RichEditor-prefix.pch"; sourceTree = ""; }; + D57136E16C5B4AB9AB7A94E1D4ABB337 /* NSFont+Traits.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSFont+Traits.swift"; sourceTree = ""; }; + DA9E72101047C7CD34D5282BD1C77BDC /* RichTextView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RichTextView.swift; sourceTree = ""; }; + DFF02483AC606AA7820349786DBF2254 /* String+BulletPoints.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "String+BulletPoints.swift"; sourceTree = ""; }; E75872E2F593B8B6339782EE7E3C5A69 /* Pods-RichEditor_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-RichEditor_Example.debug.xcconfig"; sourceTree = ""; }; - F5F00060F0CDEB413150FD7A56B6FE4B /* NSTextView+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSTextView+Convenience.swift"; sourceTree = ""; }; + EF7D93654EC2B6A6053D8A6648CC905C /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + F244702E91C6E685F5BA2D6987B1FE40 /* URL+Images.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "URL+Images.swift"; sourceTree = ""; }; F81BA55F823E056CD3D3D28A8D5D20C1 /* Pods-RichEditor_Example-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-RichEditor_Example-umbrella.h"; sourceTree = ""; }; - F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; + F85F09B296ACCCDA94F6190386F2BB35 /* NSTextView+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSTextView+Convenience.swift"; sourceTree = ""; }; FB8F192C1DB2AFD68EBD80BF4BCA3EE5 /* Pods-RichEditor_Example-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-RichEditor_Example-acknowledgements.plist"; sourceTree = ""; }; - FD461F686F69A1BFB0A2C5813F28AC93 /* RichEditor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = RichEditor.swift; sourceTree = ""; }; + FC2B4BA8B89E845BD76D631366B1FBFE /* NSAttributedString+Convenience.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Convenience.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 1BCDFD36C63FBC36F7AD099196ECA050 /* Frameworks */ = { + 00284928B797248D8455424F38F6D886 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9BF3F9198BD94616E0C42F0699D95AFE /* Cocoa.framework in Frameworks */, + 8874BA765074C42CEB58254AE2800049 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 1CF0937F9DDF524BE6DAF05A129AE433 /* Frameworks */ = { + 0F4D3B1A269F6876A7091BF57CF3A73F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 313BD9ED3E74B7BF07318C75C7E4B209 /* Cocoa.framework in Frameworks */, + 9A254BED5778147A73492444EBA6C921 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 5E17D4063904328899DF08534605FA5A /* Frameworks */ = { + E060117DA6A110BF4674F1260F2931B8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 23BC0A53B7E82CBE56FAB5FE93F72454 /* Cocoa.framework in Frameworks */, + 80E85A637E7F7A4AD8D5FCA863ABAEE2 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -135,28 +148,47 @@ name = Products; sourceTree = ""; }; - 5D597647C1837016DB5DEE9441C3AC29 /* Extensions */ = { + 2326B4987C3B9AFA94303C8757663FA3 /* RichEditor */ = { isa = PBXGroup; children = ( - 69A9690509FF5347B2F9DFCF3E9F2C10 /* CGFloat+Formatting.swift */, - A251AEAB57C4F0623E305D7D2E8532AC /* Dictionary+Convenience.swift */, - 9455CBD61FFB81885A48813278F02D34 /* Dictionary+TypingAttributes.swift */, - 18E8EE0559C8F99B7E39836E1FE070F7 /* NSAttributedString+Convenience.swift */, - 49272EE7EB4A435C69FF23CC4EE3356E /* NSFont+Traits.swift */, - 6F00A72D1E8405B7D893897CC7CA91B9 /* NSMenu+Convenience.swift */, - F5F00060F0CDEB413150FD7A56B6FE4B /* NSTextView+Convenience.swift */, - CE033E35858AB3ED75D20E3EAB4DC0E0 /* String+Convenience.swift */, - BE01E19523EA675A006448BE /* String+BulletPoints.swift */, - 2EB0AA13D264AE2AC3F8842D4551C3FA /* URL+Images.swift */, - ); - name = Extensions; - path = RichEditor/Extensions; + E8CA886F6C7558FA8FCC7D851E168772 /* Classes */, + 68CA17CA1C702588EF3D3CA3A3EFA36F /* Extensions */, + 478E2D6617B4088F5099C48969BDB7F4 /* Pod */, + 2C73D645CCA75970C55598C750B86FD4 /* Support Files */, + ); + name = RichEditor; + path = ../..; + sourceTree = ""; + }; + 2C73D645CCA75970C55598C750B86FD4 /* Support Files */ = { + isa = PBXGroup; + children = ( + 19DEDE74A17AAA45396B073BCDE61F03 /* RichEditor.modulemap */, + 9C4DA4EBB36575E4E9B814A8FCCA85F9 /* RichEditor-dummy.m */, + 981711BDD70430BE43460591AD8A5FB6 /* RichEditor-Info.plist */, + 0C0DD6EEF4315289FCBDE14AF91C521A /* RichEditor-prefix.pch */, + 9D09BC28AE31D4C8F80B6527BCC7455F /* RichEditor-umbrella.h */, + BCD7588E60EAAF63ECDC95F5C77F0EC3 /* RichEditor.debug.xcconfig */, + 301D4308094E7BD12919A4575B04FBE8 /* RichEditor.release.xcconfig */, + ); + name = "Support Files"; + path = "Example/Pods/Target Support Files/RichEditor"; + sourceTree = ""; + }; + 478E2D6617B4088F5099C48969BDB7F4 /* Pod */ = { + isa = PBXGroup; + children = ( + EF7D93654EC2B6A6053D8A6648CC905C /* LICENSE */, + 7EE9B0C7AB56792B82AE6DDFBCB69278 /* README.md */, + CD61309C06A4E8637D52126E2C7DBDAD /* RichEditor.podspec */, + ); + name = Pod; sourceTree = ""; }; 6592CC5554C50B0B25AE4190F0989C3E /* Development Pods */ = { isa = PBXGroup; children = ( - 75FE472FF7DAE75D8BB3C9836F7F22CF /* RichEditor */, + 2326B4987C3B9AFA94303C8757663FA3 /* RichEditor */, ); name = "Development Pods"; sourceTree = ""; @@ -178,16 +210,21 @@ path = "Target Support Files/Pods-RichEditor_Example"; sourceTree = ""; }; - 6E546CA28221404FB1A52B5CB225081B /* Classes */ = { + 68CA17CA1C702588EF3D3CA3A3EFA36F /* Extensions */ = { isa = PBXGroup; children = ( - BECFB61C23E3EECD00544CD5 /* FontStyling.swift */, - FD461F686F69A1BFB0A2C5813F28AC93 /* RichEditor.swift */, - AD0EA0312AC6030E860747A25DC571C6 /* RichEditor+Stack.swift */, - BE8DA31A23EC05BF003FD3B9 /* RichTextView.swift */, - ); - name = Classes; - path = RichEditor/Classes; + 071B9BF6D8AB167238AA11BCF5A79037 /* CGFloat+Formatting.swift */, + 02E9D9D841B1BB46A544FC8E16D1384A /* Dictionary+Convenience.swift */, + 6E5CC592EB1734293957B042968A42A5 /* Dictionary+TypingAttributes.swift */, + FC2B4BA8B89E845BD76D631366B1FBFE /* NSAttributedString+Convenience.swift */, + D57136E16C5B4AB9AB7A94E1D4ABB337 /* NSFont+Traits.swift */, + 27D57E615C9274FE04C6DA7EC71D719B /* NSMenu+Convenience.swift */, + F85F09B296ACCCDA94F6190386F2BB35 /* NSTextView+Convenience.swift */, + DFF02483AC606AA7820349786DBF2254 /* String+BulletPoints.swift */, + 7CB10A22B4382AE30548F7C84C88077E /* String+Convenience.swift */, + F244702E91C6E685F5BA2D6987B1FE40 /* URL+Images.swift */, + ); + path = Extensions; sourceTree = ""; }; 7238F50B6F69AF8EF70D156F5FF1CD45 /* Targets Support Files */ = { @@ -199,18 +236,6 @@ name = "Targets Support Files"; sourceTree = ""; }; - 75FE472FF7DAE75D8BB3C9836F7F22CF /* RichEditor */ = { - isa = PBXGroup; - children = ( - 6E546CA28221404FB1A52B5CB225081B /* Classes */, - 5D597647C1837016DB5DEE9441C3AC29 /* Extensions */, - D4E930A6A2D4210D14D44C446F02D068 /* Pod */, - DF669FD323EF5ECAE2CD4011DD46C386 /* Support Files */, - ); - name = RichEditor; - path = ../..; - sourceTree = ""; - }; 86229C0FBF81EF5E99ADA6414A9F5340 /* Pods-RichEditor_Tests */ = { isa = PBXGroup; children = ( @@ -227,12 +252,19 @@ path = "Target Support Files/Pods-RichEditor_Tests"; sourceTree = ""; }; - CBD43E0947D94E318A0ED1350CFF29ED /* OS X */ = { + C678D5BB4BA43C96C60AF8BCA0610913 /* RichEditor */ = { isa = PBXGroup; children = ( - F940C819049CFF8741C0F5E3E075E607 /* Cocoa.framework */, - ); - name = "OS X"; + BFF188972288B2EEB3BF8F7A42C8271C /* RichEditor.swift */, + 66AF401DD28F49C6BD30A1056C700812 /* RichEditor+Stack.swift */, + BE858C70257D23A9000A363C /* RichEditor+KeyboardShortcuts.swift */, + BE9E15A6257D1EC70091899E /* RichEditor+Core.swift */, + BE9E1596257D1E2C0091899E /* RichEditor+Styling.swift */, + BE9E1589257D1B600091899E /* RichEditor+Links.swift */, + BE858C63257D2272000A363C /* RichEditor+BulletPoints.swift */, + BE858C6B257D234D000A363C /* RichEditor+Attachments.swift */, + ); + path = RichEditor; sourceTree = ""; }; CF1408CF629C7361332E53B88F7BD30C = { @@ -246,62 +278,57 @@ ); sourceTree = ""; }; - D4E930A6A2D4210D14D44C446F02D068 /* Pod */ = { + E0A1E60606E0BF6E2E10F1F01350DFE8 /* Frameworks */ = { isa = PBXGroup; children = ( - 20ADEE3AF901163A2015C61F70F7A86C /* LICENSE */, - C2BAE0C9C417B1A9DD0B78201E8BA424 /* README.md */, - 04C23FA109C1860D5276E72F7F2FC876 /* RichEditor.podspec */, + E2A8756CA1FF5258A0344D53C5C60EC1 /* OS X */, ); - name = Pod; + name = Frameworks; sourceTree = ""; }; - DF669FD323EF5ECAE2CD4011DD46C386 /* Support Files */ = { + E2A8756CA1FF5258A0344D53C5C60EC1 /* OS X */ = { isa = PBXGroup; children = ( - 37303EE5B7B7587725CAEFBDB2AAAFC6 /* RichEditor.modulemap */, - 1DDD9677F8A6D1D1146C9D7825045650 /* RichEditor.xcconfig */, - 83E51373070107D858809F1858B92F75 /* RichEditor-dummy.m */, - 54263055724CEB76E9F92BEFFA4C309D /* RichEditor-Info.plist */, - D395F611462715F382FF0D95A6EC9866 /* RichEditor-prefix.pch */, - 8D4D2C78F451C3024F602512CCCAF1FF /* RichEditor-umbrella.h */, + C1430F003D39D9906881A6AC724B7931 /* Cocoa.framework */, ); - name = "Support Files"; - path = "Example/Pods/Target Support Files/RichEditor"; + name = "OS X"; sourceTree = ""; }; - E0A1E60606E0BF6E2E10F1F01350DFE8 /* Frameworks */ = { + E8CA886F6C7558FA8FCC7D851E168772 /* Classes */ = { isa = PBXGroup; children = ( - CBD43E0947D94E318A0ED1350CFF29ED /* OS X */, + 6094D6B615760AE64B312B7DD1B41D80 /* FontStyling.swift */, + DA9E72101047C7CD34D5282BD1C77BDC /* RichTextView.swift */, + C678D5BB4BA43C96C60AF8BCA0610913 /* RichEditor */, ); - name = Frameworks; + name = Classes; + path = RichEditor/Classes; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - E8A3EDB5BC273526194E2FA30274AFE9 /* Headers */ = { + 6C226D0D8A4A17DD926C4A9A531DB81D /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - F6CAD5FEC417DE0F2B3D1813196A40D9 /* Pods-RichEditor_Tests-umbrella.h in Headers */, + 5D63D32A6748C568A673F760D9B1A095 /* RichEditor-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - EE7004908186C477508AB6D032001FD1 /* Headers */ = { + 8A397FF18B3138523FC29D14A172C204 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 30FA46EAE153CB6F25D6BF98983690DE /* RichEditor-umbrella.h in Headers */, + EA841A2166492D897C0256F520A466DF /* Pods-RichEditor_Tests-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; - FB79FE10E4878C0824117DA651C132BF /* Headers */ = { + 9691B3D34F00C8E4EA993DEA39E9C381 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - ED78BB100505E34D1356BA2EA468A3A8 /* Pods-RichEditor_Example-umbrella.h in Headers */, + E36FD18935FD9721274ACE40C527DB70 /* Pods-RichEditor_Example-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -310,12 +337,12 @@ /* Begin PBXNativeTarget section */ 369E4AC0D183973D19A4522DC26B1027 /* RichEditor */ = { isa = PBXNativeTarget; - buildConfigurationList = 986F11F221B34D924F753DDF993D38FD /* Build configuration list for PBXNativeTarget "RichEditor" */; + buildConfigurationList = 78ED7F5107AE6F1761901E6645B0BF10 /* Build configuration list for PBXNativeTarget "RichEditor" */; buildPhases = ( - EE7004908186C477508AB6D032001FD1 /* Headers */, - 9295B4C7B1C218E45E06F5A765A35328 /* Sources */, - 1BCDFD36C63FBC36F7AD099196ECA050 /* Frameworks */, - 1FDA3171983A7E1B0C3F79E9B26570F6 /* Resources */, + 6C226D0D8A4A17DD926C4A9A531DB81D /* Headers */, + E6A23D09C1D641AE169AB9DE8D26D885 /* Sources */, + E060117DA6A110BF4674F1260F2931B8 /* Frameworks */, + DF57A2AD6F398286F628272D343D5526 /* Resources */, ); buildRules = ( ); @@ -328,17 +355,17 @@ }; 83FFF657196094B71F147D25444AC5E8 /* Pods-RichEditor_Example */ = { isa = PBXNativeTarget; - buildConfigurationList = DE53115D7979187D22BA060DA9239C43 /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Example" */; + buildConfigurationList = 61F496FD6A347EA730D1B76F3B075C9B /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Example" */; buildPhases = ( - FB79FE10E4878C0824117DA651C132BF /* Headers */, - CC36946270410D38241FA692E602069E /* Sources */, - 5E17D4063904328899DF08534605FA5A /* Frameworks */, - 02D2A99031F2499BD6A403B233A3A288 /* Resources */, + 9691B3D34F00C8E4EA993DEA39E9C381 /* Headers */, + 042D8D369FA7B594F028E87BB9152BCB /* Sources */, + 00284928B797248D8455424F38F6D886 /* Frameworks */, + D53D4DA6D88CE9EBEE9DD605857DC4B0 /* Resources */, ); buildRules = ( ); dependencies = ( - 9B01A94CC581493AD73E678051A81FB8 /* PBXTargetDependency */, + 4837F65686B426832F355C7D5F8DA7CA /* PBXTargetDependency */, ); name = "Pods-RichEditor_Example"; productName = "Pods-RichEditor_Example"; @@ -347,17 +374,17 @@ }; B4E954A1D15CD0CAF78A93E224D871A8 /* Pods-RichEditor_Tests */ = { isa = PBXNativeTarget; - buildConfigurationList = 74D0F7B16314F3BBFDC435C6CFEE3E81 /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Tests" */; + buildConfigurationList = 67446F36A5D0635C480765F07981DC82 /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Tests" */; buildPhases = ( - E8A3EDB5BC273526194E2FA30274AFE9 /* Headers */, - D2529AB9D355BBFA525EB502FFE5C6C5 /* Sources */, - 1CF0937F9DDF524BE6DAF05A129AE433 /* Frameworks */, - 35ED6996B33E11C1D9379A1323D9A122 /* Resources */, + 8A397FF18B3138523FC29D14A172C204 /* Headers */, + 3402377A2B71E4DA4812BD8F85B61841 /* Sources */, + 0F4D3B1A269F6876A7091BF57CF3A73F /* Frameworks */, + D6560009BD65CF324D9D950ED1837EE2 /* Resources */, ); buildRules = ( ); dependencies = ( - C46A865F59A4FBCB547EAD1C0F8DAD6D /* PBXTargetDependency */, + 5D32FF548D672779A6415196557771BC /* PBXTargetDependency */, ); name = "Pods-RichEditor_Tests"; productName = "Pods-RichEditor_Tests"; @@ -371,12 +398,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1100; - LastUpgradeCheck = 1130; - TargetAttributes = { - 83FFF657196094B71F147D25444AC5E8 = { - LastSwiftMigration = 1120; - }; - }; + LastUpgradeCheck = 1220; }; buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 8.0"; @@ -399,21 +421,21 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 02D2A99031F2499BD6A403B233A3A288 /* Resources */ = { + D53D4DA6D88CE9EBEE9DD605857DC4B0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 1FDA3171983A7E1B0C3F79E9B26570F6 /* Resources */ = { + D6560009BD65CF324D9D950ED1837EE2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 35ED6996B33E11C1D9379A1323D9A122 /* Resources */ = { + DF57A2AD6F398286F628272D343D5526 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -423,69 +445,74 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 9295B4C7B1C218E45E06F5A765A35328 /* Sources */ = { + 042D8D369FA7B594F028E87BB9152BCB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0536DAE2BFB65CC1E6D7C6649FD7EFB7 /* CGFloat+Formatting.swift in Sources */, - BECFB61D23E3EEE700544CD5 /* FontStyling.swift in Sources */, - A8836C9983F0524A5E4B072BE09FC94F /* Dictionary+Convenience.swift in Sources */, - 0F1AC95398343A8CF891F54CC7BFAE65 /* Dictionary+TypingAttributes.swift in Sources */, - CD62B3390B5113D97E69AA709C045E42 /* NSAttributedString+Convenience.swift in Sources */, - 520D7B6078E649241EF5AE352149AC1B /* NSFont+Traits.swift in Sources */, - 50025A3B35CC646480609CA43E02C312 /* NSMenu+Convenience.swift in Sources */, - E928BD52A2FA5B5754CFF7FE6BF96FDE /* NSTextView+Convenience.swift in Sources */, - 18B9CEEDC03524F19A7AEA7C02AC7CCC /* RichEditor+Stack.swift in Sources */, - 3CA198A87B67998867951D1AF37ADA6C /* RichEditor-dummy.m in Sources */, - 1BA7597F4A23D7C273ACB71FA65DBC79 /* RichEditor.swift in Sources */, - BE8DA31B23EC05BF003FD3B9 /* RichTextView.swift in Sources */, - CB7ACBA6C79C31828BBC3E446C3019D2 /* String+Convenience.swift in Sources */, - B047CCC24055F178CA45836F188DC624 /* URL+Images.swift in Sources */, - BE01E19623EA675A006448BE /* String+BulletPoints.swift in Sources */, + 5A2D4CDF5A0E51D01A5D82360D7E1CC1 /* Pods-RichEditor_Example-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - CC36946270410D38241FA692E602069E /* Sources */ = { + 3402377A2B71E4DA4812BD8F85B61841 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - EE28395F3119EB766AD91F469011ED9E /* Pods-RichEditor_Example-dummy.m in Sources */, + 79E2DF02E273DA397678B6A539A47CBF /* Pods-RichEditor_Tests-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - D2529AB9D355BBFA525EB502FFE5C6C5 /* Sources */ = { + E6A23D09C1D641AE169AB9DE8D26D885 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C45D099150C6AD0684B37FC07F26ADFF /* Pods-RichEditor_Tests-dummy.m in Sources */, + 3231B4EB5A3F9E0127050B4BC50A848F /* CGFloat+Formatting.swift in Sources */, + CE4E385709B899CCFE8EA8D94F3FB51B /* Dictionary+Convenience.swift in Sources */, + BE9E15A7257D1EC70091899E /* RichEditor+Core.swift in Sources */, + BE858C6C257D234D000A363C /* RichEditor+Attachments.swift in Sources */, + 46F18EEA7380DCAA3DFA2A2DBDB7DE47 /* Dictionary+TypingAttributes.swift in Sources */, + BE9E1597257D1E2C0091899E /* RichEditor+Styling.swift in Sources */, + CF5D3BE0876B3482291A53BFCF829B39 /* FontStyling.swift in Sources */, + 0AF7337A69A0A6F7D39CA20C3DE26C81 /* NSAttributedString+Convenience.swift in Sources */, + 15D4E2AC46F31C52C4E0CF7E22E9F20C /* NSFont+Traits.swift in Sources */, + D5461764728CE0BC0BADAF5B57A83608 /* NSMenu+Convenience.swift in Sources */, + 496B91CF2C718CE03A198AAD0A722F66 /* NSTextView+Convenience.swift in Sources */, + 8C4870DA051B560F5B0ADC9483484054 /* RichEditor+Stack.swift in Sources */, + 94CEE51779C852F26F3337870292D67E /* RichEditor-dummy.m in Sources */, + BE9E158A257D1B600091899E /* RichEditor+Links.swift in Sources */, + BE858C64257D2272000A363C /* RichEditor+BulletPoints.swift in Sources */, + 30D9C51F34FFBC23A8FB97DCA0640231 /* RichEditor.swift in Sources */, + 5C9E64FF8A167B25946A584A1070C2BE /* RichTextView.swift in Sources */, + 90A50D1687A3A559F874FAA916AE1DD7 /* String+BulletPoints.swift in Sources */, + D8033D0D61D7DF6DD501BEF6C02FC8C9 /* String+Convenience.swift in Sources */, + BE858C71257D23A9000A363C /* RichEditor+KeyboardShortcuts.swift in Sources */, + 4C40196C7E0F65E5D455CA5772A775A3 /* URL+Images.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 9B01A94CC581493AD73E678051A81FB8 /* PBXTargetDependency */ = { + 4837F65686B426832F355C7D5F8DA7CA /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = RichEditor; target = 369E4AC0D183973D19A4522DC26B1027 /* RichEditor */; - targetProxy = C63A103C00E4469B94FED29545A837CD /* PBXContainerItemProxy */; + targetProxy = B6416DC58E5EE859558D2DDBF26FCCAA /* PBXContainerItemProxy */; }; - C46A865F59A4FBCB547EAD1C0F8DAD6D /* PBXTargetDependency */ = { + 5D32FF548D672779A6415196557771BC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "Pods-RichEditor_Example"; target = 83FFF657196094B71F147D25444AC5E8 /* Pods-RichEditor_Example */; - targetProxy = 4A437524988058980C39DD1DD50F45C9 /* PBXContainerItemProxy */; + targetProxy = FDBF6431CCE754159BA43DA298026204 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 1EEE4866EADDD97B584AC0916AA71226 /* Release */ = { + 74EAAAC8E659A2ED904EE4A4B7D3F0AD /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 2168F8B7DB4A93C65C73705E9A7D299C /* Pods-RichEditor_Tests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; @@ -495,7 +522,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; @@ -514,82 +540,11 @@ }; name = Release; }; - 52A5437B4C24F2B73C549A76D2B5C124 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C699BBD208ED791C1AEFF10837E2E44D /* Pods-RichEditor_Example.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MACOSX_DEPLOYMENT_TARGET = 10.13; - MODULEMAP_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 762FF63D10ACA98ACF339ECA6796B16C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 4CA10B9349BA650D24EC7F70C0FBB86A /* Pods-RichEditor_Tests.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests-Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MACOSX_DEPLOYMENT_TARGET = 10.13; - MODULEMAP_FILE = "Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 9382317ED75CFC741F429FC224405042 /* Debug */ = { + 7599BD86FD28576C20F8B9B6B5ACBEC9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -612,6 +567,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -650,12 +606,12 @@ }; name = Debug; }; - ABEB5129A2136CCCBA79FAE7B1F6F8B6 /* Debug */ = { + B450B37363839269F597D694BFFA4613 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1DDD9677F8A6D1D1146C9D7825045650 /* RichEditor.xcconfig */; + baseConfigurationReference = C699BBD208ED791C1AEFF10837E2E44D /* Pods-RichEditor_Example.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; @@ -665,32 +621,30 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - GCC_PREFIX_HEADER = "Target Support Files/RichEditor/RichEditor-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/RichEditor/RichEditor-Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.13; - MODULEMAP_FILE = "Target Support Files/RichEditor/RichEditor.modulemap"; - PRODUCT_MODULE_NAME = RichEditor; - PRODUCT_NAME = RichEditor; + MODULEMAP_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - B5DFD60355098EEB305C86C8CA9E66C6 /* Debug */ = { + BB3A0B2F3A2BAAE151452811B3C66F77 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E75872E2F593B8B6339782EE7E3C5A69 /* Pods-RichEditor_Example.debug.xcconfig */; + baseConfigurationReference = 4CA10B9349BA650D24EC7F70C0FBB86A /* Pods-RichEditor_Tests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; @@ -700,13 +654,12 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.13; - MODULEMAP_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.modulemap"; + MODULEMAP_FILE = "Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.modulemap"; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; @@ -714,17 +667,47 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + BFA7E768508C1CD71E105FDF1FBA5FE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BCD7588E60EAAF63ECDC95F5C77F0EC3 /* RichEditor.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/RichEditor/RichEditor-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/RichEditor/RichEditor-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MODULEMAP_FILE = "Target Support Files/RichEditor/RichEditor.modulemap"; + PRODUCT_MODULE_NAME = RichEditor; + PRODUCT_NAME = RichEditor; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; - E4DEAC9B52796426D2A37E7074A1738D /* Release */ = { + C0AB89DB0533D8E2B9201FA6579824B7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -747,6 +730,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -780,12 +764,11 @@ }; name = Release; }; - F609204835396D581A45242019AF954D /* Release */ = { + C72AA466519F8642A8AE1C8BA9421DC8 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1DDD9677F8A6D1D1146C9D7825045650 /* RichEditor.xcconfig */; + baseConfigurationReference = 301D4308094E7BD12919A4575B04FBE8 /* RichEditor.release.xcconfig */; buildSettings = { CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; @@ -795,7 +778,6 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; GCC_PREFIX_HEADER = "Target Support Files/RichEditor/RichEditor-prefix.pch"; INFOPLIST_FILE = "Target Support Files/RichEditor/RichEditor-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -813,41 +795,74 @@ }; name = Release; }; + C809254A49BCD0F438D084AC99B3C79E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E75872E2F593B8B6339782EE7E3C5A69 /* Pods-RichEditor_Example.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MODULEMAP_FILE = "Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { isa = XCConfigurationList; buildConfigurations = ( - 9382317ED75CFC741F429FC224405042 /* Debug */, - E4DEAC9B52796426D2A37E7074A1738D /* Release */, + 7599BD86FD28576C20F8B9B6B5ACBEC9 /* Debug */, + C0AB89DB0533D8E2B9201FA6579824B7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 74D0F7B16314F3BBFDC435C6CFEE3E81 /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Tests" */ = { + 61F496FD6A347EA730D1B76F3B075C9B /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Example" */ = { isa = XCConfigurationList; buildConfigurations = ( - 762FF63D10ACA98ACF339ECA6796B16C /* Debug */, - 1EEE4866EADDD97B584AC0916AA71226 /* Release */, + C809254A49BCD0F438D084AC99B3C79E /* Debug */, + B450B37363839269F597D694BFFA4613 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 986F11F221B34D924F753DDF993D38FD /* Build configuration list for PBXNativeTarget "RichEditor" */ = { + 67446F36A5D0635C480765F07981DC82 /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( - ABEB5129A2136CCCBA79FAE7B1F6F8B6 /* Debug */, - F609204835396D581A45242019AF954D /* Release */, + BB3A0B2F3A2BAAE151452811B3C66F77 /* Debug */, + 74EAAAC8E659A2ED904EE4A4B7D3F0AD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - DE53115D7979187D22BA060DA9239C43 /* Build configuration list for PBXNativeTarget "Pods-RichEditor_Example" */ = { + 78ED7F5107AE6F1761901E6645B0BF10 /* Build configuration list for PBXNativeTarget "RichEditor" */ = { isa = XCConfigurationList; buildConfigurations = ( - B5DFD60355098EEB305C86C8CA9E66C6 /* Debug */, - 52A5437B4C24F2B73C549A76D2B5C124 /* Release */, + BFA7E768508C1CD71E105FDF1FBA5FE5 /* Debug */, + C72AA466519F8642A8AE1C8BA9421DC8 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-frameworks.sh b/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-frameworks.sh index 3bdcb9e..d90f441 100755 --- a/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-frameworks.sh +++ b/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example-frameworks.sh @@ -19,9 +19,8 @@ mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 # This protects against multiple targets copying the same framework dependency at the same time. The solution # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html @@ -45,9 +44,19 @@ install_framework() source="$(readlink "${source}")" fi + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" local basename basename="$(basename -s .framework "$1")" @@ -80,69 +89,52 @@ install_framework() done fi } - # Copies and strips a vendored dSYM install_dsym() { local source="$1" + warn_missing_arch=${2:-true} if [ -r "$source" ]; then - # Copy the dSYM into a the targets temp dir. + # Copy the dSYM into the targets temp dir. echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" local basename - basename="$(basename -s .framework.dSYM "$source")" - binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" - # Strip invalid architectures so "fat" simulator / device frameworks work on device + # Strip invalid architectures from the dSYM. if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then - strip_invalid_archs "$binary" + strip_invalid_archs "$binary" "$warn_missing_arch" fi - - if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" else # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" fi fi } -# Copies the bcsymbolmap files of a vendored framework -install_bcsymbolmap() { - local bcsymbolmap_path="$1" - local destination="${BUILT_PRODUCTS_DIR}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identity - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 # Strip invalid architectures strip_invalid_archs() { binary="$1" + warn_missing_arch=${2:-true} # Get architectures for current target binary binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" # Intersect them with the architectures we are building for intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" # If there are no archs supported by this binary then warn the user if [[ -z "$intersected_archs" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - STRIP_BINARY_RETVAL=0 + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 return fi stripped="" @@ -156,9 +148,31 @@ strip_invalid_archs() { if [[ "$stripped" ]]; then echo "Stripped $binary of architectures:$stripped" fi - STRIP_BINARY_RETVAL=1 + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" } +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} if [[ "$CONFIGURATION" == "Debug" ]]; then install_framework "${BUILT_PRODUCTS_DIR}/RichEditor/RichEditor.framework" diff --git a/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.debug.xcconfig b/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.debug.xcconfig index f0df671..84880a3 100644 --- a/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.debug.xcconfig +++ b/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.debug.xcconfig @@ -1,4 +1,5 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor/RichEditor.framework/Headers" @@ -9,4 +10,5 @@ PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.release.xcconfig b/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.release.xcconfig index f0df671..84880a3 100644 --- a/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.release.xcconfig +++ b/Example/Pods/Target Support Files/Pods-RichEditor_Example/Pods-RichEditor_Example.release.xcconfig @@ -1,4 +1,5 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor/RichEditor.framework/Headers" @@ -9,4 +10,5 @@ PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.debug.xcconfig b/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.debug.xcconfig index b82eb79..9c66d65 100644 --- a/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.debug.xcconfig +++ b/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.debug.xcconfig @@ -1,3 +1,4 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor/RichEditor.framework/Headers" @@ -6,4 +7,5 @@ PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.release.xcconfig b/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.release.xcconfig index b82eb79..9c66d65 100644 --- a/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.release.xcconfig +++ b/Example/Pods/Target Support Files/Pods-RichEditor_Tests/Pods-RichEditor_Tests.release.xcconfig @@ -1,3 +1,4 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RichEditor/RichEditor.framework/Headers" @@ -6,4 +7,5 @@ PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/RichEditor/RichEditor.debug.xcconfig b/Example/Pods/Target Support Files/RichEditor/RichEditor.debug.xcconfig new file mode 100644 index 0000000..049947f --- /dev/null +++ b/Example/Pods/Target Support Files/RichEditor/RichEditor.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CODE_SIGN_IDENTITY = +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RichEditor +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/Pods/Target Support Files/RichEditor/RichEditor.release.xcconfig b/Example/Pods/Target Support Files/RichEditor/RichEditor.release.xcconfig new file mode 100644 index 0000000..049947f --- /dev/null +++ b/Example/Pods/Target Support Files/RichEditor/RichEditor.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CODE_SIGN_IDENTITY = +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RichEditor +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Example/RichEditor.xcodeproj/project.pbxproj b/Example/RichEditor.xcodeproj/project.pbxproj index 21ed0ad..3431a5e 100644 --- a/Example/RichEditor.xcodeproj/project.pbxproj +++ b/Example/RichEditor.xcodeproj/project.pbxproj @@ -195,7 +195,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 1130; + LastUpgradeCheck = 1220; ORGANIZATIONNAME = CocoaPods; TargetAttributes = { ED83F67720348A760038D96B = { @@ -379,6 +379,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -438,6 +439,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/Example/RichEditor.xcodeproj/xcshareddata/xcschemes/RichEditor-Example.xcscheme b/Example/RichEditor.xcodeproj/xcshareddata/xcschemes/RichEditor-Example.xcscheme index a0e5eb1..f33e8ad 100644 --- a/Example/RichEditor.xcodeproj/xcshareddata/xcschemes/RichEditor-Example.xcscheme +++ b/Example/RichEditor.xcodeproj/xcshareddata/xcschemes/RichEditor-Example.xcscheme @@ -1,6 +1,6 @@ [Any] - { - var allValues = [Any]() - let fullRange = self.string.fullRange - let options = NSAttributedString.EnumerationOptions.longestEffectiveRangeNotRequired - - self.enumerateAttribute(attribute, in: fullRange, options: options, using: {(valueOpt, range, stop) in - if let value = valueOpt { - allValues.append(value) - } - }) - - return allValues - } - /** Calculates all the various fonts that exist within this NSAttributedString - returns: All NSFonts that are used in this NSAttributedString */ - public func allFonts() -> [NSFont] - { + var allFonts: [NSFont] { var fonts = [NSFont]() self.enumerateAttribute(NSAttributedString.Key.font, in: self.string.fullRange, options: .longestEffectiveRangeNotRequired, using: {(value, range, stop) in let font = value as! NSFont @@ -59,8 +37,7 @@ extension NSAttributedString Calculates all the text colours that are used in this attributed string - returns: An array of NSColors, representing all the text colours in this attributed string */ - public func allTextColours() -> [NSColor] - { + var allTextColours: [NSColor] { var colours = [NSColor]() self.enumerateAttribute(.foregroundColor, in: self.string.fullRange, options: .longestEffectiveRangeNotRequired, using: {(value, range, finished) in if value != nil { @@ -78,8 +55,7 @@ extension NSAttributedString return colours } - public func allHighlightColours() -> [NSColor] - { + var allHighlightColours: [NSColor] { var colours = [NSColor]() self.enumerateAttribute(.backgroundColor, in: self.string.fullRange, options: .longestEffectiveRangeNotRequired, using: {(value, range, finished) in if value != nil { @@ -101,8 +77,7 @@ extension NSAttributedString Calculates all the NSTextAttachments that are contained in this attributed string - returns: An array of NSTextAttachments, representing all the attachments in this attributed string */ - public func allAttachments() -> [NSTextAttachment] - { + var allAttachments: [NSTextAttachment] { var attachments = [NSTextAttachment]() self.enumerateAttribute(.attachment, in: self.string.fullRange, options: .longestEffectiveRangeNotRequired, using: {(value, range, finished) in if value != nil { @@ -115,6 +90,27 @@ extension NSAttributedString return attachments } + //MARK: - Basic Attribute Fetching + /** + Collects all the types of the attribute that we're after + - parameter attribute: The NSAttributedString.Key values we're searching for + - returns: An array of all the values that correlated with the provided attribute key + */ + fileprivate func all(of attribute: NSAttributedString.Key) -> [Any] + { + var allValues = [Any]() + let fullRange = self.string.fullRange + let options = NSAttributedString.EnumerationOptions.longestEffectiveRangeNotRequired + + self.enumerateAttribute(attribute, in: fullRange, options: options, using: {(valueOpt, range, stop) in + if let value = valueOpt { + allValues.append(value) + } + }) + + return allValues + } + //MARK: - Attribute Checking /** Iterates over every font that exists within this NSAttributedString, and checks if any of the fonts contain the desired NSFontTraitMask diff --git a/RichEditor/Extensions/NSFont+Traits.swift b/Extensions/NSFont+Traits.swift similarity index 100% rename from RichEditor/Extensions/NSFont+Traits.swift rename to Extensions/NSFont+Traits.swift diff --git a/RichEditor/Extensions/NSMenu+Convenience.swift b/Extensions/NSMenu+Convenience.swift similarity index 100% rename from RichEditor/Extensions/NSMenu+Convenience.swift rename to Extensions/NSMenu+Convenience.swift diff --git a/RichEditor/Extensions/NSTextView+Convenience.swift b/Extensions/NSTextView+Convenience.swift similarity index 95% rename from RichEditor/Extensions/NSTextView+Convenience.swift rename to Extensions/NSTextView+Convenience.swift index 7594cd1..ac3375c 100644 --- a/RichEditor/Extensions/NSTextView+Convenience.swift +++ b/Extensions/NSTextView+Convenience.swift @@ -9,8 +9,14 @@ import Foundation import AppKit -extension NSTextView -{ +extension NSTextView { + + struct LineInfo { + let lineNumber: Int + let lineRange: NSRange + let lineString: String + } + /** Determines if the user has selected (ie. highlighted) any text - returns: A boolean value indicative of if any text is selected @@ -23,6 +29,59 @@ extension NSTextView return self.selectedRange().location } + /** + Calculates which line number (using a 0 based index) our caret is on, the range of this line (in comparison to the whole string), and the string that makes up that line of text. + Will return nil if there is no caret present, and a portion of text is highlighted instead. + + A pain point of this function is that it cannot return the current line number when it's found, but rather + has to wait for every single line to be iterated through first. This is because the enumerateSubstrings() function + on the String is not an actual loop, and as such we cannot return or break within it. + + - returns: The line number that the caret is on, the range of our line, and the string that makes up that line of text + */ + var currentLine: LineInfo { + //The line number that we're currently iterating on + var lineNumber = 0 + + //The line number & line of text that we believe the caret to be on + var selectedLineNumber = 0 + var selectedLineRange = NSRange(location: 0, length: 0) + var selectedLineOfText = "" + + var foundSelectedLine = false + + //Iterate over every line in our TextView + self.string.enumerateSubstrings(in: self.string.startIndex..= startOfLine && self.caretLocation <= endOfLine { + //Mark the line number + selectedLineNumber = lineNumber + selectedLineOfText = substring ?? "" + selectedLineRange = range + + foundSelectedLine = true + } + + lineNumber += 1 + } + + //If we're not at the starting point, and we didn't find a current line, then we're at the end of our TextView + if self.caretLocation > 0 && !foundSelectedLine { + selectedLineNumber = lineNumber + selectedLineOfText = "" + selectedLineRange = NSRange(location: self.caretLocation, length: 0) + } + + return LineInfo(lineNumber: selectedLineNumber, lineRange: selectedLineRange, lineString: selectedLineOfText) + } + /** Replaces the current NSString/NSAttributedString that is currently within the NSTextView and replaces it with the provided HTML string. @@ -73,7 +132,7 @@ extension NSTextView public func iterateThroughAllAttachments() { - let attachments = self.attributedString().allAttachments() + let attachments = self.attributedString().allAttachments for attachment in attachments { guard let fileWrapper = attachment.fileWrapper else { continue @@ -96,61 +155,7 @@ extension NSTextView print("") } } - - /** - Calculates which line number (using a 0 based index) our caret is on, the range of this line (in comparison to the whole string), and the string that makes up that line of text. - Will return nil if there is no caret present, and a portion of text is highlighted instead. - - A pain point of this function is that it cannot return the current line number when it's found, but rather - has to wait for every single line to be iterated through first. This is because the enumerateSubstrings() function - on the String is not an actual loop, and as such we cannot return or break within it. - - - returns: The line number that the caret is on, the range of our line, and the string that makes up that line of text - */ - func currentLine() -> (lineNumber: Int, lineRange: NSRange, lineString: String) - { - //The line number that we're currently iterating on - var lineNumber = 0 - //The line number & line of text that we believe the caret to be on - var selectedLineNumber = 0 - var selectedLineRange = NSRange(location: 0, length: 0) - var selectedLineOfText = "" - - var foundSelectedLine = false - - //Iterate over every line in our TextView - self.string.enumerateSubstrings(in: self.string.startIndex..= startOfLine && self.caretLocation <= endOfLine { - //Mark the line number - selectedLineNumber = lineNumber - selectedLineOfText = substring ?? "" - selectedLineRange = range - - foundSelectedLine = true - } - - lineNumber += 1 - } - - //If we're not at the starting point, and we didn't find a current line, then we're at the end of our TextView - if self.caretLocation > 0 && !foundSelectedLine { - selectedLineNumber = lineNumber - selectedLineOfText = "" - selectedLineRange = NSRange(location: self.caretLocation, length: 0) - } - - return (selectedLineNumber, selectedLineRange, selectedLineOfText) - } - public func append(_ string: String) { let textViewText = NSMutableAttributedString(attributedString: self.attributedString()) diff --git a/RichEditor/Extensions/String+BulletPoints.swift b/Extensions/String+BulletPoints.swift similarity index 100% rename from RichEditor/Extensions/String+BulletPoints.swift rename to Extensions/String+BulletPoints.swift diff --git a/RichEditor/Extensions/String+Convenience.swift b/Extensions/String+Convenience.swift similarity index 100% rename from RichEditor/Extensions/String+Convenience.swift rename to Extensions/String+Convenience.swift diff --git a/RichEditor/Extensions/URL+Images.swift b/Extensions/URL+Images.swift similarity index 100% rename from RichEditor/Extensions/URL+Images.swift rename to Extensions/URL+Images.swift diff --git a/RichEditor/Classes/FontStyling.swift b/RichEditor/Classes/FontStyling.swift index 6c5f13f..3c7d0be 100644 --- a/RichEditor/Classes/FontStyling.swift +++ b/RichEditor/Classes/FontStyling.swift @@ -35,10 +35,85 @@ public struct FontStyling public fileprivate(set) var textColours : [NSColor] public fileprivate(set) var highlightColours: [NSColor] + public var boldTrait: FontTrait { + //If we're ONLY bold + if self.isBold && !self.isUnbold { + return FontTrait.hasTrait + } + + //If we're ONLY unbold + else if !self.isBold && self.isUnbold { + return FontTrait.hasNoTrait + } + + //If we're BOTH bold and unbold + else if self.isBold && self.isUnbold { + return FontTrait.both + } + + fatalError("Failed to reach conclusion for BoldTrait, for FontStyling: \(self)") + } + + public var italicsTrait: FontTrait { + //If we're ONLY italic + if self.isItalic && !self.isUnitalic { + return FontTrait.hasTrait + } + + //If we're ONLY unitalic + else if !self.isItalic && self.isUnitalic { + return FontTrait.hasNoTrait + } + + //If we're BOTH italic and unitalic + else if self.isItalic && self.isUnitalic { + return FontTrait.both + } + + fatalError("Failed to reach conclusion for ItalicTrait, for FontStyling: \(self)") + } + + public var underlineTrait: FontTrait { + //If we're ONLY underline + if self.isUnderline && !self.isUnunderline { + return FontTrait.hasTrait + } + + //If we're ONLY un-underline + else if !self.isUnderline && self.isUnunderline { + return FontTrait.hasNoTrait + } + + //If we're BOTH underline and un-underline + else if self.isUnderline && self.isUnunderline { + return FontTrait.both + } + + fatalError("Failed to reach conclusion for UnderlineTrait, for FontStyling: \(self)") + } + + public var strikethroughTrait: FontTrait { + //If we're ONLY strikethrough + if self.isStrikethrough && !self.isUnstrikethrough { + return FontTrait.hasTrait + } + + //If we're ONLY un-strikethrough + else if !self.isStrikethrough && self.isUnstrikethrough { + return FontTrait.hasNoTrait + } + + //If we're BOTH strikethrough and un-strikethrough + else if self.isStrikethrough && self.isUnstrikethrough { + return FontTrait.both + } + + fatalError("Failed to reach conclusion for StrikethroughTrait, for FontStyling: \(self)") + } + //MARK: - FontStyling - init(attributedString: NSAttributedString) - { - self.textColours = attributedString.allTextColours() + init(attributedString: NSAttributedString) { + self.textColours = attributedString.allTextColours self.isBold = attributedString.contains(trait: .boldFontMask) self.isUnbold = attributedString.doesNotContain(trait: .boldFontMask) @@ -58,14 +133,13 @@ public struct FontStyling self.isStrikethrough = strikethroughQualities.atParts self.isUnstrikethrough = strikethroughQualities.notAtParts - self.textColours = attributedString.allTextColours() - self.highlightColours = attributedString.allHighlightColours() + self.textColours = attributedString.allTextColours + self.highlightColours = attributedString.allHighlightColours - self.fonts = attributedString.allFonts() + self.fonts = attributedString.allFonts } - init(typingAttributes: [NSAttributedString.Key: Any]) - { + init(typingAttributes: [NSAttributedString.Key: Any]) { let font = typingAttributes[NSAttributedString.Key.font] as! NSFont self.isBold = font.contains(trait: .boldFontMask) @@ -110,111 +184,30 @@ public struct FontStyling - parameter nsFontTraitMask: The NSFontTraitMask that we need to match to a FontTrait enum - returns: A FontTrait enum that will correlate with the NSFontTraitMask */ - public func fontTraitFor(nsFontTraitMask: NSFontTraitMask) -> FontTrait - { + public func fontTraitFor(nsFontTraitMask: NSFontTraitMask) -> FontTrait { switch (nsFontTraitMask) { case .boldFontMask: - return self.boldTrait() + return self.boldTrait case .italicFontMask: - return self.italicsTrait() + return self.italicsTrait default: fatalError("Failed to reach conclusion for determining correlating FontTrait and NSFontTraitMask. NSFontTraitMask: \(nsFontTraitMask)") } } - public func boldTrait() -> FontTrait - { - //If we're ONLY bold - if self.isBold && !self.isUnbold { - return FontTrait.hasTrait - } - - //If we're ONLY unbold - else if !self.isBold && self.isUnbold { - return FontTrait.hasNoTrait - } - - //If we're BOTH bold and unbold - else if self.isBold && self.isUnbold { - return FontTrait.both - } - - fatalError("Failed to reach conclusion for BoldTrait, for FontStyling: \(self)") - } - - public func italicsTrait() -> FontTrait - { - //If we're ONLY italic - if self.isItalic && !self.isUnitalic { - return FontTrait.hasTrait - } - - //If we're ONLY unitalic - else if !self.isItalic && self.isUnitalic { - return FontTrait.hasNoTrait - } - - //If we're BOTH italic and unitalic - else if self.isItalic && self.isUnitalic { - return FontTrait.both - } - - fatalError("Failed to reach conclusion for ItalicTrait, for FontStyling: \(self)") - } - - public func underlineTrait() -> FontTrait - { - //If we're ONLY underline - if self.isUnderline && !self.isUnunderline { - return FontTrait.hasTrait - } - - //If we're ONLY un-underline - else if !self.isUnderline && self.isUnunderline { - return FontTrait.hasNoTrait - } - - //If we're BOTH underline and un-underline - else if self.isUnderline && self.isUnunderline { - return FontTrait.both - } - - fatalError("Failed to reach conclusion for UnderlineTrait, for FontStyling: \(self)") - } - - public func strikethroughTrait() -> FontTrait - { - //If we're ONLY strikethrough - if self.isStrikethrough && !self.isUnstrikethrough { - return FontTrait.hasTrait - } - - //If we're ONLY un-strikethrough - else if !self.isStrikethrough && self.isUnstrikethrough { - return FontTrait.hasNoTrait - } - - //If we're BOTH strikethrough and un-strikethrough - else if self.isStrikethrough && self.isUnstrikethrough { - return FontTrait.both - } - - fatalError("Failed to reach conclusion for StrikethroughTrait, for FontStyling: \(self)") - } - - public func trait(with key: NSAttributedString.Key) -> FontTrait - { + public func trait(with key: NSAttributedString.Key) -> FontTrait { switch (key) { case .strikethroughStyle: - return self.strikethroughTrait() + return self.strikethroughTrait case .underlineStyle: - return self.underlineTrait() + return self.underlineTrait default: fatalError("NSAttributedString.Key has not been accounted for in FontTrait determination: \(key).") } } + } diff --git a/RichEditor/Classes/RichEditor.swift b/RichEditor/Classes/RichEditor.swift deleted file mode 100644 index 2b22630..0000000 --- a/RichEditor/Classes/RichEditor.swift +++ /dev/null @@ -1,482 +0,0 @@ -// -// RichEditor.swift -// Nimble -// -// Created by Will Lumley on 30/1/20. -// - -import Foundation -import AppKit - -public protocol RichEditorDelegate -{ - func fontStylingChanged(_ fontStyling: FontStyling) - func richEditorTextChanged(_ richEditor: RichEditor) -} - -public class RichEditor: NSView -{ - //The NSTextView stack - /*------------------------------------------------------------*/ - public fileprivate(set) lazy var textStorage = NSTextStorage() - public fileprivate(set) lazy var layoutManager = NSLayoutManager() - public fileprivate(set) lazy var textContainer = NSTextContainer() - public fileprivate(set) lazy var textView = RichTextView(frame: CGRect(), textContainer: self.textContainer, delegate: self) - public fileprivate(set) lazy var scrollview = NSScrollView() - /*------------------------------------------------------------*/ - - ///The FontStyling that contains information of the 'relevant' text - fileprivate var selectedTextFontStyling: FontStyling? - { - didSet { - self.richEditorDelegate?.fontStylingChanged(self.fontStyling) - } - } - - ///The marker that will be used for bullet points - internal static var bulletPointMarker = "•\u{00A0}" //NSTextList.MarkerFormat.circle - - /** - Returns the FontStyling object that was derived from the selected text, or the future text if nothing is selected - - returns: The FontStyling object for the relevant text - */ - public var fontStyling: FontStyling { - return self.selectedTextFontStyling ?? FontStyling(typingAttributes: self.textView.typingAttributes) - } - - ///The delegate which will notify the listener of significant events - public var richEditorDelegate: RichEditorDelegate? - - //MARK: - NSView - override init(frame frameRect: NSRect) - { - super.init(frame: frameRect) - self.setup() - } - - required init?(coder decoder: NSCoder) - { - super.init(coder: decoder) - self.setup() - } - - /** - Perform the initial setup operations to get a functional NSTextView running - */ - fileprivate func setup() - { - self.textView.delegate = self - - self.addSubview(self.scrollview) - self.configureTextView(isHorizontalScrollingEnabled: false) - self.configureTextViewLayout() - - self.textView.textStorage?.delegate = self - self.textView.layoutManager?.defaultAttachmentScaling = NSImageScaling.scaleProportionallyDown - - self.selectedTextFontStyling = nil - } -} - -//MARK: - Public Interface -extension RichEditor -{ - /** - Toggles the bold attribute for the selected text, or the future text if no text is selected - */ - public func toggleBold() - { - self.toggleTextView(with: .boldFontMask) - } - - /** - Toggles the italics attribute for the selected text, or the future text if no text is selected - */ - public func toggleItalic() - { - self.toggleTextView(with: .italicFontMask) - } - - /** - Toggles the underline attribute for the selected text, or the future text if no text is selected - - parameter style: The style of the underline that we want - */ - public func toggleUnderline(_ style: NSUnderlineStyle) - { - self.toggleTextView(with: .underlineStyle, negativeValue: 0, positiveValue: style.rawValue) - } - - /** - Toggles the strikethrough attribute for the selected text, or the future text if no text is selected - */ - public func toggleStrikethrough(_ style: NSUnderlineStyle) - { - self.toggleTextView(with: .strikethroughStyle, negativeValue: 0, positiveValue: style.rawValue) - } - - public func apply(textColour: NSColor) - { - let colourAttr = [NSAttributedString.Key.foregroundColor: textColour] - self.add(attributes: colourAttr, onlyHighlightedText: self.textView.hasSelectedText) - } - - public func apply(highlightColour: NSColor) - { - let colourAttr = [NSAttributedString.Key.backgroundColor: highlightColour] - self.add(attributes: colourAttr, onlyHighlightedText: self.textView.hasSelectedText) - } - - public func apply(font: NSFont) - { - let fontAttr = [NSAttributedString.Key.font: font] - self.add(attributes: fontAttr, onlyHighlightedText: self.textView.hasSelectedText) - } - - public func startBulletPoints() - { - let currentLine = self.textView.currentLine() - - //Get the string that makes up our current string, and find out where it sits in our TextView - let currentLineStr = currentLine.lineString - let currentLineRange = currentLine.lineRange - - //If our current line already has a bullet point, remove it - if currentLineStr.isBulletPoint { - //Get the line in our TextView that our caret is on, and remove our bulletpoint - var noBulletPointStr = currentLineStr.replacingOccurrences(of: "\(RichEditor.bulletPointMarker) ", with: "") - noBulletPointStr = currentLineStr.replacingOccurrences(of: RichEditor.bulletPointMarker, with: "") - - self.textView.replaceCharacters(in: currentLineRange, with: noBulletPointStr) - } - //If our current line doesn't already have a bullet point appended to it, prepend one - else { - //Get the line in our TextView that our caret is on, and prepend a bulletpoint to it - let bulletPointStr = "\(RichEditor.bulletPointMarker) \(currentLineStr)" - self.textView.replaceCharacters(in: currentLineRange, with: bulletPointStr) - } - } - - /** - Inserts a clickable link into the NSTextView - - parameter link: The URI/URL that will be opened upon the clicking of this link - - parameter name: The text that will be displayed to the user - - parameter position: The location of the NSTextView's string where the link will be - inserted. If nil, the cursors position is used instead. - */ - public func insert(link: String, with name: String, at position: Int?) - { - let attrString = NSMutableAttributedString(string: name) - attrString.addAttribute(NSAttributedString.Key.link, value: link, range: name.fullRange) - - let insertionPosition = position ?? self.textView.selectedRange().location - self.textView.textStorage!.insert(attrString, at: insertionPosition) - } - - public func promptUserForAttachments(windowForModal: NSWindow?) - { - let openPanel = NSOpenPanel() - openPanel.allowsMultipleSelection = true - openPanel.canChooseDirectories = false - openPanel.canCreateDirectories = false - openPanel.canChooseFiles = true - - if let window = windowForModal { - openPanel.beginSheetModal(for: window, completionHandler: {(modalResponse) in - if modalResponse == NSApplication.ModalResponse.OK { - let selectedURLs = openPanel.urls - self.insertAttachments(at: selectedURLs) - } - }) - } - else { - openPanel.begin(completionHandler: {(modalResponse) in - if modalResponse == NSApplication.ModalResponse.OK { - let selectedURLs = openPanel.urls - self.insertAttachments(at: selectedURLs) - } - }) - } - } - - public func insertAttachments(at urls: [URL]) - { - self.textView.layoutManager?.defaultAttachmentScaling = NSImageScaling.scaleProportionallyDown - - print("Inserting attachments at URLs: \(urls)") - - //Iterate over every URL and create a NSTextAttachment from it - for url in urls { - let attachment = url.textAttachment() - let attachmentAttrStr = NSAttributedString(attachment: attachment) - - self.textView.textStorage?.append(attachmentAttrStr) - } - } - - /** - Uses the NSTextView's attributed string to create a HTML string that - represents the content held within the NSTextView. - The NSAttribtedString -> HTML String conversion uses the cocoa - native data(from...) function. - - throws: An error, if the NSAttribtedString -> HTML String conversion fails - - returns: The string object that is the HTML - */ - public func html() throws -> String? - { - let attrStr = self.textView.attributedString() - let documentAttributes = [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.html] - - let htmlData = try attrStr.data(from: attrStr.string.fullRange, documentAttributes: documentAttributes) - if let htmlString = String(data:htmlData, encoding:String.Encoding.utf8) { - //print("HTML: \(htmlString)") - return htmlString - } - - return nil - } - - /** - Returns the NSFont object that was derived from the selected text, or the future text if nothing is selected - - returns: The NSFont object for the relevant text - */ - public func currentFont() -> NSFont - { - //If we have highlighted text, we'll analyse the font of the highlighted text - if self.textView.hasSelectedText { - let range = self.textView.selectedRange() - - //Create an attributed string out of ONLY the highlighted text - guard let attr = self.textView.attributedSubstring(forProposedRange: range, actualRange: nil) else { - fatalError("Failed to create AttributedString.") - } - - let fonts = attr.allFonts() - if fonts.count < 1 { - fatalError("AttributedString had no fonts: \(attr)") - } - - return fonts[0] - } - - //We have not highlighted text, so we'll just use the 'future' font - else { - let typingAttributes = self.textView.typingAttributes - - let font = typingAttributes[NSAttributedString.Key.font] as! NSFont - return font - } - } -} - -//MARK: - Private Interface -extension RichEditor -{ - /** - Toggles a certain font trait (bold, italic, etc) for the RichTextView. - If text is highlighted, then only the highlighted text will have its font toggled. If no text is highlighted - then all 'future' text will have the new traits. - - - parameter trait: This is the trait that you want the font to adhere to - */ - fileprivate func toggleTextView(with fontTrait: NSFontTraitMask) - { - //Get the current font, and make a 'new' version of it, with the traits provided to us - let currentFont = self.currentFont() - var newFont = currentFont - - let fontStyling = self.fontStyling - let fontStylingTrait = fontStyling.fontTraitFor(nsFontTraitMask: fontTrait) - - print("\nOldFont: \(currentFont)") - switch (fontStylingTrait) { - //If we're ONLY bold at the moment, let's make it unbold - case .hasTrait: - newFont = NSFontManager.shared.convert(currentFont, toNotHaveTrait: fontTrait) - - //If we're ONLY unbold at the moment, let's make it bold - case .hasNoTrait: - newFont = NSFontManager.shared.convert(currentFont, toHaveTrait: fontTrait) - - //If we're BOTH bold and unbold, we'll make it bold - case .both: - newFont = NSFontManager.shared.convert(currentFont, toHaveTrait: fontTrait) - } - print("NewFont: \(newFont)\n") - - let updatedFontAttr = [NSAttributedString.Key.font: newFont] - self.add(attributes: updatedFontAttr, onlyHighlightedText: self.textView.hasSelectedText) - - self.richEditorDelegate?.fontStylingChanged(self.fontStyling) - } - - /** - Toggles a certain NSAttributeString.Key (underline, strikethrough, etc) for the RichTextView. - If text is highlighted, then only the highlighted text will have its NSAttributedText toggled. - If no text is highlighted then all 'future' text will have the new attributes. - - - parameter attribute: This is the trait that you want the NSAttributedString to adhere to - - parameter negativeValue: This is the 'off' value for the attribute. If `attribute` was - NSUnderlineStyle, then the negativeValue would be NSUnderlineStyle.styleNone.rawValue - - parameter positiveValue: This is the 'on' value for the attribute. If `attribute` was - NSUnderlineStyle, then the positive would be NSUnderlineStyle.styleSingle.rawValue - */ - fileprivate func toggleTextView(with attribute: NSAttributedString.Key, negativeValue: Any, positiveValue: Any) - { - let trait = self.fontStyling.trait(with: attribute) - - var newAttr = [NSAttributedString.Key: Any]() - switch (trait) { - case .hasTrait: - newAttr = [attribute: negativeValue] - - case .hasNoTrait: - newAttr = [attribute: positiveValue] - - case .both: - newAttr = [attribute: positiveValue] - } - - self.add(attributes: newAttr, onlyHighlightedText: self.textView.hasSelectedText) - self.richEditorDelegate?.fontStylingChanged(self.fontStyling) - } - - /** - Adds the provided NSAttributedString.Key attributes to the TextView. The attributes will either - be applied to the text that is selected, or to all 'future' text. This is dependant on the - onlyHighlightedText argument. - - parameter attributes: The attributes that we wish to apply to our NSTextView - - parameter onlyHighlightedText: If true, the attributes will be applied to only the highlighted - text. If false, the attributes will be applied to all 'future' text - */ - fileprivate func add(attributes: [NSAttributedString.Key: Any], onlyHighlightedText: Bool) - { - //If we're only modifying the text that's highlighted - if onlyHighlightedText { - let selectedRange = self.textView.selectedRange() - - self.textStorage.addAttributes(attributes, range: selectedRange) - - //Create an attributed string out of ONLY the highlighted text - guard let attr = self.textView.attributedSubstring(forProposedRange: selectedRange, actualRange: nil) else { - return - } - - //Ensure the UI is updated with the new FontStyling state's - self.selectedTextFontStyling = FontStyling(attributedString: attr) - } - - //If we're modifying all 'future' text - else { - //Get the existing TypingAttributes, and merge it into our new attributes dictionary - var typingAttributes = self.textView.typingAttributes - //print("Old TypingAttributes: \(typingAttributes)") - - typingAttributes.merge(newDict: attributes) - //print("New TypingAttributes: \(typingAttributes)\n") - - self.textView.typingAttributes = typingAttributes - } - - self.richEditorDelegate?.fontStylingChanged(self.fontStyling) - } -} - -//MARK: - NSTextStorage Delegate -extension RichEditor: NSTextStorageDelegate -{ - public func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) - { - self.richEditorDelegate?.richEditorTextChanged(self) - } -} - -//MARK: - NSTextViewDelegate -extension RichEditor: NSTextViewDelegate -{ - public func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool - { - //Get all the lines of the text - //Get the line of text that we're on - - guard let newString = replacementString else { return true } - - //If the user just hit enter/newline - if newString == "\n" { - let currentLine = textView.currentLine() - - //If the line we're currently on is prefixed with a bullet point, append a bullet point to the next line - let currentLineStr = currentLine.lineString - if currentLineStr.isBulletPoint { - - let currentLineRange = currentLine.lineRange - - //If our current line is just an empty bullet point line, remove the bullet point and turn it into a regular line - if currentLineStr == RichEditor.bulletPointMarker { - self.textView.replaceCharacters(in: currentLineRange, with: "") - } - - //If our current line is a full bullet point line, append a brand spanking new bullet point line below our current line for our user - else { - let bulletPointStr = "\(currentLineStr)\n\(RichEditor.bulletPointMarker)" - self.textView.replaceCharacters(in: currentLineRange, with: bulletPointStr) - } - - return false - } - } - - return true - } - - /* - func textView(_ textView: NSTextView, urlForContentsOf textAttachment: NSTextAttachment, at charIndex: Int) -> URL? - { - print("TextAttachment: \(textAttachment)") - print("CharIndex: \(charIndex)") - - let fileWrapper = textAttachment.fileWrapper! - //let metaFileWrappers = fileWrapper.fileWrappers - let data = fileWrapper.regularFileContents ?? Data() - - //print("FileWrapper.fileWrappers: \(String(describing: metaFileWrappers))") - print("Data: \(data)") - - print("") - return nil - } - */ - - public func textViewDidChangeSelection(_ notification: Notification) - { - let selectedRange = self.textView.selectedRange() - let isSelected = selectedRange.length > 0 - - if !isSelected { - self.selectedTextFontStyling = nil - return - } - - //Create an attributed string out of ONLY the highlighted text - guard let attr = self.textView.attributedSubstring(forProposedRange: selectedRange, actualRange: nil) else { - return - } - - self.selectedTextFontStyling = FontStyling(attributedString: attr) - } -} - -extension RichEditor: KeyboardShortcutDelegate -{ - public func commandPressed(character: CommandShortcut) - { - switch character { - case .b: - self.toggleBold() - case .i: - self.toggleItalic() - case .u: - self.toggleUnderline(.single) - default:() - } - } -} diff --git a/RichEditor/Classes/RichEditor/RichEditor+Attachments.swift b/RichEditor/Classes/RichEditor/RichEditor+Attachments.swift new file mode 100644 index 0000000..3c56f9e --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor+Attachments.swift @@ -0,0 +1,51 @@ +// +// RichEditor+Attachments.swift +// RichEditor +// +// Created by William Lumley on 7/12/20. +// + +import Foundation + +extension RichEditor { + + public func promptUserForAttachments(windowForModal: NSWindow?) { + let openPanel = NSOpenPanel() + openPanel.allowsMultipleSelection = true + openPanel.canChooseDirectories = false + openPanel.canCreateDirectories = false + openPanel.canChooseFiles = true + + if let window = windowForModal { + openPanel.beginSheetModal(for: window, completionHandler: {(modalResponse) in + if modalResponse == NSApplication.ModalResponse.OK { + let selectedURLs = openPanel.urls + self.insertAttachments(at: selectedURLs) + } + }) + } + else { + openPanel.begin(completionHandler: {(modalResponse) in + if modalResponse == NSApplication.ModalResponse.OK { + let selectedURLs = openPanel.urls + self.insertAttachments(at: selectedURLs) + } + }) + } + } + + public func insertAttachments(at urls: [URL]) { + self.textView.layoutManager?.defaultAttachmentScaling = NSImageScaling.scaleProportionallyDown + + print("Inserting attachments at URLs: \(urls)") + + //Iterate over every URL and create a NSTextAttachment from it + for url in urls { + let attachment = url.textAttachment() + let attachmentAttrStr = NSAttributedString(attachment: attachment) + + self.textView.textStorage?.append(attachmentAttrStr) + } + } + +} diff --git a/RichEditor/Classes/RichEditor/RichEditor+BulletPoints.swift b/RichEditor/Classes/RichEditor/RichEditor+BulletPoints.swift new file mode 100644 index 0000000..2ba7f7b --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor+BulletPoints.swift @@ -0,0 +1,108 @@ +// +// RichEditor+BulletPoints.swift +// RichEditor +// +// Created by William Lumley on 7/12/20. +// + +import Foundation + +extension RichEditor { + + public func startBulletPoints() { + let currentLine = self.textView.currentLine + + //Get the string that makes up our current string, and find out where it sits in our TextView + let currentLineStr = currentLine.lineString + let currentLineRange = currentLine.lineRange + + //If our current line already has a bullet point, remove it + if currentLineStr.isBulletPoint { + //Get the line in our TextView that our caret is on, and remove our bulletpoint + var noBulletPointStr = currentLineStr.replacingOccurrences(of: "\(RichEditor.bulletPointMarker) ", with: "") + noBulletPointStr = currentLineStr.replacingOccurrences(of: RichEditor.bulletPointMarker, with: "") + + self.textView.replaceCharacters(in: currentLineRange, with: noBulletPointStr) + } + //If our current line doesn't already have a bullet point appended to it, prepend one + else { + //Get the line in our TextView that our caret is on, and prepend a bulletpoint to it + let bulletPointStr = "\(RichEditor.bulletPointMarker) \(currentLineStr)" + self.textView.replaceCharacters(in: currentLineRange, with: bulletPointStr) + } + } + +} + +extension RichEditor: NSTextViewDelegate { + + public func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool { + //Get all the lines of the text + //Get the line of text that we're on + + guard let newString = replacementString else { return true } + + //If the user just hit enter/newline + if newString == "\n" { + let currentLine = textView.currentLine + + //If the line we're currently on is prefixed with a bullet point, append a bullet point to the next line + let currentLineStr = currentLine.lineString + if currentLineStr.isBulletPoint { + + let currentLineRange = currentLine.lineRange + + //If our current line is just an empty bullet point line, remove the bullet point and turn it into a regular line + if currentLineStr == RichEditor.bulletPointMarker { + self.textView.replaceCharacters(in: currentLineRange, with: "") + } + + //If our current line is a full bullet point line, append a brand spanking new bullet point line below our current line for our user + else { + let bulletPointStr = "\(currentLineStr)\n\(RichEditor.bulletPointMarker)" + self.textView.replaceCharacters(in: currentLineRange, with: bulletPointStr) + } + + return false + } + } + + return true + } + + /* + func textView(_ textView: NSTextView, urlForContentsOf textAttachment: NSTextAttachment, at charIndex: Int) -> URL? + { + print("TextAttachment: \(textAttachment)") + print("CharIndex: \(charIndex)") + + let fileWrapper = textAttachment.fileWrapper! + //let metaFileWrappers = fileWrapper.fileWrappers + let data = fileWrapper.regularFileContents ?? Data() + + //print("FileWrapper.fileWrappers: \(String(describing: metaFileWrappers))") + print("Data: \(data)") + + print("") + return nil + } + */ + + public func textViewDidChangeSelection(_ notification: Notification) { + let selectedRange = self.textView.selectedRange() + let isSelected = selectedRange.length > 0 + + if !isSelected { + self.selectedTextFontStyling = nil + return + } + + //Create an attributed string out of ONLY the highlighted text + guard let attr = self.textView.attributedSubstring(forProposedRange: selectedRange, actualRange: nil) else { + return + } + + self.selectedTextFontStyling = FontStyling(attributedString: attr) + } + +} diff --git a/RichEditor/Classes/RichEditor/RichEditor+Core.swift b/RichEditor/Classes/RichEditor/RichEditor+Core.swift new file mode 100644 index 0000000..fd06426 --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor+Core.swift @@ -0,0 +1,117 @@ +// +// RichEditor+Core.swift +// RichEditor +// +// Created by William Lumley on 7/12/20. +// + +import Foundation + +extension RichEditor { + + /** + Toggles a certain font trait (bold, italic, etc) for the RichTextView. + If text is highlighted, then only the highlighted text will have its font toggled. If no text is highlighted + then all 'future' text will have the new traits. + + - parameter trait: This is the trait that you want the font to adhere to + */ + internal func toggleTextView(with fontTrait: NSFontTraitMask) { + //Get the current font, and make a 'new' version of it, with the traits provided to us + let currentFont = self.currentFont + var newFont = currentFont + + let fontStyling = self.fontStyling + let fontStylingTrait = fontStyling.fontTraitFor(nsFontTraitMask: fontTrait) + + print("\nOldFont: \(currentFont)") + switch (fontStylingTrait) { + //If we're ONLY bold at the moment, let's make it unbold + case .hasTrait: + newFont = NSFontManager.shared.convert(currentFont, toNotHaveTrait: fontTrait) + + //If we're ONLY unbold at the moment, let's make it bold + case .hasNoTrait: + newFont = NSFontManager.shared.convert(currentFont, toHaveTrait: fontTrait) + + //If we're BOTH bold and unbold, we'll make it bold + case .both: + newFont = NSFontManager.shared.convert(currentFont, toHaveTrait: fontTrait) + } + print("NewFont: \(newFont)\n") + + let updatedFontAttr = [NSAttributedString.Key.font: newFont] + self.add(attributes: updatedFontAttr, onlyHighlightedText: self.textView.hasSelectedText) + + self.richEditorDelegate?.fontStylingChanged(self.fontStyling) + } + + /** + Toggles a certain NSAttributeString.Key (underline, strikethrough, etc) for the RichTextView. + If text is highlighted, then only the highlighted text will have its NSAttributedText toggled. + If no text is highlighted then all 'future' text will have the new attributes. + + - parameter attribute: This is the trait that you want the NSAttributedString to adhere to + - parameter negativeValue: This is the 'off' value for the attribute. If `attribute` was + NSUnderlineStyle, then the negativeValue would be NSUnderlineStyle.styleNone.rawValue + - parameter positiveValue: This is the 'on' value for the attribute. If `attribute` was + NSUnderlineStyle, then the positive would be NSUnderlineStyle.styleSingle.rawValue + */ + internal func toggleTextView(with attribute: NSAttributedString.Key, negativeValue: Any, positiveValue: Any) { + let trait = self.fontStyling.trait(with: attribute) + + var newAttr = [NSAttributedString.Key: Any]() + switch (trait) { + case .hasTrait: + newAttr = [attribute: negativeValue] + + case .hasNoTrait: + newAttr = [attribute: positiveValue] + + case .both: + newAttr = [attribute: positiveValue] + } + + self.add(attributes: newAttr, onlyHighlightedText: self.textView.hasSelectedText) + self.richEditorDelegate?.fontStylingChanged(self.fontStyling) + } + + /** + Adds the provided NSAttributedString.Key attributes to the TextView. The attributes will either + be applied to the text that is selected, or to all 'future' text. This is dependant on the + onlyHighlightedText argument. + - parameter attributes: The attributes that we wish to apply to our NSTextView + - parameter onlyHighlightedText: If true, the attributes will be applied to only the highlighted + text. If false, the attributes will be applied to all 'future' text + */ + internal func add(attributes: [NSAttributedString.Key: Any], onlyHighlightedText: Bool) { + //If we're only modifying the text that's highlighted + if onlyHighlightedText { + let selectedRange = self.textView.selectedRange() + + self.textStorage.addAttributes(attributes, range: selectedRange) + + //Create an attributed string out of ONLY the highlighted text + guard let attr = self.textView.attributedSubstring(forProposedRange: selectedRange, actualRange: nil) else { + return + } + + //Ensure the UI is updated with the new FontStyling state's + self.selectedTextFontStyling = FontStyling(attributedString: attr) + } + + //If we're modifying all 'future' text + else { + //Get the existing TypingAttributes, and merge it into our new attributes dictionary + var typingAttributes = self.textView.typingAttributes + //print("Old TypingAttributes: \(typingAttributes)") + + typingAttributes.merge(newDict: attributes) + //print("New TypingAttributes: \(typingAttributes)\n") + + self.textView.typingAttributes = typingAttributes + } + + self.richEditorDelegate?.fontStylingChanged(self.fontStyling) + } +} diff --git a/RichEditor/Classes/RichEditor/RichEditor+KeyboardShortcuts.swift b/RichEditor/Classes/RichEditor/RichEditor+KeyboardShortcuts.swift new file mode 100644 index 0000000..266df63 --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor+KeyboardShortcuts.swift @@ -0,0 +1,24 @@ +// +// RichEditor+KeyboardShortcuts.swift +// RichEditor +// +// Created by William Lumley on 7/12/20. +// + +import Foundation + +extension RichEditor: KeyboardShortcutDelegate { + + public func commandPressed(character: CommandShortcut) { + switch character { + case .b: + self.toggleBold() + case .i: + self.toggleItalic() + case .u: + self.toggleUnderline(.single) + default:() + } + } + +} diff --git a/RichEditor/Classes/RichEditor/RichEditor+Links.swift b/RichEditor/Classes/RichEditor/RichEditor+Links.swift new file mode 100644 index 0000000..6f2e054 --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor+Links.swift @@ -0,0 +1,27 @@ +// +// RichEditor+Links.swift +// RichEditor +// +// Created by William Lumley on 7/12/20. +// + +import Foundation + +extension RichEditor { + + /** + Inserts a clickable link into the NSTextView + - parameter link: The URI/URL that will be opened upon the clicking of this link + - parameter name: The text that will be displayed to the user + - parameter position: The location of the NSTextView's string where the link will be + inserted. If nil, the cursors position is used instead. + */ + public func insert(link: String, with name: String, at position: Int?) { + let attrString = NSMutableAttributedString(string: name) + attrString.addAttribute(NSAttributedString.Key.link, value: link, range: name.fullRange) + + let insertionPosition = position ?? self.textView.selectedRange().location + self.textView.textStorage!.insert(attrString, at: insertionPosition) + } + +} diff --git a/RichEditor/Classes/RichEditor+Stack.swift b/RichEditor/Classes/RichEditor/RichEditor+Stack.swift similarity index 100% rename from RichEditor/Classes/RichEditor+Stack.swift rename to RichEditor/Classes/RichEditor/RichEditor+Stack.swift diff --git a/RichEditor/Classes/RichEditor/RichEditor+Styling.swift b/RichEditor/Classes/RichEditor/RichEditor+Styling.swift new file mode 100644 index 0000000..efeb3e5 --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor+Styling.swift @@ -0,0 +1,56 @@ +// +// RichEditor+Styling.swift +// RichEditor +// +// Created by William Lumley on 7/12/20. +// + +import Foundation + +extension RichEditor { + + /** + Toggles the bold attribute for the selected text, or the future text if no text is selected + */ + public func toggleBold() { + self.toggleTextView(with: .boldFontMask) + } + + /** + Toggles the italics attribute for the selected text, or the future text if no text is selected + */ + public func toggleItalic() { + self.toggleTextView(with: .italicFontMask) + } + + /** + Toggles the underline attribute for the selected text, or the future text if no text is selected + - parameter style: The style of the underline that we want + */ + public func toggleUnderline(_ style: NSUnderlineStyle) { + self.toggleTextView(with: .underlineStyle, negativeValue: 0, positiveValue: style.rawValue) + } + + /** + Toggles the strikethrough attribute for the selected text, or the future text if no text is selected + */ + public func toggleStrikethrough(_ style: NSUnderlineStyle) { + self.toggleTextView(with: .strikethroughStyle, negativeValue: 0, positiveValue: style.rawValue) + } + + public func apply(textColour: NSColor) { + let colourAttr = [NSAttributedString.Key.foregroundColor: textColour] + self.add(attributes: colourAttr, onlyHighlightedText: self.textView.hasSelectedText) + } + + public func apply(highlightColour: NSColor) { + let colourAttr = [NSAttributedString.Key.backgroundColor: highlightColour] + self.add(attributes: colourAttr, onlyHighlightedText: self.textView.hasSelectedText) + } + + public func apply(font: NSFont) { + let fontAttr = [NSAttributedString.Key.font: font] + self.add(attributes: fontAttr, onlyHighlightedText: self.textView.hasSelectedText) + } + +} diff --git a/RichEditor/Classes/RichEditor/RichEditor.swift b/RichEditor/Classes/RichEditor/RichEditor.swift new file mode 100644 index 0000000..d1c4066 --- /dev/null +++ b/RichEditor/Classes/RichEditor/RichEditor.swift @@ -0,0 +1,130 @@ +// +// RichEditor.swift +// Nimble +// +// Created by Will Lumley on 30/1/20. +// + +import Foundation +import AppKit + +public protocol RichEditorDelegate +{ + func fontStylingChanged(_ fontStyling: FontStyling) + func richEditorTextChanged(_ richEditor: RichEditor) +} + +public class RichEditor: NSView +{ + //The NSTextView stack + /*------------------------------------------------------------*/ + public private(set) lazy var textStorage = NSTextStorage() + public private(set) lazy var layoutManager = NSLayoutManager() + public private(set) lazy var textContainer = NSTextContainer() + public private(set) lazy var textView = RichTextView(frame: CGRect(), textContainer: self.textContainer, delegate: self) + public private(set) lazy var scrollview = NSScrollView() + /*------------------------------------------------------------*/ + + ///The FontStyling that contains information of the 'relevant' text + internal var selectedTextFontStyling: FontStyling? { + didSet { + self.richEditorDelegate?.fontStylingChanged(self.fontStyling) + } + } + + /// The marker that will be used for bullet points + internal static var bulletPointMarker = "•\u{00A0}" //NSTextList.MarkerFormat.circle + + /// Returns the FontStyling object that was derived from the selected text, or the future text if nothing is selected + public var fontStyling: FontStyling { + return self.selectedTextFontStyling ?? FontStyling(typingAttributes: self.textView.typingAttributes) + } + + /// The delegate which will notify the listener of significant events + public var richEditorDelegate: RichEditorDelegate? + + /// Returns the NSFont object that was derived from the selected text, or the future text if nothing is selected + public var currentFont: NSFont { + //If we have highlighted text, we'll analyse the font of the highlighted text + if self.textView.hasSelectedText { + let range = self.textView.selectedRange() + + //Create an attributed string out of ONLY the highlighted text + guard let attr = self.textView.attributedSubstring(forProposedRange: range, actualRange: nil) else { + fatalError("Failed to create AttributedString.") + } + + let fonts = attr.allFonts + if fonts.count < 1 { + fatalError("AttributedString had no fonts: \(attr)") + } + + return fonts[0] + } + + //We have not highlighted text, so we'll just use the 'future' font + else { + let typingAttributes = self.textView.typingAttributes + + let font = typingAttributes[NSAttributedString.Key.font] as! NSFont + return font + } + } + + // MARK: - NSView + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + self.setup() + } + + required init?(coder decoder: NSCoder) { + super.init(coder: decoder) + self.setup() + } + + /** + Perform the initial setup operations to get a functional NSTextView running + */ + fileprivate func setup() { + self.textView.delegate = self + + self.addSubview(self.scrollview) + self.configureTextView(isHorizontalScrollingEnabled: false) + self.configureTextViewLayout() + + self.textView.textStorage?.delegate = self + self.textView.layoutManager?.defaultAttachmentScaling = NSImageScaling.scaleProportionallyDown + + self.selectedTextFontStyling = nil + } + + /** + Uses the NSTextView's attributed string to create a HTML string that + represents the content held within the NSTextView. + The NSAttribtedString -> HTML String conversion uses the cocoa + native data(from...) function. + - throws: An error, if the NSAttribtedString -> HTML String conversion fails + - returns: The string object that is the HTML + */ + public func html() throws -> String? { + let attrStr = self.textView.attributedString() + let documentAttributes = [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.html] + + let htmlData = try attrStr.data(from: attrStr.string.fullRange, documentAttributes: documentAttributes) + if let htmlString = String(data:htmlData, encoding:String.Encoding.utf8) { + //print("HTML: \(htmlString)") + return htmlString + } + + return nil + } +} + +//MARK: - NSTextStorage Delegate +extension RichEditor: NSTextStorageDelegate { + + public func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) { + self.richEditorDelegate?.richEditorTextChanged(self) + } + +} diff --git a/RichEditor/Classes/RichTextView.swift b/RichEditor/Classes/RichTextView.swift index 4484642..09dbc79 100644 --- a/RichEditor/Classes/RichTextView.swift +++ b/RichEditor/Classes/RichTextView.swift @@ -48,6 +48,13 @@ public class RichTextView: NSTextView super.init(frame: frameRect) } + fileprivate func setup() + { + if #available(OSX 10.14, *) { + self.usesAdaptiveColorMappingForDarkAppearance = true + } + } + override public func performKeyEquivalent(with event: NSEvent) -> Bool { //Only process our event if it's a keydown event