diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..620fcb3e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,78 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.json] +indent_size = 2 + +[*.{cs,cpp,h,c,cc,cxx,hpp,hxx}] +indent_size = 4 + +[*.{cpp,h,c,cc,cxx,hpp,hxx}] +cpp_indent_braces = false +cpp_indent_multi_line_relative_to = statement_begin +cpp_new_line_before_open_brace_namespace = new_line +cpp_new_line_before_open_brace_type = new_line +cpp_new_line_before_open_brace_function = new_line +cpp_new_line_before_open_brace_block = new_line +cpp_new_line_before_catch = true +cpp_new_line_before_else = true +cpp_new_line_before_finally = true +cpp_space_before_function_open_parenthesis = false +cpp_space_within_parameter_list_parentheses = true +cpp_space_between_empty_parameter_list_parentheses = true +cpp_space_after_keywords_in_control_flow_statements = true +cpp_space_within_control_flow_statement_parentheses = true +cpp_space_before_semicolon_in_for_statement = false +cpp_space_after_semicolon_in_for_statement = true +cpp_space_around_binary_operator = insert +cpp_space_around_assignment_operator = insert + +[*.xaml] +charset = utf-8-bom + +[*.cs] +charset = utf-8-bom +# New line settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation settings +csharp_indent_case_contents = true +csharp_indent_switch_labels = false +csharp_indent_labels = one_less_than_current + +# Spacing settings +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_between_method_call_parameter_list_parentheses = true +csharp_space_between_method_declaration_parameter_list_parentheses = true +csharp_space_between_method_call_empty_parameter_list_parentheses = true +csharp_space_between_method_declaration_empty_parameter_list_parentheses = true +csharp_space_between_parentheses = control_flow_statements, expressions + +# Style settings +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_prefer_braces = false:silent +csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = outside_namespace:silent diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..0e330d98 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "PilotAIAssistantControl"] + path = PilotAIAssistantControl + url = https://github.com/mitchcapper/PilotAIAssistantControl diff --git a/PilotAIAssistantControl b/PilotAIAssistantControl new file mode 160000 index 00000000..1daf03c3 --- /dev/null +++ b/PilotAIAssistantControl @@ -0,0 +1 @@ +Subproject commit 1daf03c3c068af9486c5880813ce53f16ab4041a diff --git a/RegExpressWPFNET/Directory.Build.props b/RegExpressWPFNET/Directory.Build.props new file mode 100644 index 00000000..fb3bb5b6 --- /dev/null +++ b/RegExpressWPFNET/Directory.Build.props @@ -0,0 +1,5 @@ + + + net9.0-windows7.0 + + diff --git a/RegExpressWPFNET/RegExpressLibrary/IRegexEngine.cs b/RegExpressWPFNET/RegExpressLibrary/IRegexEngine.cs index 78cd4595..36f11dfe 100644 --- a/RegExpressWPFNET/RegExpressLibrary/IRegexEngine.cs +++ b/RegExpressWPFNET/RegExpressLibrary/IRegexEngine.cs @@ -14,8 +14,27 @@ namespace RegExpressLibrary public delegate void RegexEngineOptionsChanged( IRegexEngine sender, RegexEngineOptionsChangedArgs args ); + public interface IBaseEngine + { + string Name { get; } + string? ExportOptions( ); // (JSON) + } + public interface IAIBase : IBaseEngine + { + string AIPatternType => "Regex"; + string AIPatternCodeblockType => "regex"; + string AIAdditionalSystemPrompt => "If the language supports named capture groups, use these by default. " + + "If the user has ignoring patterned whitespace enabled in the options, use multi-lines and minimal in-regex comments for complex regexes with nice whitespace formatting to make it more readable. "; + + string ReferenceTextHeader => "Users current target text"; + string GetSystemPrompt( ) => $"You are a {Name} {AIPatternType} expert assistant. The user has questions about their {AIPatternType} patterns and target text. " + + $"Provide {AIPatternType} patterns inside Markdown code blocks (```{AIPatternCodeblockType} ... ```). " + + "Explain how the pattern works briefly. " + + AIAdditionalSystemPrompt + + $"They currently have these engine options enabled:\n```json\n{ExportOptions( )}\n```"; + } - public interface IRegexEngine + public interface IRegexEngine : IAIBase { event RegexEngineOptionsChanged? OptionsChanged; event EventHandler? FeatureMatrixReady; @@ -26,8 +45,6 @@ public interface IRegexEngine (string Kind, string? Version) CombinedId => (Kind, Version); - string Name { get; } - string Subtitle { get; } RegexEngineCapabilityEnum Capabilities { get; } @@ -36,8 +53,6 @@ public interface IRegexEngine Control GetOptionsControl( ); - string? ExportOptions( ); // (JSON) - void ImportOptions( string? json ); RegexMatches GetMatches( ICancellable cnc, [StringSyntax( StringSyntaxAttribute.Regex )] string pattern, string text ); diff --git a/RegExpressWPFNET/RegExpressLibrary/InternalConfig.cs b/RegExpressWPFNET/RegExpressLibrary/InternalConfig.cs new file mode 100644 index 00000000..a3907c10 --- /dev/null +++ b/RegExpressWPFNET/RegExpressLibrary/InternalConfig.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics; +using System.Windows; + +namespace RegExpressLibrary +{ + public static class InternalConfig + { + public static bool SHOW_DEBUG_BUTTONS = false; + public static bool DEBUG_LOG_AI_MESSAGES = true; + + /// + /// as the target text can be long lets not keep the old versions around two options are update the target text with the new text, or remove the old one add a note and add the new one + /// + public static bool DONT_UPDATE_OLD_AI_TEXT_MARK_REMOVED = true; + public static string[]? limited_engine_dlls; + [Flags] + public enum ON_EXCEPTION_ACTION + { + None, + MessageBox = 1 << 0, + DebuggerBreak = 1 << 1, + Rethrow = 1 << 2, + IncludeStackTrace = 1 << 3, + IncludeTraceDetails = 1 << 4, + + } + public static ON_EXCEPTION_ACTION ON_EXCEPTION_DEBUGGER_ATTACHED = ON_EXCEPTION_ACTION.DebuggerBreak | ON_EXCEPTION_ACTION.IncludeTraceDetails | ON_EXCEPTION_ACTION.IncludeStackTrace; + public static ON_EXCEPTION_ACTION ON_EXCEPTION_STANDARD = ON_EXCEPTION_ACTION.MessageBox; + public static void HandleOtherCriticalError( String error, [System.Runtime.CompilerServices.CallerLineNumber] int source_line_number = 0, [System.Runtime.CompilerServices.CallerMemberName] string member_name = "", [System.Runtime.CompilerServices.CallerFilePath] string source_file_path = "" ) => + _CriticalError( new StringBuilder( error ), source_line_number, member_name, source_file_path ); + private static void _CriticalError( StringBuilder sb, [System.Runtime.CompilerServices.CallerLineNumber] int source_line_number = 0, [System.Runtime.CompilerServices.CallerMemberName] string member_name = "", [System.Runtime.CompilerServices.CallerFilePath] string source_file_path = "" ) + { + var ON_EXCEPTION = Debugger.IsAttached ? ON_EXCEPTION_DEBUGGER_ATTACHED : ON_EXCEPTION_STANDARD; + if( ON_EXCEPTION.HasFlag( ON_EXCEPTION_ACTION.IncludeTraceDetails ) ) + { + sb.Append( "Member: " ).AppendLine( member_name ); + sb.Append( "File: " ).AppendLine( source_file_path ); + sb.Append( "Line: " ).AppendLine( source_line_number.ToString( ) ); + } + + string message = sb.ToString( ); + + // MessageBox (on UI thread if possible) + if( ON_EXCEPTION.HasFlag( ON_EXCEPTION_ACTION.MessageBox ) ) + { + try + { + if( Application.Current?.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess( ) ) + Application.Current.Dispatcher.Invoke( ( ) => MessageBox.Show( message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error ) ); + else + MessageBox.Show( message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error ); + + } + catch + { + // Fallback to Debug output if UI unavailable + Debug.WriteLine( message ); + } + } + + // Break into debugger if requested and a debugger is attached + if( ON_EXCEPTION.HasFlag( ON_EXCEPTION_ACTION.DebuggerBreak ) && Debugger.IsAttached ) + Debugger.Break( ); + + + // Always log to debug output for visibility + Debug.WriteLine( message ); + + } + + public static bool HandleException( String msg, Exception exception, [System.Runtime.CompilerServices.CallerLineNumber] int source_line_number = 0, [System.Runtime.CompilerServices.CallerMemberName] string member_name = "", [System.Runtime.CompilerServices.CallerFilePath] string source_file_path = "", [CallerArgumentExpression( "exception" )] string exceptionName = "" ) + { + if( exception == null ) return false; + var ON_EXCEPTION = Debugger.IsAttached ? ON_EXCEPTION_DEBUGGER_ATTACHED : ON_EXCEPTION_STANDARD; + + // Build a diagnostic message + StringBuilder sb = new( ); + + sb.AppendLine( "Exception! " ); + if( !String.IsNullOrWhiteSpace( msg ) ) + sb.AppendLine( msg + " " ); + if( ON_EXCEPTION.HasFlag( ON_EXCEPTION_ACTION.IncludeTraceDetails ) ) + { + sb.Append( "Member: " ).AppendLine( member_name ); + sb.Append( "File: " ).AppendLine( source_file_path ); + sb.Append( "Line: " ).AppendLine( source_line_number.ToString( ) ); + } + sb.Append( "Exception Var: " ).AppendLine( exceptionName ); + sb.Append( "Type: " ).AppendLine( exception.GetType( ).FullName ); + sb.Append( "Message: " ).AppendLine( exception.Message ); + + if( ON_EXCEPTION.HasFlag( ON_EXCEPTION_ACTION.IncludeStackTrace ) ) + { + var st = exception.StackTrace; + if( !string.IsNullOrWhiteSpace( st ) ) + { + sb.AppendLine( "StackTrace:" ); + sb.AppendLine( st ); + } + } + _CriticalError( sb, source_line_number, member_name, source_file_path ); + + return ( ON_EXCEPTION.HasFlag( ON_EXCEPTION_ACTION.Rethrow ) ); + } + /// + /// Returns true if caller should rethrow the exception + /// + /// + /// + /// + /// + /// + /// + public static bool HandleException( Exception exception, [System.Runtime.CompilerServices.CallerLineNumber] int source_line_number = 0, [System.Runtime.CompilerServices.CallerMemberName] string member_name = "", [System.Runtime.CompilerServices.CallerFilePath] string source_file_path = "", [CallerArgumentExpression( "exception" )] string exceptionName = "" ) => + HandleException( string.Empty, exception, source_line_number, member_name, source_file_path, exceptionName ); + + } +} diff --git a/RegExpressWPFNET/RegExpressLibrary/PluginUtilities.cs b/RegExpressWPFNET/RegExpressLibrary/PluginUtilities.cs index 73cdd9e9..4b978bc1 100644 --- a/RegExpressWPFNET/RegExpressLibrary/PluginUtilities.cs +++ b/RegExpressWPFNET/RegExpressLibrary/PluginUtilities.cs @@ -64,9 +64,8 @@ public static class PluginLoader } catch( Exception exc ) { - if( Debugger.IsAttached ) Debugger.Break( ); - - MessageBox.Show( ownerWindow, $"Failed to load plugins using '{enginesJsonPath}'.\r\n\r\n{exc.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation ); + if (InternalConfig.HandleException($"Failed to load plugins using '{enginesJsonPath}'", exc )) + throw; return (null, null); } @@ -81,6 +80,11 @@ public static class PluginLoader { foreach( EngineData engine_data in engines_data.engines ) { + if ( InternalConfig.limited_engine_dlls?.Length > 0 && !InternalConfig.limited_engine_dlls.Any( dll => engine_data.path.Contains(dll, StringComparison.CurrentCultureIgnoreCase) ) ) + { + Debug.WriteLine( $"Skipping plugin due to limited_engine_dlls \"{engine_data.path}\"..." ); + continue; + } string plugin_absolute_path = Path.Combine( plugin_root_folder, engine_data.path! ); try @@ -106,18 +110,17 @@ public static class PluginLoader } catch( Exception exc ) { - if( Debugger.IsAttached ) Debugger.Break( ); + if (InternalConfig.HandleException( $"Failed to create plugin \"{engine_data.path}\"", exc )) + throw; - MessageBox.Show( ownerWindow, $"Failed to create plugin \"{engine_data.path}\".\r\n\r\n{exc.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation ); } } } } catch( Exception exc ) { - if( Debugger.IsAttached ) Debugger.Break( ); - - MessageBox.Show( $"Failed to load plugin \"{engine_data.path}\".\r\n\r\n{exc.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation ); + if (InternalConfig.HandleException( $"Failed to create plugin \"{engine_data.path}\"", exc )) + throw; } } } @@ -142,4 +145,4 @@ public static class PluginLoader return (plugins, no_fm_plugins); } -} \ No newline at end of file +} diff --git a/RegExpressWPFNET/RegExpressLibrary/ProcessHelper.cs b/RegExpressWPFNET/RegExpressLibrary/ProcessHelper.cs index 68a9aae8..77ed1a3c 100644 --- a/RegExpressWPFNET/RegExpressLibrary/ProcessHelper.cs +++ b/RegExpressWPFNET/RegExpressLibrary/ProcessHelper.cs @@ -129,7 +129,8 @@ public bool Start( ICancellable cnc ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if (InternalConfig.HandleException( exc )) + throw; } } ) { @@ -155,7 +156,8 @@ public bool Start( ICancellable cnc ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if (InternalConfig.HandleException( exc )) + throw; } } ) { @@ -174,7 +176,8 @@ public bool Start( ICancellable cnc ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if (InternalConfig.HandleException( exc )) + throw; } } ) { @@ -222,7 +225,8 @@ public bool Start( ICancellable cnc ) if( unchecked((uint)exc.HResult) != 0x80004005 && // 'E_ACCESSDENIED' unchecked((uint)exc.HResult) != 0x80131509 ) // -2146233079, "Cannot process request because the process () has exited." { - if( Debugger.IsAttached ) Debugger.Break( ); + if (InternalConfig.HandleException( exc )) + throw; } // ignore diff --git a/RegExpressWPFNET/RegExpressLibrary/RegExpressLibrary.csproj b/RegExpressWPFNET/RegExpressLibrary/RegExpressLibrary.csproj index cbaade07..df6d8bb0 100644 --- a/RegExpressWPFNET/RegExpressLibrary/RegExpressLibrary.csproj +++ b/RegExpressWPFNET/RegExpressLibrary/RegExpressLibrary.csproj @@ -1,7 +1,6 @@ - net9.0-windows7.0 enable true diff --git a/RegExpressWPFNET/RegExpressLibrary/UI/CheckboxWithNote.xaml b/RegExpressWPFNET/RegExpressLibrary/UI/CheckboxWithNote.xaml index 8af92aba..2181e3ae 100644 --- a/RegExpressWPFNET/RegExpressLibrary/UI/CheckboxWithNote.xaml +++ b/RegExpressWPFNET/RegExpressLibrary/UI/CheckboxWithNote.xaml @@ -9,7 +9,7 @@ x:Name="userControl" DataContextChanged="userControl_DataContextChanged" > - + diff --git a/RegExpressWPFNET/RegExpressWPFNET.slnx b/RegExpressWPFNET/RegExpressWPFNET.slnx index bfd60797..3a2f8093 100644 --- a/RegExpressWPFNET/RegExpressWPFNET.slnx +++ b/RegExpressWPFNET/RegExpressWPFNET.slnx @@ -3,6 +3,10 @@ + + + + @@ -37,6 +41,9 @@ + + + @@ -142,6 +149,7 @@ + diff --git a/RegExpressWPFNET/RegExpressWPFNET/Adorners/UnderliningAdorner.cs b/RegExpressWPFNET/RegExpressWPFNET/Adorners/UnderliningAdorner.cs index 323bc3d5..e2432394 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Adorners/UnderliningAdorner.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Adorners/UnderliningAdorner.cs @@ -98,7 +98,8 @@ public void SetRangesToUnderline( IReadOnlyList<(TextPointer start, TextPointer // TODO: 'ExecutionEngineException' is now obsolete. _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if (RegExpressLibrary.InternalConfig.HandleException( exc )) + throw; // ignore and accept the ranges } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/App.xaml b/RegExpressWPFNET/RegExpressWPFNET/App.xaml index 2fc9d44e..aed9901a 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/App.xaml +++ b/RegExpressWPFNET/RegExpressWPFNET/App.xaml @@ -8,11 +8,16 @@ xmlns:sys="clr-namespace:System;assembly=mscorlib" StartupUri="MainWindow.xaml" Startup="App_Startup" + ThemeMode="System" > - - + + + + + + + + - - + - @@ -613,7 +625,7 @@ --> - + diff --git a/RegExpressWPFNET/RegExpressWPFNET/App.xaml.cs b/RegExpressWPFNET/RegExpressWPFNET/App.xaml.cs index bdc3b420..168ffcc8 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/App.xaml.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/App.xaml.cs @@ -1,4 +1,5 @@ -using System; +using RegExpressWPFNET.Code; +using System; using System.Collections.Generic; using System.Configuration; using System.Data; @@ -48,7 +49,7 @@ private void App_Startup( object sender, StartupEventArgs e ) SetForegroundWindow( other_process.MainWindowHandle ); - Shutdown( ); + Environment.Exit(0); // Calling shutdown still constructs MainWindow.xaml which in turn attempts to save the session before it loads erasing the session data return; } @@ -57,10 +58,15 @@ private void App_Startup( object sender, StartupEventArgs e ) if( other_process != null ) { - Shutdown( ); + Environment.Exit(0); // Calling shutdown still constructs MainWindow.xaml which in turn attempts to save the session before it loads erasing the session data return; } + var onlyEngine = Utilities.GetCommandLineArgArr( "only-engine-dll" ); + if( onlyEngine.Length > 0 ) + { + RegExpressLibrary.InternalConfig.limited_engine_dlls = onlyEngine; + } } @@ -93,17 +99,20 @@ private void CurrentDomain_UnhandledException( object sender, UnhandledException private void TaskScheduler_UnobservedTaskException( object? sender, UnobservedTaskExceptionEventArgs e ) { - if( Debugger.IsAttached ) Debugger.Break( ); + if (RegExpressLibrary.InternalConfig.HandleException( e.Exception )) + throw e.Exception; } private void App_DispatcherUnhandledException( object sender, DispatcherUnhandledExceptionEventArgs e ) { - if( Debugger.IsAttached ) Debugger.Break( ); + if (RegExpressLibrary.InternalConfig.HandleException( e.Exception )) + throw e.Exception; } private void Dispatcher_UnhandledException( object sender, DispatcherUnhandledExceptionEventArgs e ) { - if( Debugger.IsAttached ) Debugger.Break( ); + if (RegExpressLibrary.InternalConfig.HandleException( e.Exception )) + throw e.Exception; } } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/ChangeEventHelper.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/ChangeEventHelper.cs index 5964f2bf..a046f89a 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/ChangeEventHelper.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/ChangeEventHelper.cs @@ -82,7 +82,7 @@ public void Invoke( CancellationToken ct, Action action ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + RegExpressLibrary.InternalConfig.HandleException( exc ); throw; } } @@ -106,7 +106,7 @@ public void Do( Action action ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + RegExpressLibrary.InternalConfig.HandleException( exc ); throw; } finally diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/OutputBuilder.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/OutputBuilder.cs index 229dbbc3..0c124666 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/OutputBuilder.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/OutputBuilder.cs @@ -63,12 +63,13 @@ public OutputBuilder( StyleInfo? specialStyleInfo ) sb.Clear( ); runs.Clear( ); isPreviousSpecial = false; + int realLength = 0; for( int i = 0; i < text.Length; i++ ) { - if( i >= maxLength ) + if( realLength >= maxLength ) { - AppendSpecial( @"…" ); + realLength += AppendSpecial( @"…" ); break; } @@ -78,13 +79,13 @@ public OutputBuilder( StyleInfo? specialStyleInfo ) switch( c ) { case '\r': - AppendSpecial( @"\r" ); + realLength += AppendSpecial( @"\r" ); continue; case '\n': - AppendSpecial( @"\n" ); + realLength += AppendSpecial( @"\n" ); continue; case '\t': - AppendSpecial( @"\t" ); + realLength += AppendSpecial( @"\t" ); continue; } @@ -100,7 +101,7 @@ public OutputBuilder( StyleInfo? specialStyleInfo ) ( c >= UnicodeRanges.Hebrew.FirstCodePoint && c < UnicodeRanges.Hebrew.FirstCodePoint + UnicodeRanges.Hebrew.Length && char.GetUnicodeCategory( c ) == UnicodeCategory.OtherLetter ) ) { - AppendNormal( c ); + realLength += AppendNormal( c ); continue; } @@ -138,13 +139,13 @@ public OutputBuilder( StyleInfo? specialStyleInfo ) case UnicodeCategory.OtherSymbol: //case UnicodeCategory.OtherNotAssigned: */ - AppendNormal( c ); + realLength += AppendNormal( c ); break; default: - AppendCode( c ); + realLength += AppendCode( c ); break; } @@ -209,7 +210,7 @@ public OutputBuilder( StyleInfo? specialStyleInfo ) } - private void AppendSpecial( string s ) + private int AppendSpecial( string s ) { if( isPreviousSpecial || specialStyleInfo == null ) { @@ -226,16 +227,17 @@ private void AppendSpecial( string s ) sb.Clear( ).Append( s ); isPreviousSpecial = true; } + return s.Length; } - private void AppendCode( char c ) + private int AppendCode( char c ) { - AppendSpecial( $@"\u{(int)c:X4}" ); + return AppendSpecial( $@"\u{(int)c:X4}" ); } - private void AppendNormal( char c ) + private int AppendNormal( char c ) { if( !isPreviousSpecial ) { @@ -252,6 +254,7 @@ private void AppendNormal( char c ) sb.Clear( ).Append( c ); isPreviousSpecial = false; } + return 1; } } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/ResumableLoop.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/ResumableLoop.cs index f54e774d..d3a00d27 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/ResumableLoop.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/ResumableLoop.cs @@ -220,7 +220,7 @@ void ThreadProc( ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + InternalConfig.HandleException( exc ); throw; // TODO: maybe restart the loop? } @@ -237,7 +237,7 @@ void ThreadProc( ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + InternalConfig.HandleException(exc); throw; } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/RtbUtilities.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/RtbUtilities.cs index 134b0f7f..2d0b0e3e 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/RtbUtilities.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/RtbUtilities.cs @@ -477,7 +477,7 @@ public static void BringIntoViewInvoked( ICancellable cnc, RichTextBox rtb, Text if( rect_to_bring.IsEmpty ) { - if( Debugger.IsAttached ) Debugger.Break( ); + InternalConfig.HandleOtherCriticalError("Rect is empty"); return; } @@ -490,7 +490,7 @@ public static void BringIntoViewInvoked( RichTextBox rtb, Rect rect, bool isRect { if( rect.IsEmpty ) { - if( Debugger.IsAttached ) Debugger.Break( ); + InternalConfig.HandleOtherCriticalError("Rect is empty"); return; } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/TabData.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/TabData.cs index dab38671..35419af3 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/TabData.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/TabData.cs @@ -38,6 +38,8 @@ public sealed class TabData class AllTabData { public List Tabs { get; set; } = new( ); + public PilotAIAssistantControl.AIUserConfig AIConfig { get; set; } = new( ); + public bool AITabOpen { get; set; } = false; } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/UITaskHelper.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/UITaskHelper.cs index 2ecba6fd..e24eb384 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/UITaskHelper.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/UITaskHelper.cs @@ -47,7 +47,7 @@ public static void Invoke( DispatcherObject obj, CancellationToken ct, Action ac catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + RegExpressLibrary.InternalConfig.HandleException( exc ); throw; } } @@ -66,7 +66,8 @@ public static void Invoke( DispatcherObject obj, Action action ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached && !( exc is TaskCanceledException ) ) Debugger.Break( ); + if(!( exc is TaskCanceledException ) ) + RegExpressLibrary.InternalConfig.HandleException( exc ); throw; } } @@ -118,7 +119,7 @@ static void Execute( Action action ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + RegExpressLibrary.InternalConfig.HandleException( exc ); throw; } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Code/Utilities.cs b/RegExpressWPFNET/RegExpressWPFNET/Code/Utilities.cs index 63ccdfeb..03b4b442 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Code/Utilities.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/Code/Utilities.cs @@ -47,6 +47,52 @@ public static double PointsFromInvariantString( string value ) return PixelsFromInvariantString( value ) * r; } + private static string[]? _commandLineArgs; + public static bool GetCommandLineArg( String arg, out String? NextVal ) { + NextVal=null; + var arr = GetCommandLineArgArr( arg, 1 ); + if (arr.Length == 0) + return false; + NextVal = arr[0]; + return true; + } + /// + /// return an array with each occurrence of the arg, allows --ignore a --ignore b + /// + /// + /// + public static string[] GetCommandLineArgArr( string arg, int max = 0 ) + { + _commandLineArgs ??= Environment.GetCommandLineArgs( ); + List ret = new(); + bool addNext = false; + string targetArg = "--" + arg; + + foreach( var cmdArg in _commandLineArgs ) + { + if( cmdArg.Equals( targetArg, StringComparison.CurrentCultureIgnoreCase) ) + { + addNext = true; + } + else if( addNext ) + { + ret.Add( cmdArg ); + if (ret.Count == max) + break; + addNext = false; + } + } + + return ret.ToArray( ); + } + public static string? GetCommandLineArgStr( String arg ) + { + if (! GetCommandLineArg( arg, out String? NextVal )) + return null; + return NextVal; + } + public static bool GetCommandLineExists(String arg) => GetCommandLineArg( arg, out _ ); + [Conditional( "DEBUG" )] public static void DbgSimpleLog( Exception exc, [CallerFilePath] string? filePath = null, [CallerMemberName] string? memberName = null, [CallerLineNumber] int lineNumber = 0 ) @@ -69,7 +115,8 @@ public static void DbgSaveXAML( string filename, FlowDocument doc ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if (RegExpressLibrary.InternalConfig.HandleException( exc )) + throw; } } @@ -88,7 +135,8 @@ public static void DbgLoadXAML( FlowDocument doc, string filename ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if (RegExpressLibrary.InternalConfig.HandleException( exc )) + throw; } } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/Controls/BaseBoolNullConverter.cs b/RegExpressWPFNET/RegExpressWPFNET/Controls/BaseBoolNullConverter.cs new file mode 100644 index 00000000..fc68d8fd --- /dev/null +++ b/RegExpressWPFNET/RegExpressWPFNET/Controls/BaseBoolNullConverter.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; + +namespace RegExpressWPFNET.Controls +{ + + public class BoolNullVisibilityCollapsedConverter( ) : BaseBoolNullConverter( Visibility.Visible, Visibility.Collapsed ), IValueConverter + { + } + public abstract class BaseBoolNullConverter( object true_val, object false_val ) + { + + public object Convert( object value, Type targetType, object parameter, CultureInfo? culture ) + { + var res = GetConvertResult( value, parameter ); + if( targetType == typeof( Boolean ) ) + return res; + return res ? true_val : false_val; + } + //uwp + public object Convert( object value, Type targetType, object parameter, string language ) => Convert( value, targetType, parameter, default( CultureInfo ) ); + public static bool GetConvertResult( object value, object parameter ) + { + bool res = true; + if( value == null ) + res = false; + else if( value is string sv ) + res = !String.IsNullOrWhiteSpace( sv ); + else if( value is bool bv ) + res = bv; + if( parameter != null && ( ( parameter.GetType( ) == typeof( bool ) && ( (bool)parameter ) ) || ( parameter.GetType( ) == typeof( string ) && new string[] { "true", "1" }.Contains( parameter as string ) ) ) ) + res = !res; + return res; + } + public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) => throw new NotImplementedException( ); + public object ConvertBack( object value, Type targetType, object parameter, string language ) => throw new NotImplementedException( ); + } +} diff --git a/RegExpressWPFNET/RegExpressWPFNET/Engines.json b/RegExpressWPFNET/RegExpressWPFNET/Engines.json index ec34da9f..11fa10d2 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/Engines.json +++ b/RegExpressWPFNET/RegExpressWPFNET/Engines.json @@ -3,6 +3,10 @@ { "path": "Engines\\DotNET9\\DotNET9Plugin.dll" }, + { + "path": "Engines\\HtmlAgilityPack\\HtmlAgilityPackPlugin.dll", + "no_fm": true + }, { "path": "Engines\\DotNETFramework4_8\\DotNETFrameworkPlugin.dll", "no_fm": true diff --git a/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml b/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml index 397814f2..450c2ef0 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml +++ b/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml @@ -3,6 +3,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:pia="clr-namespace:PilotAIAssistantControl;assembly=PilotAIAssistantControlWPF" + xmlns:local="clr-namespace:RegExpressWPFNET" xmlns:sys="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" @@ -33,8 +35,27 @@ + + + + + + + + + + + - + + + + + @@ -187,5 +208,5 @@ - + diff --git a/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml.cs b/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml.cs index f4876b14..eac24a14 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml.cs +++ b/RegExpressWPFNET/RegExpressWPFNET/MainWindow.xaml.cs @@ -27,6 +27,8 @@ using System.Windows.Shapes; using System.Windows.Threading; using System.Xml; + +using PilotAIAssistantControl; using RegExpressLibrary; using RegExpressWPFNET.Code; using Path = System.IO.Path; @@ -130,7 +132,8 @@ private async void Window_Loaded( object sender, RoutedEventArgs e ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if( RegExpressLibrary.InternalConfig.HandleException( exc ) ) + throw; } #if DEBUG @@ -176,6 +179,13 @@ private async void Window_Loaded( object sender, RoutedEventArgs e ) { taskBarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None; } + + // Import AI settings + //all_tab_data.AIConfig = new(); + ucAi.ImportData( all_tab_data.AIConfig ); + if( all_tab_data.AITabOpen ) + ucAi.IsExpanded = true; + } // --- Delay effect @@ -209,7 +219,14 @@ private void tabControlMain_SelectionChanged( object sender, SelectionChangedEve var old_metrics = old_uc_main.GetMetrics( ); new_uc_main.ApplyMetrics( old_metrics, full: false ); } + if( new_uc_main != null ) + { + AiOptions.CurrentTab = new_uc_main; + ucAi.Configure( AiOptions ); + } + } + OurIAIUIOptions AiOptions = new( ); private void Window_Closing( object sender, System.ComponentModel.CancelEventArgs e ) @@ -224,7 +241,7 @@ private void Window_Closing( object sender, System.ComponentModel.CancelEventArg } catch( Exception exc ) { - if( Debugger.IsAttached ) Debugger.Break( ); + if( Debugger.IsAttached ) InternalConfig.HandleException( exc ); else Debug.Fail( exc.Message, exc.ToString( ) ); // ignore @@ -399,6 +416,30 @@ void UCMain_Changed( object? sender, EventArgs e ) AutoSaveLoop.SignalWaitAndExecute( ); } + void UCMain_ScopeToNewTabRequested( object? sender, ScopeToMatchEventArgs e ) + { + if( !IsFullyLoaded ) return; + if( sender is not UCMain sourceUcMain ) return; + + var tab_data = new TabData( ); + sourceUcMain.ExportTabData( tab_data ); + + var fullText = sourceUcMain.ucText.GetTextData(tab_data.Eol).Text ?? ""; //we need to manually get the text data with the proper EOL as the offsets are based on the EOL the user has selected. + + int start = Math.Max( 0, e.Segment.Index ); + int length = Math.Min( e.Segment.Length, fullText.Length - start ); + tab_data.Text = fullText.Substring( start, length ); + tab_data.Pattern=""; + var title = "Scoped Tab"; + if (tabControl.SelectedItem is TabItem ti && ti.Header != null){ + var curTitle = ti.Header.ToString(); + var lastSpace = curTitle.LastIndexOf(' '); + if (lastSpace != -1) + title += " of" + curTitle.Substring(lastSpace); + } + AddNewTab( tab_data,tabControl.SelectedIndex+1, title ); + } + void AutoSaveThreadProc( ICancellable cnc ) { Dispatcher.InvokeAsync( SaveAllTabData, DispatcherPriority.ApplicationIdle ); @@ -428,6 +469,10 @@ void SaveAllTabData( ) all_data.Tabs.Add( tab_data ); } + + all_data.AIConfig = ucAi.ExportData( ); + all_data.AITabOpen = ucAi.IsExpanded; + string json = JsonSerializer.Serialize( all_data, PluginLoader.JsonOptions ); string my_file = GetMyDataFile( ); @@ -465,7 +510,8 @@ void SaveAllTabData( ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if( InternalConfig.HandleException( exc ) ) + throw; // ignore } @@ -518,7 +564,7 @@ void SaveWindowPlacement( ) } catch( Exception exc ) { - if( Debugger.IsAttached ) Debugger.Break( ); + if( Debugger.IsAttached ) InternalConfig.HandleException( exc ); else Debug.Fail( exc.Message, exc.ToString( ) ); // ignore @@ -588,7 +634,7 @@ void TryRestoreWindowPlacement( ) catch( Exception exc ) { _ = exc; - if( Debugger.IsAttached ) Debugger.Break( ); + if( Debugger.IsAttached ) InternalConfig.HandleException( exc ); // ignore } @@ -609,7 +655,7 @@ void RestoreMaximisedState( ) #region Tabs - TabItem AddNewTab( TabData? tabData ) + TabItem AddNewTab( TabData? tabData, int insertAt=-1, String? forceTitle=null ) { int max = GetMainTabs( ) @@ -632,7 +678,7 @@ TabItem AddNewTab( TabData? tabData ) var new_tab_item = new TabItem { //Header = string.IsNullOrWhiteSpace( tab_data?.Name ) ? $"Tab {max + 1}" : tab_data.Name; - Header = $"Regex {max + 1}", + Header = forceTitle ?? $"Regex {max + 1}", HeaderTemplate = (DataTemplate)tabControl.Resources["TabTemplate"] }; @@ -643,12 +689,14 @@ TabItem AddNewTab( TabData? tabData ) }; new_tab_item.Content = uc_main; - - tabControl.Items.Insert( tabControl.Items.IndexOf( tabItemNew ), new_tab_item ); + if (insertAt == -1) + insertAt = tabControl.Items.IndexOf( tabItemNew ); + tabControl.Items.Insert( insertAt, new_tab_item ); if( tabData != null ) uc_main.ApplyTabData( tabData ); uc_main.Changed += UCMain_Changed; + uc_main.ScopeToNewTabRequested += UCMain_ScopeToNewTabRequested; tabControl.SelectedItem = new_tab_item; //? @@ -774,10 +822,54 @@ void GoToOptions( ) } + private void AiExpander_Expanded( object sender, RoutedEventArgs e ) + { + // Connect the AI panel to the current tab when expanded + var uc_main = GetActiveUCMain( ); + if( uc_main != null ) + { + AiOptions.CurrentTab = uc_main; + ucAi.Configure( AiOptions ); + } + } + + [GeneratedRegex( @"^Regex\s*(\d+)$" )] private static partial Regex HeaderParserRegex( ); #endregion + + + public class OurIAIUIOptions : AIOptions + { + public override string HintForUserInput => "Ask about a regex or matching..."; + public override string ReferenceTextDisplayName => "Target Text"; + public override string FormatUserQuestion( string userQuestion ) => $"Current pattern:\n```{CurrentTab.CurrentRegexEngine?.AIPatternCodeblockType}\n{CurrentTab.ucPattern.GetTextData( "\n" ).Text}\n```\n\nMy question: {userQuestion}"; + public UCMain? CurrentTab; + + public override string GetCurrentReferenceText( ) => CurrentTab?.ucText.GetTextData( "\n" ).Text; + public override IEnumerable CodeblockActions => [ GenericCodeblockAction.ClipboardAction, + new GenericCodeblockAction("📝 Use as Pattern", async ( block ) => + { + + if( CurrentTab == null ) return false; + CurrentTab.ucPattern.SetText( block.Code ); + return true; + } ) + { + Tooltip="Use this code block as the regex pattern", + FeedbackOnAction="✓ Applied!" + } + ]; + + public override void HandleDebugMessage( string msg ) + { + if( InternalConfig.DEBUG_LOG_AI_MESSAGES ) + System.Diagnostics.Debug.WriteLine( msg ); + } + + public override string GetSystemPrompt( ) => CurrentTab?.CurrentRegexEngine?.GetSystemPrompt( ) ?? null; + } } } diff --git a/RegExpressWPFNET/RegExpressWPFNET/RegExpressWPFNET.csproj b/RegExpressWPFNET/RegExpressWPFNET/RegExpressWPFNET.csproj index 63997f06..fe2bd14b 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/RegExpressWPFNET.csproj +++ b/RegExpressWPFNET/RegExpressWPFNET/RegExpressWPFNET.csproj @@ -2,10 +2,11 @@ WinExe - net9.0-windows7.0 enable true + preview RegExpress.ico + app.manifest @@ -19,6 +20,12 @@ + + + + + + diff --git a/RegExpressWPFNET/RegExpressWPFNET/UCMain.xaml b/RegExpressWPFNET/RegExpressWPFNET/UCMain.xaml index b5097ad8..924f418d 100644 --- a/RegExpressWPFNET/RegExpressWPFNET/UCMain.xaml +++ b/RegExpressWPFNET/RegExpressWPFNET/UCMain.xaml @@ -5,15 +5,23 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:RegExpressWPFNET" mc:Ignorable="d" - Width="800" - Height="450" - Loaded="UserControl_Loaded" + Width="800" + Height="450" + Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded" - IsVisibleChanged="UserControl_IsVisibleChanged" + IsVisibleChanged="UserControl_IsVisibleChanged" > - - + + + + + @@ -67,7 +75,8 @@ - +