Skip to content

Commit 7b79fbf

Browse files
committed
Allow async JavaScript evaluation
1 parent 8f3ed42 commit 7b79fbf

File tree

5 files changed

+85
-24
lines changed

5 files changed

+85
-24
lines changed

MarkEditKit/Sources/Extensions/WKWebView+Extension.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@ import MarkEditCore
1212
*/
1313

1414
public extension WKWebView {
15+
func evaluateJavaScript(
16+
_ script: String,
17+
callAsync: Bool,
18+
completionHandler: (@MainActor @Sendable (Any?, (any Error)?) -> Void)? = nil
19+
) {
20+
if callAsync {
21+
callAsyncJavaScript(script, in: nil, in: .page) { result in
22+
switch result {
23+
case .success(let value):
24+
completionHandler?(value, nil)
25+
case .failure(let error):
26+
completionHandler?(nil, error)
27+
}
28+
}
29+
} else {
30+
evaluateJavaScript(script, completionHandler: completionHandler)
31+
}
32+
}
33+
1534
func showInspector() {
1635
let objectSel = sel_getUid("_inspector")
1736
let methodSel = sel_getUid("show")

MarkEditMac/Resources/Localizable.xcstrings

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,22 @@
177177
}
178178
}
179179
},
180+
"Allows the evaluation to return a Promise object. When enabled, the JavaScript must return a value." : {
181+
"localizations" : {
182+
"zh-Hans" : {
183+
"stringUnit" : {
184+
"state" : "translated",
185+
"value" : "允许代码执行返回一个 Promise 对象。启用时,JavaScript 代码必须返回一个值。"
186+
}
187+
},
188+
"zh-Hant" : {
189+
"stringUnit" : {
190+
"state" : "translated",
191+
"value" : "允許程式碼執行返回一個 Promise 物件。啟用時,JavaScript 程式碼必須返回一個值。"
192+
}
193+
}
194+
}
195+
},
180196
"Always" : {
181197
"comment" : "Always show invisibles",
182198
"localizations" : {
@@ -296,6 +312,22 @@
296312
}
297313
}
298314
},
315+
"Call Async JavaScript" : {
316+
"localizations" : {
317+
"zh-Hans" : {
318+
"stringUnit" : {
319+
"state" : "translated",
320+
"value" : "调用异步 JavaScript"
321+
}
322+
},
323+
"zh-Hant" : {
324+
"stringUnit" : {
325+
"state" : "translated",
326+
"value" : "呼叫非同步 JavaScript"
327+
}
328+
}
329+
}
330+
},
299331
"Cancel" : {
300332
"comment" : "Button title, cancel an action",
301333
"localizations" : {
@@ -556,34 +588,34 @@
556588
}
557589
}
558590
},
559-
"Creates a new document, with optional parameters to set the file name and the initial content." : {
591+
"Create New Document" : {
560592
"localizations" : {
561593
"zh-Hans" : {
562594
"stringUnit" : {
563595
"state" : "translated",
564-
"value" : "新建一个文档,可通过可选参数来设置文件名和初始内容。"
596+
"value" : "创建新文档"
565597
}
566598
},
567599
"zh-Hant" : {
568600
"stringUnit" : {
569601
"state" : "translated",
570-
"value" : "新建一個文件,可透過可選變數來設定檔案名和初始內容。"
602+
"value" : "建立新文件"
571603
}
572604
}
573605
}
574606
},
575-
"Create New Document" : {
607+
"Creates a new document, with optional parameters to set the file name and the initial content." : {
576608
"localizations" : {
577609
"zh-Hans" : {
578610
"stringUnit" : {
579611
"state" : "translated",
580-
"value" : "创建新文档"
612+
"value" : "新建一个文档,可通过可选参数来设置文件名和初始内容。"
581613
}
582614
},
583615
"zh-Hant" : {
584616
"stringUnit" : {
585617
"state" : "translated",
586-
"value" : "建立新文件"
618+
"value" : "新建一個文件,可透過可選變數來設定檔案名和初始內容。"
587619
}
588620
}
589621
}
@@ -893,35 +925,35 @@
893925
}
894926
}
895927
},
896-
"Evaluates JavaScript and gets the result on the active document; throws an error if no editor is opened." : {
928+
"Evaluate JavaScript with ${content}" : {
929+
"extractionState" : "manual",
897930
"localizations" : {
898931
"zh-Hans" : {
899932
"stringUnit" : {
900933
"state" : "translated",
901-
"value" : "在当前活跃文档运行 JavaScript 并获取结果,没有打开的编辑器时将抛出异常。"
934+
"value" : "使用 ${content} 运行 JavaScript"
902935
}
903936
},
904937
"zh-Hant" : {
905938
"stringUnit" : {
906939
"state" : "translated",
907-
"value" : "在當前活躍文件執行 JavaScript 並獲取結果,沒有開啟的編輯器時將丟擲異常。"
940+
"value" : "使用 ${content} 執行 JavaScript"
908941
}
909942
}
910943
}
911944
},
912-
"Evaluate JavaScript with ${content}" : {
913-
"extractionState" : "manual",
945+
"Evaluates JavaScript and gets the result on the active document; throws an error if no editor is opened." : {
914946
"localizations" : {
915947
"zh-Hans" : {
916948
"stringUnit" : {
917949
"state" : "translated",
918-
"value" : "使用 ${content} 运行 JavaScript"
950+
"value" : "在当前活跃文档运行 JavaScript 并获取结果,没有打开的编辑器时将抛出异常。"
919951
}
920952
},
921953
"zh-Hant" : {
922954
"stringUnit" : {
923955
"state" : "translated",
924-
"value" : "使用 ${content} 執行 JavaScript"
956+
"value" : "在當前活躍文件執行 JavaScript 並獲取結果,沒有開啟的編輯器時將丟擲異常。"
925957
}
926958
}
927959
}
@@ -3098,35 +3130,35 @@
30983130
}
30993131
}
31003132
},
3101-
"Updates file content of the active document; throws an error if no editor is opened." : {
3133+
"Update file with ${content}" : {
3134+
"extractionState" : "manual",
31023135
"localizations" : {
31033136
"zh-Hans" : {
31043137
"stringUnit" : {
31053138
"state" : "translated",
3106-
"value" : "更新当前活跃文档的内容,没有打开的编辑器时将抛出异常。"
3139+
"value" : "使用 ${content} 更新文件"
31073140
}
31083141
},
31093142
"zh-Hant" : {
31103143
"stringUnit" : {
31113144
"state" : "translated",
3112-
"value" : "更新當前活躍檔案的內容,沒有開啟的編輯器時將丟擲異常。"
3145+
"value" : "使用 ${content} 更新檔案"
31133146
}
31143147
}
31153148
}
31163149
},
3117-
"Update file with ${content}" : {
3118-
"extractionState" : "manual",
3150+
"Updates file content of the active document; throws an error if no editor is opened." : {
31193151
"localizations" : {
31203152
"zh-Hans" : {
31213153
"stringUnit" : {
31223154
"state" : "translated",
3123-
"value" : "使用 ${content} 更新文件"
3155+
"value" : "更新当前活跃文档的内容,没有打开的编辑器时将抛出异常。"
31243156
}
31253157
},
31263158
"zh-Hant" : {
31273159
"stringUnit" : {
31283160
"state" : "translated",
3129-
"value" : "使用 ${content} 更新檔案"
3161+
"value" : "更新當前活躍檔案的內容,沒有開啟的編輯器時將丟擲異常。"
31303162
}
31313163
}
31323164
}
@@ -3342,4 +3374,4 @@
33423374
}
33433375
},
33443376
"version" : "1.0"
3345-
}
3377+
}

MarkEditMac/Resources/MarkEdit.sdef

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
<parameter type="text" name="JavaScript" code="mdjs" description="The JavaScript code to evaluate.">
8484
<cocoa key="script"/>
8585
</parameter>
86+
<parameter type="boolean" name="callAsyncJavaScript" code="cajs" default="false" description="Allows the evaluation to return a Promise object. When enabled, the JavaScript must return a value."/>
8687
<result description="The value returned once the script finishes executing.">
8788
<type type="any"/>
8889
</result>

MarkEditMac/Sources/Scripting/EditorDocument+Scripting.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ extension EditorDocument {
9393
return nil
9494
}
9595

96+
let callAsync = command.evaluatedArguments?["callAsyncJavaScript"] as? Bool ?? false
9697
command.suspendExecution()
97-
targetEditor.webView.evaluateJavaScript(script) { value, error in
98+
99+
targetEditor.webView.evaluateJavaScript(script, callAsync: callAsync) { value, error in
98100
if let error = error as NSError? {
99101
ScriptingError.jsEvaluationError(error).applyToCommand(command)
100102
command.resumeExecution(withResult: NSAppleEventDescriptor.null())

MarkEditMac/Sources/Shortcuts/Intents/EvaluateJavaScriptIntent.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@
66
//
77

88
import AppIntents
9+
import MarkEditKit
910

1011
struct EvaluateJavaScriptIntent: AppIntent {
1112
static let title: LocalizedStringResource = "Evaluate JavaScript"
1213
static let description = IntentDescription("Evaluates JavaScript and gets the result on the active document; throws an error if no editor is opened.")
14+
1315
static var parameterSummary: some ParameterSummary {
14-
Summary("Evaluate JavaScript with \(\.$content)")
16+
Summary("Evaluate JavaScript with \(\.$content)") {
17+
\.$callAsync
18+
}
1519
}
1620

1721
@Parameter(title: "Content", inputOptions: String.IntentInputOptions(capitalizationType: .none, multiline: true, autocorrect: false, smartQuotes: false, smartDashes: false))
1822
var content: String
1923

24+
@Parameter(title: "Call Async JavaScript", description: "Allows the evaluation to return a Promise object. When enabled, the JavaScript must return a value.", default: false)
25+
var callAsync: Bool
26+
2027
@MainActor
2128
func perform() async throws -> some ReturnsValue<String> {
2229
guard let currentEditor else {
@@ -26,7 +33,7 @@ struct EvaluateJavaScriptIntent: AppIntent {
2633
// We do not directly use the async version of evaluateJavaScript,
2734
// mainly because that it **sometimes** emits Optional(nil) unwrapping error.
2835
return try await withCheckedThrowingContinuation { continuation in
29-
currentEditor.webView.evaluateJavaScript(content) { value, error in
36+
currentEditor.webView.evaluateJavaScript(content, callAsync: callAsync) { value, error in
3037
// Here we have to deal with this typing hell
3138
continuation.resume(with: .success(.result(value: String(describing: value ?? error ?? "undefined"))))
3239
}

0 commit comments

Comments
 (0)