Skip to content

Commit 414d917

Browse files
committed
Convert InvokeScript to autoserialize and use new JavaScript Wrapper
Fixes #16 Should be able to clean-up #13 later now.
1 parent 1c4dbc0 commit 414d917

File tree

10 files changed

+146
-63
lines changed

10 files changed

+146
-63
lines changed

MonacoEditorComponent/CodeEditor.Events.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ private async void _themeListener_ThemeChanged(ThemeListener sender)
105105
{
106106
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
107107
{
108-
await this.InvokeScriptAsync("changeTheme", sender.CurrentTheme.ToString(), sender.IsHighContrast.ToString());
108+
await this.InvokeScriptAsync("changeTheme", args: new string[] { sender.CurrentTheme.ToString(), sender.IsHighContrast.ToString() });
109109
});
110110
}
111111

MonacoEditorComponent/CodeEditor.Methods.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public IAsyncAction RevealRangeInCenterIfOutsideViewportAsync(IRange range)
103103
public IAsyncAction AddActionAsync(IActionDescriptor action)
104104
{
105105
_parentAccessor.RegisterAction("Action" + action.Id, new Action(() => { action?.Run(this); }));
106-
return this.InvokeScriptAsync("addAction", JsonConvert.SerializeObject(action)).AsAsyncAction();
106+
return this.InvokeScriptAsync("addAction", action).AsAsyncAction();
107107
}
108108

109109
public IAsyncOperation<string> AddCommandAsync(int keybinding, CommandHandler handler)
@@ -115,14 +115,14 @@ public IAsyncOperation<string> AddCommandAsync(int keybinding, CommandHandler ha
115115
{
116116
var name = "Command" + keybinding;
117117
_parentAccessor.RegisterAction(name, new Action(() => { handler?.Invoke(); }));
118-
return this.InvokeScriptAsync("addCommand", keybinding.ToString(), name, context).AsAsyncOperation();
118+
return this.InvokeScriptAsync("addCommand", new object[] { keybinding, name, context }).AsAsyncOperation();
119119
}
120120

121121
public IAsyncOperation<ContextKey> CreateContextKeyAsync(string key, bool defaultValue)
122122
{
123123
var ck = new ContextKey(this, key, defaultValue);
124124

125-
return this.InvokeScriptAsync("createContext", JsonConvert.SerializeObject(ck)).ContinueWith((noop) =>
125+
return this.InvokeScriptAsync("createContext", ck).ContinueWith((noop) =>
126126
{
127127
return ck;
128128
}).AsAsyncOperation();
@@ -133,7 +133,7 @@ public IModel GetModel()
133133
return this._model;
134134
}
135135

136-
public IAsyncOperation<IEnumerable<Marker>> GetModelMarkers() // TODO: Filter (string? owner, Uri? resource, int? take)
136+
public IAsyncOperation<IEnumerable<Marker>> GetModelMarkersAsync() // TODO: Filter (string? owner, Uri? resource, int? take)
137137
{
138138
return this.SendScriptAsync("JSON.stringify(monaco.editor.getModelMarkers());").ContinueWith((result) =>
139139
{
@@ -147,9 +147,9 @@ public IAsyncOperation<IEnumerable<Marker>> GetModelMarkers() // TODO: Filter (s
147147
}).AsAsyncOperation();
148148
}
149149

150-
public IAsyncAction SetModelMarkers(string owner, [ReadOnlyArray] IMarkerData[] markers)
150+
public IAsyncAction SetModelMarkersAsync(string owner, [ReadOnlyArray] IMarkerData[] markers)
151151
{
152-
return this.SendScriptAsync("monaco.editor.setModelMarkers(model, JSON.parse('" + JsonConvert.ToString(owner) + "'), JSON.parse('" + JsonConvert.SerializeObject(markers) + "'));").AsAsyncAction();
152+
return this.SendScriptAsync("monaco.editor.setModelMarkers(model, " + JsonConvert.ToString(owner) + ", " + JsonConvert.SerializeObject(markers) + ");").AsAsyncAction();
153153
}
154154

155155
public IAsyncOperation<Position> GetPositionAsync()
@@ -175,13 +175,14 @@ public IAsyncOperation<Position> GetPositionAsync()
175175
/// <returns></returns>
176176
private IAsyncAction DeltaDecorationsHelperAsync([ReadOnlyArray] IModelDeltaDecoration[] newDecorations)
177177
{
178-
var newDecorationsAdjust = newDecorations ?? new IModelDeltaDecoration[0];
178+
var newDecorationsAdjust = newDecorations ?? Array.Empty<IModelDeltaDecoration>();
179179

180180
// Update Styles
181181
return InvokeScriptAsync("updateStyle", CssStyleBroker.Instance.GetStyles()).ContinueWith((noop) =>
182182
{
183183
// Send Command to Modify Decorations
184-
return InvokeScriptAsync("updateDecorations", Json.ObjectArray(newDecorationsAdjust.Cast<IJsonable>()));
184+
// IMPORTANT: Need to cast to object here as we want this to be a single array object passed as a parameter, not a list of parameters to expand.
185+
return InvokeScriptAsync("updateDecorations", (object)newDecorationsAdjust);
185186
}).AsAsyncAction();
186187
}
187188
}

MonacoEditorComponent/CodeEditor.Properties.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ public static DependencyProperty DecorationsProperty
217217

218218
/// <summary>
219219
/// Gets or sets the hint Markers.
220-
/// Note: This property is a helper for <see cref="SetModelMarkers(string, IMarkerData[])"/>; use this property or the method, not both.
220+
/// Note: This property is a helper for <see cref="SetModelMarkersAsync(string, IMarkerData[])"/>; use this property or the method, not both.
221221
/// </summary>
222222
public IObservableVector<IMarkerData> Markers
223223
{
@@ -242,15 +242,15 @@ public IObservableVector<IMarkerData> Markers
242242
e.NewValue == null)
243243
{
244244
// TODO: Can I simplify this in this case?
245-
await editor.SetModelMarkers("CodeEditor", Array.Empty<IMarkerData>());
245+
await editor.SetModelMarkersAsync("CodeEditor", Array.Empty<IMarkerData>());
246246
}
247247
var value = e.NewValue as IObservableVector<IMarkerData>;
248248

249249
if (value != null)
250250
{
251251
if (value.Count > 0)
252252
{
253-
await editor.SetModelMarkers("CodeEditor", value.ToArray());
253+
await editor.SetModelMarkersAsync("CodeEditor", value.ToArray());
254254
}
255255

256256
value.VectorChanged += async (s, cce) =>
@@ -261,7 +261,7 @@ public IObservableVector<IMarkerData> Markers
261261
// Need to recall mutex as this is called from outside of this initial callback setting it up.
262262
using (await editor._mutexMarkers.LockAsync())
263263
{
264-
await editor.SetModelMarkers("CodeEditor", collection.ToArray());
264+
await editor.SetModelMarkersAsync("CodeEditor", collection.ToArray());
265265
}
266266
}
267267
};

MonacoEditorComponent/CodeEditor.cs

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public CodeEditor()
6767
this.Options.PropertyChanged += async (s, p) =>
6868
{
6969
// TODO: Check for Language property and call other method instead?
70-
await this.InvokeScriptAsync("updateOptions", new string[] { (s as IEditorConstructionOptions)?.ToJson() });
70+
await this.InvokeScriptAsync("updateOptions", s);
7171
};
7272
}
7373

@@ -111,11 +111,11 @@ internal async Task<string> SendScriptAsync(string script,
111111
{
112112
try
113113
{
114-
return await this._view.RunScriptAsync(script);
114+
return await this._view.RunScriptAsync(script, member, file, line);
115115
}
116116
catch (Exception e)
117117
{
118-
InternalException?.Invoke(this, new JavaScriptExecutionException(member, file, line, script, e));
118+
InternalException?.Invoke(this, e);
119119
}
120120
}
121121
else
@@ -128,35 +128,34 @@ internal async Task<string> SendScriptAsync(string script,
128128
return string.Empty;
129129
}
130130

131-
internal async Task<string> InvokeScriptAsync(string method, params string[] args)
131+
internal async Task<string> InvokeScriptAsync(
132+
string method,
133+
object arg,
134+
[CallerMemberName] string member = null,
135+
[CallerFilePath] string file = null,
136+
[CallerLineNumber] int line = 0,
137+
bool serialize = true)
138+
{
139+
return await this.InvokeScriptAsync(method, new object[] { arg }, member, file, line, serialize);
140+
}
141+
142+
internal async Task<string> InvokeScriptAsync(
143+
string method,
144+
object[] args,
145+
[CallerMemberName] string member = null,
146+
[CallerFilePath] string file = null,
147+
[CallerLineNumber] int line = 0,
148+
bool serialize = true)
132149
{
133150
if (_initialized)
134151
{
135-
if (Dispatcher.HasThreadAccess)
152+
try
136153
{
137-
try
138-
{
139-
return await this._view.InvokeScriptAsync(method, args);
140-
}
141-
catch (Exception e)
142-
{
143-
InternalException?.Invoke(this, e);
144-
}
154+
return await this._view.InvokeScriptAsync(method, member, file, line, serialize, args);
145155
}
146-
else
156+
catch (Exception e)
147157
{
148-
return await Dispatcher.RunTaskAsync(async () =>
149-
{
150-
try
151-
{
152-
return await this._view.InvokeScriptAsync(method, args);
153-
}
154-
catch (Exception e)
155-
{
156-
InternalException?.Invoke(this, e);
157-
return string.Empty;
158-
}
159-
});
158+
InternalException?.Invoke(this, e);
160159
}
161160
}
162161
else

MonacoEditorComponent/Extensions/WebViewExtensions.cs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using Monaco.Helpers;
2+
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Serialization;
24
using System;
5+
using System.Linq;
36
using System.Runtime.CompilerServices;
7+
using System.Runtime.Serialization;
48
using System.Threading.Tasks;
59
using Windows.Data.Json;
610
using Windows.UI.Xaml.Controls;
@@ -61,6 +65,51 @@ private static async Task<string> RunScriptHelperAsync(WebView _view, string scr
6165

6266
return returnstring;
6367
}
68+
69+
private static JsonSerializerSettings _settings = new JsonSerializerSettings()
70+
{
71+
NullValueHandling = NullValueHandling.Ignore,
72+
ContractResolver = new CamelCasePropertyNamesContractResolver()
73+
};
74+
75+
public static async Task<string> InvokeScriptAsync(
76+
this WebView _view,
77+
string method,
78+
[CallerMemberName] string member = null,
79+
[CallerFilePath] string file = null,
80+
[CallerLineNumber] int line = 0,
81+
bool serialize = true,
82+
params object[] args) // TODO: Figure out how to actually make 'params' work here, possible?
83+
{
84+
string[] sanitizedargs;
85+
86+
if (serialize)
87+
{
88+
sanitizedargs = args.Select(item =>
89+
{
90+
if (item is int || item is double)
91+
{
92+
return item.ToString();
93+
}
94+
else if (item is string)
95+
{
96+
return JsonConvert.ToString(item);
97+
}
98+
else
99+
{
100+
return JsonConvert.SerializeObject(item, _settings);
101+
}
102+
}).ToArray();
103+
}
104+
else
105+
{
106+
sanitizedargs = args.Select(item => item.ToString()).ToArray();
107+
}
108+
109+
var script = method + "(" + string.Join(",", sanitizedargs) + ");";
110+
111+
return await RunScriptAsync(_view, script, member, file, line);
112+
}
64113
}
65114

66115
internal sealed class JavaScriptExecutionException : Exception
@@ -74,7 +123,7 @@ internal sealed class JavaScriptExecutionException : Exception
74123
public int LineNumber { get; private set; }
75124

76125
public JavaScriptExecutionException(string member, string filename, int line, string script, Exception inner)
77-
: base("Error Executing JavaScript Code", inner)
126+
: base("Error Executing JavaScript Code for " + member + "\nLine " + line + " of " + filename + "\n" + script + "\n", inner)
78127
{
79128
this.Member = member;
80129
this.FileName = filename;

MonacoEditorComponent/Monaco/Editor/ContextKey.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public ContextKey(CodeEditor editor, string key, bool defaultValue)
3131

3232
private async void UpdateValueAsync()
3333
{
34-
await _editor.InvokeScriptAsync("updateContext", Key, JsonConvert.SerializeObject(Value));
34+
await _editor.InvokeScriptAsync("updateContext", new object[] { Key, Value });
3535
}
3636

3737
public bool Get()

MonacoEditorComponent/Monaco/Editor/IModelDecorationOptions.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Monaco.Helpers;
2+
using Newtonsoft.Json;
23
using System;
34
using System.Collections.Generic;
45
using System.Linq;
@@ -14,11 +15,25 @@ namespace Monaco.Editor
1415
#pragma warning disable CS1591
1516
public sealed class IModelDecorationOptions: IJsonable
1617
{
18+
[JsonProperty("isWholeLine")]
1719
public bool IsWholeLine { get; set; }
20+
21+
[JsonProperty("hoverMessage")]
1822
public string[] HoverMessage { get; set; }
23+
24+
[JsonProperty("glyphMarginHoverMessage")]
1925
public string[] GlyphMarginHoverMessage { get; set; }
26+
27+
[JsonConverter(typeof(CssStyleConverter))]
28+
[JsonProperty("className")]
2029
public CssLineStyle ClassName { get; set; }
30+
31+
[JsonConverter(typeof(CssStyleConverter))]
32+
[JsonProperty("glyphMarginClassName")]
2133
public CssGlyphStyle GlyphMarginClassName { get; set; }
34+
35+
[JsonConverter(typeof(CssStyleConverter))]
36+
[JsonProperty("inlineClassName")]
2237
public CssInlineStyle InlineClassName { get; set; }
2338

2439
// TODO: Use JsonConvert
@@ -61,4 +76,28 @@ public string ToJson()
6176
}
6277
}
6378
#pragma warning restore CS1591
79+
80+
internal class CssStyleConverter: JsonConverter
81+
{
82+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
83+
{
84+
var style = value as ICssStyle;
85+
writer.WriteValue(style.Name);
86+
}
87+
88+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
89+
{
90+
throw new NotImplementedException("Unnecessary because CanRead is false.");
91+
}
92+
93+
public override bool CanRead
94+
{
95+
get { return false; }
96+
}
97+
98+
public override bool CanConvert(Type objectType)
99+
{
100+
return objectType == typeof(ICssStyle);
101+
}
102+
}
64103
}

MonacoEditorComponent/Monaco/Editor/IModelDeltaDecoration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Monaco.Helpers;
2+
using Newtonsoft.Json;
23
using System;
34
using System.Collections.Generic;
45
using System.Linq;

MonacoEditorComponent/MonacoEditor.html

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@
6565
Parent.callAction("Loaded");
6666
});
6767

68-
var addAction = function (actionStr) {
69-
let action = JSON.parse(actionStr);
70-
68+
var addAction = function (action) {
7169
action.run = function (ed) {
7270
Parent.callAction("Action" + action.id)
7371
};
@@ -81,15 +79,14 @@
8179
}, context);
8280
};
8381

84-
var createContext = function (contextStr) {
85-
let context = JSON.parse(contextStr);
82+
var createContext = function (context) {
8683
if (context) {
8784
contexts[context.key] = editor.createContextKey(context.key, context.defaultValue);
8885
}
8986
};
9087

9188
var updateContext = function (key, value) {
92-
contexts[key].set(JSON.parse(value));
89+
contexts[key].set(value);
9390
}
9491

9592
var updateContent = function (content) {
@@ -100,7 +97,11 @@
10097
};
10198

10299
var updateDecorations = function (newHighlights) {
103-
decorations = editor.deltaDecorations(decorations, JSON.parse(newHighlights));
100+
if (newHighlights) {
101+
decorations = editor.deltaDecorations(decorations, newHighlights);
102+
} else {
103+
decorations = editor.deltaDecorations(decorations, []);
104+
}
104105
};
105106

106107
var updateStyle = function (innerStyle) {
@@ -123,14 +124,7 @@
123124
return {};
124125
};
125126

126-
var updateOptions = function (newOptions) {
127-
let opt = null;
128-
try {
129-
opt = JSON.parse(newOptions);
130-
} finally {
131-
132-
}
133-
127+
var updateOptions = function (opt) {
134128
if (opt != null && typeof opt === "object") {
135129
editor.updateOptions(opt);
136130
}

0 commit comments

Comments
 (0)