diff --git a/CodeMirror6/CodeMirror6Wrapper.razor.cs b/CodeMirror6/CodeMirror6Wrapper.razor.cs index 6267dd0d..0bc747f6 100644 --- a/CodeMirror6/CodeMirror6Wrapper.razor.cs +++ b/CodeMirror6/CodeMirror6Wrapper.razor.cs @@ -111,6 +111,11 @@ public partial class CodeMirror6Wrapper : ComponentBase, IAsyncDisposable /// [Parameter] public Func>> LintDocument { get; set; } = (_, _) => Task.FromResult(new List()); /// + /// The CodeMirror setup + /// + /// + [Parameter] public CodeMirrorSetup Setup { get; set; } = new(); + /// /// Additional attributes to be applied to the container element /// /// @@ -214,7 +219,7 @@ public async Task> LintingRequestedFromJS(string code /// protected override void OnInitialized() { - Config = new CodeMirrorConfiguration( + Config = new( Doc, Placeholder, Theme?.ToString(), diff --git a/CodeMirror6/CodeMirrorJsInterop.cs b/CodeMirror6/CodeMirrorJsInterop.cs index c942a05b..d855a091 100644 --- a/CodeMirror6/CodeMirrorJsInterop.cs +++ b/CodeMirror6/CodeMirrorJsInterop.cs @@ -40,7 +40,13 @@ internal async Task ModuleInvokeVoidAsync(string method, params object?[] args) /// Methods to set JS CodeMirror properties to reflect the values of the blazor wrapper parameters. Internal use only. /// /// - internal CMSetters PropertySetters => _setters ??= new(_dotnetHelperRef, cm6WrapperComponent.Config, this); + internal CMSetters PropertySetters => _setters ??= new( + _dotnetHelperRef, + cm6WrapperComponent.Config, + cm6WrapperComponent.Setup, + this + ); + /// /// Methods to invoke JS CodeMirror commands. /// @@ -64,6 +70,7 @@ public async ValueTask DisposeAsync() internal class CMSetters( DotNetObjectReference _dotnetHelperRef, CodeMirrorConfiguration config, + CodeMirrorSetup setup, CodeMirrorJsInterop cmJsInterop ) { @@ -74,7 +81,8 @@ CodeMirrorJsInterop cmJsInterop public Task InitCodeMirror() => cmJsInterop.ModuleInvokeVoidAsync( "initCodeMirror", _dotnetHelperRef, - config + config, + setup ); /// diff --git a/CodeMirror6/Models/CodeMirrorSetup.cs b/CodeMirror6/Models/CodeMirrorSetup.cs new file mode 100644 index 00000000..b8ea2d23 --- /dev/null +++ b/CodeMirror6/Models/CodeMirrorSetup.cs @@ -0,0 +1,107 @@ +using System.Text.Json.Serialization; + +namespace CodeMirror6.Models; + +/// +/// Stores the configuration of a CodeMirror instance (what plugins to load). +/// Cannot be changed after the instance is created. +/// +public readonly record struct CodeMirrorSetup +{ + /// + /// Default constructor + /// + public CodeMirrorSetup() + { + } + + /// + /// Whether to show line numbers to the left of the editor. + /// + [JsonPropertyName("lineNumbers")] public bool LineNumbers { get; init; } = true; + + /// + /// Whether to highlight the line gutter when the cursor is on it. + /// + [JsonPropertyName("highlightActiveLineGutter")] public bool HighlightActiveLineGutter { get; init; } = true; + + /// + /// Whether to highlight special characters (whitespace, tabs, newlines). + /// + [JsonPropertyName("highlightSpecialChars")] public bool HighlightSpecialChars { get; init; } = true; + + /// + /// Whether to enable undo/redo. + /// + [JsonPropertyName("history")] public bool History { get; init; } = true; + + /// + /// Whether to enable code folding. + /// + [JsonPropertyName("foldGutter")] public bool FoldGutter { get; init; } = true; + + /// + /// Whether to draw the selection when the editor is focused. + /// + [JsonPropertyName("drawSelection")] public bool DrawSelection { get; init; } = true; + + /// + /// Whether to show a cursor marker when the editor is focused. + /// + [JsonPropertyName("dropCursor")] public bool DropCursor { get; init; } = true; + + /// + /// Whether to allow multiple selections. + /// + [JsonPropertyName("allowMultipleSelections")] public bool AllowMultipleSelections { get; init; } = true; + + /// + /// Whether to indent the current line when a character is typed that might cause the line to be re-indented. + /// + [JsonPropertyName("indentOnInput")] public bool IndentOnInput { get; init; } = true; + + /// + /// Whether to enable syntax highlighting. + /// + [JsonPropertyName("syntaxHighlighting")] public bool SyntaxHighlighting { get; init; } = true; + + /// + /// Whether to highlight matching brackets. + /// + [JsonPropertyName("bracketMatching")] public bool BracketMatching { get; init; } = true; + + /// + /// Whether to automatically close brackets. + /// + [JsonPropertyName("closeBrackets")] public bool CloseBrackets { get; init; } = true; + + /// + /// Whether to enable autocompletion. + /// + [JsonPropertyName("autocompletion")] public bool Autocompletion { get; init; } = true; + + /// + /// Whether to enable rectangular selection. + /// + [JsonPropertyName("rectangularSelection")] public bool RectangularSelection { get; init; } = true; + + /// + /// Whether to enable crosshair selection. + /// + [JsonPropertyName("crossHairSelection")] public bool CrossHairSelection { get; init; } = true; + + /// + /// Whether to highlight the active line. + /// + [JsonPropertyName("highlightActiveLine")] public bool HighlightActiveLine { get; init; } = true; + + /// + /// Whether to highlight selection matches. + /// + [JsonPropertyName("highlightSelectionMatches")] public bool HighlightSelectionMatches { get; init; } = true; + + /// + /// Whether to enable preview images. + /// + [JsonPropertyName("previewImages")] public bool PreviewImages { get; init; } = true; +} diff --git a/CodeMirror6/NodeLib/src/CmSetup.ts b/CodeMirror6/NodeLib/src/CmSetup.ts new file mode 100644 index 00000000..31735d78 --- /dev/null +++ b/CodeMirror6/NodeLib/src/CmSetup.ts @@ -0,0 +1,25 @@ +/** + * Stores the configuration of a CodeMirror instance (what plugins to load). + * Cannot be changed after the instance is created. + */ +export class CmSetup +{ + public lineNumbers: boolean + public highlightActiveLineGutter: boolean + public highlightSpecialChars: boolean + public history: boolean + public foldGutter: boolean + public drawSelection: boolean + public dropCursor: boolean + public allowMultipleSelections: boolean + public indentOnInput: boolean + public syntaxHighlighting: boolean + public bracketMatching: boolean + public closeBrackets: boolean + public autocompletion: boolean + public rectangularSelection: boolean + public crosshairCursor: boolean + public highlightActiveLine: boolean + public highlightSelectionMatches: boolean + public previewImages: boolean +} diff --git a/CodeMirror6/NodeLib/src/index.ts b/CodeMirror6/NodeLib/src/index.ts index 2230ff99..d97c7a35 100644 --- a/CodeMirror6/NodeLib/src/index.ts +++ b/CodeMirror6/NodeLib/src/index.ts @@ -8,11 +8,11 @@ import { indentWithTab, history, historyKeymap, cursorSyntaxLeft, moveLineDown, moveLineUp, selectSyntaxLeft, selectSyntaxRight, cursorSyntaxRight, selectParentSyntax, indentLess, indentMore, copyLineUp, copyLineDown, indentSelection, deleteLine, cursorMatchingBracket, toggleComment, toggleBlockComment, - simplifySelection, insertBlankLine, selectLine, undo, redo, redoSelection, undoSelection + simplifySelection, insertBlankLine, selectLine, undo, redo, redoSelection, undoSelection, } from "@codemirror/commands" import { indentUnit, defaultHighlightStyle, syntaxHighlighting, indentOnInput, bracketMatching, - foldGutter, foldKeymap + foldGutter, foldKeymap, } from "@codemirror/language" import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete" import { searchKeymap, highlightSelectionMatches } from "@codemirror/search" @@ -32,6 +32,7 @@ import { } from "./CmCommands" import { images } from "./CmImages" import { externalLintSource, getExternalLinterConfig } from "./CmLint" +import { CmSetup } from "./CmSetup" /** * Initialize a new CodeMirror instance @@ -42,7 +43,8 @@ import { externalLintSource, getExternalLinterConfig } from "./CmLint" export function initCodeMirror( id: string, dotnetHelper: any, - initialConfig: CmConfiguration + initialConfig: CmConfiguration, + setup: CmSetup ) { CMInstances[id] = new CmInstance() CMInstances[id].dotNetHelper = dotnetHelper @@ -59,28 +61,6 @@ export function initCodeMirror( CMInstances[id].editableCompartment.of(EditorView.editable.of(initialConfig.editable)), EditorView.updateListener.of(async (update) => { await updateListenerExtension(dotnetHelper, update) }), - - images(), - linter(async view => await externalLintSource(view, dotnetHelper), getExternalLinterConfig()), - - // Basic Setup - lineNumbers(), - highlightActiveLineGutter(), - highlightSpecialChars(), - history(), - foldGutter(), - drawSelection(), - dropCursor(), - EditorState.allowMultipleSelections.of(true), - indentOnInput(), - syntaxHighlighting(defaultHighlightStyle, { fallback: true }), - bracketMatching(), - closeBrackets(), - autocompletion({}), - rectangularSelection(), - crosshairCursor(), - highlightActiveLine(), - highlightSelectionMatches(), keymap.of([ ...closeBracketsKeymap, @@ -121,6 +101,28 @@ export function initCodeMirror( ]) ] + // Basic Setup + if (setup.lineNumbers === true) extensions.push(lineNumbers()) + if (setup.highlightActiveLineGutter === true) extensions.push(highlightActiveLineGutter()) + if (setup.highlightSpecialChars === true) extensions.push(highlightSpecialChars()) + if (setup.history === true) extensions.push(history()) + if (setup.foldGutter === true) extensions.push(foldGutter()) + if (setup.drawSelection === true) extensions.push(drawSelection()) + if (setup.dropCursor === true) extensions.push(dropCursor()) + if (setup.indentOnInput === true) extensions.push(indentOnInput()) + if (setup.syntaxHighlighting === true) extensions.push(syntaxHighlighting(defaultHighlightStyle, { fallback: true })) + if (setup.bracketMatching === true) extensions.push(bracketMatching()) + if (setup.closeBrackets === true) extensions.push(closeBrackets()) + if (setup.autocompletion === true) extensions.push(autocompletion({})) + if (setup.rectangularSelection === true) extensions.push(rectangularSelection()) + if (setup.crosshairCursor === true) extensions.push(crosshairCursor()) + if (setup.highlightActiveLine === true) extensions.push(highlightActiveLine()) + if (setup.highlightSelectionMatches === true) extensions.push(highlightSelectionMatches()) + if (setup.previewImages === true) extensions.push(images()) + + extensions.push(linter(async view => await externalLintSource(view, dotnetHelper), getExternalLinterConfig())) + if (setup.allowMultipleSelections === true) EditorState.allowMultipleSelections.of(true) + CMInstances[id].state = EditorState.create({ doc: initialConfig.doc, extensions: extensions, diff --git a/Examples.Common/Example.razor b/Examples.Common/Example.razor index 1bd51345..295552d7 100644 --- a/Examples.Common/Example.razor +++ b/Examples.Common/Example.razor @@ -38,6 +38,7 @@ Language=@Language AutoFormatMarkdownHeaders=@AutoFormatMarkdownHeaders LintDocument=@LintDocument + Setup=@Setup style="max-width: 100%; min-height: 40em; " > @@ -209,6 +210,12 @@ func main() {{ private ThemeMirrorTheme Theme = ThemeMirrorTheme.OneDark; private CodeMirrorLanguage Language = CodeMirrorLanguage.Markdown; private bool AutoFormatMarkdownHeaders = true; + private readonly CodeMirrorSetup Setup = new() { + HighlightActiveLine = false, + LineNumbers = true, + HighlightActiveLineGutter = false, + HighlightSelectionMatches = false, + }; private string ButtonClass(CodeMirrorState state, string docStyleTag) => state.MarkdownStylesAtSelections?.Contains(docStyleTag) == true ? "btn btn-primary" : "btn";