An editor for Microsoft Adaptive Cards that supports the new templating language and DOESN'T use JavaScript, because JavaScript isn't a real programming language.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

311 lines
12 KiB

using Monaco.Editor;
using Monaco.Helpers;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
namespace Monaco
{
partial class CodeEditor : IParentAccessorAcceptor
{
public bool IsSettingValue { get; set; }
/// <summary>
/// Get or Set the CodeEditor Text.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty TextPropertyField =
DependencyProperty.Register("Text", typeof(string), typeof(CodeEditor), new PropertyMetadata(string.Empty, (d, e) => {
if (!(d as CodeEditor).IsSettingValue)
{
(d as CodeEditor)?.InvokeScriptAsync("updateContent", e.NewValue.ToString());
}
}));
public static DependencyProperty TextProperty
{
get
{
return TextPropertyField;
}
}
/// <summary>
/// Get the current Primary Selected CodeEditor Text.
/// </summary>
public string SelectedText
{
get { return (string)GetValue(SelectedTextProperty); }
set { SetValue(SelectedTextProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty SelectedTextPropertyField =
DependencyProperty.Register(nameof(SelectedText), typeof(string), typeof(CodeEditor), new PropertyMetadata(string.Empty, (d, e) => {
if (!(d as CodeEditor).IsSettingValue)
{
(d as CodeEditor)?.InvokeScriptAsync("updateSelectedContent", e.NewValue.ToString());
}
}));
public static DependencyProperty SelectedTextProperty
{
get
{
return SelectedTextPropertyField;
}
}
public Selection SelectedRange
{
get { return (Selection)GetValue(SelectedRangeProperty); }
set { SetValue(SelectedRangeProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedRange. This enables animation, styling, binding, etc...
private static readonly DependencyProperty SelectedRangePropertyField =
DependencyProperty.Register(nameof(SelectedRange), typeof(Selection), typeof(CodeEditor), new PropertyMetadata(null));
public static DependencyProperty SelectedRangeProperty
{
get
{
return SelectedRangePropertyField;
}
}
/// <summary>
/// Set the Syntax Language for the Code CodeEditor.
///
/// Note: Most likely to change or move location.
/// </summary>
public string CodeLanguage
{
get { return (string)GetValue(CodeLanguageProperty); }
set { SetValue(CodeLanguageProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty CodeLanguagePropertyField =
DependencyProperty.Register("CodeLanguage", typeof(string), typeof(CodeEditor), new PropertyMetadata("xml", (d, e) => {
var editor = d as CodeEditor;
if (editor.Options != null)
{
// Will trigger its own update of Options, but need this for initialization changes.
editor.Options.Language = e.NewValue.ToString();
}
// TODO: Push this to Options property change check instead...
// Changes to Language are ignored in Updated Options.
// https://microsoft.github.io/monaco-editor/api/modules/monaco.editor.html#setmodellanguage.
(d as CodeEditor)?.InvokeScriptAsync("updateLanguage", e.NewValue.ToString());
}));
internal static DependencyProperty CodeLanguageProperty
{
get
{
return CodeLanguagePropertyField;
}
}
/// <summary>
/// Get or set the CodeEditor Options.
/// </summary>
public IEditorConstructionOptions Options
{
get { return (IEditorConstructionOptions)GetValue(OptionsProperty); }
set { SetValue(OptionsProperty, value); }
}
// Using a DependencyProperty as the backing store for Options. This enables animation, styling, binding, etc...
private static readonly DependencyProperty OptionsPropertyField =
DependencyProperty.Register("Options", typeof(IEditorConstructionOptions), typeof(CodeEditor), new PropertyMetadata(new IEditorConstructionOptions(), (d, e) => {
var value = e.NewValue as IEditorConstructionOptions;
if (value != null)
{
var editor = d as CodeEditor;
if (editor != null)
{
editor.InvokeScriptAsync("updateOptions", value.ToJson());
// Register for sub-property changes on new object
value.PropertyChanged -= editor.Options_PropertyChanged;
value.PropertyChanged += editor.Options_PropertyChanged;
}
}
}));
public static DependencyProperty OptionsProperty
{
get
{
return OptionsPropertyField;
}
}
/// <summary>
/// Get or Set the CodeEditor Text.
/// </summary>
public bool HasGlyphMargin
{
get { return (bool)GetValue(HasGlyphMarginProperty); }
set { SetValue(HasGlyphMarginProperty, value); }
}
// Using a DependencyProperty as the backing store for HorizontalLayout. This enables animation, styling, binding, etc...
private static readonly DependencyProperty HasGlyphMarginPropertyField =
DependencyProperty.Register("HasGlyphMargin", typeof(bool), typeof(CodeEditor), new PropertyMetadata(false, (d, e) => {
(d as CodeEditor).Options.GlyphMargin = e.NewValue as bool?;
}));
public static DependencyProperty HasGlyphMarginProperty
{
get
{
return HasGlyphMarginPropertyField;
}
}
/// <summary>
/// Gets or sets text Decorations.
/// </summary>
public IObservableVector<IModelDeltaDecoration> Decorations
{
get { return (IObservableVector<IModelDeltaDecoration>)GetValue(DecorationsProperty); }
set { SetValue(DecorationsProperty, value); }
}
private AsyncLock _mutexLineDecorations = new AsyncLock();
// Using a DependencyProperty as the backing store for Options. This enables animation, styling, binding, etc...
private static readonly DependencyProperty DecorationsPropertyField =
DependencyProperty.Register("Decorations", typeof(IModelDeltaDecoration), typeof(CodeEditor), new PropertyMetadata(null, async (d, e) => {
var editor = d as CodeEditor;
if (editor != null)
{
// We only want to do this one at a time per editor.
using (await editor._mutexLineDecorations.LockAsync())
{
var old = e.OldValue as IObservableVector<IModelDeltaDecoration>;
// Clear out the old line decorations if we're replacing them or setting back to null
if ((old != null && old.Count > 0) ||
e.NewValue == null)
{
await editor.DeltaDecorationsHelperAsync(null);
}
var value = e.NewValue as IObservableVector<IModelDeltaDecoration>;
if (value != null)
{
if (value.Count > 0)
{
await editor.DeltaDecorationsHelperAsync(value.ToArray());
}
value.VectorChanged -= editor.Decorations_VectorChanged;
value.VectorChanged += editor.Decorations_VectorChanged;
}
}
}
}));
private async void Decorations_VectorChanged(IObservableVector<IModelDeltaDecoration> sender, IVectorChangedEventArgs @event)
{
if (sender != null)
{
// Need to recall mutex as this is called from outside of this initial callback setting it up.
using (await _mutexLineDecorations.LockAsync())
{
await DeltaDecorationsHelperAsync(sender.ToArray());
}
}
}
public static DependencyProperty DecorationsProperty
{
get
{
return DecorationsPropertyField;
}
}
/// <summary>
/// Gets or sets the hint Markers.
/// Note: This property is a helper for <see cref="SetModelMarkersAsync(string, IMarkerData[])"/>; use this property or the method, not both.
/// </summary>
public IObservableVector<IMarkerData> Markers
{
get { return (IObservableVector<IMarkerData>)GetValue(MarkersProperty); }
set { SetValue(MarkersProperty, value); }
}
private AsyncLock _mutexMarkers = new AsyncLock();
// Using a DependencyProperty as the backing store for Options. This enables animation, styling, binding, etc...
private static readonly DependencyProperty MarkersPropertyField =
DependencyProperty.Register("Markers", typeof(IMarkerData), typeof(CodeEditor), new PropertyMetadata(null, async (d, e) => {
var editor = d as CodeEditor;
if (editor != null)
{
// We only want to do this one at a time per editor.
using (await editor._mutexMarkers.LockAsync())
{
var old = e.OldValue as IObservableVector<IMarkerData>;
// Clear out the old markers if we're replacing them or setting back to null
if ((old != null && old.Count > 0) ||
e.NewValue == null)
{
// TODO: Can I simplify this in this case?
await editor.SetModelMarkersAsync("CodeEditor", Array.Empty<IMarkerData>());
}
var value = e.NewValue as IObservableVector<IMarkerData>;
if (value != null)
{
if (value.Count > 0)
{
await editor.SetModelMarkersAsync("CodeEditor", value.ToArray());
}
value.VectorChanged -= editor.Markers_VectorChanged;
value.VectorChanged += editor.Markers_VectorChanged;
}
}
}
}));
private async void Markers_VectorChanged(IObservableVector<IMarkerData> sender, IVectorChangedEventArgs @event)
{
if (sender != null)
{
// Need to recall mutex as this is called from outside of this initial callback setting it up.
using (await _mutexMarkers.LockAsync())
{
await SetModelMarkersAsync("CodeEditor", sender.ToArray());
}
}
}
public static DependencyProperty MarkersProperty
{
get
{
return MarkersPropertyField;
}
}
}
}