├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── dotnet-ci.yml ├── .gitignore ├── Directory.Build.props ├── Directory.Packages.props ├── NatashaPad.slnx ├── README.md ├── Roadmap.md ├── resources └── NatashaPad-Intro.gif ├── src ├── .gitignore ├── NatashaPad.Core │ ├── DumpOutHelper.cs │ ├── Dumper.cs │ ├── DumperResolver.cs │ ├── NatashaPad.Core.csproj │ ├── ScriptEngine.cs │ └── ScriptOptions.cs ├── NatashaPad.Mvvm │ ├── DefaultViewContainer.cs │ ├── IViewContainer.cs │ ├── MessageBox │ │ ├── DefaultErrorMessageBoxService.cs │ │ ├── IMessageBoxServiceProvider.cs │ │ └── MessageBoxServiceExtensions.cs │ ├── NatashaPad.Mvvm.csproj │ ├── Properties │ │ ├── Resource.Designer.cs │ │ └── Resource.resx │ ├── ViewContainerOptions.cs │ ├── ViewLocatorExtensions.cs │ └── Windows │ │ ├── DefaultCurrentWindowService.cs │ │ ├── DefaultDialogService.cs │ │ ├── DefaultWindowManager.cs │ │ ├── DefaultWindowProvider.cs │ │ ├── IWindowProvider.cs │ │ └── IWindowService.cs └── NatashaPad │ ├── App.xaml │ ├── App.xaml.cs │ ├── Mvvm │ ├── CollectionItem.cs │ └── RemovableCollection.cs │ ├── NatashaPad.csproj │ ├── Properties │ ├── AssemblyInfo.cs │ ├── Resource.Designer.cs │ └── Resource.resx │ ├── Themes │ ├── Common.TextBlock.xaml │ ├── Common.xaml │ └── DialogStyles.xaml │ ├── ViewModels │ ├── Base │ │ ├── DialogViewModelBase.cs │ │ └── ViewModelBase.cs │ ├── CommonParam.cs │ ├── MainViewModel.cs │ ├── NugetManageViewModel.cs │ ├── Package.cs │ └── UsingManageViewModel.cs │ └── Views │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── NugetManageView.xaml │ ├── NugetManageView.xaml.cs │ ├── UsingManageView.xaml │ └── UsingManageView.xaml.cs └── test └── NatashaPad.Test ├── DumperTest.cs ├── NatashaPad.Test.csproj └── ScriptEngineTest.cs /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome:http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Don't use tabs for indentation. 7 | [*] 8 | indent_style = space 9 | # (Please don't specify an indent_size here; that has too many unintended consequences.) 10 | 11 | # Code files 12 | [*.{cs,csx,vb,vbx}] 13 | indent_size = 4 14 | insert_final_newline = true 15 | charset = utf-8-bom 16 | 17 | # Xml project files 18 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] 19 | indent_size = 2 20 | 21 | # Xml config files 22 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] 23 | indent_size = 2 24 | 25 | # JSON files 26 | [*.json] 27 | indent_size = 2 28 | 29 | # Dotnet code style settings: 30 | [*.{cs,vb}] 31 | # File header 32 | file_header_template = Copyright (c) NatashaPad. All rights reserved.\nLicensed under the Apache license. 33 | 34 | # Sort using and Import directives with System.* appearing first 35 | dotnet_sort_system_directives_first = false 36 | # Avoid "this." and "Me." if not necessary 37 | dotnet_style_qualification_for_field = false:suggestion 38 | dotnet_style_qualification_for_property = false:suggestion 39 | dotnet_style_qualification_for_method = false:suggestion 40 | dotnet_style_qualification_for_event = false:suggestion 41 | 42 | # Use language keywords instead of framework type names for type references 43 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 44 | dotnet_style_predefined_type_for_member_access = true:suggestion 45 | 46 | # Suggest more modern language features when available 47 | dotnet_style_object_initializer = true:suggestion 48 | dotnet_style_collection_initializer = true:suggestion 49 | dotnet_style_coalesce_expression = true:suggestion 50 | dotnet_style_null_propagation = true:suggestion 51 | dotnet_style_explicit_tuple_names = true:suggestion 52 | 53 | # CSharp code style settings: 54 | [*.cs] 55 | # namespace style 56 | csharp_style_namespace_declarations=file_scoped:warning 57 | 58 | # Prefer "var" everywhere 59 | csharp_style_var_for_built_in_types = true:suggestion 60 | csharp_style_var_when_type_is_apparent = true:suggestion 61 | csharp_style_var_elsewhere = true:suggestion 62 | 63 | # Prefer method-like constructs to have a block body 64 | csharp_style_expression_bodied_methods = false:none 65 | csharp_style_expression_bodied_constructors = false:none 66 | csharp_style_expression_bodied_operators = false:none 67 | 68 | # Prefer property-like constructs to have an expression-body 69 | csharp_style_expression_bodied_properties = true:none 70 | csharp_style_expression_bodied_indexers = true:none 71 | csharp_style_expression_bodied_accessors = true:none 72 | 73 | # Suggest more modern language features when available 74 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 75 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 76 | csharp_style_inlined_variable_declaration = true:suggestion 77 | csharp_style_throw_expression = true:suggestion 78 | csharp_style_conditional_delegate_call = true:suggestion 79 | 80 | # Newline settings 81 | csharp_new_line_before_open_brace = all 82 | csharp_new_line_before_else = true 83 | csharp_new_line_before_catch = true 84 | csharp_new_line_before_finally = true 85 | csharp_new_line_before_members_in_object_initializers = true 86 | csharp_new_line_before_members_in_anonymous_types = true 87 | 88 | # Fix formatting, https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#rule-id-ide0055-fix-formatting 89 | dotnet_diagnostic.IDE00055.severity = warning 90 | # Remove unnecessary usings, https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005 91 | dotnet_diagnostic.IDE0005.severity = warning 92 | # File header template 93 | dotnet_diagnostic.IDE0073.severity = warning 94 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | *.sh text eol=lf 7 | *.ps1 text eol=crlf 8 | 9 | ############################################################################### 10 | # Set default behavior for command prompt diff. 11 | # 12 | # This is need for earlier builds of msysgit that does not have it on by 13 | # default for csharp files. 14 | # Note: This is only used by command line 15 | ############################################################################### 16 | #*.cs diff=csharp 17 | 18 | ############################################################################### 19 | # Set the merge driver for project and solution files 20 | # 21 | # Merging from the command prompt will add diff markers to the files if there 22 | # are conflicts (Merging from VS is not affected by the settings below, in VS 23 | # the diff markers are never inserted). Diff markers may cause the following 24 | # file extensions to fail to load in VS. An alternative would be to treat 25 | # these files as binary and thus will always conflict and require user 26 | # intervention with every merge. To do so, just uncomment the entries below 27 | ############################################################################### 28 | #*.sln merge=binary 29 | #*.csproj merge=binary 30 | #*.vbproj merge=binary 31 | #*.vcxproj merge=binary 32 | #*.vcproj merge=binary 33 | #*.dbproj merge=binary 34 | #*.fsproj merge=binary 35 | #*.lsproj merge=binary 36 | #*.wixproj merge=binary 37 | #*.modelproj merge=binary 38 | #*.sqlproj merge=binary 39 | #*.wwaproj merge=binary 40 | 41 | ############################################################################### 42 | # behavior for image files 43 | # 44 | # image files are treated as binary by default. 45 | ############################################################################### 46 | #*.jpg binary 47 | #*.png binary 48 | #*.gif binary 49 | 50 | ############################################################################### 51 | # diff behavior for common document formats 52 | # 53 | # Convert binary document formats to text before diffing them. This feature 54 | # is only available from the command line. Turn it on by uncommenting the 55 | # entries below. 56 | ############################################################################### 57 | #*.doc diff=astextplain 58 | #*.DOC diff=astextplain 59 | #*.docx diff=astextplain 60 | #*.DOCX diff=astextplain 61 | #*.dot diff=astextplain 62 | #*.DOT diff=astextplain 63 | #*.pdf diff=astextplain 64 | #*.PDF diff=astextplain 65 | #*.rtf diff=astextplain 66 | #*.RTF diff=astextplain 67 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-ci.yml: -------------------------------------------------------------------------------- 1 | name: dotnet-ci 2 | 3 | # Trigger the workflow on push or pull request 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | build: 8 | 9 | runs-on: windows-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Setup .NET SDK 14 | uses: actions/setup-dotnet@v4 15 | with: 16 | dotnet-version: 9.0.x 17 | - name: dotnet info 18 | run: dotnet --info 19 | - name: build 20 | run: dotnet build 21 | - name: test 22 | run: dotnet test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # Cutom files 7 | localBuild/ 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | [Oo]ut/ 30 | 31 | # Visual Studio 2015 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # .NET Core 50 | project.lock.json 51 | project.fragment.lock.json 52 | artifacts/ 53 | **/Properties/launchSettings.json 54 | 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.rsp 66 | *.sbr 67 | *.tlb 68 | *.tli 69 | *.tlh 70 | *.tmp 71 | *.tmp_proj 72 | *.log 73 | *.vspscc 74 | *.vssscc 75 | .builds 76 | *.pidb 77 | *.svclog 78 | *.scc 79 | 80 | # Chutzpah Test files 81 | _Chutzpah* 82 | 83 | # Visual C++ cache files 84 | ipch/ 85 | *.aps 86 | *.ncb 87 | *.opendb 88 | *.opensdf 89 | *.sdf 90 | *.cachefile 91 | *.VC.db 92 | *.VC.VC.opendb 93 | 94 | # Visual Studio profiler 95 | *.psess 96 | *.vsp 97 | *.vspx 98 | *.sap 99 | 100 | # TFS 2012 Local Workspace 101 | $tf/ 102 | 103 | # Guidance Automation Toolkit 104 | *.gpState 105 | 106 | # ReSharper is a .NET coding add-in 107 | _ReSharper*/ 108 | *.[Rr]e[Ss]harper 109 | *.DotSettings.user 110 | 111 | # JustCode is a .NET coding add-in 112 | .JustCode 113 | 114 | # TeamCity is a build add-in 115 | _TeamCity* 116 | 117 | # DotCover is a Code Coverage Tool 118 | *.dotCover 119 | 120 | # Visual Studio code coverage results 121 | *.coverage 122 | *.coveragexml 123 | 124 | # NCrunch 125 | _NCrunch_* 126 | .*crunch*.local.xml 127 | nCrunchTemp_* 128 | 129 | # MightyMoose 130 | *.mm.* 131 | AutoTest.Net/ 132 | 133 | # Web workbench (sass) 134 | .sass-cache/ 135 | 136 | # Installshield output folder 137 | [Ee]xpress/ 138 | 139 | # DocProject is a documentation generator add-in 140 | DocProject/buildhelp/ 141 | DocProject/Help/*.HxT 142 | DocProject/Help/*.HxC 143 | DocProject/Help/*.hhc 144 | DocProject/Help/*.hhk 145 | DocProject/Help/*.hhp 146 | DocProject/Help/Html2 147 | DocProject/Help/html 148 | 149 | # Click-Once directory 150 | publish/ 151 | 152 | # Publish Web Output 153 | *.[Pp]ublish.xml 154 | *.azurePubxml 155 | # TODO: Comment the next line if you want to checkin your web deploy settings 156 | # but database connection strings (with potential passwords) will be unencrypted 157 | *.pubxml 158 | *.publishproj 159 | 160 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 161 | # checkin your Azure Web App publish settings, but sensitive information contained 162 | # in these scripts will be unencrypted 163 | PublishScripts/ 164 | 165 | # NuGet Packages 166 | *.nupkg 167 | # The packages folder can be ignored because of Package Restore 168 | **/packages/* 169 | # except build/, which is used as an MSBuild target. 170 | !**/packages/build/ 171 | # Uncomment if necessary however generally it will be regenerated when needed 172 | #!**/packages/repositories.config 173 | # NuGet v3's project.json files produces more ignorable files 174 | *.nuget.props 175 | *.nuget.targets 176 | 177 | # Microsoft Azure Build Output 178 | csx/ 179 | *.build.csdef 180 | 181 | # Microsoft Azure Emulator 182 | ecf/ 183 | rcf/ 184 | 185 | # Windows Store app package directories and files 186 | AppPackages/ 187 | BundleArtifacts/ 188 | Package.StoreAssociation.xml 189 | _pkginfo.txt 190 | 191 | # Visual Studio cache files 192 | # files ending in .cache can be ignored 193 | *.[Cc]ache 194 | # but keep track of directories ending in .cache 195 | !*.[Cc]ache/ 196 | 197 | # Others 198 | ClientBin/ 199 | ~$* 200 | *~ 201 | *.dbmdl 202 | *.dbproj.schemaview 203 | *.jfm 204 | *.pfx 205 | *.publishsettings 206 | orleans.codegen.cs 207 | 208 | # Since there are multiple workflows, uncomment next line to ignore bower_components 209 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 210 | #bower_components/ 211 | 212 | # RIA/Silverlight projects 213 | Generated_Code/ 214 | 215 | # Backup & report files from converting an old project file 216 | # to a newer Visual Studio version. Backup files are not needed, 217 | # because we have git ;-) 218 | _UpgradeReport_Files/ 219 | Backup*/ 220 | UpgradeLog*.XML 221 | UpgradeLog*.htm 222 | 223 | # SQL Server files 224 | *.mdf 225 | *.ldf 226 | *.ndf 227 | 228 | # Business Intelligence projects 229 | *.rdl.data 230 | *.bim.layout 231 | *.bim_*.settings 232 | 233 | # Microsoft Fakes 234 | FakesAssemblies/ 235 | 236 | # GhostDoc plugin setting file 237 | *.GhostDoc.xml 238 | 239 | # Node.js Tools for Visual Studio 240 | .ntvs_analysis.dat 241 | node_modules/ 242 | 243 | # Typescript v1 declaration files 244 | typings/ 245 | 246 | # Visual Studio 6 build log 247 | *.plg 248 | 249 | # Visual Studio 6 workspace options file 250 | *.opt 251 | 252 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 253 | *.vbw 254 | 255 | # Visual Studio LightSwitch build output 256 | **/*.HTMLClient/GeneratedArtifacts 257 | **/*.DesktopClient/GeneratedArtifacts 258 | **/*.DesktopClient/ModelManifest.xml 259 | **/*.Server/GeneratedArtifacts 260 | **/*.Server/ModelManifest.xml 261 | _Pvt_Extensions 262 | 263 | # Paket dependency manager 264 | .paket/paket.exe 265 | paket-files/ 266 | 267 | # FAKE - F# Make 268 | .fake/ 269 | 270 | # JetBrains Rider 271 | .idea/ 272 | *.sln.iml 273 | 274 | # CodeRush 275 | .cr/ 276 | 277 | # Python Tools for Visual Studio (PTVS) 278 | __pycache__/ 279 | *.pyc 280 | 281 | # Cake - Uncomment if you are using it 282 | tools/** 283 | build/tools/** 284 | 285 | # Telerik's JustMock configuration file 286 | *.jmconfig 287 | 288 | # BizTalk build output 289 | *.btp.cs 290 | *.btm.cs 291 | *.odx.cs 292 | *.xsd.cs 293 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0 4 | 1 5 | 0 6 | $(VersionMajor).$(VersionMinor).$(VersionPatch) 7 | develop 8 | 9 | 10 | net9.0 11 | preview 12 | enable 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 4.13.0 5 | true 6 | direct 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /NatashaPad.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NatashaPad 2 | 3 | ## Intro 4 | 5 | [![dotnet-ci](https://github.com/night-moon-studio/NatashaPad/actions/workflows/dotnet-ci.yml/badge.svg)](https://github.com/night-moon-studio/NatashaPad/actions/workflows/dotnet-ci.yml) 6 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnight-moon-studio%2FNatashaPad.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnight-moon-studio%2FNatashaPad?ref=badge_shield) 7 | 8 | Another dotnet debug tool like LinqPad and dotnet fiddle, powered by Roslyn and Natasha 9 | 10 | ![Intro](./resources/NatashaPad-Intro.gif) 11 | 12 | 13 | 14 | ## Contributing 15 | 16 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/night-moon-studio/NatashaPad/pulls) 17 | 18 | If you'd like to contribute, just create a [Pull Request](https://github.com/night-moon-studio/NatashaPad/pulls), or give us [Bug Report](https://github.com/night-moon-studio/NatashaPad/issues/new). 19 | 20 | Thanks for our contributors. 21 | 22 | 23 | ## Acknowledgements 24 | 25 | - [Roslyn](https://github.com/dotnet/roslyn) 26 | - [Natasha](https://github.com/dotnetcore/Natasha) 27 | 28 | 29 | ## License 30 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnight-moon-studio%2FNatashaPad.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnight-moon-studio%2FNatashaPad?ref=badge_large) 31 | -------------------------------------------------------------------------------- /Roadmap.md: -------------------------------------------------------------------------------- 1 | # NatashaPad RoadMap 2 | 3 | ## Core 4 | 5 | - [x] Nuget Reference Support 6 | 7 | ## Editor features 8 | 9 | - [ ] Better Output Design and exception handle 10 | - [ ] AutoComplete 11 | - [ ] Debug support 12 | 13 | ## Extensions 14 | 15 | - [ ] Blazor ui client 16 | -------------------------------------------------------------------------------- /resources/NatashaPad-Intro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/night-moon-studio/NatashaPad/4212b263c2258ff8b311b76c50683af00c08f6d1/resources/NatashaPad-Intro.gif -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | NatashaPad.Web -------------------------------------------------------------------------------- /src/NatashaPad.Core/DumpOutHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Diagnostics; 5 | 6 | namespace NatashaPad; 7 | 8 | public static class DumpOutHelper 9 | { 10 | public static Action OutputAction = (str) => { Debug.WriteLine(str); }; 11 | } 12 | -------------------------------------------------------------------------------- /src/NatashaPad.Core/Dumper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using WeihanLi.Extensions; 5 | 6 | namespace NatashaPad; 7 | 8 | public interface IDumper 9 | { 10 | Func TypePredicate { get; } 11 | 12 | string Dump(object obj); 13 | } 14 | 15 | public class DefaultDumper : IDumper 16 | { 17 | public static readonly IDumper Instance = new DefaultDumper(); 18 | 19 | public Func TypePredicate { get; } = _ => true; 20 | 21 | public string Dump(object obj) 22 | { 23 | return obj.ToJsonOrString(); 24 | } 25 | } 26 | 27 | public static class DumperExtensions 28 | { 29 | public static void Dump(this object obj) 30 | { 31 | string dumpedResult; 32 | if (obj is null) 33 | { 34 | dumpedResult = "(null)"; 35 | } 36 | else 37 | { 38 | dumpedResult = DefaultDumper.Instance.Dump(obj); 39 | } 40 | DumpOutHelper.OutputAction?.Invoke(dumpedResult); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/NatashaPad.Core/DumperResolver.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad; 5 | 6 | public class DumperResolver 7 | { 8 | private readonly ICollection _dumpers; 9 | 10 | public DumperResolver(IEnumerable dumpers) 11 | { 12 | _dumpers = dumpers.Reverse().ToArray(); 13 | } 14 | 15 | public IDumper Resolve(Type type) 16 | { 17 | foreach (var dumper in _dumpers) 18 | { 19 | if (dumper.TypePredicate(type)) 20 | return dumper; 21 | } 22 | 23 | return DefaultDumper.Instance; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/NatashaPad.Core/NatashaPad.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NatashaPad 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/NatashaPad.Core/ScriptEngine.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using Microsoft.CodeAnalysis.CSharp.Scripting; 5 | using Microsoft.CodeAnalysis.Scripting; 6 | using ReferenceResolver; 7 | using System.Reflection; 8 | using WeihanLi.Common.Helpers; 9 | using WeihanLi.Extensions; 10 | 11 | namespace NatashaPad; 12 | 13 | public interface INScriptEngine 14 | { 15 | Task Execute(string code, NScriptOptions scriptOptions, CancellationToken cancellationToken = default); 16 | 17 | Task Eval(string code, NScriptOptions scriptOptions, CancellationToken cancellationToken = default); 18 | } 19 | 20 | public class CSharpScriptEngine : INScriptEngine 21 | { 22 | private const BindingFlags MainMethodBindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; 23 | 24 | static CSharpScriptEngine() 25 | { 26 | try 27 | { 28 | NatashaInitializer.Preheating(); 29 | } 30 | catch (Exception ex) 31 | { 32 | InvokeHelper.OnInvokeException?.Invoke(ex); 33 | } 34 | } 35 | 36 | private readonly IReferenceResolverFactory _referenceResolverFactory; 37 | public CSharpScriptEngine(IReferenceResolverFactory referenceResolverFactory) 38 | { 39 | _referenceResolverFactory = referenceResolverFactory; 40 | } 41 | 42 | public async Task Execute(string code, NScriptOptions scriptOptions, CancellationToken cancellationToken = default) 43 | { 44 | scriptOptions.UsingList.Add("NatashaPad"); 45 | code = $"{scriptOptions.UsingList.Where(x => !string.IsNullOrWhiteSpace(x)).Select(ns => $"using {ns};") 46 | .StringJoin(Environment.NewLine)}{Environment.NewLine}{code}"; 47 | 48 | using var domain = NatashaManagement.CreateRandomDomain(); 49 | var assBuilder = new AssemblyCSharpBuilder(); 50 | assBuilder.Add(code); 51 | assBuilder.Domain = domain; 52 | assBuilder.ConfigCompilerOption(options => 53 | { 54 | options.SetOutputKind(OutputKind.ConsoleApplication); 55 | }); 56 | 57 | var defaultReferences = 58 | await _referenceResolverFactory.GetResolver(ReferenceType.FrameworkReference) 59 | .Resolve(FrameworkReferenceResolver.FrameworkNames.Default, scriptOptions.TargetFramework, cancellationToken); 60 | foreach (var reference in defaultReferences) 61 | { 62 | try 63 | { 64 | domain.LoadAssemblyFromFile(reference); 65 | } 66 | catch 67 | { 68 | // ignore 69 | } 70 | } 71 | // add reference 72 | if (scriptOptions.References.Count > 0) 73 | { 74 | var references = await scriptOptions 75 | .References 76 | .Select(r => _referenceResolverFactory.GetResolver(r.ReferenceType) 77 | .Resolve(r.Reference, scriptOptions.TargetFramework, cancellationToken) 78 | ) 79 | .WhenAll() 80 | .ContinueWith(r => r.Result.SelectMany(_ => _), cancellationToken); 81 | // add reference 82 | foreach (var reference in references.Distinct()) 83 | { 84 | if (!string.IsNullOrEmpty(reference)) 85 | { 86 | try 87 | { 88 | domain.LoadAssemblyFromFile(reference); 89 | } 90 | catch 91 | { 92 | // ignore 93 | } 94 | } 95 | } 96 | } 97 | 98 | var assembly = assBuilder.GetAssembly(); 99 | 100 | using var capture = await ConsoleOutput.CaptureAsync(); 101 | var entryPoint = assembly.EntryPoint 102 | ?? assembly.GetType("Program")?.GetMethod("Main", MainMethodBindingFlags) 103 | ?? assembly.GetType("Program")?.GetMethod("MainAsync", MainMethodBindingFlags) 104 | ; 105 | if (null != entryPoint) 106 | { 107 | var returnValue = entryPoint.Invoke(null, entryPoint.GetParameters().Select(p => p.ParameterType.GetDefaultValue()).ToArray()); 108 | await TaskHelper.ToTask(returnValue); 109 | } 110 | else 111 | { 112 | throw new ArgumentException("can not find entry point"); 113 | } 114 | if (!string.IsNullOrEmpty(capture.StandardOutput)) 115 | { 116 | DumpOutHelper.OutputAction?.Invoke(capture.StandardOutput); 117 | } 118 | if (!string.IsNullOrEmpty(capture.StandardError)) 119 | { 120 | DumpOutHelper.OutputAction?.Invoke($"Error:{capture.StandardError}"); 121 | } 122 | } 123 | 124 | public async Task Eval(string code, NScriptOptions scriptOptions, CancellationToken cancellationToken = default) 125 | { 126 | var defaultReferences = 127 | await _referenceResolverFactory.GetResolver(ReferenceType.FrameworkReference) 128 | .ResolveMetadataReferences(FrameworkReferenceResolver.FrameworkNames.Default, scriptOptions.TargetFramework, cancellationToken) 129 | .ContinueWith(r => r.Result.ToArray()); 130 | scriptOptions.UsingList.Add("NatashaPad"); 131 | var options = ScriptOptions.Default 132 | .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest) 133 | .AddReferences(defaultReferences) 134 | .AddReferences(new[] 135 | { 136 | MetadataReference.CreateFromFile(typeof(DumperResolver).Assembly.Location) 137 | }) 138 | .WithImports(scriptOptions.UsingList.Where(x => !string.IsNullOrWhiteSpace(x))) 139 | ; 140 | 141 | if (scriptOptions.References.Count > 0) 142 | { 143 | var references = await scriptOptions.References 144 | .Select(r => _referenceResolverFactory.GetResolver(r.ReferenceType) 145 | .ResolveMetadataReferences(r.Reference, scriptOptions.TargetFramework, cancellationToken)) 146 | .WhenAll() 147 | .ContinueWith(r => r.Result.SelectMany(_ => _), cancellationToken) 148 | ; 149 | options = options.AddReferences(references); 150 | } 151 | 152 | return await CSharpScript.EvaluateAsync(code, options, cancellationToken: cancellationToken); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/NatashaPad.Core/ScriptOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using ReferenceResolver; 5 | 6 | namespace NatashaPad; 7 | 8 | public class NScriptOptions 9 | { 10 | public HashSet UsingList { get; } = new() 11 | { 12 | "System", 13 | "System.Collections.Generic", 14 | "System.Diagnostics", 15 | "System.Linq", 16 | "System.Linq.Expressions", 17 | "System.IO", 18 | "System.Reflection", 19 | "System.Text", 20 | "System.Text.RegularExpressions", 21 | "System.Threading", 22 | "System.Threading.Tasks", 23 | }; 24 | 25 | public string TargetFramework { get; set; } = "net8.0"; 26 | 27 | public HashSet References { get; } = new(); 28 | } 29 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/DefaultViewContainer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Mvvm; 5 | 6 | internal class DefaultViewContainer : IViewTypeInfoLocator 7 | { 8 | private readonly Dictionary vmToviewMap; 9 | private readonly Dictionary viewToInfoMap; 10 | 11 | public DefaultViewContainer() 12 | { 13 | vmToviewMap = new Dictionary(); 14 | viewToInfoMap = new Dictionary(); 15 | } 16 | 17 | internal DefaultViewContainer(IEnumerable infos) 18 | { 19 | vmToviewMap = infos.ToDictionary(x => x.ViewModelType, x => x.ViewType); 20 | viewToInfoMap = infos.ToDictionary(x => x.ViewType, x => x.ViewInfo); 21 | } 22 | 23 | public Type GetView(Type vmType) 24 | { 25 | if (!vmToviewMap.TryGetValue(vmType, out var type)) 26 | { 27 | throw new KeyNotFoundException( 28 | string.Format(Properties.Resource.CannotFindMatchedViewTypeOfFormatString, 29 | vmType.Name)); 30 | } 31 | 32 | return type; 33 | } 34 | 35 | public ViewInfo GetViewInfo(Type viewType) 36 | { 37 | if (!viewToInfoMap.TryGetValue(viewType, out var info)) 38 | { 39 | throw new KeyNotFoundException( 40 | string.Format(Properties.Resource.CannotFindMatchedViewInfoOfFormatString, 41 | viewType.Name)); 42 | } 43 | 44 | return info; 45 | } 46 | } 47 | 48 | internal class DefaultViewLocator : IViewInstanceLocator 49 | { 50 | private readonly IViewTypeInfoLocator viewLocator; 51 | private readonly IServiceProvider serviceProvider; 52 | 53 | public DefaultViewLocator( 54 | IViewTypeInfoLocator viewLocator, 55 | IServiceProvider serviceProvider) 56 | { 57 | this.viewLocator = viewLocator; 58 | this.serviceProvider = serviceProvider; 59 | } 60 | 61 | public object GetView(Type viewModelType) 62 | { 63 | return serviceProvider.GetService(viewLocator.GetView(viewModelType)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/IViewContainer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Mvvm; 5 | 6 | public interface IViewTypeInfoLocator 7 | { 8 | Type GetView(Type viewModelType); 9 | 10 | ViewInfo GetViewInfo(Type viewType); 11 | } 12 | 13 | public interface IViewInstanceLocator 14 | { 15 | object GetView(Type viewModelType); 16 | } 17 | 18 | public static class ViewContainerExtensions 19 | { 20 | public static object GetView(this IViewInstanceLocator locator) 21 | { 22 | return locator.GetView(typeof(TViewModel)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/MessageBox/DefaultErrorMessageBoxService.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Mvvm.MessageBox; 5 | 6 | internal class DefaultErrorMessageBoxService : IErrorMessageBoxService 7 | { 8 | public void ShowError(string title, string content) 9 | { 10 | System.Windows.MessageBox.Show(content, title); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/MessageBox/IMessageBoxServiceProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Mvvm.MessageBox; 5 | 6 | public interface IInfoMessageBoxService 7 | { 8 | void ShowInfo(string title, string content); 9 | } 10 | 11 | public interface IWarnMessageBoxService 12 | { 13 | void ShowWarn(string title, string content); 14 | } 15 | 16 | public interface IErrorMessageBoxService 17 | { 18 | void ShowError(string title, string content); 19 | } 20 | 21 | public enum AskResponse 22 | { 23 | Option1, 24 | Option2, 25 | Option3, 26 | Option4, 27 | Option5 28 | } 29 | 30 | public interface IAskMessageBoxService 31 | { 32 | AskResponse ShowAsk(string title, string content); 33 | } 34 | 35 | public interface IConfirmMessageBoxService 36 | { 37 | bool ShowConfirm(string title, string content); 38 | } 39 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/MessageBox/MessageBoxServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Mvvm.MessageBox; 5 | 6 | public static class MessageBoxServiceExtensions 7 | { 8 | public static void Show(this IErrorMessageBoxService messageBoxService, string content) 9 | { 10 | messageBoxService.ShowError(Properties.Resource.ErrorMessageBoxTitleString, content); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/NatashaPad.Mvvm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(TargetFramework)-windows 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | True 15 | True 16 | Resource.resx 17 | 18 | 19 | 20 | 21 | 22 | ResXFileCodeGenerator 23 | Resource.Designer.cs 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Properties/Resource.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace NatashaPad.Mvvm.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resource { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resource() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NatashaPad.Mvvm.Properties.Resource", typeof(Resource).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 查找类似 找不到 {0} 匹配的 视图信息! 的本地化字符串。 65 | /// 66 | internal static string CannotFindMatchedViewInfoOfFormatString { 67 | get { 68 | return ResourceManager.GetString("CannotFindMatchedViewInfoOfFormatString", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// 查找类似 找不到 {0} 匹配的 视图类型! 的本地化字符串。 74 | /// 75 | internal static string CannotFindMatchedViewTypeOfFormatString { 76 | get { 77 | return ResourceManager.GetString("CannotFindMatchedViewTypeOfFormatString", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// 查找类似 执行发生异常 的本地化字符串。 83 | /// 84 | internal static string ErrorMessageBoxTitleString { 85 | get { 86 | return ResourceManager.GetString("ErrorMessageBoxTitleString", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// 查找类似 未找到视图模型所匹配的窗口实例,请确认窗口是否已经关闭! 的本地化字符串。 92 | /// 93 | internal static string FoundNoWindowErrorString { 94 | get { 95 | return ResourceManager.GetString("FoundNoWindowErrorString", resourceCulture); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Properties/Resource.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 找不到 {0} 匹配的 视图信息! 122 | 123 | 124 | 找不到 {0} 匹配的 视图类型! 125 | 126 | 127 | 执行发生异常 128 | 129 | 130 | 未找到视图模型所匹配的窗口实例,请确认窗口是否已经关闭! 131 | 132 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/ViewContainerOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Collections.ObjectModel; 5 | using System.Windows; 6 | 7 | namespace NatashaPad.Mvvm; 8 | 9 | public class ViewContainerOptions : Collection 10 | { 11 | public void Register() 12 | { 13 | Add(new RegisterInfo(typeof(TView), typeof(TViewModel))); 14 | } 15 | 16 | public void Register(Action action) 17 | { 18 | var reg = new RegisterInfo(typeof(TView), typeof(TViewModel)); 19 | 20 | var viewInfo = new ViewInfo(); 21 | action?.Invoke(viewInfo); 22 | reg.ViewInfo = viewInfo; 23 | 24 | Add(reg); 25 | } 26 | } 27 | 28 | public sealed class RegisterInfo 29 | { 30 | public RegisterInfo(Type viewType, Type viewModelType, string name) 31 | { 32 | ViewType = viewType; 33 | ViewModelType = viewModelType; 34 | Name = name; 35 | } 36 | 37 | public RegisterInfo(Type viewType, Type viewModelType) : this(viewType, viewModelType, string.Empty) 38 | { 39 | } 40 | 41 | public string Name { get; } 42 | 43 | public Type ViewType { get; } 44 | public Type ViewModelType { get; } 45 | 46 | public ViewInfo ViewInfo { get; set; } 47 | } 48 | 49 | public class ViewInfo 50 | { 51 | public int? Width { get; set; } 52 | public int? Height { get; set; } 53 | 54 | public string Title { get; set; } 55 | 56 | public WindowStartupLocation WindowStartupLocation { get; set; } = WindowStartupLocation.Manual; 57 | 58 | public SizeToContent? SizeToContent { get; set; } 59 | } 60 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/ViewLocatorExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.DependencyInjection.Extensions; 6 | using NatashaPad.Mvvm.MessageBox; 7 | using NatashaPad.Mvvm.Windows; 8 | 9 | namespace NatashaPad.Mvvm; 10 | 11 | public static class ViewLocatorExtensions 12 | { 13 | public static void UsingViewLocator(this IServiceCollection services, 14 | Action action) 15 | { 16 | var options = new ViewContainerOptions(); 17 | action?.Invoke(options); 18 | 19 | foreach (var item in options) 20 | { 21 | services.AddTransient(item.ViewType); 22 | services.AddTransient(item.ViewModelType); 23 | } 24 | 25 | services.TryAddSingleton(new DefaultViewContainer(options)); 26 | services.TryAddSingleton(s => s.GetService()); 27 | 28 | services.TryAddSingleton(); 29 | 30 | services.TryAddSingleton(); 31 | 32 | services.TryAddSingleton(); 33 | 34 | services.TryAddTransient(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Windows/DefaultCurrentWindowService.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows; 5 | 6 | namespace NatashaPad.Mvvm.Windows; 7 | 8 | internal class DefaultCurrentWindowService : ICurrentWindowService 9 | { 10 | private readonly Window window; 11 | 12 | public DefaultCurrentWindowService(Window window) 13 | { 14 | this.window = window; 15 | } 16 | 17 | public void Close() 18 | { 19 | window.Close(); 20 | } 21 | 22 | public void Hide() 23 | { 24 | window.Hide(); 25 | } 26 | 27 | public void Show() 28 | { 29 | window.Show(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Windows/DefaultDialogService.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows; 5 | 6 | namespace NatashaPad.Mvvm.Windows; 7 | 8 | internal class DefaultDialogService : IDialogService, IWindowService 9 | { 10 | private readonly Window window; 11 | 12 | public DefaultDialogService(Window window) 13 | { 14 | this.window = window; 15 | } 16 | 17 | public void Close() 18 | { 19 | window.Close(); 20 | } 21 | 22 | public void Hide() 23 | { 24 | window.Hide(); 25 | } 26 | 27 | public void Show() 28 | { 29 | window.Show(); 30 | } 31 | 32 | public void ShowDialog() 33 | { 34 | window.ShowDialog(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Windows/DefaultWindowManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows; 5 | 6 | namespace NatashaPad.Mvvm.Windows; 7 | 8 | internal class DefaultWindowManager : IWindowManager 9 | { 10 | private readonly IViewInstanceLocator locator; 11 | private readonly IWindowProvider windowProvider; 12 | 13 | public DefaultWindowManager( 14 | IViewInstanceLocator locator, 15 | IWindowProvider windowProvider) 16 | { 17 | this.locator = locator; 18 | this.windowProvider = windowProvider; 19 | 20 | windowMap = new Dictionary(); 21 | } 22 | 23 | private readonly Dictionary windowMap; 24 | 25 | public ICurrentWindowService GetCurrent(TViewModel viewModel) 26 | { 27 | if (!windowMap.TryGetValue(viewModel, out var window)) 28 | { 29 | throw new ArgumentException(Properties.Resource.FoundNoWindowErrorString); 30 | } 31 | 32 | return new DefaultCurrentWindowService(window); 33 | } 34 | 35 | public IDialogService GetDialogService(TViewModel viewModel) 36 | { 37 | var view = locator.GetView(typeof(TViewModel)); 38 | var window = windowProvider.Create(view, viewModel); 39 | window.Closed += Window_Closed; 40 | 41 | windowMap[viewModel] = window; 42 | return new DefaultDialogService(window); 43 | } 44 | 45 | private void Window_Closed(object sender, EventArgs e) 46 | { 47 | windowMap.Remove(GetViewModel()); 48 | 49 | object GetViewModel() 50 | { 51 | foreach (var pair in windowMap) 52 | { 53 | if (pair.Value == sender) 54 | { 55 | return pair.Key; 56 | } 57 | } 58 | 59 | //关闭窗口时,找不到注册信息? 60 | throw new NotImplementedException(); 61 | } 62 | } 63 | 64 | public IWindowService GetWindowService(TViewModel viewModel) 65 | { 66 | return GetDialogService(viewModel); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Windows/DefaultWindowProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows; 5 | 6 | namespace NatashaPad.Mvvm.Windows; 7 | 8 | public class DefaultWindowProvider : IWindowProvider 9 | { 10 | private readonly IViewTypeInfoLocator viewTypeInfoLocator; 11 | 12 | public DefaultWindowProvider(IViewTypeInfoLocator viewTypeInfoLocator) 13 | { 14 | this.viewTypeInfoLocator = viewTypeInfoLocator; 15 | } 16 | 17 | public Window Create(object view, object viewModel) 18 | { 19 | var viewInfo = viewTypeInfoLocator.GetViewInfo(view.GetType()); 20 | 21 | if (!(view is Window window)) 22 | { 23 | window = new Window 24 | { 25 | Content = view 26 | }; 27 | 28 | if (viewInfo != default) 29 | { 30 | if (viewInfo.Width.HasValue) 31 | { 32 | window.Width = viewInfo.Width.Value; 33 | } 34 | 35 | if (viewInfo.Height.HasValue) 36 | { 37 | window.Height = viewInfo.Height.Value; 38 | } 39 | 40 | if (viewInfo.SizeToContent.HasValue) 41 | { 42 | window.SizeToContent = viewInfo.SizeToContent.Value; 43 | } 44 | 45 | window.WindowStartupLocation = viewInfo.WindowStartupLocation; 46 | window.Title = viewInfo.Title; 47 | } 48 | } 49 | 50 | window.DataContext = viewModel; 51 | return window; 52 | 53 | /* 54 | * TODO: 可选的解法: 55 | * 在资源文件中定义View和ViewModel的映射关系 56 | * 预先设计好默认window 57 | * window中的内容元素的内容绑定到DataContext 58 | * 注册时,提供name,窗口尺寸等其它信息 59 | * 这样,就不需要在这个方法里硬编码,把扩展性开放出去 60 | */ 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Windows/IWindowProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows; 5 | 6 | namespace NatashaPad.Mvvm.Windows; 7 | 8 | public interface IWindowProvider 9 | { 10 | Window Create(object view, object viewModel); 11 | } 12 | -------------------------------------------------------------------------------- /src/NatashaPad.Mvvm/Windows/IWindowService.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Mvvm.Windows; 5 | 6 | public interface IWindowManager 7 | { 8 | ICurrentWindowService GetCurrent(TViewModel viewModel); 9 | 10 | IWindowService GetWindowService(TViewModel viewModel); 11 | 12 | IDialogService GetDialogService(TViewModel viewModel); 13 | } 14 | 15 | public interface IWindowService 16 | { 17 | void Show(); 18 | 19 | void Hide(); 20 | 21 | void Close(); 22 | } 23 | 24 | public interface IDialogService : IWindowService 25 | { 26 | void ShowDialog(); 27 | } 28 | 29 | public interface ICurrentWindowService : IWindowService 30 | { 31 | } 32 | -------------------------------------------------------------------------------- /src/NatashaPad/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/NatashaPad/App.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using MediatR; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.DependencyInjection.Extensions; 7 | using NatashaPad.Mvvm; 8 | using NatashaPad.Mvvm.Windows; 9 | using NatashaPad.ViewModels; 10 | using NatashaPad.Views; 11 | using ReferenceResolver; 12 | using System.Windows; 13 | using System.Windows.Threading; 14 | using WeihanLi.Common; 15 | 16 | namespace NatashaPad; 17 | 18 | /// 19 | /// Interaction logic for App.xaml 20 | /// 21 | public partial class App : Application 22 | { 23 | protected override void OnStartup(StartupEventArgs e) 24 | { 25 | Init(); 26 | base.OnStartup(e); 27 | } 28 | 29 | private void ConfigureServices(IServiceCollection services) 30 | { 31 | services.AddSingleton(); 32 | services.AddSingleton(); 33 | services.AddSingleton(); 34 | services.AddTransient(); 35 | services.TryAddSingleton(); 36 | services.AddSingleton(); 37 | 38 | services.AddTransient(); 39 | 40 | services.AddMediatR(typeof(App)); 41 | 42 | services.AddSingleton(Dispatcher.CurrentDispatcher); 43 | services.AddReferenceResolvers(); 44 | 45 | services.UsingViewLocator(options => 46 | { 47 | options.Register(); 48 | options.Register(opt => 49 | { 50 | opt.Width = 600; 51 | opt.Height = 400; 52 | opt.WindowStartupLocation = WindowStartupLocation.CenterScreen; 53 | opt.Title = NatashaPad.Properties.Resource.UsingManageTitleString; 54 | }); 55 | options.Register(opt => 56 | { 57 | opt.Width = 800; 58 | opt.Height = 450; 59 | opt.WindowStartupLocation = WindowStartupLocation.CenterScreen; 60 | opt.Title = NatashaPad.Properties.Resource.NugetManageTitleString; 61 | }); 62 | }); 63 | } 64 | 65 | private void Init() 66 | { 67 | IServiceCollection services = new ServiceCollection(); 68 | ConfigureServices(services); 69 | DependencyResolver.SetDependencyResolver(services); 70 | 71 | var windowService = DependencyResolver.ResolveRequiredService() 72 | .GetWindowService(DependencyResolver.ResolveRequiredService()); 73 | windowService.Show(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/NatashaPad/Mvvm/CollectionItem.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using Prism.Commands; 5 | using Prism.Mvvm; 6 | using System.Windows.Input; 7 | 8 | namespace NatashaPad.Mvvm; 9 | 10 | public class CollectionItem : BindableBase 11 | { 12 | public event EventHandler NeedDeleteMe; 13 | 14 | private ICommand _deleteThisCommand; 15 | public ICommand DeleteThisCommand => _deleteThisCommand ??= new DelegateCommand(FireDeleteMe); 16 | 17 | private void FireDeleteMe() => NeedDeleteMe?.Invoke(this, EventArgs.Empty); 18 | } 19 | -------------------------------------------------------------------------------- /src/NatashaPad/Mvvm/RemovableCollection.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Collections.ObjectModel; 5 | 6 | namespace NatashaPad.Mvvm; 7 | 8 | public class RemovableCollection : ObservableCollection 9 | where T : CollectionItem 10 | { 11 | protected override void InsertItem(int index, T item) 12 | { 13 | base.InsertItem(index, item); 14 | if (item != null) 15 | { 16 | item.NeedDeleteMe += Item_NeedDeleteMe; 17 | } 18 | } 19 | 20 | protected virtual void Item_NeedDeleteMe(object sender, EventArgs e) 21 | { 22 | Remove((T)sender); 23 | } 24 | 25 | protected override void RemoveItem(int index) 26 | { 27 | var item = this[index]; 28 | base.RemoveItem(index); 29 | item.NeedDeleteMe -= Item_NeedDeleteMe; 30 | } 31 | 32 | protected override void ClearItems() 33 | { 34 | foreach (var item in Items) 35 | { 36 | item.NeedDeleteMe -= Item_NeedDeleteMe; 37 | } 38 | 39 | base.ClearItems(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/NatashaPad/NatashaPad.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | $(TargetFramework)-windows 5 | true 6 | en 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | True 30 | True 31 | Resource.resx 32 | 33 | 34 | PublicResXFileCodeGenerator 35 | Resource.Designer.cs 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/NatashaPad/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows; 5 | 6 | [assembly: ThemeInfo( 7 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 8 | //(used if a resource is not found in the page, 9 | // or application resource dictionaries) 10 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 11 | //(used if a resource is not found in the page, 12 | // app, or any theme specific resource dictionaries) 13 | )] 14 | -------------------------------------------------------------------------------- /src/NatashaPad/Properties/Resource.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace NatashaPad.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resource { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resource() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NatashaPad.Properties.Resource", typeof(Resource).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to 执行发生异常. 65 | /// 66 | public static string ErrorMessageBoxTitleString { 67 | get { 68 | return ResourceManager.GetString("ErrorMessageBoxTitleString", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to 引用管理. 74 | /// 75 | public static string NugetManageTitleString { 76 | get { 77 | return ResourceManager.GetString("NugetManageTitleString", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Using管理. 83 | /// 84 | public static string UsingManageTitleString { 85 | get { 86 | return ResourceManager.GetString("UsingManageTitleString", resourceCulture); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/NatashaPad/Properties/Resource.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 执行发生异常 122 | 123 | 124 | 引用管理 125 | 126 | 127 | Using管理 128 | 129 | -------------------------------------------------------------------------------- /src/NatashaPad/Themes/Common.TextBlock.xaml: -------------------------------------------------------------------------------- 1 |  3 | 16 | -------------------------------------------------------------------------------- /src/NatashaPad/Themes/Common.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/NatashaPad/Themes/DialogStyles.xaml: -------------------------------------------------------------------------------- 1 |  4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 22 | 100 | 101 | 105 | 106 | 107 | 108 | 110 | 111 | 112 | 113 | 114 | 115 | 117 | 121 | 25 | 26 | 29 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 43 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/NatashaPad/Views/UsingManageView.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using System.Windows.Controls; 5 | 6 | namespace NatashaPad.Views; 7 | 8 | /// 9 | /// Interaction logic for UsingManageView.xaml 10 | /// 11 | public partial class UsingManageView : UserControl 12 | { 13 | public UsingManageView() 14 | { 15 | InitializeComponent(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/NatashaPad.Test/DumperTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | namespace NatashaPad.Test; 5 | 6 | public class DumperTest 7 | { 8 | private readonly IDumper _dumper = DefaultDumper.Instance; 9 | 10 | [Theory] 11 | [InlineData("NatashaPad")] 12 | public void StringTest(string str) 13 | { 14 | Assert.Equal(str, _dumper.Dump(str)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/NatashaPad.Test/NatashaPad.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | en 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/NatashaPad.Test/ScriptEngineTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) NatashaPad. All rights reserved. 2 | // Licensed under the Apache license. 3 | 4 | using Microsoft.Extensions.DependencyInjection; 5 | using ReferenceResolver; 6 | using WeihanLi.Extensions; 7 | 8 | namespace NatashaPad.Test; 9 | 10 | public class ScriptEngineTest : IDisposable 11 | { 12 | private readonly ITestOutputHelper _testOutputHelper; 13 | private readonly INScriptEngine _scriptEngine; 14 | private readonly ServiceProvider _serviceProvider; 15 | 16 | public ScriptEngineTest(ITestOutputHelper testOutputHelper) 17 | { 18 | _testOutputHelper = testOutputHelper; 19 | _serviceProvider = new ServiceCollection() 20 | .AddReferenceResolvers() 21 | .BuildServiceProvider(); 22 | _scriptEngine = new CSharpScriptEngine(new ReferenceResolverFactory(_serviceProvider)); 23 | } 24 | 25 | [Fact] 26 | public async Task ExecuteTest() 27 | { 28 | DumpOutHelper.OutputAction += Output; 29 | try 30 | { 31 | await _scriptEngine.Execute("\"Hello NatashaPad\".Dump();", new NScriptOptions(), TestContext.Current.CancellationToken); 32 | 33 | await _scriptEngine.Execute("Console.WriteLine(\"Hello NatashaPad\");", new NScriptOptions(), TestContext.Current.CancellationToken); 34 | } 35 | catch (NatashaException ex) 36 | { 37 | _testOutputHelper.WriteLine(ex.Diagnostics.Select(d => d.ToString()) 38 | .StringJoin(Environment.NewLine)); 39 | throw; 40 | } 41 | finally 42 | { 43 | DumpOutHelper.OutputAction -= Output; 44 | } 45 | } 46 | 47 | [Fact] 48 | public async Task ExecuteTestWithReference() 49 | { 50 | DumpOutHelper.OutputAction += Output; 51 | try 52 | { 53 | var options = new NScriptOptions(); 54 | options.References.Add(new NuGetReference("WeihanLi.Npoi", "2.4.2")); 55 | options.UsingList.Add("WeihanLi.Npoi"); 56 | await _scriptEngine.Execute("CsvHelper.GetCsvText(new[]{1,2,3}).Dump();", options, TestContext.Current.CancellationToken); 57 | } 58 | catch (NatashaException ex) 59 | { 60 | _testOutputHelper.WriteLine(ex.Diagnostics.Select(d => d.ToString()).StringJoin(Environment.NewLine)); 61 | throw; 62 | } 63 | finally 64 | { 65 | DumpOutHelper.OutputAction -= Output; 66 | } 67 | } 68 | 69 | [Fact] 70 | public async Task EvalTest() 71 | { 72 | var result = await _scriptEngine.Eval("1+1", new NScriptOptions(), TestContext.Current.CancellationToken); 73 | Assert.Equal(2, result); 74 | 75 | result = await _scriptEngine.Eval("\"Hello \" + \"NatashaPad\"", new NScriptOptions(), TestContext.Current.CancellationToken); 76 | Assert.Equal("Hello NatashaPad", result); 77 | 78 | result = await _scriptEngine.Eval("DateTime.Today.ToString(\"yyyyMMdd\")", new NScriptOptions(), TestContext.Current.CancellationToken); 79 | Assert.Equal(DateTime.Today.ToString("yyyyMMdd"), result); 80 | } 81 | 82 | [Fact] 83 | public async Task EvalTestWithReference() 84 | { 85 | var options = new NScriptOptions(); 86 | options.References.Add(new NuGetReference("WeihanLi.Npoi", "2.4.2")); 87 | options.UsingList.Add("WeihanLi.Npoi"); 88 | var result = await _scriptEngine.Eval("CsvHelper.GetCsvText(Enumerable.Range(1, 3))", options, TestContext.Current.CancellationToken); 89 | Assert.NotNull(result); 90 | } 91 | 92 | public void Dispose() 93 | { 94 | _serviceProvider.Dispose(); 95 | } 96 | 97 | private void Output(string msg) 98 | { 99 | _testOutputHelper.WriteLine(msg); 100 | } 101 | 102 | } 103 | --------------------------------------------------------------------------------