@expensify/react-native-live-markdown 0.1.193 → 0.1.194
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/cpp/MarkdownParser.cpp +35 -0
- package/android/src/main/cpp/{MarkdownUtils.h → MarkdownParser.h} +5 -5
- package/android/src/main/cpp/OnLoad.cpp +2 -2
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownParser.java +71 -0
- package/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java +4 -47
- package/package.json +1 -1
- package/android/src/main/cpp/MarkdownUtils.cpp +0 -33
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#include "MarkdownParser.h"
|
|
2
|
+
#include "MarkdownGlobal.h"
|
|
3
|
+
|
|
4
|
+
#include <fbjni/fbjni.h>
|
|
5
|
+
|
|
6
|
+
using namespace facebook;
|
|
7
|
+
|
|
8
|
+
namespace expensify {
|
|
9
|
+
namespace livemarkdown {
|
|
10
|
+
jni::local_ref<jni::JString> MarkdownParser::nativeParse(
|
|
11
|
+
jni::alias_ref<jhybridobject> jThis,
|
|
12
|
+
jni::alias_ref<jni::JString> text,
|
|
13
|
+
const int parserId) {
|
|
14
|
+
static std::mutex workletRuntimeMutex; // this needs to be global since the worklet runtime is also global
|
|
15
|
+
const auto lock = std::lock_guard<std::mutex>(workletRuntimeMutex);
|
|
16
|
+
|
|
17
|
+
const auto markdownRuntime = expensify::livemarkdown::getMarkdownRuntime();
|
|
18
|
+
jsi::Runtime &rt = markdownRuntime->getJSIRuntime();
|
|
19
|
+
|
|
20
|
+
const auto markdownWorklet = expensify::livemarkdown::getMarkdownWorklet(parserId);
|
|
21
|
+
|
|
22
|
+
const auto input = jsi::String::createFromUtf8(rt, text->toStdString());
|
|
23
|
+
const auto output = markdownRuntime->runGuarded(markdownWorklet, input);
|
|
24
|
+
|
|
25
|
+
const auto json = rt.global().getPropertyAsObject(rt, "JSON").getPropertyAsFunction(rt, "stringify").call(rt, output).asString(rt).utf8(rt);
|
|
26
|
+
return jni::make_jstring(json);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
void MarkdownParser::registerNatives() {
|
|
30
|
+
registerHybrid({
|
|
31
|
+
makeNativeMethod("nativeParse", MarkdownParser::nativeParse)});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
} // namespace livemarkdown
|
|
35
|
+
} // namespace expensify
|
|
@@ -15,16 +15,16 @@ using namespace facebook;
|
|
|
15
15
|
namespace expensify {
|
|
16
16
|
namespace livemarkdown {
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class MarkdownParser : public jni::HybridClass<MarkdownParser>,
|
|
19
19
|
public jsi::HostObject {
|
|
20
20
|
public:
|
|
21
21
|
static constexpr auto kJavaDescriptor =
|
|
22
|
-
"Lcom/expensify/livemarkdown/
|
|
22
|
+
"Lcom/expensify/livemarkdown/MarkdownParser;";
|
|
23
23
|
|
|
24
|
-
static jni::local_ref<jni::JString>
|
|
24
|
+
static jni::local_ref<jni::JString> nativeParse(
|
|
25
25
|
jni::alias_ref<jhybridobject> jThis,
|
|
26
|
-
jni::alias_ref<jni::JString>
|
|
27
|
-
int parserId);
|
|
26
|
+
jni::alias_ref<jni::JString> text,
|
|
27
|
+
const int parserId);
|
|
28
28
|
|
|
29
29
|
static void registerNatives();
|
|
30
30
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#include <fbjni/fbjni.h>
|
|
2
2
|
|
|
3
|
-
#include "
|
|
3
|
+
#include "MarkdownParser.h"
|
|
4
4
|
#include "RuntimeDecorator.h"
|
|
5
5
|
|
|
6
6
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|
7
7
|
return facebook::jni::initialize(
|
|
8
|
-
vm, [] { expensify::livemarkdown::
|
|
8
|
+
vm, [] { expensify::livemarkdown::MarkdownParser::registerNatives(); });
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
extern "C" JNIEXPORT void JNICALL Java_com_expensify_livemarkdown_LiveMarkdownModule_injectJSIBindings(JNIEnv *env, jobject thiz, jlong jsiRuntime) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package com.expensify.livemarkdown;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.bridge.ReactContext;
|
|
6
|
+
import com.facebook.react.util.RNLog;
|
|
7
|
+
|
|
8
|
+
import org.json.JSONArray;
|
|
9
|
+
import org.json.JSONException;
|
|
10
|
+
import org.json.JSONObject;
|
|
11
|
+
|
|
12
|
+
import java.util.Collections;
|
|
13
|
+
import java.util.LinkedList;
|
|
14
|
+
import java.util.List;
|
|
15
|
+
|
|
16
|
+
public class MarkdownParser {
|
|
17
|
+
private final @NonNull ReactContext mReactContext;
|
|
18
|
+
private String mPrevText;
|
|
19
|
+
private int mPrevParserId;
|
|
20
|
+
private List<MarkdownRange> mPrevMarkdownRanges;
|
|
21
|
+
|
|
22
|
+
public MarkdownParser(@NonNull ReactContext reactContext) {
|
|
23
|
+
mReactContext = reactContext;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private native String nativeParse(String text, int parserId);
|
|
27
|
+
|
|
28
|
+
public synchronized List<MarkdownRange> parse(String text, int parserId) {
|
|
29
|
+
if (text.equals(mPrevText) && parserId == mPrevParserId) {
|
|
30
|
+
return mPrevMarkdownRanges;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
String json;
|
|
34
|
+
try {
|
|
35
|
+
json = nativeParse(text, parserId);
|
|
36
|
+
} catch (Exception e) {
|
|
37
|
+
// Skip formatting, runGuarded will show the error in LogBox
|
|
38
|
+
mPrevText = text;
|
|
39
|
+
mPrevParserId = parserId;
|
|
40
|
+
mPrevMarkdownRanges = Collections.emptyList();
|
|
41
|
+
return mPrevMarkdownRanges;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
List<MarkdownRange> markdownRanges = new LinkedList<>();
|
|
45
|
+
try {
|
|
46
|
+
JSONArray ranges = new JSONArray(json);
|
|
47
|
+
for (int i = 0; i < ranges.length(); i++) {
|
|
48
|
+
JSONObject range = ranges.getJSONObject(i);
|
|
49
|
+
String type = range.getString("type");
|
|
50
|
+
int start = range.getInt("start");
|
|
51
|
+
int length = range.getInt("length");
|
|
52
|
+
int depth = range.optInt("depth", 1);
|
|
53
|
+
if (length == 0 || start + length > text.length()) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
markdownRanges.add(new MarkdownRange(type, start, length, depth));
|
|
57
|
+
}
|
|
58
|
+
} catch (JSONException e) {
|
|
59
|
+
RNLog.w(mReactContext, "[react-native-live-markdown] Incorrect schema of worklet parser output: " + e.getMessage());
|
|
60
|
+
mPrevText = text;
|
|
61
|
+
mPrevParserId = parserId;
|
|
62
|
+
mPrevMarkdownRanges = Collections.emptyList();
|
|
63
|
+
return mPrevMarkdownRanges;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
mPrevText = text;
|
|
67
|
+
mPrevParserId = parserId;
|
|
68
|
+
mPrevMarkdownRanges = markdownRanges;
|
|
69
|
+
return mPrevMarkdownRanges;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -8,15 +8,9 @@ import androidx.annotation.NonNull;
|
|
|
8
8
|
|
|
9
9
|
import com.expensify.livemarkdown.spans.*;
|
|
10
10
|
import com.facebook.react.bridge.ReactContext;
|
|
11
|
-
import com.facebook.react.util.RNLog;
|
|
12
11
|
import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
|
|
13
12
|
import com.facebook.soloader.SoLoader;
|
|
14
13
|
|
|
15
|
-
import org.json.JSONArray;
|
|
16
|
-
import org.json.JSONException;
|
|
17
|
-
import org.json.JSONObject;
|
|
18
|
-
|
|
19
|
-
import java.util.LinkedList;
|
|
20
14
|
import java.util.List;
|
|
21
15
|
import java.util.Objects;
|
|
22
16
|
|
|
@@ -25,19 +19,13 @@ public class MarkdownUtils {
|
|
|
25
19
|
SoLoader.loadLibrary("livemarkdown");
|
|
26
20
|
}
|
|
27
21
|
|
|
28
|
-
private static synchronized native String nativeParseMarkdown(String input, int parserId);
|
|
29
|
-
|
|
30
22
|
public MarkdownUtils(@NonNull ReactContext reactContext) {
|
|
31
|
-
mReactContext = reactContext;
|
|
32
23
|
mAssetManager = reactContext.getAssets();
|
|
24
|
+
mMarkdownParser = new MarkdownParser(reactContext);
|
|
33
25
|
}
|
|
34
26
|
|
|
35
|
-
private final @NonNull ReactContext mReactContext;
|
|
36
27
|
private final @NonNull AssetManager mAssetManager;
|
|
37
|
-
|
|
38
|
-
private String mPrevInput;
|
|
39
|
-
private String mPrevOutput;
|
|
40
|
-
private int mPrevParserId;
|
|
28
|
+
private final @NonNull MarkdownParser mMarkdownParser;
|
|
41
29
|
|
|
42
30
|
private MarkdownStyle mMarkdownStyle;
|
|
43
31
|
private int mParserId;
|
|
@@ -55,39 +43,8 @@ public class MarkdownUtils {
|
|
|
55
43
|
|
|
56
44
|
removeSpans(ssb);
|
|
57
45
|
|
|
58
|
-
String
|
|
59
|
-
|
|
60
|
-
if (input.equals(mPrevInput) && mParserId == mPrevParserId) {
|
|
61
|
-
output = mPrevOutput;
|
|
62
|
-
} else {
|
|
63
|
-
try {
|
|
64
|
-
output = nativeParseMarkdown(input, mParserId);
|
|
65
|
-
} catch (Exception e) {
|
|
66
|
-
output = "[]";
|
|
67
|
-
}
|
|
68
|
-
mPrevInput = input;
|
|
69
|
-
mPrevOutput = output;
|
|
70
|
-
mPrevParserId = mParserId;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
List<MarkdownRange> markdownRanges = new LinkedList<>();
|
|
74
|
-
try {
|
|
75
|
-
JSONArray ranges = new JSONArray(output);
|
|
76
|
-
for (int i = 0; i < ranges.length(); i++) {
|
|
77
|
-
JSONObject range = ranges.getJSONObject(i);
|
|
78
|
-
String type = range.getString("type");
|
|
79
|
-
int start = range.getInt("start");
|
|
80
|
-
int length = range.getInt("length");
|
|
81
|
-
int depth = range.optInt("depth", 1);
|
|
82
|
-
int end = start + length;
|
|
83
|
-
if (length == 0 || end > input.length()) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
markdownRanges.add(new MarkdownRange(type, start, length, depth));
|
|
87
|
-
}
|
|
88
|
-
} catch (JSONException e) {
|
|
89
|
-
RNLog.w(mReactContext, "[react-native-live-markdown] Incorrect schema of worklet parser output: " + e.getMessage());
|
|
90
|
-
}
|
|
46
|
+
String text = ssb.toString();
|
|
47
|
+
List<MarkdownRange> markdownRanges = mMarkdownParser.parse(text, mParserId);
|
|
91
48
|
|
|
92
49
|
for (MarkdownRange markdownRange : markdownRanges) {
|
|
93
50
|
applyRange(ssb, markdownRange);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expensify/react-native-live-markdown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.194",
|
|
4
4
|
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#include "MarkdownUtils.h"
|
|
2
|
-
#include "MarkdownGlobal.h"
|
|
3
|
-
|
|
4
|
-
#include <fbjni/fbjni.h>
|
|
5
|
-
|
|
6
|
-
using namespace facebook;
|
|
7
|
-
|
|
8
|
-
namespace expensify {
|
|
9
|
-
namespace livemarkdown {
|
|
10
|
-
jni::local_ref<jni::JString> MarkdownUtils::nativeParseMarkdown(
|
|
11
|
-
jni::alias_ref<jhybridobject> jThis,
|
|
12
|
-
jni::alias_ref<jni::JString> input,
|
|
13
|
-
int parserId) {
|
|
14
|
-
// This method is synchronized (see MarkdownUtils.java) so we don't need a mutex here.
|
|
15
|
-
const auto markdownRuntime = expensify::livemarkdown::getMarkdownRuntime();
|
|
16
|
-
jsi::Runtime &rt = markdownRuntime->getJSIRuntime();
|
|
17
|
-
|
|
18
|
-
const auto markdownWorklet = expensify::livemarkdown::getMarkdownWorklet(parserId);
|
|
19
|
-
|
|
20
|
-
const auto text = jsi::String::createFromUtf8(rt, input->toStdString());
|
|
21
|
-
const auto result = markdownRuntime->runGuarded(markdownWorklet, text);
|
|
22
|
-
|
|
23
|
-
const auto json = rt.global().getPropertyAsObject(rt, "JSON").getPropertyAsFunction(rt, "stringify").call(rt, result).asString(rt).utf8(rt);
|
|
24
|
-
return jni::make_jstring(json);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
void MarkdownUtils::registerNatives() {
|
|
28
|
-
registerHybrid({
|
|
29
|
-
makeNativeMethod("nativeParseMarkdown", MarkdownUtils::nativeParseMarkdown)});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
} // namespace livemarkdown
|
|
33
|
-
} // namespace expensify
|