diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml
index af9e8051..58f82b85 100644
--- a/.github/workflows/ci-dev.yml
+++ b/.github/workflows/ci-dev.yml
@@ -1,6 +1,10 @@
name: CI Develop
on:
+ workflow_run:
+ workflows: ["Auto Fix Format"]
+ types:
+ - completed # 在工作流完成时触发
pull_request:
branches: [dev]
paths-ignore:
@@ -10,15 +14,9 @@ on:
- 'LICENSE'
- 'README*'
- 'CHANGELOG*'
- push:
+ merge_group:
branches: [dev]
- paths-ignore:
- - '**.md'
- - 'docs/**'
- - '.github/**/*.md'
- - 'LICENSE'
- - 'README*'
- - 'CHANGELOG*'
+ types: [checks_requested]
concurrency:
group: ci-dev-${{ github.ref }}
@@ -34,26 +32,11 @@ env:
jobs:
# ========================================
- # 1. 代码格式检查
- # ========================================
- format:
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: 安装 .NET SDK
- uses: actions/setup-dotnet@v5
- with:
- dotnet-version: ${{ env.DOTNET_VERSION }}
-
- - name: 检查代码格式
- run: dotnet format --verify-no-changes --verbosity diagnostic
-
- # ========================================
- # 2. 构建 + 测试 (Debug)
+ # 1. 构建 + 测试 (Debug)
# ========================================
build-and-test:
runs-on: windows-latest
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
needs: format
steps:
- uses: actions/checkout@v6
@@ -62,6 +45,7 @@ jobs:
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
+ ref: ${{ github.event.workflow_run.head_branch }}
- name: 缓存 NuGet 包
uses: actions/cache@v4
@@ -73,6 +57,9 @@ jobs:
- name: 还原 NuGet 包
run: dotnet restore
+ - name: Build (Debug)
+ run: dotnet build --no-restore --configuration Debug
+
- name: 运行 EasyCon 测试
shell: cmd
run: ci\test.bat Debug
diff --git a/src/EasyCon2.Avalonia.Core/AvaloniaRuntime.cs b/src/EasyCon2.Avalonia.Core/AvaloniaRuntime.cs
index 5a4052dd..1193c393 100644
--- a/src/EasyCon2.Avalonia.Core/AvaloniaRuntime.cs
+++ b/src/EasyCon2.Avalonia.Core/AvaloniaRuntime.cs
@@ -1,4 +1,5 @@
using Avalonia;
+using Avalonia.Markup.Xaml.Styling;
using Avalonia.Themes.Fluent;
namespace EasyCon2.Avalonia.Core;
@@ -16,6 +17,13 @@ public static void EnsureInitialized()
.LogToTrace()
.SetupWithoutStarting();
app.Instance.Styles.Add(new FluentTheme());
+
+ var editStyle = new StyleInclude(new Uri("avares://EasyCon2.Avalonia.Core"))
+ {
+ Source = new Uri("avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml")
+ };
+ app.Instance.Styles.Add(editStyle);
+
_initialized = true;
}
}
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/EasyCon2.Avalonia.Core.csproj b/src/EasyCon2.Avalonia.Core/EasyCon2.Avalonia.Core.csproj
index a2df51a1..66e2b740 100644
--- a/src/EasyCon2.Avalonia.Core/EasyCon2.Avalonia.Core.csproj
+++ b/src/EasyCon2.Avalonia.Core/EasyCon2.Avalonia.Core.csproj
@@ -21,7 +21,12 @@
+
+
+
+
+
diff --git a/src/EasyCon2.Avalonia.Core/Editor/CodeCompletionController.cs b/src/EasyCon2.Avalonia.Core/Editor/CodeCompletionController.cs
new file mode 100644
index 00000000..8eb5860c
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Editor/CodeCompletionController.cs
@@ -0,0 +1,125 @@
+using Avalonia.Input;
+using AvaloniaEdit;
+using AvaloniaEdit.CodeCompletion;
+using System.Diagnostics;
+
+namespace EasyCon2.Avalonia.Core.Editor;
+
+internal class CodeCompletionController : IDisposable
+{
+ private readonly TextEditor _editor;
+ private readonly ICompletionProvider _completionProvider;
+ private bool _enableAutoCompletion;
+ private CompletionWindow _completionWindow;
+ private bool _isDisposed;
+
+ public bool EnableAutoCompletion
+ {
+ get => _enableAutoCompletion;
+ set => _enableAutoCompletion = value;
+ }
+
+ public CodeCompletionController(TextEditor editor, ICompletionProvider completionProvider, bool enableAutoCompletion = true)
+ {
+ _editor = editor ?? throw new ArgumentNullException(nameof(editor));
+ _completionProvider = completionProvider;
+ _enableAutoCompletion = enableAutoCompletion;
+
+ _editor.TextArea.TextEntering += OnTextEntering;
+ _editor.TextArea.TextEntered += OnTextEntered;
+ _editor.TextArea.KeyDown += OnKeyDown;
+ _editor.LostFocus += (_, _) => CloseCompletionWindow();
+ }
+
+ private async void OnTextEntered(object sender, TextInputEventArgs e)
+ {
+ if (!_enableAutoCompletion || string.IsNullOrEmpty(e.Text)) return;
+
+ var line = _editor.TextArea.Document.GetLineByNumber(_editor.TextArea.Caret.Line);
+ if (_completionProvider.ShouldTriggerCompletion(e.Text[0],
+ _editor.TextArea.Document.GetText(line.Offset, line.Length),
+ _editor.TextArea.Caret.Column))
+ {
+ await ShowCompletionWindow();
+ }
+ }
+
+ private void OnTextEntering(object sender, TextInputEventArgs e)
+ {
+ if (_completionWindow != null && e.Text is " " or "\t" or "\n" or "\r")
+ CloseCompletionWindow();
+ }
+
+ private void OnKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Space && e.KeyModifiers.HasFlag(KeyModifiers.Control))
+ {
+ e.Handled = true;
+ _ = ShowCompletionWindow();
+ }
+ else if (e.Key == Key.Escape && _completionWindow != null)
+ {
+ CloseCompletionWindow();
+ e.Handled = true;
+ }
+ }
+
+ private async Task ShowCompletionWindow()
+ {
+ CloseCompletionWindow();
+
+ try
+ {
+ var currentWord = _completionProvider.GetCurrentWord(
+ _editor.Document,
+ _editor.TextArea.Caret.Offset
+ );
+
+ var completions = await _completionProvider.GetCompletions(
+ _editor.Document,
+ _editor.TextArea.Caret.Offset,
+ currentWord);
+
+ if (!completions.Any()) return;
+
+ _completionWindow = new CompletionWindow(_editor.TextArea)
+ {
+ CloseWhenCaretAtBeginning = true,
+ CloseAutomatically = true,
+ };
+
+ var data = _completionWindow.CompletionList.CompletionData;
+ foreach (var c in completions)
+ data.Add(c);
+
+ if (!string.IsNullOrEmpty(currentWord))
+ _completionWindow.StartOffset = _editor.TextArea.Caret.Offset - currentWord.Length;
+
+ _completionWindow.Closed += (_, _) => _completionWindow = null;
+ _completionWindow.Show();
+
+ if (!string.IsNullOrEmpty(currentWord))
+ _completionWindow.CompletionList.SelectItem(currentWord);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"显示代码提示时出错: {ex.Message}");
+ CloseCompletionWindow();
+ }
+ }
+
+ public void CloseCompletionWindow()
+ {
+ _completionWindow?.Close();
+ _completionWindow = null;
+ }
+
+ public void Dispose()
+ {
+ if (!_isDisposed)
+ {
+ CloseCompletionWindow();
+ _isDisposed = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Editor/EcpCompletionData.cs b/src/EasyCon2.Avalonia.Core/Editor/EcpCompletionData.cs
new file mode 100644
index 00000000..6f5e6509
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Editor/EcpCompletionData.cs
@@ -0,0 +1,26 @@
+using Avalonia.Media;
+using AvaloniaEdit.CodeCompletion;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Editing;
+
+namespace EasyCon2.Avalonia.Core.Editor;
+
+internal class EcpCompletionData : ICompletionData
+{
+ public EcpCompletionData(string text) { Text = text; }
+
+ public IImage Image => null;
+
+ public string Text { get; }
+
+ public object Content => Text;
+
+ public object Description => $"描述:{Text}";
+
+ public double Priority => 0.9;
+
+ public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
+ {
+ textArea.Document.Replace(completionSegment, Text);
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCon2/Helper/EcpCompletionProvider.cs b/src/EasyCon2.Avalonia.Core/Editor/EcpCompletionProvider.cs
similarity index 66%
rename from src/EasyCon2/Helper/EcpCompletionProvider.cs
rename to src/EasyCon2.Avalonia.Core/Editor/EcpCompletionProvider.cs
index 0e3323ce..fcd0988f 100644
--- a/src/EasyCon2/Helper/EcpCompletionProvider.cs
+++ b/src/EasyCon2.Avalonia.Core/Editor/EcpCompletionProvider.cs
@@ -1,19 +1,24 @@
+using AvaloniaEdit;
+using AvaloniaEdit.CodeCompletion;
+using AvaloniaEdit.Document;
using EasyCon.Core;
-using EasyCon2.Models;
-using ICSharpCode.AvalonEdit;
-using ICSharpCode.AvalonEdit.CodeCompletion;
-using ICSharpCode.AvalonEdit.Document;
-namespace EasyCon2.Helper;
+namespace EasyCon2.Avalonia.Core.Editor;
-public delegate IEnumerable GetImgLabel();
+public interface ICompletionProvider
+{
+ Task> GetCompletions(ITextSource textSource, int offset, string cur);
+ bool ShouldTriggerCompletion(char triggerChar, string currentLineText, int caretIndex);
+ string GetCurrentWord(TextDocument document, int offset);
+}
internal class EcpCompletionProvider(TextEditor textEditor) : ICompletionProvider
{
private readonly TextEditor Editor = textEditor;
- public GetImgLabel GetImgLabel;
+ public Func> GetImgLabel;
- private readonly List _keywords = [
+ private readonly List _keywords =
+ [
"IMPORT",
"IF", "ELIF", "ELSE", "ENDIF",
"FOR", "TO", "NEXT", "BREAK", "CONTINUE",
@@ -26,8 +31,9 @@ internal class EcpCompletionProvider(TextEditor textEditor) : ICompletionProvide
"LEFT", "RIGHT", "UP", "DOWN",
"UPLEFT", "UPRIGHT", "DOWNLEFT", "DOWNRIGHT",
"RAND", "AMIIBO", "BEEP",
- ];
- public async Task> GetCompletions(ITextSource textSource, int offset, string cur)
+ ];
+
+ public Task> GetCompletions(ITextSource textSource, int offset, string cur)
{
var completions = new List();
@@ -35,43 +41,33 @@ public async Task> GetCompletions(ITextSource textS
{
var ilnames = GetImgLabel?.Invoke() ?? [];
foreach (var name in ilnames)
- {
completions.Add(new EcpCompletionData($"@{name}"));
- }
}
else if (cur.StartsWith('_') || cur.StartsWith('$'))
{
var tok = Scripter.GetTokens(Editor.Text, cur);
foreach (var name in tok)
- {
completions.Add(new EcpCompletionData(name));
- }
}
- else if (char.IsLetter(cur[0]))
+ else if (cur.Length > 0 && char.IsLetter(cur[0]))
{
var kw = _keywords.Where(ch => ch.StartsWith(cur, StringComparison.OrdinalIgnoreCase));
foreach (var item in kw)
- {
completions.Add(new EcpCompletionData(item));
- }
}
- return completions;
- }
- private bool IsWordPart(char c)
- {
- return char.IsLetterOrDigit(c) || "@$_".IndexOf(c) != -1;
+ return Task.FromResult>(completions);
}
public string GetCurrentWord(TextDocument document, int offset)
{
int start = offset;
while (start > 0 && IsWordPart(document.GetCharAt(start - 1)))
- {
start--;
- }
return document.GetText(start, offset - start);
}
public bool ShouldTriggerCompletion(char triggerChar, string currentLineText, int caretIndex) => true;
+
+ private static bool IsWordPart(char c) => char.IsLetterOrDigit(c) || "@$_".IndexOf(c) != -1;
}
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Editor/EcsHighlightingLoader.cs b/src/EasyCon2.Avalonia.Core/Editor/EcsHighlightingLoader.cs
new file mode 100644
index 00000000..8c2a9939
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Editor/EcsHighlightingLoader.cs
@@ -0,0 +1,49 @@
+using AvaloniaEdit.Highlighting;
+using AvaloniaEdit.Highlighting.Xshd;
+using System.Reflection;
+using System.Xml;
+
+namespace EasyCon2.Avalonia.Core.Editor;
+
+public static class EcsHighlightingLoader
+{
+ private static bool _registered;
+
+ public static void RegisterAll()
+ {
+ if (_registered) return;
+
+ var asm = Assembly.GetExecutingAssembly();
+
+ RegisterFromResource(asm, "EasyCon2.Avalonia.Core.Resources.Scripts.ecp.xshd",
+ "ECScript", [".txt", ".ecs"]);
+ RegisterFromResource(asm, "EasyCon2.Avalonia.Core.Resources.Scripts.lua.xshd",
+ "Lua", [".lua"]);
+ RegisterFromResource(asm, "EasyCon2.Avalonia.Core.Resources.Scripts.Python-Mode.xshd",
+ "Python", [".py"]);
+
+ _registered = true;
+ }
+
+ public static IHighlightingDefinition? GetByExtension(string extension)
+ {
+ RegisterAll();
+ return HighlightingManager.Instance.GetDefinitionByExtension(extension);
+ }
+
+ public static IHighlightingDefinition? GetByName(string name)
+ {
+ RegisterAll();
+ return HighlightingManager.Instance.GetDefinition(name);
+ }
+
+ private static void RegisterFromResource(Assembly asm, string resourceName,
+ string name, string[] extensions)
+ {
+ using var stream = asm.GetManifestResourceStream(resourceName);
+ if (stream == null) return;
+ using var reader = XmlReader.Create(stream);
+ var definition = HighlightingLoader.Load(reader, HighlightingManager.Instance);
+ HighlightingManager.Instance.RegisterHighlighting(name, extensions, definition);
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorControl.axaml b/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorControl.axaml
new file mode 100644
index 00000000..664ffaaf
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorControl.axaml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorControl.axaml.cs b/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorControl.axaml.cs
new file mode 100644
index 00000000..4bff52e3
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorControl.axaml.cs
@@ -0,0 +1,131 @@
+using Avalonia.Controls;
+using Avalonia.Input;
+using AvaloniaEdit;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Highlighting;
+using AvaloniaEdit.Search;
+
+namespace EasyCon2.Avalonia.Core.Editor;
+
+public partial class ScriptEditorControl : UserControl
+{
+ private readonly TextEditor _editor;
+ private readonly SearchPanel _searchPanel;
+ private CodeCompletionController _completionController;
+ private EcpCompletionProvider _completionProvider;
+
+ public event EventHandler? EditorTextChanged;
+ public event Action? FileDropped;
+
+ public TextEditor TextEditor => _editor;
+
+ public bool EnableAutoCompletion
+ {
+ get => _completionController?.EnableAutoCompletion ?? false;
+ set { if (_completionController != null) _completionController.EnableAutoCompletion = value; }
+ }
+
+ public string Text
+ {
+ get => _editor.Text;
+ set => _editor.Text = value;
+ }
+
+ public bool IsModified
+ {
+ get => _editor.IsModified;
+ set => _editor.IsModified = value;
+ }
+
+ public bool IsReadOnly
+ {
+ get => _editor.IsReadOnly;
+ set => _editor.IsReadOnly = value;
+ }
+
+ public string? FileName
+ {
+ get => _editor.Document?.FileName;
+ set
+ {
+ if (_editor.Document != null)
+ _editor.Document.FileName = value;
+ }
+ }
+
+ public IHighlightingDefinition? SyntaxHighlighting
+ {
+ get => _editor.SyntaxHighlighting;
+ set => _editor.SyntaxHighlighting = value;
+ }
+
+ public int SelectionStart => _editor.SelectionStart;
+ public int SelectionLength => _editor.SelectionLength;
+ public string SelectedText => _editor.SelectedText;
+
+ public ScriptEditorControl()
+ {
+ InitializeComponent();
+ _editor = this.FindControl("Editor")!;
+ _editor.TextChanged += (_, _) => EditorTextChanged?.Invoke(this, EventArgs.Empty);
+
+ SetupDragDrop();
+ _searchPanel = SearchPanel.Install(_editor);
+ InitCompletion();
+ }
+
+ private void InitCompletion()
+ {
+ _completionProvider = new EcpCompletionProvider(_editor);
+ _completionController = new CodeCompletionController(_editor, _completionProvider);
+ }
+
+ public void SetImgLabelProvider(Func> provider)
+ {
+ _completionProvider.GetImgLabel = provider;
+ }
+
+ public void OpenSearchPanel() => _searchPanel.Open();
+
+ private void SetupDragDrop()
+ {
+ DragDrop.SetAllowDrop(_editor, true);
+ DragDrop.AddDragOverHandler(_editor, OnDragOver);
+ DragDrop.AddDropHandler(_editor, OnDrop);
+ }
+
+ private void OnDragOver(object? sender, DragEventArgs e)
+ {
+ e.DragEffects = e.DataTransfer.Contains(DataFormat.File)
+ ? DragDropEffects.Copy : DragDropEffects.None;
+ }
+
+ private void OnDrop(object? sender, DragEventArgs e)
+ {
+ if (!e.DataTransfer.Contains(DataFormat.File)) return;
+ var files = e.DataTransfer.TryGetFiles();
+ var path = files?.FirstOrDefault()?.Path.LocalPath;
+ if (path != null)
+ FileDropped?.Invoke(path);
+ }
+
+ public void Load(string path)
+ {
+ using var stream = File.OpenRead(path);
+ _editor.Load(stream);
+ FileName = path;
+ }
+ public void Save(string path)
+ {
+ using var stream = File.Create(path);
+ _editor.Save(stream);
+ }
+ public void Clear() => _editor.Clear();
+ public void ScrollToLine(int line) => _editor.ScrollToLine(line);
+ public void ScrollToHome() => _editor.ScrollToHome();
+ public void Select(int offset, int length) => _editor.Select(offset, length);
+
+ public IDocument Document => _editor.Document;
+ public TextDocument TextDocument => _editor.Document;
+ public AvaloniaEdit.Editing.TextArea TextArea => _editor.TextArea;
+}
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorHost.cs b/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorHost.cs
new file mode 100644
index 00000000..257a90b8
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Editor/ScriptEditorHost.cs
@@ -0,0 +1,13 @@
+namespace EasyCon2.Avalonia.Core.Editor;
+
+public static class ScriptEditorHost
+{
+ public static ScriptEditorControl CreateControl()
+ {
+ AvaloniaRuntime.EnsureInitialized();
+ EcsHighlightingLoader.RegisterAll();
+ var control = new ScriptEditorControl();
+ control.SyntaxHighlighting = EcsHighlightingLoader.GetByName("ECScript");
+ return control;
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Resources/Scripts/Python-Mode.xshd b/src/EasyCon2.Avalonia.Core/Resources/Scripts/Python-Mode.xshd
new file mode 100644
index 00000000..23091d63
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Resources/Scripts/Python-Mode.xshd
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ TODO
+ FIXME
+
+
+ HACK
+ UNDONE
+
+
+
+
+
+ \#
+
+
+
+ '''
+ '''
+
+
+ """
+ """
+
+
+
+ "
+ "
+
+
+
+
+
+ '
+ '
+
+
+
+
+
+
+ and
+ as
+ assert
+ break
+ class
+ continue
+ def
+ del
+ elif
+ else
+ except
+ exec
+ False
+ finally
+ for
+ from
+ global
+ if
+ import
+ in
+ is
+ lambda
+ None
+ nonlocal
+ not
+ or
+ pass
+ print
+ raise
+ return
+ True
+ try
+ while
+ with
+ yield
+ async
+ await
+
+
+
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\() # followed by (
+
+
+ \b0[xX][0-9a-fA-F]+ # hex number
+ ( \b\d+(\.[0-9]+)? #number with optional floating point
+ | \.[0-9]+ #or just starting with floating point
+ )
+ ([eE][+-]?[0-9]+)? # optional exponent
+
+
+
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Resources/Scripts/ecp.xshd b/src/EasyCon2.Avalonia.Core/Resources/Scripts/ecp.xshd
new file mode 100644
index 00000000..cc689827
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Resources/Scripts/ecp.xshd
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TODO
+ FIXME
+
+
+ HACK
+ UNDONE
+
+
+
+
+
+
+
+
+ "
+ "
+
+
+ '
+ '
+
+
+
+ \$[\w]+
+ \@[\w]+
+ _[\w]+
+
+
+ \b\d+(\.\d+)?([eE][+-]?\d+)?
+
+
+ IMPORT
+ FOR
+ TO
+ STEP
+ NEXT
+ BREAK
+ CONTINUE
+ WHILE
+ END
+ IF
+ ELIF
+ ELSE
+ ENDIF
+ FUNC
+ RETURN
+ ENDFUNC
+ CALL
+ and
+ or
+
+
+
+ RAND
+ TIME
+ PRINT
+ ALERT
+ WAIT
+ AMIIBO
+ BEEP
+
+
+
+ A
+ B
+ X
+ Y
+ L
+ R
+ ZL
+ ZR
+ PLUS
+ MINUS
+ LCLICK
+ RCLICK
+ HOME
+ CAPTURE
+ UP
+ UPRIGHT
+ RIGHT
+ DOWNRIGHT
+ DOWN
+ DOWNLEFT
+ LEFT
+ UPLEFT
+ LS
+ RS
+
+
+
+
+
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/Resources/Scripts/lua.xshd b/src/EasyCon2.Avalonia.Core/Resources/Scripts/lua.xshd
new file mode 100644
index 00000000..22151ce3
--- /dev/null
+++ b/src/EasyCon2.Avalonia.Core/Resources/Scripts/lua.xshd
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TODO
+ FIXME
+
+
+ HACK
+ UNDONE
+
+
+
+
+
+
+
+ ---
+
+
+
+
+
+
+
+
+ --\[[=]*\[
+ \][=]*]
+
+
+
+
+ --
+
+
+
+ "
+ "
+
+
+
+
+
+
+
+ '
+ '
+
+
+
+
+
+
+
+ \[[=]*\[
+ \][=]*]
+
+
+
+ true
+ false
+
+
+
+ and
+ break
+ do
+ else
+ elseif
+ end
+ false
+ for
+ function
+ if
+ in
+ local
+
+ not
+ or
+ repeat
+ return
+ then
+ true
+ until
+ while
+ using
+ continue
+
+
+
+ break
+ return
+
+
+
+ local
+
+
+
+ nil
+
+
+
+
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\() # followed by (
+
+
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\") # followed by "
+
+
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\') # followed by '
+
+
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\{) # followed by {
+
+
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\[) # followed by [
+
+
+
+
+ \b0[xX][0-9a-fA-F]+ # hex number
+ |
+ ( \b\d+(\.[0-9]+)? #number with optional floating point
+ | \.[0-9]+ #or just starting with floating point
+ )
+ ([eE][+-]?[0-9]+)? # optional exponent
+
+
+
+ [?,.;()\[\]{}+\-/%*<>^+~!|&]+
+
+
+
\ No newline at end of file
diff --git a/src/EasyCon2.Avalonia.Core/VPad/VPadService.cs b/src/EasyCon2.Avalonia.Core/VPad/VPadService.cs
index 0969707b..53f13830 100644
--- a/src/EasyCon2.Avalonia.Core/VPad/VPadService.cs
+++ b/src/EasyCon2.Avalonia.Core/VPad/VPadService.cs
@@ -80,8 +80,5 @@ private void RegisterEscapeKey()
() => false);
}
- public void UnregisterAllKeys()
- {
- _keyBinder.UnregisterAllKeys();
- }
+ public void Deactivate() => Active = false;
}
\ No newline at end of file
diff --git a/src/EasyCon2/App/MainForm.Designer.cs b/src/EasyCon2/App/MainForm.Designer.cs
index cb65e5ea..e5bba229 100644
--- a/src/EasyCon2/App/MainForm.Designer.cs
+++ b/src/EasyCon2/App/MainForm.Designer.cs
@@ -1,3 +1,4 @@
+using Avalonia.Win32.Interoperability;
using EasyCon2.Forms;
namespace EasyCon2.App;
@@ -47,7 +48,7 @@ private void InitializeComponent()
menuItemAbout = new ToolStripMenuItem();
mainSplit = new SplitContainer();
contentPanel = new Panel();
- editorHost = new System.Windows.Forms.Integration.ElementHost();
+ editorHost = new WinFormsAvaloniaControlHost();
logPanel = new Panel();
clsLogBtn = new Button();
logTxtBox = new RichLogBox();
@@ -80,7 +81,6 @@ private void InitializeComponent()
btnCheckUpdate = new Button();
btnSource = new Button();
scriptTitleLabel = new Label();
- findPanel1 = new FindPanel();
sideBar = new Panel();
btnPageLog = new Button();
btnPageEditor = new Button();
@@ -361,7 +361,6 @@ private void InitializeComponent()
contentPanel.Controls.Add(burnPanel);
contentPanel.Controls.Add(settingsPanel);
contentPanel.Controls.Add(scriptTitleLabel);
- contentPanel.Controls.Add(findPanel1);
contentPanel.Dock = DockStyle.Fill;
contentPanel.Location = new Point(40, 0);
contentPanel.Name = "contentPanel";
@@ -812,20 +811,7 @@ private void InitializeComponent()
scriptTitleLabel.Text = "未命名脚本";
scriptTitleLabel.TextAlign = ContentAlignment.MiddleLeft;
scriptTitleLabel.Visible = false;
- //
- // findPanel1
- //
- findPanel1.Anchor = AnchorStyles.Top | AnchorStyles.Right;
- findPanel1.BackColor = Color.Transparent;
- findPanel1.BorderStyle = BorderStyle.FixedSingle;
- findPanel1.Location = new Point(349, 17);
- findPanel1.Name = "findPanel1";
- findPanel1.Replaced = null;
- findPanel1.Size = new Size(250, 98);
- findPanel1.TabIndex = 2;
- findPanel1.Target = null;
- findPanel1.Visible = false;
- //
+ //
// sideBar
//
sideBar.BackColor = Color.FromArgb(230, 229, 224);
@@ -1292,7 +1278,7 @@ private void InitializeComponent()
private ToolStripMenuItem menuItemAbout;
// Editor
- private System.Windows.Forms.Integration.ElementHost editorHost;
+ private WinFormsAvaloniaControlHost editorHost;
private Label scriptTitleLabel;
// Sidebar
@@ -1339,9 +1325,6 @@ private void InitializeComponent()
private RichLogBox logTxtBox;
private Button clsLogBtn;
- // Find
- private FindPanel findPanel1;
-
// Status
private StatusStrip statusStrip;
private ToolStripStatusLabel toolStripStatusLabel1;
diff --git a/src/EasyCon2/App/MainForm.cs b/src/EasyCon2/App/MainForm.cs
index 46271e37..c6edb263 100644
--- a/src/EasyCon2/App/MainForm.cs
+++ b/src/EasyCon2/App/MainForm.cs
@@ -1,19 +1,17 @@
+using AvaloniaEdit.Folding;
+using AvaloniaEdit.Highlighting;
using EasyCon.Core;
using EasyCon.Core.Config;
using EasyCon.Script.Assembly;
using EasyCon2.App.Models;
using EasyCon2.App.Services;
+using EasyCon2.Avalonia.Core.Editor;
using EasyCon2.Avalonia.Core.VPad;
using EasyCon2.Forms;
using EasyCon2.Helper;
-using Resources = EasyCon2.UI.Common.Properties.Resources;
using EasyCon2.Views;
using EasyDevice;
using EasyScript;
-using ICSharpCode.AvalonEdit;
-using ICSharpCode.AvalonEdit.Folding;
-using ICSharpCode.AvalonEdit.Highlighting;
-using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -21,9 +19,9 @@
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
-using System.Xml;
using AvaColor = Avalonia.Media.Color;
using AvaColors = Avalonia.Media.Colors;
+using Resources = EasyCon2.UI.Common.Properties.Resources;
namespace EasyCon2.App;
@@ -43,8 +41,7 @@ public partial class MainForm : Form, IOutputAdapter, IControllerAdapter
private readonly AppState _state = new();
// Editor
- private readonly TextEditor _textEditor = new();
- private CodeCompletionController? _completionController;
+ private ScriptEditorControl _textEditor;
private FoldingManager? _foldingManager;
private CustomFoldingStrategy? _foldingStrategy;
private VPadService? _vpadService;
@@ -80,45 +77,32 @@ public MainForm()
private void InitEditor()
{
- _textEditor.ShowLineNumbers = true;
-
- // Syntax highlighting
- var ecpHighlighting = HighlightingLoader.Load(
- XmlReader.Create(new MemoryStream(Resources.ecp)),
- HighlightingManager.Instance);
- HighlightingManager.Instance.RegisterHighlighting("ECScript", [".txt", ".ecs"], ecpHighlighting);
-
- var luaHighlighting = HighlightingLoader.Load(
- XmlReader.Create(new MemoryStream(Resources.lua)),
- HighlightingManager.Instance);
- HighlightingManager.Instance.RegisterHighlighting("Lua", [".lua"], luaHighlighting);
-
- var pyHighlighting = HighlightingLoader.Load(
- XmlReader.Create(new MemoryStream(Resources.Python_Mode)),
- HighlightingManager.Instance);
- HighlightingManager.Instance.RegisterHighlighting("Python", [".py"], pyHighlighting);
-
- _textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("ECScript");
- _textEditor.DragEnter += TextEditor_DragEnter;
- _textEditor.Drop += TextEditor_DragDrop;
- _textEditor.TextChanged += TextEditor_TextChanged;
-
- // Code completion
- var completionProvider = new EcpCompletionProvider(_textEditor);
- completionProvider.GetImgLabel += () => _captureService.LoadedLabels.Select(il => il.name);
- _completionController = new CodeCompletionController(
- _textEditor, completionProvider, _configService.Config.EnableAutoCompletion);
+ _textEditor = ScriptEditorHost.CreateControl();
+ _textEditor.EditorTextChanged += TextEditor_TextChanged;
+ _textEditor.FileDropped += (path) =>
+ {
+ try
+ {
+ if (!FileClose()) return;
+ FileOpen(path);
+ }
+ catch
+ {
+ MessageBox.Show("打开失败了,原因未知", "打开脚本");
+ }
+ };
// Folding
_foldingManager = FoldingManager.Install(_textEditor.TextArea);
_foldingStrategy = new CustomFoldingStrategy();
- _foldingStrategy.UpdateFoldings(_foldingManager, _textEditor.Document);
+ _foldingStrategy.UpdateFoldings(_foldingManager, _textEditor.TextDocument);
- // Find panel
- findPanel1.InitEditor(_textEditor);
+ // Completion
+ _textEditor.SetImgLabelProvider(() => _captureService.LoadedLabels.Select(il => il.name));
+ _textEditor.EnableAutoCompletion = _configService.Config.EnableAutoCompletion;
- // Host AvalonEdit in ElementHost
- editorHost.Child = _textEditor;
+ // Host in Avalonia control host
+ editorHost.Content = _textEditor;
}
private void InitServices()
@@ -289,7 +273,7 @@ private async void runStopBtn_Click(object sender, EventArgs e)
// Compile first
var externalGetters = _captureService.BuildExternalGetters();
var (success, errorLine, error) = _scriptService.Compile(
- _textEditor.Text, _textEditor.Document.FileName, externalGetters);
+ _textEditor.Text, _textEditor.TextDocument.FileName, externalGetters);
if (!success)
{
@@ -322,6 +306,7 @@ private async void runStopBtn_Click(object sender, EventArgs e)
_state.ScriptStartTime = DateTime.Now;
_state.ScriptRunning = true;
+ _vpadService?.Deactivate();
var pad = new GamePadAdapter(_deviceService.Device);
_scriptService.Run(this, pad);
@@ -340,7 +325,7 @@ private async void formatBtn_Click(object sender, EventArgs e)
{
var externalGetters = _captureService.BuildExternalGetters();
var (success, formatted, errorLine, error) = _scriptService.Format(
- _textEditor.Text, _textEditor.Document.FileName, externalGetters);
+ _textEditor.Text, _textEditor.TextDocument.FileName, externalGetters);
if (success)
{
@@ -555,7 +540,7 @@ private void btnFlash_Click(object sender, EventArgs e)
// Compile
var externalGetters = _captureService.BuildExternalGetters();
var (success, errorLine, error) = _scriptService.Compile(
- _textEditor.Text, _textEditor.Document.FileName, externalGetters);
+ _textEditor.Text, _textEditor.TextDocument.FileName, externalGetters);
if (!success)
{
MessageBox.Show(errorLine != null ? $"{errorLine}:{error}" : error, "编译出错");
@@ -631,7 +616,7 @@ private void btnGenFirmware_Click(object sender, EventArgs e)
// Compile
var externalGetters = _captureService.BuildExternalGetters();
var (success, errorLine, error) = _scriptService.Compile(
- _textEditor.Text, _textEditor.Document.FileName, externalGetters);
+ _textEditor.Text, _textEditor.TextDocument.FileName, externalGetters);
if (!success)
{
MessageBox.Show(errorLine != null ? $"{errorLine}:{error}" : error, "编译出错");
@@ -813,9 +798,8 @@ private bool FileOpen(string? path = null)
_textEditor.Load(filePath);
_textEditor.IsModified = false;
- _textEditor.Document.FileName = filePath;
- _textEditor.SyntaxHighlighting = HighlightingManager.Instance
- .GetDefinitionByExtension(Path.GetExtension(filePath));
+ _textEditor.FileName = filePath;
+ _textEditor.SyntaxHighlighting = EcsHighlightingLoader.GetByExtension(Path.GetExtension(filePath));
_state.CurrentFilePath = filePath;
_state.IsModified = false;
StatusShow("文件已打开");
@@ -836,7 +820,7 @@ private bool FileSave(bool asNew = false)
if (dlg.ShowDialog() != DialogResult.OK)
return false;
_state.CurrentFilePath = dlg.FileName;
- _textEditor.Document.FileName = dlg.FileName;
+ _textEditor.FileName = dlg.FileName;
}
_textEditor.Save(_state.CurrentFilePath);
@@ -856,39 +840,18 @@ private bool FileClose()
}
_state.CurrentFilePath = null;
_state.IsModified = false;
- _textEditor.Document.FileName = null;
+ _textEditor.FileName = null;
_textEditor.Clear();
_textEditor.IsModified = false;
StatusShow("文件已关闭");
return true;
}
- private void TextEditor_DragEnter(object? sender, System.Windows.DragEventArgs e)
- {
- e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop)
- ? System.Windows.DragDropEffects.All
- : System.Windows.DragDropEffects.None;
- }
-
- private void TextEditor_DragDrop(object? sender, System.Windows.DragEventArgs e)
- {
- try
- {
- var path = (string[])e.Data.GetData(DataFormats.FileDrop, false);
- if (!FileClose()) return;
- FileOpen(path[0]);
- }
- catch
- {
- MessageBox.Show("打开失败了,原因未知", "打开脚本");
- }
- }
-
private void TextEditor_TextChanged(object? sender, EventArgs e)
{
_state.IsModified = _textEditor.IsModified;
if (_configService.Config.EnableAutoCompletion)
- _foldingStrategy?.UpdateFoldings(_foldingManager!, _textEditor.Document);
+ _foldingStrategy?.UpdateFoldings(_foldingManager!, _textEditor.TextDocument);
_scriptService.Reset();
}
@@ -898,52 +861,41 @@ private void TextEditor_TextChanged(object? sender, EventArgs e)
private void menuItemFindReplace_Click(object sender, EventArgs e)
{
- if (_textEditor.SelectedText.Length > 0)
- findPanel1.Target = _textEditor.SelectedText;
- findPanel1.Show();
- findPanel1.BringToFront();
+ _textEditor.OpenSearchPanel();
}
private void menuItemFindNext_Click(object sender, EventArgs e)
{
- if (_textEditor.SelectedText.Length > 0)
- findPanel1.Target = _textEditor.SelectedText;
- var index = findPanel1.Find();
- if (index == -1)
- {
- MessageBox.Show("到底了");
- return;
- }
- _textEditor.Select(index, findPanel1.Target!.Length);
- _textEditor.ScrollToLine(_textEditor.Document.GetLineByOffset(index).LineNumber);
+ // SearchPanel 内部处理 F3 快捷键
}
private void menuItemToggleComment_Click(object sender, EventArgs e)
{
int startOffset = _textEditor.SelectionStart;
int endOffset = startOffset + _textEditor.SelectionLength;
- var startLine = _textEditor.Document.GetLineByOffset(startOffset);
- var endLine = _textEditor.Document.GetLineByOffset(endOffset);
+ var doc = _textEditor.TextDocument;
+ var startLine = doc.GetLineByOffset(startOffset);
+ var endLine = doc.GetLineByOffset(endOffset);
var docomment = false;
for (int lineNum = endLine.LineNumber; lineNum >= startLine.LineNumber; lineNum--)
{
- var line = _textEditor.Document.GetLineByNumber(lineNum);
- if (Scripter.CanComment(_textEditor.Document.GetText(line)))
+ var line = doc.GetLineByNumber(lineNum);
+ if (Scripter.CanComment(doc.GetText(line)))
{
docomment = true;
break;
}
}
- using (_textEditor.Document.RunUpdate())
+ using (doc.RunUpdate())
{
for (int lineNum = endLine.LineNumber; lineNum >= startLine.LineNumber; lineNum--)
{
- var line = _textEditor.Document.GetLineByNumber(lineNum);
- var text = _textEditor.Document.GetText(line);
+ var line = doc.GetLineByNumber(lineNum);
+ var text = doc.GetText(line);
text = Scripter.ToggleComment(text, docomment);
- _textEditor.Document.Replace(line, text);
+ doc.Replace(line, text);
}
}
}
@@ -955,9 +907,8 @@ private void menuItemToggleComment_Click(object sender, EventArgs e)
private void chkAutoCompletion_CheckedChanged(object sender, EventArgs e)
{
_configService.Config.EnableAutoCompletion = chkAutoCompletion.Checked;
+ _textEditor.EnableAutoCompletion = chkAutoCompletion.Checked;
_configService.Save();
- if (_completionController != null)
- _completionController.EnableAutoCompletion = chkAutoCompletion.Checked;
}
private void chkFolding_CheckedChanged(object sender, EventArgs e)
@@ -965,7 +916,7 @@ private void chkFolding_CheckedChanged(object sender, EventArgs e)
_configService.Config.ShowControllerHelp = chkFolding.Checked;
_configService.Save();
if (chkFolding.Checked)
- _foldingStrategy?.UpdateFoldings(_foldingManager!, _textEditor.Document);
+ _foldingStrategy?.UpdateFoldings(_foldingManager!, _textEditor.TextDocument);
else
_foldingManager?.Clear();
}
@@ -1183,11 +1134,11 @@ public void Alert(string message)
#region Form Lifecycle
- protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ protected override bool ProcessDialogKey(Keys keyData)
{
- if (keyData == Keys.Escape)
- findPanel1.Hide();
- return base.ProcessCmdKey(ref msg, keyData);
+ if (editorHost.ContainsFocus)
+ return false;
+ return base.ProcessDialogKey(keyData);
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
diff --git a/src/EasyCon2/EasyCon2.csproj b/src/EasyCon2/EasyCon2.csproj
index 4fbef52d..19e5f54e 100644
--- a/src/EasyCon2/EasyCon2.csproj
+++ b/src/EasyCon2/EasyCon2.csproj
@@ -1,41 +1,33 @@
-
WinExe
warnings
true
- true
net10.0-windows7.0
true
..\EasyCon2.UI.Common\Resources\Icons\favicon.ico
app.manifest
$(NoWarn);WFO1000
-
-
-
-
.\deps\libamiibo.dll
-
Always
-
diff --git a/src/EasyCon2/EasyConForm.Designer.cs b/src/EasyCon2/EasyConForm.Designer.cs
index 295f55f8..029e459c 100644
--- a/src/EasyCon2/EasyConForm.Designer.cs
+++ b/src/EasyCon2/EasyConForm.Designer.cs
@@ -1,3 +1,5 @@
+using Avalonia.Win32.Interoperability;
+
namespace EasyCon2.Forms
{
partial class EasyConForm
@@ -84,9 +86,8 @@ private void InitializeComponent()
groupBox2 = new GroupBox();
genFwButton = new Button();
comboBoxBoardType = new ComboBox();
- findPanel1 = new FindPanel();
buttonShowController = new Button();
- editorHost = new System.Windows.Forms.Integration.ElementHost();
+ editorHost = new WinFormsAvaloniaControlHost();
groupBox3 = new GroupBox();
ComPort = new ComboBox();
buttonSerialPortConnect = new Button();
@@ -601,20 +602,7 @@ private void InitializeComponent()
comboBoxBoardType.Size = new Size(94, 28);
comboBoxBoardType.TabIndex = 5;
//
- // findPanel1
- //
- findPanel1.AccessibleName = "查找窗口";
- findPanel1.Anchor = AnchorStyles.Top | AnchorStyles.Right;
- findPanel1.BackColor = Color.Transparent;
- findPanel1.BorderStyle = BorderStyle.FixedSingle;
- findPanel1.Location = new Point(184, 17);
- findPanel1.Name = "findPanel1";
- findPanel1.Replaced = null;
- findPanel1.Size = new Size(250, 98);
- findPanel1.TabIndex = 1;
- findPanel1.Target = null;
- findPanel1.Visible = false;
- //
+ //
// buttonShowController
//
buttonShowController.AccessibleName = "打开虚拟手柄";
@@ -876,7 +864,6 @@ private void InitializeComponent()
// scriptContainer
//
scriptContainer.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
- scriptContainer.Controls.Add(findPanel1);
scriptContainer.Controls.Add(scriptTitleLabel);
scriptContainer.Controls.Add(scriptPanel);
scriptContainer.Location = new Point(313, 32);
@@ -981,7 +968,7 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem 采集卡类型ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem 设置ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem 显示调试信息ToolStripMenuItem;
- private System.Windows.Forms.Integration.ElementHost editorHost;
+ private WinFormsAvaloniaControlHost editorHost;
private ToolStripMenuItem 推送设置ToolStripMenuItem;
private ToolStripMenuItem 代码自动补全ToolStripMenuItem;
private ToolStripMenuItem 蓝牙ToolStripMenuItem;
@@ -1001,7 +988,6 @@ private void InitializeComponent()
private ToolStripMenuItem 編輯ToolStripMenuItem;
private ToolStripMenuItem 查找替換ToolStripMenuItem;
private ToolStripMenuItem 查找下一个ToolStripMenuItem;
- private FindPanel findPanel1;
private ToolStripMenuItem 显示折叠ToolStripMenuItem;
private ToolStripMenuItem 注释取消注释ToolStripMenuItem;
private ToolStripMenuItem 脚本语法ToolStripMenuItem;
diff --git a/src/EasyCon2/EasyConForm.Script.cs b/src/EasyCon2/EasyConForm.Script.cs
index a1ccdf01..69178fd3 100644
--- a/src/EasyCon2/EasyConForm.Script.cs
+++ b/src/EasyCon2/EasyConForm.Script.cs
@@ -33,7 +33,7 @@ private async Task ScriptCompile()
}));
try
{
- var diag = _program.Parse(textEditor.Text, textEditor.Document.FileName, externalGetters);
+ var diag = _program.Parse(scriptEditor.Text, scriptEditor.FileName, externalGetters);
if (diag.HasErrors())
{
var d1 = diag.Where(d => d.IsError).First();
@@ -76,7 +76,7 @@ private async void ScriptRun()
}
}
- _vpadService?.UnregisterAllKeys();
+ _vpadService?.Deactivate();
StatusShowLog("开始运行");
scriptRunning = true;
diff --git a/src/EasyCon2/EasyConForm.cs b/src/EasyCon2/EasyConForm.cs
index c53b079f..5f9255c6 100644
--- a/src/EasyCon2/EasyConForm.cs
+++ b/src/EasyCon2/EasyConForm.cs
@@ -1,16 +1,14 @@
+using AvaloniaEdit.Folding;
+using AvaloniaEdit.Highlighting;
using EasyCon.Core;
using EasyCon.Core.Config;
using EasyCon.Script.Assembly;
+using EasyCon2.Avalonia.Core.Editor;
using EasyCon2.Avalonia.Core.VPad;
using EasyCon2.Helper;
-using Resources = EasyCon2.UI.Common.Properties.Resources;
using EasyCon2.Views;
using EasyDevice;
using EasyScript;
-using ICSharpCode.AvalonEdit;
-using ICSharpCode.AvalonEdit.Folding;
-using ICSharpCode.AvalonEdit.Highlighting;
-using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using System.Diagnostics;
using System.IO;
using System.Media;
@@ -18,9 +16,9 @@
using System.Reflection;
using System.Text.Json;
using System.Text.RegularExpressions;
-using System.Xml;
using AvaColor = Avalonia.Media.Color;
using AvaColors = Avalonia.Media.Colors;
+using Resources = EasyCon2.UI.Common.Properties.Resources;
namespace EasyCon2.Forms
{
@@ -28,8 +26,7 @@ public partial class EasyConForm : Form, IOutputAdapter, IControllerAdapter
{
private readonly string VER = Assembly.GetEntryAssembly()?.GetCustomAttribute()
?.InformationalVersion;
- private readonly TextEditor textEditor = new();
- private CodeCompletionController _completionController;
+ private ScriptEditorControl scriptEditor;
private VPadService? _vpadService;
private NintendoSwitch NS = new();
@@ -47,9 +44,9 @@ public partial class EasyConForm : Form, IOutputAdapter, IControllerAdapter
const string FirmwarePath = @"Firmware\";
private readonly string defaultName = "未命名脚本";
- private string fileName => textEditor.Document.FileName == null ? defaultName : Path.GetFileName(textEditor.Document.FileName);
+ private string fileName => scriptEditor.FileName == null ? defaultName : Path.GetFileName(scriptEditor.FileName);
- private string curILPath => textEditor.Document.FileName == null ? "" : Path.Combine(Path.GetDirectoryName(textEditor.Document.FileName), "ImgLabel");
+ private string curILPath => scriptEditor.FileName == null ? "" : Path.Combine(Path.GetDirectoryName(scriptEditor.FileName), "ImgLabel");
private readonly List captureTypes = [];
@@ -60,13 +57,11 @@ public EasyConForm()
LoadConfig();
}
- protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ protected override bool ProcessDialogKey(Keys keyData)
{
- if (keyData == Keys.Escape)
- {
- findPanel1.Hide();
- }
- return base.ProcessCmdKey(ref msg, keyData);
+ if (editorHost.ContainsFocus)
+ return false;
+ return base.ProcessDialogKey(keyData);
}
protected override void WndProc(ref Message m)
@@ -88,8 +83,8 @@ protected override void WndProc(ref Message m)
base.WndProc(ref m);
}
- private FoldingManager _foldingManager;
- private CustomFoldingStrategy _foldingStrategy;
+ private FoldingManager? _foldingManager;
+ private CustomFoldingStrategy? _foldingStrategy;
private void EasyConForm_Load(object sender, EventArgs e)
{
@@ -127,29 +122,29 @@ private void EasyConForm_FormClosing(object sender, FormClosingEventArgs e)
private void InitEditor()
{
- textEditor.ShowLineNumbers = true;
- var syntaxHighlighting = HighlightingLoader.Load(XmlReader.Create(new MemoryStream(Resources.ecp)), HighlightingManager.Instance);
- HighlightingManager.Instance.RegisterHighlighting("ECScript", [".txt", ".ecs"], syntaxHighlighting);
- var luaHighlighting = HighlightingLoader.Load(XmlReader.Create(new MemoryStream(Resources.lua)), HighlightingManager.Instance);
- HighlightingManager.Instance.RegisterHighlighting("Lua", [".lua"], luaHighlighting);
- var pyHighlighting = HighlightingLoader.Load(XmlReader.Create(new MemoryStream(Resources.Python_Mode)), HighlightingManager.Instance);
- HighlightingManager.Instance.RegisterHighlighting("Python", [".py"], pyHighlighting);
- textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("ECScript");
- textEditor.DragEnter += new System.Windows.DragEventHandler(this.textBoxScript_DragEnter);
- textEditor.Drop += new System.Windows.DragEventHandler(this.textBoxScript_DragDrop);
- textEditor.TextChanged += new EventHandler(this.textBoxScript_TextChanged);
-
- var completionProvider = new EcpCompletionProvider(textEditor);
- completionProvider.GetImgLabel += () => captureVideo.LoadedLabels.Select(il => il.name);
- _completionController = new CodeCompletionController(textEditor, completionProvider, _config.EnableAutoCompletion);
-
- _foldingManager = FoldingManager.Install(textEditor.TextArea);
+ scriptEditor = ScriptEditorHost.CreateControl();
+ scriptEditor.EditorTextChanged += textBoxScript_TextChanged;
+ scriptEditor.FileDropped += (path) =>
+ {
+ try
+ {
+ if (!FileClose()) return;
+ FileOpen(path);
+ }
+ catch
+ {
+ MessageBox.Show("打开失败了,原因未知", "打开脚本");
+ }
+ };
+
+ _foldingManager = FoldingManager.Install(scriptEditor.TextArea);
_foldingStrategy = new CustomFoldingStrategy();
- _foldingStrategy.UpdateFoldings(_foldingManager, textEditor.Document);
+ _foldingStrategy.UpdateFoldings(_foldingManager, scriptEditor.TextDocument);
- findPanel1.InitEditor(textEditor);
+ scriptEditor.SetImgLabelProvider(() => captureVideo.LoadedLabels.Select(il => il.name));
+ scriptEditor.EnableAutoCompletion = _config.EnableAutoCompletion;
- editorHost.Child = textEditor;
+ editorHost.Content = scriptEditor;
}
private void InitEvent()
@@ -294,13 +289,13 @@ private void UpdateUI()
Invoke(delegate
{
// script edited
- scriptTitleLabel.Text = textEditor.IsModified ? $"{fileName}(已编辑)" : fileName;
+ scriptTitleLabel.Text = scriptEditor.IsModified ? $"{fileName}(已编辑)" : fileName;
// update record script to text
if (NS.recordState == RecordState.RECORD_START)
{
buttonRecord.Text = "停止录制";
- textEditor.Text = NS.GetRecordScript();
- this.textEditor.ScrollToHome();
+ scriptEditor.Text = NS.GetRecordScript();
+ scriptEditor.ScrollToHome();
}
if (captureVideo.IsOpened)
{
@@ -377,7 +372,7 @@ private void StatusShowLog(string str)
private void ScriptSelectLine(int index)
{
- textEditor.ScrollToLine(index);
+ scriptEditor.ScrollToLine(index);
}
private Board GetSelectedBoard()
@@ -512,10 +507,10 @@ private bool FileOpen(string path = "")
_currentFile = openFileDialog.FileName;
}
- textEditor.Load(_currentFile);
- textEditor.IsModified = false;
- textEditor.Document.FileName = _currentFile;
- textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(Path.GetExtension(_currentFile));
+ scriptEditor.Load(_currentFile);
+ scriptEditor.IsModified = false;
+ scriptEditor.FileName = _currentFile;
+ scriptEditor.SyntaxHighlighting = EcsHighlightingLoader.GetByExtension(Path.GetExtension(_currentFile));
StatusShowLog("文件已打开");
return true;
}
@@ -529,22 +524,22 @@ private bool FileSave(bool saveAs = false)
saveFileDialog.Filter = "脚本文件 (*.txt,*.ecs)|*.txt;*.ecs|所有文件(*.*)|*.*";
saveFileDialog.FileName = "未命名脚本.txt";
- if (saveAs || textEditor.Document.FileName == null)
+ if (saveAs || scriptEditor.FileName == null)
{
if (saveFileDialog.ShowDialog() != DialogResult.OK)
return false;
- textEditor.Document.FileName = saveFileDialog.FileName;
+ scriptEditor.FileName = saveFileDialog.FileName;
}
- textEditor.Save(textEditor.Document.FileName);
- textEditor.IsModified = false;
+ scriptEditor.Save(scriptEditor.FileName);
+ scriptEditor.IsModified = false;
StatusShowLog("文件已保存");
return true;
}
private bool FileClose()
{
- if (textEditor.IsModified)
+ if (scriptEditor.IsModified)
{
var r = MessageBox.Show("文件已编辑,是否保存?", "", MessageBoxButtons.YesNoCancel);
if (r == DialogResult.Cancel)
@@ -555,9 +550,9 @@ private bool FileClose()
return false;
}
}
- textEditor.Document.FileName = null;
- textEditor.Clear();
- textEditor.IsModified = false;
+ scriptEditor.FileName = null;
+ scriptEditor.Clear();
+ scriptEditor.IsModified = false;
StatusShowLog("文件已关闭");
return true;
}
@@ -600,8 +595,8 @@ private async void compileButton_Click(object sender, EventArgs e)
// 格式化代码并确保逗号后面总是有空格
var formattedCode = _program.ToCode().Trim();
formattedCode = Regex.Replace(formattedCode, ",(?! )", ", ");
- textEditor.Text = formattedCode;
- textEditor.Select(0, 0);
+ scriptEditor.Text = formattedCode;
+ scriptEditor.Select(0, 0);
}
else
{
@@ -618,10 +613,10 @@ private async void compileButton_Click(object sender, EventArgs e)
}
}
- private void textBoxScript_TextChanged(object sender, EventArgs e)
+ private void textBoxScript_TextChanged(object? sender, EventArgs e)
{
- if (显示折叠ToolStripMenuItem.Checked)
- _foldingStrategy.UpdateFoldings(_foldingManager, textEditor.Document);
+ if (显示折叠ToolStripMenuItem.Checked && _foldingStrategy != null && _foldingManager != null)
+ _foldingStrategy.UpdateFoldings(_foldingManager, scriptEditor.TextDocument);
ScriptReset();
}
@@ -630,7 +625,7 @@ private void buttonScriptRunStop_Click(object sender, EventArgs e)
runStopBtn.Enabled = false;
if (!scriptRunning)
{
- if (textEditor.Document.FileName != null && textEditor.IsModified)
+ if (scriptEditor.FileName != null && scriptEditor.IsModified)
{
MessageBox.Show("您还没有保存脚本,请先保存后再运行");
}
@@ -773,7 +768,7 @@ private void buttonRecord_Click(object sender, EventArgs e)
}
buttonRecord.Text = "停止录制";
buttonRecordPause.Enabled = true;
- textEditor.IsEnabled = false;
+ scriptEditor.IsReadOnly = true;
NS.StartRecord();
}
else
@@ -781,7 +776,7 @@ private void buttonRecord_Click(object sender, EventArgs e)
Debug.Write("stop");
buttonRecord.Text = "录制脚本";
buttonRecordPause.Enabled = false;
- textEditor.IsEnabled = true;
+ scriptEditor.IsReadOnly = false;
NS.StopRecord();
}
}
@@ -795,33 +790,6 @@ private void buttonRecordPause_Click(object sender, EventArgs e)
}
}
- private void textBoxScript_DragDrop(object sender, System.Windows.DragEventArgs e)
- {
- try
- {
- var path = (string[])e.Data.GetData(DataFormats.FileDrop, false);
- if (!FileClose())
- return;
- FileOpen(path[0]);
- }
- catch
- {
- MessageBox.Show("打开失败了,原因未知", "打开脚本");
- }
- }
-
- private void textBoxScript_DragEnter(object sender, System.Windows.DragEventArgs e)
- {
- if (e.Data.GetDataPresent(DataFormats.FileDrop))
- {
- e.Effects = System.Windows.DragDropEffects.All;
- }
- else
- {
- e.Effects = System.Windows.DragDropEffects.None;
- }
- }
-
#region EasyCon菜单功能
private void 新建ToolStripMenuItem_Click(object sender, EventArgs e)
{
@@ -856,25 +824,12 @@ private void textBoxScript_DragEnter(object sender, System.Windows.DragEventArgs
private void 查找替換ToolStripMenuItem_Click(object sender, EventArgs e)
{
- if (textEditor.SelectedText.Length > 0)
- findPanel1.Target = textEditor.SelectedText;
- findPanel1.Show();
+ scriptEditor.OpenSearchPanel();
}
private void 查找下一个ToolStripMenuItem_Click(object sender, EventArgs e)
{
- if (textEditor.SelectedText.Length > 0)
- findPanel1.Target = textEditor.SelectedText;
- var index = findPanel1.Find();
-
- if (index == -1)
- {
- MessageBox.Show("到底了");
- return;
- }
-
- textEditor.Select(index, findPanel1.Target.Length);
- textEditor.ScrollToLine(textEditor.Document.GetLineByOffset(index).LineNumber);
+ // SearchPanel 内部处理 F3 快捷键
}
private void DeviceTypeItem_Click(object sender, EventArgs e)
@@ -973,12 +928,9 @@ public void RefreshAlert()
private void 代码自动补全ToolStripMenuItem_Click(object sender, EventArgs e)
{
var menu = (ToolStripMenuItem)sender;
- // CheckOnClick已自动切换状态,直接读取当前值
_config.EnableAutoCompletion = menu.Checked;
+ scriptEditor.EnableAutoCompletion = menu.Checked;
SaveConfig();
-
- // 直接改变补全控制器的状态,无需重新初始化
- _completionController.EnableAutoCompletion = _config.EnableAutoCompletion;
}
private void 设备驱动配置ToolStripMenuItem_Click(object sender, EventArgs e)
@@ -1064,42 +1016,39 @@ public void RefreshAlert()
var menu = (ToolStripMenuItem)sender;
menu.Checked = !menu.Checked;
if (menu.Checked)
- _foldingStrategy.UpdateFoldings(_foldingManager, textEditor.Document);
+ _foldingStrategy?.UpdateFoldings(_foldingManager!, scriptEditor.TextDocument);
else
- _foldingManager.Clear();
+ _foldingManager?.Clear();
}
private void 注释取消注释ToolStripMenuItem_Click(object sender, EventArgs e)
{
- // 获取选择范围
- int startOffset = textEditor.SelectionStart;
- int endOffset = startOffset + textEditor.SelectionLength;
-
- // 获取开始和结束行
- var startLine = textEditor.Document.GetLineByOffset(startOffset);
- var endLine = textEditor.Document.GetLineByOffset(endOffset);
- Debug.WriteLine($"选择范围:{startLine.LineNumber}-{endLine.LineNumber}");
+ int startOffset = scriptEditor.SelectionStart;
+ int endOffset = startOffset + scriptEditor.SelectionLength;
+ var doc = scriptEditor.TextDocument;
+ var startLine = doc.GetLineByOffset(startOffset);
+ var endLine = doc.GetLineByOffset(endOffset);
var docomment = false;
for (int lineNum = endLine.LineNumber; lineNum >= startLine.LineNumber; lineNum--)
{
- var line = textEditor.Document.GetLineByNumber(lineNum);
- if (Scripter.CanComment(textEditor.Document.GetText(line)))
+ var line = doc.GetLineByNumber(lineNum);
+ if (Scripter.CanComment(doc.GetText(line)))
{
docomment = true;
break;
}
}
- using (textEditor.Document.RunUpdate())
+ using (doc.RunUpdate())
{
for (int lineNum = endLine.LineNumber; lineNum >= startLine.LineNumber; lineNum--)
{
- var line = textEditor.Document.GetLineByNumber(lineNum);
- var text = textEditor.Document.GetText(line);
+ var line = doc.GetLineByNumber(lineNum);
+ var text = doc.GetText(line);
text = Scripter.ToggleComment(text, docomment);
- textEditor.Document.Replace(line, text);
+ doc.Replace(line, text);
}
}
}
diff --git a/src/EasyCon2/Forms/CaptureVideoForm.cs b/src/EasyCon2/Forms/CaptureVideoForm.cs
index 365879de..d98ef3dc 100644
--- a/src/EasyCon2/Forms/CaptureVideoForm.cs
+++ b/src/EasyCon2/Forms/CaptureVideoForm.cs
@@ -1,11 +1,11 @@
using EasyCon.Capture;
using EasyCon2.Helper;
-using Resources = EasyCon2.UI.Common.Properties.Resources;
using OpenCvSharp.Extensions;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.IO;
using Mat = OpenCvSharp.Mat;
+using Resources = EasyCon2.UI.Common.Properties.Resources;
namespace EasyCon2.Forms
{
diff --git a/src/EasyCon2/Forms/ESPConfig.cs b/src/EasyCon2/Forms/ESPConfig.cs
index 143d8c41..bd5d4517 100644
--- a/src/EasyCon2/Forms/ESPConfig.cs
+++ b/src/EasyCon2/Forms/ESPConfig.cs
@@ -1,6 +1,5 @@
using EasyCapture;
using EasyCon2.Models;
-using Resources = EasyCon2.UI.Common.Properties.Resources;
using EasyDevice;
using LibAmiibo.Data;
using LibAmiibo.Data.Figurine;
@@ -10,6 +9,7 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
+using Resources = EasyCon2.UI.Common.Properties.Resources;
namespace EasyCon2.Forms
{
diff --git a/src/EasyCon2/Forms/FindPanel.Designer.cs b/src/EasyCon2/Forms/FindPanel.Designer.cs
deleted file mode 100644
index 239477ce..00000000
--- a/src/EasyCon2/Forms/FindPanel.Designer.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-namespace EasyCon2.Forms
-{
- partial class FindPanel
- {
- ///
- /// 必需的设计器变量。
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// 清理所有正在使用的资源。
- ///
- /// 如果应释放托管资源,为 true;否则为 false。
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region 组件设计器生成的代码
-
- ///
- /// 设计器支持所需的方法 - 不要修改
- /// 使用代码编辑器修改此方法的内容。
- ///
- private void InitializeComponent()
- {
- components = new System.ComponentModel.Container();
- searchBtn = new Button();
- searchTBox = new TextBox();
- bindingSource1 = new BindingSource(components);
- replaceTBox = new TextBox();
- replaceBtn = new Button();
- closeBtn = new Button();
- ((System.ComponentModel.ISupportInitialize)bindingSource1).BeginInit();
- SuspendLayout();
- //
- // searchBtn
- //
- searchBtn.FlatAppearance.BorderSize = 0;
- searchBtn.FlatStyle = FlatStyle.Flat;
- searchBtn.Location = new Point(228, 30);
- searchBtn.Name = "searchBtn";
- searchBtn.Size = new Size(69, 35);
- searchBtn.TabIndex = 0;
- searchBtn.Text = "下一个";
- searchBtn.UseVisualStyleBackColor = true;
- searchBtn.Click += searchBtn_Click;
- //
- // searchTBox
- //
- searchTBox.DataBindings.Add(new Binding("Text", bindingSource1, "Target", true));
- searchTBox.Location = new Point(20, 34);
- searchTBox.Name = "searchTBox";
- searchTBox.Size = new Size(202, 27);
- searchTBox.TabIndex = 1;
- //
- // bindingSource1
- //
- bindingSource1.DataSource = this;
- bindingSource1.Position = 0;
- //
- // replaceTBox
- //
- replaceTBox.DataBindings.Add(new Binding("Text", bindingSource1, "Replaced", true));
- replaceTBox.Location = new Point(20, 75);
- replaceTBox.Name = "replaceTBox";
- replaceTBox.Size = new Size(202, 27);
- replaceTBox.TabIndex = 2;
- //
- // replaceBtn
- //
- replaceBtn.FlatAppearance.BorderSize = 0;
- replaceBtn.FlatStyle = FlatStyle.Flat;
- replaceBtn.Location = new Point(228, 74);
- replaceBtn.Name = "replaceBtn";
- replaceBtn.Size = new Size(69, 29);
- replaceBtn.TabIndex = 3;
- replaceBtn.Text = "替换";
- replaceBtn.UseVisualStyleBackColor = true;
- replaceBtn.Click += replaceBtn_Click;
- //
- // closeBtn
- //
- closeBtn.BackColor = Color.Transparent;
- closeBtn.FlatAppearance.BorderSize = 0;
- closeBtn.FlatStyle = FlatStyle.Flat;
- closeBtn.Location = new Point(278, -1);
- closeBtn.Name = "closeBtn";
- closeBtn.Size = new Size(27, 29);
- closeBtn.TabIndex = 4;
- closeBtn.Text = "X";
- closeBtn.UseVisualStyleBackColor = false;
- closeBtn.Click += closeBtn_Click;
- //
- // FindPanel
- //
- AutoScaleDimensions = new SizeF(9F, 20F);
- AutoScaleMode = AutoScaleMode.Font;
- BackColor = SystemColors.Control;
- Controls.Add(closeBtn);
- Controls.Add(replaceBtn);
- Controls.Add(replaceTBox);
- Controls.Add(searchTBox);
- Controls.Add(searchBtn);
- Name = "FindPanel";
- Size = new Size(305, 107);
- ((System.ComponentModel.ISupportInitialize)bindingSource1).EndInit();
- ResumeLayout(false);
- PerformLayout();
- }
-
- #endregion
-
- private Button searchBtn;
- private TextBox searchTBox;
- private TextBox replaceTBox;
- private Button replaceBtn;
- private Button closeBtn;
- private BindingSource bindingSource1;
- }
-}
diff --git a/src/EasyCon2/Forms/FindPanel.cs b/src/EasyCon2/Forms/FindPanel.cs
deleted file mode 100644
index 1a67478a..00000000
--- a/src/EasyCon2/Forms/FindPanel.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using ICSharpCode.AvalonEdit;
-using System.Diagnostics;
-
-namespace EasyCon2.Forms;
-
-public partial class FindPanel : UserControl
-{
- public FindPanel()
- {
- InitializeComponent();
- }
-
- public void InitEditor(TextEditor _editor)
- {
- editor = _editor;
- Target = editor.SelectedText;
- Replaced = string.Empty;
- }
-
- private TextEditor editor;
-
- public string Target { get; set; }
-
- public string Replaced { get; set; }
-
- private void closeBtn_Click(object sender, EventArgs e)
- {
- Hide();
- }
-
- private void searchBtn_Click(object sender, EventArgs e)
- {
- Debug.WriteLine(Target);
- var index = Find();
- if (index == -1)
- {
- MessageBox.Show("到底了");
- return;
- }
-
- editor.Select(index, Target.Length);
- editor.ScrollToLine(editor.Document.GetLineByOffset(index).LineNumber);
- }
-
- private void replaceBtn_Click(object sender, EventArgs e)
- {
- if (editor.SelectedText == Target)
- {
- editor.SelectedText = Replaced;
- }
- }
-
- public int Find()
- {
- if (Target?.Length == 0) return -1;
- return editor.Text.IndexOf(Target, editor.SelectionStart + 1);
- }
-}
\ No newline at end of file
diff --git a/src/EasyCon2/Helper/CodeCompletionController.cs b/src/EasyCon2/Helper/CodeCompletionController.cs
deleted file mode 100644
index fdd5f734..00000000
--- a/src/EasyCon2/Helper/CodeCompletionController.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-using EasyCon2.Models;
-using ICSharpCode.AvalonEdit;
-using ICSharpCode.AvalonEdit.CodeCompletion;
-using ICSharpCode.AvalonEdit.Document;
-using System.Diagnostics;
-using System.Windows.Input;
-
-namespace EasyCon2.Helper;
-
-public interface ICompletionProvider
-{
- ///
- /// 获取代码提示列表
- ///
- Task> GetCompletions(ITextSource textSource, int offset, string cur);
-
- ///
- /// 处理字符输入,决定是否触发代码提示
- ///
- bool ShouldTriggerCompletion(char triggerChar, string currentLineText, int caretIndex);
-
- ///
- /// 获取当前单词(用于过滤提示列表)
- ///
- string GetCurrentWord(TextDocument document, int offset);
-}
-
-public class CodeCompletionController : IDisposable
-{
- private readonly TextEditor _editor;
- private readonly ICompletionProvider _completionProvider;
- private bool _enableAutoCompletion;
- private CompletionWindow _completionWindow;
- private bool _isDisposed;
-
- ///
- /// 获取或设置是否启用代码自动补全功能
- ///
- public bool EnableAutoCompletion
- {
- get => _enableAutoCompletion;
- set => _enableAutoCompletion = value;
- }
-
- public CodeCompletionController(TextEditor editor, ICompletionProvider completionProvider, bool enableAutoCompletion = true)
- {
- _editor = editor ?? throw new ArgumentNullException(nameof(editor));
- _completionProvider = completionProvider;
- _enableAutoCompletion = enableAutoCompletion;
-
- SetupEventHandlers();
- }
-
- private void SetupEventHandlers()
- {
- // 文本输入事件
- _editor.TextArea.TextEntering += OnTextEntering;
- _editor.TextArea.TextEntered += OnTextEntered;
-
- // 键盘事件
- //_editor.TextArea.KeyDown += OnKeyDown;
-
- // 失去焦点时关闭提示窗口
- _editor.LostFocus += (s, e) => CloseCompletionWindow();
- }
-
- private async void OnTextEntered(object sender, TextCompositionEventArgs e)
- {
- if (!_enableAutoCompletion) return;
-
- var line = _editor.TextArea.Document.GetLineByNumber(_editor.TextArea.Caret.Line);
- if (_completionProvider.ShouldTriggerCompletion(e.Text[0],
- _editor.TextArea.Document.GetText(line.Offset, line.Length),
- _editor.TextArea.Caret.Column))
- {
- await ShowCompletionWindow();
- }
- }
-
- private void OnTextEntering(object sender, TextCompositionEventArgs e)
- {
- if (_completionWindow != null && Keyboard.Modifiers == ModifierKeys.None)
- {
- // 如果用户输入空格、换行等,关闭提示窗口
- if (e.Text == " " || e.Text == "\t" || e.Text == "\n" || e.Text == "\r")
- {
- CloseCompletionWindow();
- }
- }
- }
-
- private void OnKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- if (_completionWindow != null)
- {
- // Ctrl+Space 强制显示提示
- if (e.Key == Key.Space && Keyboard.Modifiers == ModifierKeys.Control)
- {
- e.Handled = true;
- ShowCompletionWindow();
- }
- // ESC 关闭提示窗口
- else if (e.Key == Key.Escape)
- {
- CloseCompletionWindow();
- e.Handled = true;
- }
- }
- else
- {
- // Ctrl+Space 强制显示提示
- if (e.Key == Key.Space && Keyboard.Modifiers == ModifierKeys.Control)
- {
- e.Handled = true;
- ShowCompletionWindow();
- }
- }
- }
-
- private async Task ShowCompletionWindow()
- {
- // 关闭已存在的窗口
- CloseCompletionWindow();
-
- try
- {
- // 获取当前单词用于过滤
- var currentWord = _completionProvider.GetCurrentWord(
- _editor.Document,
- _editor.TextArea.Caret.Offset
- );
-
- // 获取提示列表
- var completions = await _completionProvider.GetCompletions(
- _editor.Document,
- _editor.TextArea.Caret.Offset,
- currentWord);
-
- if (!completions.Any()) return;
-
- // 创建提示窗口
- _completionWindow = new CompletionWindow(_editor.TextArea)
- {
- CloseWhenCaretAtBeginning = true,
- CloseAutomatically = true,
- };
-
- // 设置数据源
- var data = _completionWindow.CompletionList.CompletionData;
- foreach (var c in completions)
- {
- data.Add(new EcpCompletionData(c.Text));
- }
-
- // 如果当前有单词,设置起始偏移量
- if (!string.IsNullOrEmpty(currentWord))
- {
- _completionWindow.StartOffset = _editor.TextArea.Caret.Offset - currentWord.Length;
- }
-
- // 设置事件处理
- _completionWindow.Closed += (s, e) => _completionWindow = null;
-
- // 显示窗口
- _completionWindow.Show();
-
- // 设置初始选择
- if (!string.IsNullOrEmpty(currentWord))
- {
- _completionWindow.CompletionList.SelectItem(currentWord);
- }
- }
- catch (Exception ex)
- {
- Debug.WriteLine($"显示代码提示时出错: {ex.Message}");
- CloseCompletionWindow();
- }
- }
-
- public void CloseCompletionWindow()
- {
- _completionWindow?.Close();
- _completionWindow = null;
- }
-
- public void Dispose()
- {
- if (!_isDisposed)
- {
- CloseCompletionWindow();
- _isDisposed = true;
- }
- }
-}
\ No newline at end of file
diff --git a/src/EasyCon2/Helper/CustomFoldingStrategy.cs b/src/EasyCon2/Helper/CustomFoldingStrategy.cs
index e1ad8f4f..c405c14b 100644
--- a/src/EasyCon2/Helper/CustomFoldingStrategy.cs
+++ b/src/EasyCon2/Helper/CustomFoldingStrategy.cs
@@ -1,22 +1,21 @@
-using ICSharpCode.AvalonEdit.Document;
-using ICSharpCode.AvalonEdit.Folding;
+using AvaloniaEdit.Document;
+using AvaloniaEdit.Folding;
namespace EasyCon2.Helper;
public class CustomFoldingStrategy
{
- // 定义代码块开始和结束标记
private readonly Dictionary blockPairs = new()
{
{ "if", "endif" },
{ "for", "next" },
{ "func", "endfunc" }
};
+
public IEnumerable CreateNewFoldings(TextDocument document, out int firstErrorOffset)
{
- firstErrorOffset = -1; // 没有语法错误,设为-1
+ firstErrorOffset = -1;
var newFoldings = new List();
-
var startLines = new Stack<(int lineNumber, string blockType)>();
for (int i = 0; i < document.LineCount; i++)
@@ -24,7 +23,6 @@ public IEnumerable CreateNewFoldings(TextDocument document, out int
var line = document.GetText(document.Lines[i]);
var trimmedLine = line.Trim().ToLower();
- // 检查是否是块开始
foreach (var pair in blockPairs)
{
if (trimmedLine.StartsWith(pair.Key))
@@ -34,7 +32,6 @@ public IEnumerable CreateNewFoldings(TextDocument document, out int
}
}
- // 检查是否是块结束
if (startLines.Count > 0)
{
var currentBlock = startLines.Peek();
@@ -43,19 +40,9 @@ public IEnumerable CreateNewFoldings(TextDocument document, out int
if (trimmedLine.StartsWith(endMarker) || trimmedLine == endMarker)
{
var startLine = startLines.Pop();
-
- // 创建折叠区域
var startOffset = document.Lines[startLine.lineNumber].Offset;
var endOffset = document.Lines[i].EndOffset;
- // 跳过空行
- //while (i + 1 < document.Lines.Count &&
- // string.IsNullOrWhiteSpace(document.GetText(document.Lines[i + 1])))
- //{
- // i++;
- // endOffset = document.Lines[i].EndOffset;
- //}
-
newFoldings.Add(new NewFolding
{
StartOffset = startOffset,
@@ -67,7 +54,6 @@ public IEnumerable CreateNewFoldings(TextDocument document, out int
}
}
- // 按开始位置排序
newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
return newFoldings;
}
@@ -80,7 +66,6 @@ private string GetFoldingName(TextDocument document, int lineNumber, string bloc
public void UpdateFoldings(FoldingManager manager, TextDocument document)
{
- // 此方法可用于在文档变化后更新折叠区域
int firstErrorOffset;
var newFoldings = CreateNewFoldings(document, out firstErrorOffset);
manager.UpdateFoldings(newFoldings, firstErrorOffset);
diff --git a/src/EasyCon2/Helper/MouseTranslation.cs b/src/EasyCon2/Helper/MouseTranslation.cs
index 7112e7c6..137ea006 100644
--- a/src/EasyCon2/Helper/MouseTranslation.cs
+++ b/src/EasyCon2/Helper/MouseTranslation.cs
@@ -2,53 +2,23 @@ namespace EasyCon2.Helper
{
public class MouseTranslation
{
- //Sensitivity
- public System.Windows.Point mc_sensitivity = new System.Windows.Point(0.5, 0.5);
-
- //Sensitivity with Sigmoid
- public System.Windows.Point mc_sensitivity2 = new System.Windows.Point(0.5, 0.5);
-
- //Mouse Delta Start Threshold
- public System.Windows.Point mc_mouse_delta_start_threshold = new System.Windows.Point(0.0, 0.0);
-
- //Delta Sensitivity Sigmoid Constant, -1.0 < k < 1.0
- public System.Windows.Point mc_delta_sensitivity_sigmoid_constant = new System.Windows.Point(-0.5, -0.5);
-
- //Initial Delta
- public System.Windows.Point mc_delta_initial = new System.Windows.Point(128 * 0.5, 128 * 0.5);
-
- //Delta-Stopping Threshold, 0.0 <= k <= 1.0
- public System.Windows.Point mc_delta_stop_threshold = new System.Windows.Point(128 * 0.49, 128 * 0.49);
-
- //Delta Damping Origin
- public System.Windows.Point mc_delta_damping_origin = new System.Windows.Point(128 * 0.4, 128 * 0.4);
-
- //Delta Damping, 0.0 <= k <= 1.0
- public System.Windows.Point mc_delta_damping = new System.Windows.Point(0.0, 0.0);
-
- //Delta Damping with Sigmoid, 0.0 <= k <= 1.0
- public System.Windows.Point mc_delta_damping2 = new System.Windows.Point(0.1, 0.1);
-
- //Delta Damping with Sigmoid Constant, -1.0 < k < 1.0
- public System.Windows.Point mc_delta_damping_sigmoid_constant = new System.Windows.Point(0.5, 0.5);
-
- //Maximum Delta
- public System.Windows.Point mc_delta_max = new System.Windows.Point(128 * 2.0, 128 * 2.0);
-
- //system.threadExecutionInterval = 1000.0 / mc_framerate
-
- //Mouse Camera Init
- //bool mc_enabled = false; // Toggle
- public System.Windows.Point mc_delta = new System.Windows.Point(); // Mouse Camera Delta
-
- //System.Windows.Point mc_screen_half = new System.Windows.Point(); // Screen Half
- public System.Windows.Point mc_mouse_delta = new System.Windows.Point(); // Mouse Delta
- public System.Windows.Point mc_mouse = new System.Windows.Point(); // Mouse Position
+ public (double X, double Y) mc_sensitivity = (0.5, 0.5);
+ public (double X, double Y) mc_sensitivity2 = (0.5, 0.5);
+ public (double X, double Y) mc_mouse_delta_start_threshold = (0.0, 0.0);
+ public (double X, double Y) mc_delta_sensitivity_sigmoid_constant = (-0.5, -0.5);
+ public (double X, double Y) mc_delta_initial = (128 * 0.5, 128 * 0.5);
+ public (double X, double Y) mc_delta_stop_threshold = (128 * 0.49, 128 * 0.49);
+ public (double X, double Y) mc_delta_damping_origin = (128 * 0.4, 128 * 0.4);
+ public (double X, double Y) mc_delta_damping = (0.0, 0.0);
+ public (double X, double Y) mc_delta_damping2 = (0.1, 0.1);
+ public (double X, double Y) mc_delta_damping_sigmoid_constant = (0.5, 0.5);
+ public (double X, double Y) mc_delta_max = (128 * 2.0, 128 * 2.0);
+ public (double X, double Y) mc_delta = (0, 0);
+ public (double X, double Y) mc_mouse_delta = (0, 0);
+ public (double X, double Y) mc_mouse = (0, 0);
public MouseTranslation()
{
- //Damping Initialization
- mc_delta_damping.X = 0;
mc_delta_damping.X = 1.0 - mc_delta_damping.X;
mc_delta_damping.Y = 1.0 - mc_delta_damping.Y;
mc_delta_damping2.X = 1.0 - mc_delta_damping2.X;
@@ -62,37 +32,26 @@ private double Sigmoid_Tunable(double k, double x)
public System.Drawing.Point Translate(System.Drawing.Point mouse, System.Drawing.Point center)
{
- // Mouse Delta
- // 鼠标偏移
- mc_mouse = new System.Windows.Point(mouse.X, mouse.Y);
- mc_mouse_delta = new System.Windows.Point(mc_mouse.X - center.X, mc_mouse.Y - center.Y);
+ mc_mouse = (mouse.X, mouse.Y);
+ mc_mouse_delta = (mc_mouse.X - center.X, mc_mouse.Y - center.Y);
- // 初始化偏移
- // Initial Delta
if (mc_delta.X == 0.0 && Math.Abs(mc_mouse_delta.X) > mc_mouse_delta_start_threshold.X)
mc_delta.X = mc_delta_initial.X * Math.Abs(mc_mouse_delta.X) / mc_mouse_delta.X;
if (mc_delta.Y == 0.0 && Math.Abs(mc_mouse_delta.Y) > mc_mouse_delta_start_threshold.Y)
mc_delta.Y = mc_delta_initial.Y * Math.Abs(mc_mouse_delta.Y) / mc_mouse_delta.Y;
- // 计算镜头偏移
- // Mouse Camera Delta
if (mc_delta.X != 0.0)
mc_delta.X += mc_mouse_delta.X * (mc_sensitivity.X + mc_sensitivity2.X * Math.Abs(Sigmoid_Tunable(mc_delta_sensitivity_sigmoid_constant.X, mc_delta.X / mc_delta_max.X)));
if (mc_delta.Y != 0.0)
mc_delta.Y += mc_mouse_delta.Y * (mc_sensitivity.Y + mc_sensitivity2.Y * Math.Abs(Sigmoid_Tunable(mc_delta_sensitivity_sigmoid_constant.Y, mc_delta.Y / mc_delta_max.Y)));
- // Delta Limit
- // 偏移限制
if (Math.Abs(mc_delta.X) > mc_delta_max.X)
mc_delta.X = Math.Abs(mc_delta.X) / mc_delta.X * mc_delta_max.X;
if (Math.Abs(mc_delta.Y) > mc_delta_max.Y)
mc_delta.Y = Math.Abs(mc_delta.Y) / mc_delta.Y * mc_delta_max.Y;
- // Mouse Camera Delta to Axis
- //Console.WriteLine((int)(mc_delta.X+128) + ", " + (int)(mc_delta.Y + 128));
- System.Drawing.Point translation = new System.Drawing.Point((int)(mc_delta.X + 128), (int)(mc_delta.Y + 128));
+ var translation = new System.Drawing.Point((int)(mc_delta.X + 128), (int)(mc_delta.Y + 128));
- // Delta Damping
if (mc_delta.X != 0)
{
var dt_x = mc_delta_damping_origin.X * Math.Abs(mc_delta.X) / mc_delta.X;
@@ -103,67 +62,11 @@ public System.Drawing.Point Translate(System.Drawing.Point mouse, System.Drawing
var dt_y = mc_delta_damping_origin.Y * Math.Abs(mc_delta.Y) / mc_delta.Y;
mc_delta.Y = (mc_delta.Y - dt_y) * (mc_delta_damping.Y + mc_delta_damping2.Y * Math.Abs(Sigmoid_Tunable(mc_delta_damping_sigmoid_constant.Y, mc_delta.Y / mc_delta_max.Y))) * 0.5 + dt_y;
}
- // Delta Stopping
if (Math.Abs(mc_delta.X) < mc_delta_stop_threshold.X)
mc_delta.X = 0.0;
if (Math.Abs(mc_delta.Y) < mc_delta_stop_threshold.Y)
mc_delta.Y = 0.0;
return translation;
}
-
- //this will need to be rewritten to use raw input instead of hacky WinForms input...
- //public void MouseUpdate()
- //{
- // //SwitchInputSink sink, Panel panel1
- // if (!firstTime)
- // {
- // Point m_translation = mouseTranslation.Translate(Mouse.Position, center);
- // var x = m_translation.X;
- // var y = m_translation.Y;
-
- // if (x > 255)
- // {
- // Mouse.Position = new Point(Mouse.Position.X - (x - 255), Mouse.Position.Y);
- // x = 255;
- // }
- // else if (x < 0)
- // {
- // Mouse.Position = new Point(Mouse.Position.X - x, Mouse.Position.Y);
- // x = 0;
- // }
- // if (y > 255)
- // {
- // Mouse.Position = new Point(Mouse.Position.X, Mouse.Position.Y - (y - 255));
- // y = 255;
- // }
- // else if (y < 0)
- // {
- // Mouse.Position = new Point(Mouse.Position.X, Mouse.Position.Y - y);
- // y = 0;
- // }
-
- // if (last.X != x)
- // {
- // Console.WriteLine(x + ", " + y);
- // sink.Update(InputFrame.ParseInputString("RX=" + x));
- // }
-
- // if (last.Y != y)
- // {
- // Console.WriteLine(x + ", " + y);
- // sink.Update(InputFrame.ParseInputString("RY=" + y));
- // }
- // Mouse.Position = center;
- // last = new Point(x, y);
-
- // }
- // else
- // {
- // firstTime = false;
- // //Mouse.Position = new Point(panel1.PointToScreen(Point.Empty).X + 128, panel1.PointToScreen(Point.Empty).Y + 128);
- // center = Mouse.Position;
- // }
- //}
-
}
}
\ No newline at end of file
diff --git a/src/EasyCon2/Models/EcpCompletionData.cs b/src/EasyCon2/Models/EcpCompletionData.cs
deleted file mode 100644
index 5708a793..00000000
--- a/src/EasyCon2/Models/EcpCompletionData.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using ICSharpCode.AvalonEdit.CodeCompletion;
-using ICSharpCode.AvalonEdit.Document;
-using ICSharpCode.AvalonEdit.Editing;
-using System.Windows.Media;
-
-namespace EasyCon2.Models;
-
-internal class EcpCompletionData : ICompletionData
-{
- public EcpCompletionData(string text) { Text = text; }
-
- public ImageSource Image => null;
-
- public string Text { get; private set; }
-
- public object Content { get { return this.Text; } }
-
- public object Description { get { return $"描述:{Text}"; } }
-
- public double Priority => 0.9;
-
- public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
- {
- textArea.Document.Replace(completionSegment, this.Text);
- }
-}
\ No newline at end of file
diff --git a/src/EasyCon2/ResourceHelper.cs b/src/EasyCon2/ResourceHelper.cs
index a94c9f69..0afb2320 100644
--- a/src/EasyCon2/ResourceHelper.cs
+++ b/src/EasyCon2/ResourceHelper.cs
@@ -1,5 +1,5 @@
-using System.IO;
using EasyCon2.UI.Common.Properties;
+using System.IO;
namespace EasyCon2;
@@ -7,4 +7,4 @@ internal static class ResourceHelper
{
public static Bitmap Clrlog => new(new MemoryStream(Resources.clrlog));
public static Icon CaptureVideoIcon => new(new MemoryStream(Resources.CaptureVideo));
-}
+}
\ No newline at end of file