Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RNMobile] Cover with in Cover support #13928

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,16 @@ public class GutenbergBlockProcessor: Processor {
///
public typealias Replacer = (GutenbergBlock) -> String?

// MARK: - Basic Info

let name: String

// MARK: - Regex

private enum CaptureGroups: Int {
case all = 0
case name
case attributes
case content

static let allValues: [CaptureGroups] = [.all, .name, .attributes, .content]
static let allValues: [CaptureGroups] = [.all, .name, .attributes]
}

/// Regular expression to detect attributes
/// Capture groups:
///
/// 1. The block id
/// 2. The block attributes
/// 3. Block content
///
private lazy var gutenbergBlockRegexProcessor: RegexProcessor = { [weak self]() in
let pattern = "\\<!--[ ]?(\(name))([\\s\\S]*?)-->([\\s\\S]*?)<!-- \\/\(name) -->"
let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)

return RegexProcessor(regex: regex) { (match: NSTextCheckingResult, text: String) -> String? in
return self?.process(match: match, text: text)
}
}()

// MARK: - Parsing & processing properties
private let replacer: Replacer

Expand All @@ -58,36 +37,137 @@ public class GutenbergBlockProcessor: Processor {
self.replacer = replacer
}

/// Regular expression to detect attributes of the opening tag of a block
/// Capture groups:
///
/// 1. The block id
/// 2. The block attributes
///
var openTagRegex: NSRegularExpression {
let pattern = "\\<!--[ ]?(\(name))([\\s\\S]*?)-->"
return try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
}

/// Regular expression to detect the closing tag of a block
///
var closingTagRegex: NSRegularExpression {
let pattern = "\\<!-- \\/\(name) -->"
return try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
}

// MARK: - Processing

/// Processes the block and for any needed replacements from a given opening tag match.
/// - Parameters:
/// - text: The string that the following parameter is found in.
/// - Returns: The resulting string after the necessary replacements have occured
///
public func process(_ text: String) -> String {
return gutenbergBlockRegexProcessor.process(text)
let matches = openTagRegex.matches(in: text, options: [], range: text.utf16NSRange(from: text.startIndex ..< text.endIndex))
var replacements = [(NSRange, String)]()

var lastReplacementBound = 0
for match in matches {
if match.range.lowerBound >= lastReplacementBound, let replacement = process(match, in: text) {
replacements.append(replacement)
lastReplacementBound = replacement.0.upperBound
}
}
let resultText = replace(replacements, in: text)
return resultText
}
}

/// Replaces the
/// - Parameters:
/// - replacements: An array of tuples representing first a range of text that needs to be replaced then the string to replace
/// - text: The string to perform the replacements on
///
func replace(_ replacements: [(NSRange, String)], in text: String) -> String {
let mutableString = NSMutableString(string: text)
var offset = 0
for (range, replacement) in replacements {
let lengthBefore = mutableString.length
let offsetRange = NSRange(location: range.location + offset, length: range.length)
mutableString.replaceCharacters(in: offsetRange, with: replacement)
let lengthAfter = mutableString.length
offset += (lengthAfter - lengthBefore)
}
return mutableString as String
}
}
// MARK: - Regex Match Processing Logic

private extension GutenbergBlockProcessor {
/// Processes an Gutenberg block regex match.
/// Processes the block and for any needed replacements from a given opening tag match.
/// - Parameters:
/// - match: The match reperesenting an opening block tag
/// - text: The string that the following parameter is found in.
/// - Returns: Any necessary replacements within the provided string
///
func process(match: NSTextCheckingResult, text: String) -> String? {
private func process(_ match: NSTextCheckingResult, in text: String) -> (NSRange, String)? {

var result: (NSRange, String)? = nil
if let closingRange = locateClosingTag(forMatch: match, in: text) {
let attributes = readAttributes(from: match, in: text)
let content = readContent(from: match, withClosingRange: closingRange, in: text)
let parsedContent = process(content) // Recurrsively parse nested blocks and process those seperatly
let block = GutenbergBlock(name: name, attributes: attributes, content: parsedContent)

if let replacement = replacer(block) {
let length = closingRange.upperBound - match.range.lowerBound
let range = NSRange(location: match.range.lowerBound, length: length)
result = (range, replacement)
}
}

guard match.numberOfRanges == CaptureGroups.allValues.count else {
return result
}

/// Determines the location of the closing block tag for the matching open tag
/// - Parameters:
/// - openTag: The match reperesenting an opening block tag
/// - text: The string that the following parameter is found in.
/// - Returns: The Range of the closing tag for the block
///
func locateClosingTag(forMatch openTag: NSTextCheckingResult, in text: String) -> NSRange? {
guard let index = text.indexFromLocation(openTag.range.upperBound) else {
return nil
}

let attributes = readAttributes(from: match, in: text)
let content = readContent(from: match, in: text)
let block = GutenbergBlock(name: name, attributes: attributes, content: content)
let matches = closingTagRegex.matches(in: text, options: [], range: text.utf16NSRange(from: index ..< text.endIndex))

return replacer(block)
for match in matches {
let content = readContent(from: openTag, withClosingRange: match.range, in: text)

if tagsAreBalanced(in: content) {
return match.range
}
}

return nil
}

// MARK: - Regex Match Processing Logic
/// Determines if there are an equal number of opening and closing block tags in the provided text.
/// - Parameters:
/// - text: The string to test assumes that a block with an even number represents a valid block sequence.
/// - Returns: A boolean where true represents an equal number of opening and closing block tags of the desired type
///
func tagsAreBalanced(in text: String) -> Bool {

let range = text.utf16NSRange(from: text.startIndex ..< text.endIndex)
let openTags = openTagRegex.matches(in: text, options: [], range: range)
let closingTags = closingTagRegex.matches(in: text, options: [], range: range)

/// Obtains the attributes from a block match.
return openTags.count == closingTags.count
}

/// Obtains the block attributes from a regex match.
/// - Parameters:
/// - match: The `NSTextCheckingResult` from a successful regex detection of an opening block tag
/// - text: The string that the following parameter is found in.
/// - Returns: A JSON dictionary of the block attributes
///
private func readAttributes(from match: NSTextCheckingResult, in text: String) -> [String: Any] {
func readAttributes(from match: NSTextCheckingResult, in text: String) -> [String: Any] {
guard let attributesText = match.captureGroup(in: CaptureGroups.attributes.rawValue, text: text),
let data = attributesText.data(using: .utf8 ),
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
Expand All @@ -98,13 +178,22 @@ private extension GutenbergBlockProcessor {
return jsonDictionary
}

/// Obtains the block content from a block match.
/// Obtains the block content from a regex match and range.
/// - Parameters:
/// - match: The `NSTextCheckingResult` from a successful regex detection of an opening block tag
/// - closingRange: The `NSRange` of the closing block tag
/// - text: The string that the following parameters are found in.
/// - Returns: The content between the opening and closing tags of a block
///
private func readContent(from match: NSTextCheckingResult, in text: String) -> String {
guard let content = match.captureGroup(in: CaptureGroups.content.rawValue, text: text) else {
func readContent(from match: NSTextCheckingResult, withClosingRange closingRange: NSRange, in text: String) -> String {
guard let index = text.indexFromLocation(match.range.upperBound) else {
return ""
}

guard let closingBound = text.indexFromLocation(closingRange.lowerBound) else {
return ""
}

return content
return String(text[index..<closingBound])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,19 @@ class GutenbergCoverUploadProcessor: Processor {
func process(_ text: String) -> String {
return coverBlockProcessor.process(text)
}

private func processInnerBlocks(_ outerBlock: GutenbergBlock) -> String {
var block = "<!-- wp:cover "
let attributes = outerBlock.attributes

if let jsonData = try? JSONSerialization.data(withJSONObject: attributes, options: [.sortedKeys]),
let jsonString = String(data: jsonData, encoding: .utf8) {
block += jsonString
}

block += " -->"
block += coverBlockProcessor.process(outerBlock.content)
block += "<!-- /wp:cover -->"
return block
}
}
8 changes: 4 additions & 4 deletions WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@
4629E4212440C5B20002E15C /* GutenbergCoverUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4629E4202440C5B20002E15C /* GutenbergCoverUploadProcessor.swift */; };
4629E4232440C8160002E15C /* GutenbergCoverUploadProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4629E4222440C8160002E15C /* GutenbergCoverUploadProcessorTests.swift */; };
462F4E0A18369F0B0028D2F8 /* BlogDetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 462F4E0718369F0B0028D2F8 /* BlogDetailsViewController.m */; };
46638DF6244904A3006E8439 /* GutenbergBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46638DF5244904A3006E8439 /* GutenbergBlockProcessor.swift */; };
4B2DD0F29CD6AC353C056D41 /* Pods_WordPressUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DCE7542239FBC709B90EA85 /* Pods_WordPressUITests.framework */; };
4C8A715EBCE7E73AEE216293 /* Pods_WordPressShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F47DB4A8EC2E6844E213A3FA /* Pods_WordPressShareExtension.framework */; };
4D520D4F22972BC9002F5924 /* acknowledgements.html in Resources */ = {isa = PBXBuildFile; fileRef = 4D520D4E22972BC9002F5924 /* acknowledgements.html */; };
Expand Down Expand Up @@ -2188,7 +2189,6 @@
FF8DDCDF1B5DB1C10098826F /* SettingTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FF8DDCDE1B5DB1C10098826F /* SettingTableViewCell.m */; };
FF945F701B28242300FB8AC4 /* MediaLibraryPickerDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = FF945F6F1B28242300FB8AC4 /* MediaLibraryPickerDataSource.m */; };
FF9A6E7121F9361700D36D14 /* MediaUploadHashTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9A6E7021F9361700D36D14 /* MediaUploadHashTests.swift */; };
FF9C81C42375BA8100DC4B2F /* GutenbergBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF9C81C32375BA8100DC4B2F /* GutenbergBlockProcessor.swift */; };
FFA0B7D71CAC1F9F00533B9D /* MainNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA0B7D61CAC1F9F00533B9D /* MainNavigationTests.swift */; };
FFA162311CB7031A00E2E110 /* AppSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA162301CB7031A00E2E110 /* AppSettingsViewController.swift */; };
FFABD800213423F1003C65B6 /* LinkSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFABD7FF213423F1003C65B6 /* LinkSettingsViewController.swift */; };
Expand Down Expand Up @@ -2894,6 +2894,7 @@
4629E4222440C8160002E15C /* GutenbergCoverUploadProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergCoverUploadProcessorTests.swift; sourceTree = "<group>"; };
462F4E0618369F0B0028D2F8 /* BlogDetailsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogDetailsViewController.h; sourceTree = "<group>"; };
462F4E0718369F0B0028D2F8 /* BlogDetailsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = BlogDetailsViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
46638DF5244904A3006E8439 /* GutenbergBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergBlockProcessor.swift; sourceTree = "<group>"; };
46F84612185A8B7E009D0DA5 /* PostContentProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostContentProvider.h; sourceTree = "<group>"; };
48690E659987FD4472EEDE5F /* Pods-WordPressNotificationContentExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressNotificationContentExtension.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressNotificationContentExtension/Pods-WordPressNotificationContentExtension.release.xcconfig"; sourceTree = "<group>"; };
4D520D4E22972BC9002F5924 /* acknowledgements.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = acknowledgements.html; path = "../Pods/Target Support Files/Pods-WordPress/acknowledgements.html"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4775,7 +4776,6 @@
FF945F6F1B28242300FB8AC4 /* MediaLibraryPickerDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaLibraryPickerDataSource.m; sourceTree = "<group>"; };
FF947A8C1BBE89A100B27B6A /* WordPress 39.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 39.xcdatamodel"; sourceTree = "<group>"; };
FF9A6E7021F9361700D36D14 /* MediaUploadHashTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MediaUploadHashTests.swift; path = Gutenberg/MediaUploadHashTests.swift; sourceTree = "<group>"; };
FF9C81C32375BA8100DC4B2F /* GutenbergBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergBlockProcessor.swift; sourceTree = "<group>"; };
FFA0B7D61CAC1F9F00533B9D /* MainNavigationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainNavigationTests.swift; sourceTree = "<group>"; };
FFA162301CB7031A00E2E110 /* AppSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSettingsViewController.swift; sourceTree = "<group>"; };
FFA40D4D1CB3EDD5001CB1FB /* WordPress 48.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 48.xcdatamodel"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -10236,8 +10236,8 @@
FF2EC3BF2209A144006176E1 /* GutenbergImgUploadProcessor.swift */,
FF1B11E4238FDFE70038B93E /* GutenbergGalleryUploadProcessor.swift */,
91138454228373EB00FB02B7 /* GutenbergVideoUploadProcessor.swift */,
FF9C81C32375BA8100DC4B2F /* GutenbergBlockProcessor.swift */,
4629E4202440C5B20002E15C /* GutenbergCoverUploadProcessor.swift */,
46638DF5244904A3006E8439 /* GutenbergBlockProcessor.swift */,
);
path = Processors;
sourceTree = "<group>";
Expand Down Expand Up @@ -11868,7 +11868,6 @@
4395A1592106389800844E8E /* QuickStartTours.swift in Sources */,
9A2B28EE2191B50500458F2A /* RevisionsTableViewFooter.swift in Sources */,
8BD36E022395CAEA00EFFF1C /* MediaEditorOperation+Description.swift in Sources */,
FF9C81C42375BA8100DC4B2F /* GutenbergBlockProcessor.swift in Sources */,
17F52DB72315233300164966 /* WPStyleGuide+FilterTabBar.swift in Sources */,
E1DD4CCB1CAE41B800C3863E /* PagedViewController.swift in Sources */,
B549BA681CF7447E0086C608 /* InvitePersonViewController.swift in Sources */,
Expand Down Expand Up @@ -12129,6 +12128,7 @@
E66969E21B9E67A000EC9C00 /* ReaderTopicToReaderSiteTopic37to38.swift in Sources */,
B543D2B520570B5A00D3D4CC /* WordPressComSyncService.swift in Sources */,
E14A52371E39F43E00EE203E /* AppRatingsUtility.swift in Sources */,
46638DF6244904A3006E8439 /* GutenbergBlockProcessor.swift in Sources */,
8350E49611D2C71E00A7B073 /* Media.m in Sources */,
D8B9B58F204F4EA1003C6042 /* NetworkAware.swift in Sources */,
B54346961C6A707D0010B3AD /* LanguageViewController.swift in Sources */,
Expand Down
Loading