Skip to content

Commit

Permalink
Merge pull request #46 from hawkerm/0.9-beta-prep
Browse files Browse the repository at this point in the history
v0.9 beta work
  • Loading branch information
hawkerm committed Oct 27, 2021
2 parents b390c53 + 0d4e2e8 commit 64c35b5
Show file tree
Hide file tree
Showing 73 changed files with 2,140 additions and 465 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[*.cs]

# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = none
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
# TypeScript ignores
*.js
*.js.map
package-lock.json

# Monaco Dependency (Download from https://microsoft.github.io/monaco-editor/)
MonacoEditorComponent/monaco-editor

# Webcomponents.js Dependency (Download from https://github.com/webcomponents/polyfills)
MonacoEditorComponent/webcomponents-js

# Build results
[Dd]ebug/
[Dd]ebugPublic/
Expand Down
18 changes: 18 additions & 0 deletions GenerateMonacoTypings/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Generate Monaco Typings
=======================

This [Node.js](https://nodejs.org/) project is provided as a simple way to download the required dependencies needed to take the Monaco TypeScript definition file and generate C# classes for the API via the [TypedocConverter](https://github.com/hez2010/TypedocConverter) project.

This document assumes you have a Node.js environment setup running on Windows (as PowerShell is also required), but all other dependencies should be installed/setup as part of the script.

To get started, in this directory just run:

```
npm install
```

Voila! C# Typings should be generated! Run `npm install` or `npm run postinstall` to re-generate typings again.

**Note:** The script is configured to overwrite the existing definitions within the main repo, you can configure an alternate output directory (`outdir`) via the `npm config set` command. It defaults to the `MonacoEditorComponent` directory and the namespace will automatically create a `Monaco` sub-directory.

**Note:** This script is currently meant as a guide-post, the typings generated are not all meant to be consumed directly by the project, and certain interfaces have been sculpted to provide a better experience to C# developers. This tool is mostly meant to boot-strap enabling features from the Monaco API and adapting to new versions of the Monaco API. If you have any questions or need a specific feature, please first file an issue on [our repo](https://github.com/hawkerm/monaco-editor-uwp). Thanks!
58 changes: 58 additions & 0 deletions GenerateMonacoTypings/generate-typings.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# This script run after downloading the npm package dependencies inorder to generate the C# typings.
# https://github.com/hez2010/TypedocConverter

# Requires .NET Core Runtime 3.1.x to be installed

# ------------------------
$monaco_file = ".\node_modules\monaco-editor\monaco.d.ts"
$typedoc_bin_url = $env:npm_package_config_typedocConverter #see config in package.json
$temp_dir_name = ".temp"

function Get-ScriptDirectory {
Split-Path -parent $PSCommandPath
}

$script_dir = Get-ScriptDirectory

Push-Location $script_dir

# Create Temp Directory and Output
New-Item -Name $temp_dir_name -ItemType Directory | Out-Null

# Make sure we can see our 'monaco.d.ts' file

if (!(Test-Path $monaco_file -PathType Leaf)) {
Write-Error "Monaco Definitions Not Found, run npm install first."

Pop-Location
exit
}

# Copy monaco.d.ts to monaco.ts in temp folder (need to change extension)

Copy-Item $monaco_file -Destination (Join-Path $temp_dir_name "monaco.ts")

Push-Location $temp_dir_name

# Run typedoc to generate json representation
Write-Output '{"compilerOptions":{"target":"es2020"}}' > tsconfig.json
Invoke-Expression "npx typedoc monaco.ts --json monaco.json"

# Need TypedocConverter next
Write-Host "Downloading TypedocConverter"

[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
Invoke-WebRequest -Uri $typedoc_bin_url -OutFile "TypedocConverter.zip"

Write-Host "Extracting..."
Expand-Archive "TypedocConverter.zip" -DestinationPath .

# Now run TypedocConverter on our monaco.json
Invoke-Expression ".\TypedocConverter.exe --inputfile monaco.json --splitfiles true --outputdir ../$env:npm_package_config_outdir --promise-type WinRT --nrt-disabled true"

Pop-Location

# Clean-up Temp Dir
Remove-Item $temp_dir_name -Force -Recurse -ErrorAction SilentlyContinue

Pop-Location
22 changes: 22 additions & 0 deletions GenerateMonacoTypings/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "generatemonacotypings",
"version": "1.0.0",
"description": "Used to Generate C# Typing info for the Monaco editor from its TypeScript definitions (also requires PowerShell)",
"main": "index.js",
"scripts": {
"postinstall": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./generate-typings.ps1"
},
"author": "",
"license": "MIT",
"config": {
"outdir": "../MonacoEditorComponent",
"typedocConverter": "https://github.com/hez2010/TypedocConverter/releases/download/v2.6/Windows_x64_Native.zip"
},
"dependencies": {
"monaco-editor": "0.21.3"
},
"devDependencies": {
"typedoc": "0.20.37",
"typescript": "4.2.4"
}
}
7 changes: 5 additions & 2 deletions MonacoEditorComponent.sln
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2008
# Visual Studio Version 16
VisualStudioVersion = 16.0.29709.97
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonacoEditorComponent", "MonacoEditorComponent\MonacoEditorComponent.csproj", "{DD49485C-2D9B-4824-B64E-52193B43E4CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonacoEditorTestApp", "MonacoEditorTestApp\MonacoEditorTestApp.csproj", "{854C0817-09CA-4052-8C6F-0717C2721063}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C810EDD0-CB55-4B22-AA18-A6C8B3C29FBC}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
changelog.md = changelog.md
install-dependencies.ps1 = install-dependencies.ps1
MonacoEditorComponent\MonacoEditorComponent.nuspec = MonacoEditorComponent\MonacoEditorComponent.nuspec
README.md = README.md
ThirdPartyNotices.txt = ThirdPartyNotices.txt
EndProjectSection
EndProject
Global
Expand Down
51 changes: 21 additions & 30 deletions MonacoEditorComponent/CodeEditor/CodeEditor.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Reflection;
using Windows.Foundation;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
Expand Down Expand Up @@ -40,31 +41,6 @@ public partial class CodeEditor
public new event WebKeyEventHandler KeyDown;

private ThemeListener _themeListener;

private void WebView_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
#if DEBUG
Debug.WriteLine("DOM Content Loaded");
#endif
_initialized = true;
}

private async void WebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
IsEditorLoaded = true;

// Make sure inner editor is focused
await SendScriptAsync("editor.focus();");

// If we're supposed to have focus, make sure we try and refocus on our now loaded webview.
if (FocusManager.GetFocusedElement() == this)
{
_view.Focus(FocusState.Programmatic);
}

Loaded?.Invoke(this, new RoutedEventArgs());
}

internal ParentAccessor _parentAccessor;
private KeyboardListener _keyboardListener;
private long _themeToken;
Expand All @@ -74,24 +50,26 @@ private void WebView_NavigationStarting(WebView sender, WebViewNavigationStartin
#if DEBUG
Debug.WriteLine("Navigation Starting");
#endif
_parentAccessor = new ParentAccessor(this);
_parentAccessor = new ParentAccessor(this, _queue);
_parentAccessor.AddAssemblyForTypeLookup(typeof(Range).GetTypeInfo().Assembly);
_parentAccessor.RegisterAction("Loaded", CodeEditorLoaded);

_themeListener = new ThemeListener();
_themeListener = new ThemeListener(_queue);
_themeListener.ThemeChanged += ThemeListener_ThemeChanged;
_themeToken = RegisterPropertyChangedCallback(RequestedThemeProperty, RequestedTheme_PropertyChanged);

_keyboardListener = new KeyboardListener(this);
_keyboardListener = new KeyboardListener(this, _queue);

_view.AddWebAllowedObject("Debug", new DebugLogger());
_view.AddWebAllowedObject("Parent", _parentAccessor);
_view.AddWebAllowedObject("Theme", _themeListener);
_view.AddWebAllowedObject("Accessor", _parentAccessor);
_view.AddWebAllowedObject("Theme", _themeListener); // TODO: Is this used?
_view.AddWebAllowedObject("Keyboard", _keyboardListener);
}

private async void CodeEditorLoaded()
{
_initialized = true;

if (Decorations != null && Decorations.Count > 0)
{
// Need to retrigger highlights after load if they were set before load.
Expand All @@ -100,6 +78,19 @@ private async void CodeEditorLoaded()

// Now we're done loading
Loading?.Invoke(this, new RoutedEventArgs());

// Make sure inner editor is focused
await SendScriptAsync("editor.focus();");

// If we're supposed to have focus, make sure we try and refocus on our now loaded webview.
if (FocusManager.GetFocusedElement() == this)
{
_view.Focus(FocusState.Programmatic);
}

IsEditorLoaded = true;

Loaded?.Invoke(this, new RoutedEventArgs());
}

private void WebView_NewWindowRequested(WebView sender, WebViewNewWindowRequestedEventArgs args)
Expand Down
81 changes: 58 additions & 23 deletions MonacoEditorComponent/CodeEditor/CodeEditor.Methods.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
using Monaco.Editor;
using Microsoft.Toolkit.Uwp;
using Monaco.Editor;
using Monaco.Helpers;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using Windows.Foundation;

namespace Monaco
{
/// <summary>
/// Action delegate for <see cref="CodeEditor.AddCommandAsync(int, CommandHandler)"/> and <see cref="CodeEditor.AddCommandAsync(int, CommandHandler, string)"/>.
/// </summary>
public delegate void CommandHandler();
public delegate void CommandHandler([ReadOnlyArray] object[] parameters);

/// <summary>
/// This file contains Monaco IEditor method implementations we can call on our control.
Expand Down Expand Up @@ -63,37 +65,37 @@ public IAsyncAction RevealPositionAsync(IPosition position, bool revealVerticalI

public IAsyncAction RevealPositionAsync(IPosition position, bool revealVerticalInCenter, bool revealHorizontal)
{
return SendScriptAsync("editor.revealPosition(JSON.parse('" + position.ToJson() + "'), " + JsonConvert.ToString(revealVerticalInCenter) + ", " + JsonConvert.ToString(revealHorizontal) + ")").AsAsyncAction();
return SendScriptAsync("editor.revealPosition(JSON.parse('" + JsonConvert.SerializeObject(position) + "'), " + JsonConvert.ToString(revealVerticalInCenter) + ", " + JsonConvert.ToString(revealHorizontal) + ")").AsAsyncAction();
}

public IAsyncAction RevealPositionInCenterAsync(IPosition position)
{
return SendScriptAsync("editor.revealPositionInCenter(JSON.parse('" + position.ToJson() + "'))").AsAsyncAction();
return SendScriptAsync("editor.revealPositionInCenter(JSON.parse('" + JsonConvert.SerializeObject(position) + "'))").AsAsyncAction();
}

public IAsyncAction RevealPositionInCenterIfOutsideViewportAsync(IPosition position)
{
return SendScriptAsync("editor.revealPositionInCenterIfOutsideViewport(JSON.parse('" + position.ToJson() + "'))").AsAsyncAction();
return SendScriptAsync("editor.revealPositionInCenterIfOutsideViewport(JSON.parse('" + JsonConvert.SerializeObject(position) + "'))").AsAsyncAction();
}

public IAsyncAction RevealRangeAsync(IRange range)
{
return SendScriptAsync("editor.revealRange(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
return SendScriptAsync("editor.revealRange(JSON.parse('" + JsonConvert.SerializeObject(range) + "'))").AsAsyncAction();
}

public IAsyncAction RevealRangeAtTopAsync(IRange range)
{
return SendScriptAsync("editor.revealRangeAtTop(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
return SendScriptAsync("editor.revealRangeAtTop(JSON.parse('" + JsonConvert.SerializeObject(range) + "'))").AsAsyncAction();
}

public IAsyncAction RevealRangeInCenterAsync(IRange range)
{
return SendScriptAsync("editor.revealRangeInCenter(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
return SendScriptAsync("editor.revealRangeInCenter(JSON.parse('" + JsonConvert.SerializeObject(range) + "'))").AsAsyncAction();
}

public IAsyncAction RevealRangeInCenterIfOutsideViewportAsync(IRange range)
{
return SendScriptAsync("editor.revealRangeInCenterIfOutsideViewport(JSON.parse('" + range.ToJson() + "'))").AsAsyncAction();
return SendScriptAsync("editor.revealRangeInCenterIfOutsideViewport(JSON.parse('" + JsonConvert.SerializeObject(range) + "'))").AsAsyncAction();
}
#endregion

Expand All @@ -114,26 +116,51 @@ public IAsyncOperation<string> InvokeScriptAsync(string script)
return _view.InvokeScriptAsync("eval", new[] { script });
}

private int _commandIndex = 0;

public IAsyncOperation<string> AddCommandAsync(CommandHandler handler)
{
return AddCommandAsync(0, handler, string.Empty);
}

public IAsyncOperation<string> AddCommandAsync(int keybinding, CommandHandler handler)
{
return AddCommandAsync(keybinding, handler, string.Empty);
}

public IAsyncOperation<string> AddCommandAsync(int keybinding, CommandHandler handler, string context)
{
var name = "Command" + keybinding;
_parentAccessor.RegisterAction(name, new Action(() => { handler?.Invoke(); }));
var name = "Command" + Interlocked.Increment(ref _commandIndex);
_parentAccessor.RegisterActionWithParameters(name, (parameters) =>
{
if (parameters != null && parameters.Length > 0)
{
object[] args = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
args[i] = JsonConvert.DeserializeObject<object>(parameters[i]);
}
handler?.Invoke(args);
}
else
{
handler?.Invoke(new object[] {});
}
});
return InvokeScriptAsync<string>("addCommand", new object[] { keybinding, name, context }).AsAsyncOperation();
}

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

return InvokeScriptAsync("createContext", ck).ContinueWith((noop) =>
return AsyncInfo.Run(async delegate (CancellationToken token)
{
var ck = new ContextKey(this, key, defaultValue);

await InvokeScriptAsync("createContext", ck);

return ck;
}).AsAsyncOperation();
});
}

public IModel GetModel()
Expand Down Expand Up @@ -170,15 +197,23 @@ public IAsyncAction SetPositionAsync(IPosition position)
/// <returns></returns>
private IAsyncAction DeltaDecorationsHelperAsync([ReadOnlyArray] IModelDeltaDecoration[] newDecorations)
{
var newDecorationsAdjust = newDecorations ?? Array.Empty<IModelDeltaDecoration>();

// Update Styles
return InvokeScriptAsync("updateStyle", CssStyleBroker.GetInstance(this).GetStyles()).ContinueWith((noop) =>
return AsyncInfo.Run(async delegate (CancellationToken token)
{
// Send Command to Modify Decorations
// 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.
return InvokeScriptAsync("updateDecorations", (object)newDecorationsAdjust);
}).AsAsyncAction();
await _queue.EnqueueAsync(async () =>
{
var newDecorationsAdjust = newDecorations ?? Array.Empty<IModelDeltaDecoration>();
if (_cssBroker.AssociateStyles(newDecorations))
{
// Update Styles First
await InvokeScriptAsync("updateStyle", _cssBroker.GetStyles());
}
// Send Command to Modify Decorations
// 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.
await InvokeScriptAsync("updateDecorations", (object)newDecorationsAdjust);
});
});
}
}
}
Loading

0 comments on commit 64c35b5

Please sign in to comment.