From e73cb33f2649dff3f5a9ea1c44dddc23a526edbe Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Tue, 16 Jan 2024 15:18:57 +0100 Subject: [PATCH 1/3] Improve conversion of float values on iOS (#126) --- ios/RCTMarkdownStyle.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/RCTMarkdownStyle.mm b/ios/RCTMarkdownStyle.mm index 1cbe7f0f6..0d3ef3dd6 100644 --- a/ios/RCTMarkdownStyle.mm +++ b/ios/RCTMarkdownStyle.mm @@ -49,12 +49,12 @@ - (instancetype)initWithDictionary:(nonnull NSDictionary *)json _linkColor = [RCTConvert UIColor:json[@"link"][@"color"]]; - _h1FontSize = [json[@"h1"][@"fontSize"] floatValue]; + _h1FontSize = [RCTConvert CGFloat:json[@"h1"][@"fontSize"]]; _quoteBorderColor = [RCTConvert UIColor:json[@"quote"][@"borderColor"]]; - _quoteBorderWidth = [json[@"quote"][@"borderWidth"] floatValue]; - _quoteMarginLeft = [json[@"quote"][@"marginLeft"] floatValue]; - _quotePaddingLeft = [json[@"quote"][@"paddingLeft"] floatValue]; + _quoteBorderWidth = [RCTConvert CGFloat:json[@"quote"][@"borderWidth"]]; + _quoteMarginLeft = [RCTConvert CGFloat:json[@"quote"][@"marginLeft"]]; + _quotePaddingLeft = [RCTConvert CGFloat:json[@"quote"][@"paddingLeft"]]; _codeFontFamily = [RCTConvert NSString:json[@"code"][@"fontFamily"]]; _codeColor = [RCTConvert UIColor:json[@"code"][@"color"]]; From 84f5b7e7b285d84cd6415467c3de68f79f5bfcf2 Mon Sep 17 00:00:00 2001 From: Tomek Zawadzki Date: Tue, 16 Jan 2024 15:21:07 +0100 Subject: [PATCH 2/3] Unify names of ExpensiMark styles (#124) --- .../{QuoteSpan.java => BlockquoteSpan.java} | 4 +-- .../expensify/livemarkdown/MarkdownStyle.java | 32 +++++++++---------- .../expensify/livemarkdown/MarkdownUtils.java | 16 +++++----- ios/MarkdownLayoutManager.mm | 14 ++++---- ios/RCTMarkdownStyle.h | 8 ++--- ios/RCTMarkdownStyle.mm | 16 +++++----- ios/RCTMarkdownUtils.h | 2 +- ios/RCTMarkdownUtils.mm | 12 +++---- parser/__tests__/index.test.js | 16 +++++----- parser/index.ts | 2 +- parser/react-native-live-markdown-parser.js | 2 +- src/MarkdownTextInput.tsx | 2 +- ...wnTextInputDecoratorViewNativeComponent.ts | 2 +- 13 files changed, 64 insertions(+), 64 deletions(-) rename android/src/main/java/com/expensify/livemarkdown/{QuoteSpan.java => BlockquoteSpan.java} (89%) diff --git a/android/src/main/java/com/expensify/livemarkdown/QuoteSpan.java b/android/src/main/java/com/expensify/livemarkdown/BlockquoteSpan.java similarity index 89% rename from android/src/main/java/com/expensify/livemarkdown/QuoteSpan.java rename to android/src/main/java/com/expensify/livemarkdown/BlockquoteSpan.java index 3f446b5d4..de969ca83 100644 --- a/android/src/main/java/com/expensify/livemarkdown/QuoteSpan.java +++ b/android/src/main/java/com/expensify/livemarkdown/BlockquoteSpan.java @@ -7,13 +7,13 @@ import com.facebook.react.uimanager.PixelUtil; -public class QuoteSpan implements LeadingMarginSpan { +public class BlockquoteSpan implements LeadingMarginSpan { private final int borderColor; private final float borderWidth; private final float marginLeft; private final float paddingLeft; - public QuoteSpan(int borderColor, float borderWidth, float marginLeft, float paddingLeft) { + public BlockquoteSpan(int borderColor, float borderWidth, float marginLeft, float paddingLeft) { this.borderColor = borderColor; this.borderWidth = PixelUtil.toPixelFromDIP(borderWidth); this.marginLeft = PixelUtil.toPixelFromDIP(marginLeft); diff --git a/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java b/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java index 7db53863a..00d9c11f6 100644 --- a/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java +++ b/android/src/main/java/com/expensify/livemarkdown/MarkdownStyle.java @@ -15,10 +15,10 @@ public class MarkdownStyle { private final int mSyntaxColor; private final int mLinkColor; private final float mH1FontSize; - private final int mQuoteBorderColor; - private final float mQuoteBorderWidth; - private final float mQuoteMarginLeft; - private final float mQuotePaddingLeft; + private final int mBlockquoteBorderColor; + private final float mBlockquoteBorderWidth; + private final float mBlockquoteMarginLeft; + private final float mBlockquotePaddingLeft; private final String mCodeFontFamily; private final int mCodeColor; private final int mCodeBackgroundColor; @@ -32,10 +32,10 @@ public MarkdownStyle(@NonNull ReadableMap map, @NonNull Context context) { mSyntaxColor = parseColor(map, "syntax", "color", context); mLinkColor = parseColor(map, "link", "color", context); mH1FontSize = parseFloat(map, "h1", "fontSize"); - mQuoteBorderColor = parseColor(map, "quote", "borderColor", context); - mQuoteBorderWidth = parseFloat(map, "quote", "borderWidth"); - mQuoteMarginLeft = parseFloat(map, "quote", "marginLeft"); - mQuotePaddingLeft = parseFloat(map, "quote", "paddingLeft"); + mBlockquoteBorderColor = parseColor(map, "blockquote", "borderColor", context); + mBlockquoteBorderWidth = parseFloat(map, "blockquote", "borderWidth"); + mBlockquoteMarginLeft = parseFloat(map, "blockquote", "marginLeft"); + mBlockquotePaddingLeft = parseFloat(map, "blockquote", "paddingLeft"); mCodeFontFamily = parseString(map, "code", "fontFamily"); mCodeColor = parseColor(map, "code", "color", context); mCodeBackgroundColor = parseColor(map, "code", "backgroundColor", context); @@ -85,20 +85,20 @@ public float getH1FontSize() { return mH1FontSize; } - public int getQuoteBorderColor() { - return mQuoteBorderColor; + public int getBlockquoteBorderColor() { + return mBlockquoteBorderColor; } - public float getQuoteBorderWidth() { - return mQuoteBorderWidth; + public float getBlockquoteBorderWidth() { + return mBlockquoteBorderWidth; } - public float getQuoteMarginLeft() { - return mQuoteMarginLeft; + public float getBlockquoteMarginLeft() { + return mBlockquoteMarginLeft; } - public float getQuotePaddingLeft() { - return mQuotePaddingLeft; + public float getBlockquotePaddingLeft() { + return mBlockquotePaddingLeft; } public String getCodeFontFamily() { diff --git a/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java b/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java index 5bf0cbd26..241dfccd5 100644 --- a/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +++ b/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java @@ -144,7 +144,7 @@ private Object makeH1FontSizeSpan() { return new AbsoluteSizeSpan((int) mMarkdownStyle.getH1FontSize(), true); } - private static Object makeHeadingLineHeightSpan(float lineHeight) { + private static Object makeH1LineHeightSpan(float lineHeight) { return (LineHeightSpan) (text, start, end, spanstartv, lh, fm) -> { fm.top -= lineHeight / 4; fm.ascent -= lineHeight / 4; @@ -152,11 +152,11 @@ private static Object makeHeadingLineHeightSpan(float lineHeight) { } private Object makeBlockquoteMarginSpan() { - return new QuoteSpan( - mMarkdownStyle.getQuoteBorderColor(), - mMarkdownStyle.getQuoteBorderWidth(), - mMarkdownStyle.getQuoteMarginLeft(), - mMarkdownStyle.getQuotePaddingLeft()); + return new BlockquoteSpan( + mMarkdownStyle.getBlockquoteBorderColor(), + mMarkdownStyle.getBlockquoteBorderWidth(), + mMarkdownStyle.getBlockquoteMarginLeft(), + mMarkdownStyle.getBlockquotePaddingLeft()); } public void applyMarkdownFormatting(SpannableStringBuilder ssb) { @@ -199,7 +199,7 @@ private void applyRange(SpannableStringBuilder ssb, String type, int start, int case "strikethrough": setSpan(ssb, makeStrikethroughSpan(), start, end); break; - case "mention": + case "mention-here": setSpan(ssb, makeBoldSpan(), start, end); setSpan(ssb, makeMentionHereBackgroundSpan(), start, end); break; @@ -231,7 +231,7 @@ private void applyRange(SpannableStringBuilder ssb, String type, int start, int CustomLineHeightSpan[] spans = ssb.getSpans(0, ssb.length(), CustomLineHeightSpan.class); if (spans.length >= 1) { int lineHeight = spans[0].getLineHeight(); - setSpan(ssb, makeHeadingLineHeightSpan(lineHeight * 1.5f), start, end); + setSpan(ssb, makeH1LineHeightSpan(lineHeight * 1.5f), start, end); } // NOTE: size span must be set after line height span to avoid height jumps setSpan(ssb, makeH1FontSizeSpan(), start, end); diff --git a/ios/MarkdownLayoutManager.mm b/ios/MarkdownLayoutManager.mm index ee9422a13..173628237 100644 --- a/ios/MarkdownLayoutManager.mm +++ b/ios/MarkdownLayoutManager.mm @@ -6,27 +6,27 @@ - (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origi [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin]; [self enumerateLineFragmentsForGlyphRange:glyphsToShow usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) { - __block BOOL isQuote = NO; + __block BOOL isBlockquote = NO; RCTMarkdownUtils *markdownUtils = [self valueForKey:@"markdownUtils"]; - [markdownUtils.quoteRanges enumerateObjectsUsingBlock:^(NSValue *item, NSUInteger idx, BOOL * _Nonnull stop) { + [markdownUtils.blockquoteRanges enumerateObjectsUsingBlock:^(NSValue *item, NSUInteger idx, BOOL * _Nonnull stop) { NSRange range = [item rangeValue]; NSUInteger start = range.location; NSUInteger end = start + range.length; NSUInteger location = glyphRange.location; if (location >= start && location < end) { - isQuote = YES; + isBlockquote = YES; *stop = YES; } }]; - if (isQuote) { + if (isBlockquote) { CGFloat paddingLeft = markdownUtils.backedTextInputView.textContainerInset.left; CGFloat paddingTop = markdownUtils.backedTextInputView.textContainerInset.top; - CGFloat x = paddingLeft + markdownUtils.markdownStyle.quoteMarginLeft; + CGFloat x = paddingLeft + markdownUtils.markdownStyle.blockquoteMarginLeft; CGFloat y = paddingTop + rect.origin.y; - CGFloat width = markdownUtils.markdownStyle.quoteBorderWidth; + CGFloat width = markdownUtils.markdownStyle.blockquoteBorderWidth; CGFloat height = rect.size.height; CGRect lineRect = CGRectMake(x, y, width, height); - [markdownUtils.markdownStyle.quoteBorderColor setFill]; + [markdownUtils.markdownStyle.blockquoteBorderColor setFill]; UIRectFill(lineRect); } }]; diff --git a/ios/RCTMarkdownStyle.h b/ios/RCTMarkdownStyle.h index dfd276864..76f2fe4ea 100644 --- a/ios/RCTMarkdownStyle.h +++ b/ios/RCTMarkdownStyle.h @@ -7,10 +7,10 @@ @property (nonatomic, nonnull) UIColor *syntaxColor; @property (nonatomic, nonnull) UIColor *linkColor; @property (nonatomic) CGFloat h1FontSize; -@property (nonatomic, nonnull) UIColor *quoteBorderColor; -@property (nonatomic) CGFloat quoteBorderWidth; -@property (nonatomic) CGFloat quoteMarginLeft; -@property (nonatomic) CGFloat quotePaddingLeft; +@property (nonatomic, nonnull) UIColor *blockquoteBorderColor; +@property (nonatomic) CGFloat blockquoteBorderWidth; +@property (nonatomic) CGFloat blockquoteMarginLeft; +@property (nonatomic) CGFloat blockquotePaddingLeft; @property (nonatomic, nonnull) NSString *codeFontFamily; @property (nonatomic, nonnull) UIColor *codeColor; @property (nonatomic, nonnull) UIColor *codeBackgroundColor; diff --git a/ios/RCTMarkdownStyle.mm b/ios/RCTMarkdownStyle.mm index 0d3ef3dd6..d91d038e5 100644 --- a/ios/RCTMarkdownStyle.mm +++ b/ios/RCTMarkdownStyle.mm @@ -19,10 +19,10 @@ - (instancetype)initWithStruct:(const facebook::react::MarkdownTextInputDecorato _h1FontSize = style.h1.fontSize; - _quoteBorderColor = RCTUIColorFromSharedColor(style.quote.borderColor); - _quoteBorderWidth = style.quote.borderWidth; - _quoteMarginLeft = style.quote.marginLeft; - _quotePaddingLeft = style.quote.paddingLeft; + _blockquoteBorderColor = RCTUIColorFromSharedColor(style.blockquote.borderColor); + _blockquoteBorderWidth = style.blockquote.borderWidth; + _blockquoteMarginLeft = style.blockquote.marginLeft; + _blockquotePaddingLeft = style.blockquote.paddingLeft; _codeFontFamily = RCTNSStringFromString(style.code.fontFamily); _codeColor = RCTUIColorFromSharedColor(style.code.color); @@ -51,10 +51,10 @@ - (instancetype)initWithDictionary:(nonnull NSDictionary *)json _h1FontSize = [RCTConvert CGFloat:json[@"h1"][@"fontSize"]]; - _quoteBorderColor = [RCTConvert UIColor:json[@"quote"][@"borderColor"]]; - _quoteBorderWidth = [RCTConvert CGFloat:json[@"quote"][@"borderWidth"]]; - _quoteMarginLeft = [RCTConvert CGFloat:json[@"quote"][@"marginLeft"]]; - _quotePaddingLeft = [RCTConvert CGFloat:json[@"quote"][@"paddingLeft"]]; + _quoteBorderColor = [RCTConvert UIColor:json[@"blockquote"][@"borderColor"]]; + _quoteBorderWidth = [RCTConvert CGFloat:json[@"blockquote"][@"borderWidth"]]; + _quoteMarginLeft = [RCTConvert CGFloat:json[@"blockquote"][@"marginLeft"]]; + _quotePaddingLeft = [RCTConvert CGFloat:json[@"blockquote"][@"paddingLeft"]]; _codeFontFamily = [RCTConvert NSString:json[@"code"][@"fontFamily"]]; _codeColor = [RCTConvert UIColor:json[@"code"][@"color"]]; diff --git a/ios/RCTMarkdownUtils.h b/ios/RCTMarkdownUtils.h index fbb9b80de..84508a039 100644 --- a/ios/RCTMarkdownUtils.h +++ b/ios/RCTMarkdownUtils.h @@ -4,7 +4,7 @@ @interface RCTMarkdownUtils : NSObject @property (nonatomic) RCTMarkdownStyle *markdownStyle; -@property (nonatomic) NSMutableArray *quoteRanges; +@property (nonatomic) NSMutableArray *blockquoteRanges; @property (weak, nonatomic) UIView *backedTextInputView; - (instancetype)initWithBackedTextInputView:(UIView *)backedTextInputView; diff --git a/ios/RCTMarkdownUtils.mm b/ios/RCTMarkdownUtils.mm index 9bcd08153..3ab4fde5f 100644 --- a/ios/RCTMarkdownUtils.mm +++ b/ios/RCTMarkdownUtils.mm @@ -55,7 +55,7 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input // This is a workaround that applies the NSUnderlineStyleNone to the string before iterating over ranges which resolves this problem. [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleNone] range:NSMakeRange(0, attributedString.length)]; - _quoteRanges = [NSMutableArray new]; + _blockquoteRanges = [NSMutableArray new]; [ranges enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSArray *item = obj; @@ -64,9 +64,9 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input NSUInteger length = [item[2] unsignedIntegerValue]; NSRange range = NSMakeRange(location, length); - if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"] || [type isEqualToString:@"italic"] || [type isEqualToString:@"code"] || [type isEqualToString:@"pre"] || [type isEqualToString:@"h1"]) { + if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention-here"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"] || [type isEqualToString:@"italic"] || [type isEqualToString:@"code"] || [type isEqualToString:@"pre"] || [type isEqualToString:@"h1"]) { UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:location effectiveRange:NULL]; - if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"]) { + if ([type isEqualToString:@"bold"] || [type isEqualToString:@"mention-here"] || [type isEqualToString:@"mention-user"] || [type isEqualToString:@"syntax"]) { font = [RCTFont updateFont:font withWeight:@"bold"]; } else if ([type isEqualToString:@"italic"]) { font = [RCTFont updateFont:font withStyle:@"italic"]; @@ -92,7 +92,7 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input } else if ([type isEqualToString:@"code"]) { [attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range]; [attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range]; - } else if ([type isEqualToString:@"mention"]) { + } else if ([type isEqualToString:@"mention-here"]) { [attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range]; } else if ([type isEqualToString:@"mention-user"]) { // TODO: change mention color when it mentions current user @@ -101,12 +101,12 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range]; [attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range]; } else if ([type isEqualToString:@"blockquote"]) { - CGFloat indent = _markdownStyle.quoteMarginLeft + _markdownStyle.quoteBorderWidth + _markdownStyle.quotePaddingLeft; + CGFloat indent = _markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft; NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new]; paragraphStyle.firstLineHeadIndent = indent; paragraphStyle.headIndent = indent; [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; - [_quoteRanges addObject:[NSValue valueWithRange:range]]; + [_blockquoteRanges addObject:[NSValue valueWithRange:range]]; } else if ([type isEqualToString:@"pre"]) { [attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range]; NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range; diff --git a/parser/__tests__/index.test.js b/parser/__tests__/index.test.js index aaf8784e3..5e796faf2 100644 --- a/parser/__tests__/index.test.js +++ b/parser/__tests__/index.test.js @@ -48,9 +48,9 @@ test('strikethrough', () => { ]); }); -describe('mention', () => { +describe('mention-here', () => { test('normal', () => { - expect('@here Hello!').toBeParsedAs([['mention', 0, 5]]); + expect('@here Hello!').toBeParsedAs([['mention-here', 0, 5]]); }); test('with additional letters', () => { @@ -58,7 +58,7 @@ describe('mention', () => { }); test('with punctation marks', () => { - expect('@here!').toBeParsedAs([['mention', 0, 5]]); + expect('@here!').toBeParsedAs([['mention-here', 0, 5]]); }); }); @@ -177,7 +177,7 @@ test('codeblock', () => { ]); }); -describe('quote', () => { +describe('blockquote', () => { test('with single space', () => { expect('> Hello world!').toBeParsedAs([ ['syntax', 0, 1], @@ -220,7 +220,7 @@ test('separate blockquotes', () => { ]); }); -test('heading', () => { +test('h1', () => { expect('# Hello world').toBeParsedAs([ ['syntax', 0, 2], ['h1', 2, 11], @@ -244,7 +244,7 @@ test('nested bold and italic', () => { ]); }); -describe('nested heading in blockquote', () => { +describe('nested h1 in blockquote', () => { test('without spaces', () => { expect('># Hello world').toBeParsedAs([ ['syntax', 0, 1], @@ -297,7 +297,7 @@ describe('trailing whitespace', () => { }); }); - describe('after heading', () => { + describe('after h1', () => { test('nothing', () => { expect('# Hello world').toBeParsedAs([ ['syntax', 0, 2], @@ -326,7 +326,7 @@ describe('trailing whitespace', () => { ]); }); - test('multiple quotes', () => { + test('multiple blockquotes', () => { expect('> # Hello\n> # world').toBeParsedAs([ ['syntax', 0, 1], ['blockquote', 0, 9], diff --git a/parser/index.ts b/parser/index.ts index c855b375c..c7b06a6c3 100644 --- a/parser/index.ts +++ b/parser/index.ts @@ -111,7 +111,7 @@ function parseTreeToTextAndRanges(tree: StackItem): [string, Range[]] { addChildrenWithStyle(node, 'code'); appendSyntax('`'); } else if (node.tag === '') { - addChildrenWithStyle(node, 'mention'); + addChildrenWithStyle(node, 'mention-here'); } else if (node.tag === '') { addChildrenWithStyle(node, 'mention-user'); } else if (node.tag === '
') { diff --git a/parser/react-native-live-markdown-parser.js b/parser/react-native-live-markdown-parser.js index a0feb61c8..8d4956fc5 100644 --- a/parser/react-native-live-markdown-parser.js +++ b/parser/react-native-live-markdown-parser.js @@ -31,4 +31,4 @@ $2`},{name:"removeStyle",regex:/