├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── CleanArchitectureCodeGenerator.sln ├── LICENSE ├── README.md ├── appveyor.yml ├── art ├── 2022.mp4 ├── code.png ├── dialog.png ├── dialog1.png ├── menu.png ├── menu1.png ├── nuget.png ├── task-list.png └── template.png ├── src ├── CleanArchitectureCodeGenerator.GeneratedMSBuildEditorConfig.editorconfig ├── CleanArchitectureCodeGenerator.csproj ├── CodeGenerator.cs ├── CodeGenerator.vsct ├── CodeGeneratorPackage.cs ├── FileNameDialog.xaml ├── FileNameDialog.xaml.cs ├── Helpers │ ├── Logger.cs │ ├── ProjectHelpers.cs │ ├── Utility.cs │ ├── VSHelpers.cs │ └── VsTheme.cs ├── Models │ ├── IntellisenseObject.cs │ ├── IntellisenseParser.cs │ ├── IntellisenseProperty.cs │ └── IntellisenseType.cs ├── NewItemTarget.cs ├── Properties │ └── AssemblyInfo.cs ├── Resources │ ├── icon.png │ └── logo.png ├── Services │ ├── GenerationService.cs │ └── IntellisenseWriter.cs ├── Templatemap.cs ├── Templates │ ├── .bowerrc.txt │ ├── .cs-interface.txt │ ├── .cs.txt │ ├── .html.txt │ ├── .json.txt │ ├── .md.txt │ ├── .razor.txt │ ├── .vb-interface.txt │ ├── .vb.txt │ ├── Caching │ │ └── .cachekey.cs.txt │ ├── Commands │ │ ├── AddEdit │ │ │ ├── .cs.txt │ │ │ └── .validator.cs.txt │ │ ├── Create │ │ │ ├── .cs.txt │ │ │ └── .validator.cs.txt │ │ ├── Delete │ │ │ ├── .cs.txt │ │ │ └── .validator.cs.txt │ │ ├── Import │ │ │ ├── .cs.txt │ │ │ └── .validator.cs.txt │ │ └── Update │ │ │ ├── .cs.txt │ │ │ └── .validator.cs.txt │ ├── DTOs │ │ └── .dto.cs.txt │ ├── EventHandlers │ │ ├── .created.cs.txt │ │ ├── .deleted.cs.txt │ │ └── .updated.cs.txt │ ├── Events │ │ ├── .createdevent.cs.txt │ │ ├── .deletedevent.cs.txt │ │ └── .updatedevent.cs.txt │ ├── Mappers │ │ └── .mapper.cs.txt │ ├── Pages │ │ ├── .create.razor.txt │ │ ├── .edit.razor.txt │ │ ├── .razor.txt │ │ ├── .view.razor.txt │ │ └── Components │ │ │ ├── .advancedsearchcomponent.razor.txt │ │ │ └── .formdialog.razor.txt │ ├── PermissionSet │ │ └── .cs.txt │ ├── Persistence │ │ └── Configurations │ │ │ └── .configuration.cs.txt │ ├── Queries │ │ ├── Export │ │ │ └── .cs.txt │ │ ├── GetAll │ │ │ └── .cs.txt │ │ ├── GetById │ │ │ └── .cs.txt │ │ └── Pagination │ │ │ └── .cs.txt │ ├── Specifications │ │ ├── AdvancedFilter.cs.txt │ │ ├── AdvancedSpecification.cs.txt │ │ └── ByIdSpecification.cs.txt │ ├── bower.json.txt │ ├── gruntfile.js.txt │ ├── gulpfile.js.txt │ └── package.json.txt ├── source.extension.cs └── source.extension.vsixmanifest └── vs-publish.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.vsct] 2 | 3 | indent_style = space 4 | indent_size = 4 5 | charset = utf-8-bom 6 | 7 | [*.cs] 8 | 9 | #Core editorconfig formatting - indentation 10 | 11 | indent_style = tab 12 | charset = utf-8-bom 13 | 14 | #Formatting - new line options 15 | 16 | csharp_new_line_before_catch = true 17 | csharp_new_line_before_else = true 18 | csharp_new_line_before_finally = true 19 | csharp_new_line_before_members_in_object_initializers = true 20 | csharp_new_line_before_open_brace = methods, control_blocks, object_collection_array_initializers, types 21 | 22 | #Formatting - organize using options 23 | 24 | dotnet_sort_system_directives_first = false 25 | 26 | #Formatting - spacing options 27 | 28 | csharp_space_after_cast = false 29 | csharp_space_after_colon_in_inheritance_clause = true 30 | csharp_space_after_keywords_in_control_flow_statements = true 31 | csharp_space_before_colon_in_inheritance_clause = true 32 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 33 | csharp_space_between_method_call_name_and_opening_parenthesis = false 34 | csharp_space_between_method_call_parameter_list_parentheses = false 35 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 36 | csharp_space_between_method_declaration_parameter_list_parentheses = false 37 | 38 | #Formatting - wrapping options 39 | 40 | csharp_preserve_single_line_blocks = true 41 | 42 | #Style - Code block preferences 43 | 44 | csharp_prefer_braces = true:suggestion 45 | 46 | #Style - expression bodied member options 47 | 48 | csharp_style_expression_bodied_methods = false:suggestion 49 | csharp_style_expression_bodied_properties = true:suggestion 50 | 51 | #Style - expression level options 52 | 53 | csharp_style_inlined_variable_declaration = true:suggestion 54 | dotnet_style_predefined_type_for_member_access = true:suggestion 55 | 56 | #Style - Expression-level preferences 57 | 58 | dotnet_style_object_initializer = true:suggestion 59 | 60 | #Style - implicit and explicit types 61 | 62 | csharp_style_var_elsewhere = false:suggestion 63 | csharp_style_var_for_built_in_types = false:suggestion 64 | csharp_style_var_when_type_is_apparent = false:suggestion 65 | 66 | #Style - language keyword and framework type options 67 | 68 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 69 | 70 | #Style - modifier options 71 | 72 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion 73 | 74 | #Style - Modifier preferences 75 | 76 | csharp_preferred_modifier_order = private,public,internal,protected,static,async,readonly,override:suggestion 77 | 78 | #Style - Pattern matching 79 | 80 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 81 | 82 | #Style - qualification options 83 | 84 | dotnet_style_qualification_for_method = false:suggestion 85 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: "Build" 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | 11 | jobs: 12 | build: 13 | outputs: 14 | version: ${{ steps.vsix_version.outputs.version-number }} 15 | name: Build 16 | runs-on: windows-2022 17 | env: 18 | Configuration: Release 19 | DeployExtension: False 20 | VsixManifestPath: src\source.extension.vsixmanifest 21 | VsixManifestSourcePath: src\source.extension.cs 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Setup .NET build dependencies 27 | uses: timheuer/bootstrap-dotnet@v1 28 | with: 29 | nuget: 'false' 30 | sdk: 'false' 31 | msbuild: 'true' 32 | 33 | - name: Increment VSIX version 34 | id: vsix_version 35 | uses: timheuer/vsix-version-stamp@v1 36 | with: 37 | manifest-file: ${{ env.VsixManifestPath }} 38 | vsix-token-source-file: ${{ env.VsixManifestSourcePath }} 39 | 40 | - name: Build 41 | run: msbuild /v:m -restore /p:OutDir=../_built 42 | 43 | #- name: Setup test 44 | # uses: darenm/Setup-VSTest@v1 45 | 46 | #- name: Test 47 | # run: vstest.console.exe _built\*test.dll 48 | 49 | - name: Upload artifact 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: CleanArchitectureCodeGenerator.vsix 53 | path: _built/**/*.vsix 54 | 55 | publish: 56 | needs: build 57 | runs-on: windows-latest 58 | 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - name: Download Package artifact 63 | uses: actions/download-artifact@v4 64 | with: 65 | name: CleanArchitectureCodeGenerator.vsix 66 | 67 | - name: Upload to Open VSIX 68 | uses: timheuer/openvsixpublish@v1 69 | with: 70 | vsix-file: CleanArchitectureCodeGenerator.vsix 71 | 72 | 73 | 74 | - name: Publish extension to Marketplace 75 | uses: cezarypiatek/VsixPublisherAction@1.0 76 | with: 77 | extension-file: CleanArchitectureCodeGenerator.vsix 78 | publish-manifest-file: 'vs-publish.json' 79 | personal-access-code: ${{ secrets.NUGET_API_KEY }} 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | .vs 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | x64/ 15 | build/ 16 | bld/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # MSTest test Results 21 | [Tt]est[Rr]esult*/ 22 | [Bb]uild[Ll]og.* 23 | 24 | #NUNIT 25 | *.VisualState.xml 26 | TestResult.xml 27 | 28 | # Build Results of an ATL Project 29 | [Dd]ebugPS/ 30 | [Rr]eleasePS/ 31 | dlldata.c 32 | 33 | *_i.c 34 | *_p.c 35 | *_i.h 36 | *.ilk 37 | *.meta 38 | *.obj 39 | *.pch 40 | *.pdb 41 | *.pgc 42 | *.pgd 43 | *.rsp 44 | *.sbr 45 | *.tlb 46 | *.tli 47 | *.tlh 48 | *.tmp 49 | *.tmp_proj 50 | *.log 51 | *.vspscc 52 | *.vssscc 53 | .builds 54 | *.pidb 55 | *.svclog 56 | *.scc 57 | 58 | # Chutzpah Test files 59 | _Chutzpah* 60 | 61 | # Visual C++ cache files 62 | ipch/ 63 | *.aps 64 | *.ncb 65 | *.opensdf 66 | *.sdf 67 | *.cachefile 68 | 69 | # Visual Studio profiler 70 | *.psess 71 | *.vsp 72 | *.vspx 73 | 74 | # TFS 2012 Local Workspace 75 | $tf/ 76 | 77 | # Guidance Automation Toolkit 78 | *.gpState 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper*/ 82 | *.[Rr]e[Ss]harper 83 | *.DotSettings.user 84 | 85 | # JustCode is a .NET coding addin-in 86 | .JustCode 87 | 88 | # TeamCity is a build add-in 89 | _TeamCity* 90 | 91 | # DotCover is a Code Coverage Tool 92 | *.dotCover 93 | 94 | # NCrunch 95 | *.ncrunch* 96 | _NCrunch_* 97 | .*crunch*.local.xml 98 | 99 | # MightyMoose 100 | *.mm.* 101 | AutoTest.Net/ 102 | 103 | # Web workbench (sass) 104 | .sass-cache/ 105 | 106 | # Installshield output folder 107 | [Ee]xpress/ 108 | 109 | # DocProject is a documentation generator add-in 110 | DocProject/buildhelp/ 111 | DocProject/Help/*.HxT 112 | DocProject/Help/*.HxC 113 | DocProject/Help/*.hhc 114 | DocProject/Help/*.hhk 115 | DocProject/Help/*.hhp 116 | DocProject/Help/Html2 117 | DocProject/Help/html 118 | 119 | # Click-Once directory 120 | publish/ 121 | 122 | # Publish Web Output 123 | *.[Pp]ublish.xml 124 | *.azurePubxml 125 | 126 | # NuGet Packages Directory 127 | packages/ 128 | ## TODO: If the tool you use requires repositories.config uncomment the next line 129 | #!packages/repositories.config 130 | 131 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 132 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) 133 | !packages/build/ 134 | 135 | # Windows Azure Build Output 136 | csx/ 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.dbproj.schemaview 151 | *.pfx 152 | *.publishsettings 153 | node_modules/ 154 | 155 | # RIA/Silverlight projects 156 | Generated_Code/ 157 | 158 | # Backup & report files from converting an old project file to a newer 159 | # Visual Studio version. Backup files are not needed, because we have git ;-) 160 | _UpgradeReport_Files/ 161 | Backup*/ 162 | UpgradeLog*.XML 163 | UpgradeLog*.htm 164 | 165 | # SQL Server files 166 | *.mdf 167 | *.ldf 168 | 169 | # Business Intelligence projects 170 | *.rdl.data 171 | *.bim.layout 172 | *.bim_*.settings 173 | 174 | # Microsoft Fakes 175 | FakesAssemblies/ 176 | 177 | # ========================= 178 | # Operating System Files 179 | # ========================= 180 | 181 | # OSX 182 | # ========================= 183 | 184 | .DS_Store 185 | .AppleDouble 186 | .LSOverride 187 | 188 | # Icon must ends with two \r. 189 | Icon 190 | 191 | # Thumbnails 192 | ._* 193 | 194 | # Files that might appear on external disk 195 | .Spotlight-V100 196 | .Trashes 197 | 198 | # Windows 199 | # ========================= 200 | 201 | # Windows image file caches 202 | Thumbs.db 203 | ehthumbs.db 204 | 205 | # Folder config file 206 | Desktop.ini 207 | 208 | # Recycle Bin used on file shares 209 | $RECYCLE.BIN/ 210 | 211 | # Windows Installer files 212 | *.cab 213 | *.msi 214 | *.msm 215 | *.msp 216 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Road map 2 | 3 | - [ ] Nothing yet... 4 | - [x] Fix for paths above project folder (#37) 5 | - [x] Fix for multi-project scenarios (#35) 6 | 7 | Features that have a checkmark are complete and available for 8 | download in the 9 | [nightly build](http://vsixgallery.com/extension/6A168388-B6A2-42F9-BA5A-B577D3CD4DB5/). 10 | 11 | # Change log 12 | 13 | These are the changes to each version that has been released 14 | on the official Visual Studio extension gallery. 15 | 16 | ## 3.5 17 | 18 | **2016-07-27** 19 | 20 | - [x] Support for solution 21 | - [x] Fixed UTF8 BOM issue 22 | 23 | ## 3.4 24 | 25 | **2016-06-10** 26 | 27 | - [x] Re-implemented the error logger 28 | - [x] Fixed end-of-line 29 | - [x] No longer add new file to SSDT projects. Requires manual include 30 | 31 | ## 3.3 32 | 33 | **2016-05-02** 34 | 35 | - [x] "VS15" support 36 | 37 | ## 3.2 38 | 39 | **2016-04-05** 40 | 41 | - [x] Fixed crash when command is not available 42 | - [x] Added culture to AssemblyInfo 43 | - [x] Using the Shell utils to open file 44 | 45 | ## 3.1 46 | 47 | **2016-03-14** 48 | 49 | - [x] Added changelog/roadmap 50 | - [x] Textbox too small with long folder name (#20) 51 | - [x] C#/VB templates to include folder names (#21) 52 | 53 | ## 3.0 54 | 55 | **2016-03-11** 56 | 57 | - [x] New templating engine 58 | - [x] Templates for C#, VB, HTML and JSON files. 59 | - [x] File name match template support for: 60 | - gulpfile.js 61 | - gruntfile.js 62 | - bower.json 63 | - package.json 64 | -------------------------------------------------------------------------------- /CleanArchitectureCodeGenerator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31606.5 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitectureCodeGenerator", "src\CleanArchitectureCodeGenerator.csproj", "{CAD947D3-06E2-4A76-8838-68115036B179}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{50C409A6-1C74-4C72-887A-3AC964BD505D}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | appveyor.yml = appveyor.yml 12 | CHANGELOG.md = CHANGELOG.md 13 | README.md = README.md 14 | vs-publish.json = vs-publish.json 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | CI|Any CPU = CI|Any CPU 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {CAD947D3-06E2-4A76-8838-68115036B179}.CI|Any CPU.ActiveCfg = CI|Any CPU 25 | {CAD947D3-06E2-4A76-8838-68115036B179}.CI|Any CPU.Build.0 = CI|Any CPU 26 | {CAD947D3-06E2-4A76-8838-68115036B179}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {CAD947D3-06E2-4A76-8838-68115036B179}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {CAD947D3-06E2-4A76-8838-68115036B179}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {CAD947D3-06E2-4A76-8838-68115036B179}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {D67A511C-18C8-4D62-84F3-B5B9DAF2A310} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) CleanArchitectureCodeGenerator 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Speed up your Clean Architecture development in Visual Studio! 2 | 3 | [![Build](https://github.com/neozhu/CleanArchitectureCodeGenerator/actions/workflows/build.yml/badge.svg)](https://github.com/neozhu/CleanArchitectureCodeGenerator/actions/workflows/build.yml) 4 | ![Visual Studio Marketplace Version (including pre-releases)](https://img.shields.io/visual-studio-marketplace/v/neozhu.247365) 5 | ![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/neozhu.247365?label=Downloads) 6 | 7 | [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) is a software design philosophy that separates the elements of a design into ring levels. The main rule of Clean Architecture is that code dependencies can only move from the outer levels inward. Code on the inner layers can have no knowledge of functions on the outer layers. This extension helps you generate code that adheres to this principle. 8 | 9 | ## Download the extension 10 | 11 | [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=neozhu.247365) 12 | 13 | [Open VSIX Gallery](https://www.vsixgallery.com/extension/CleanArchitecture_CodeGenerator_BlazorApp) 14 | 15 | ![image](https://github.com/neozhu/CleanArchitectureCodeGenerator/assets/1549611/fbcce4ee-f14a-47c5-8dd3-37503f4ec52e) 16 | ![image](https://github.com/neozhu/CleanArchitectureCodeGenerator/assets/1549611/72b3800a-58e5-4853-ba7e-f1d7a46286be) 17 | 18 | 19 | ### How to start 20 | 21 |
22 | ------------------------------------------------- 23 | 24 | A Visual Studio extension for easily create application features code to clean architecture project. Simply hit Shift+F2 to create an empty file in the 25 | selected folder or in the same folder as the selected file. 26 | 27 | See the [changelog](CHANGELOG.md) for updates and roadmap. 28 | 29 | 30 | ### Features 31 | 32 | This extension helps you rapidly scaffold components for your Clean Architecture project: 33 | 34 | #### Core Application Layer Components 35 | Quickly generate essential C# classes for your application layer, including: 36 | 37 | * **Commands and Validators:** For operations that change the state of your application (Add/Edit, Create, Delete, Update, Import). 38 | * `{nameofPlural}/Commands/AddEdit/AddEdit{name}Command.cs` 39 | * `{nameofPlural}/Commands/AddEdit/AddEdit{name}CommandValidator.cs` 40 | * `{nameofPlural}/Commands/Create/Create{name}Command.cs` 41 | * `{nameofPlural}/Commands/Create/Create{name}CommandValidator.cs` 42 | * `{nameofPlural}/Commands/Delete/Delete{name}Command.cs` 43 | * `{nameofPlural}/Commands/Delete/Delete{name}CommandValidator.cs` 44 | * `{nameofPlural}/Commands/Update/Update{name}Command.cs` 45 | * `{nameofPlural}/Commands/Update/Update{name}CommandValidator.cs` 46 | * `{nameofPlural}/Commands/Import/Import{name}Command.cs` 47 | * `{nameofPlural}/Commands/Import/Import{name}CommandValidator.cs` 48 | * **Data Transfer Objects (DTOs):** To define how data is sent and received. 49 | * `{nameofPlural}/DTOs/{name}Dto.cs` 50 | * **Event Handlers:** For domain events (Created, Updated, Deleted). 51 | * `{nameofPlural}/EventHandlers/{name}CreatedEventHandler.cs` 52 | * `{nameofPlural}/EventHandlers/{name}UpdatedEventHandler.cs` 53 | * `{nameofPlural}/EventHandlers/{name}DeletedEventHandler.cs` 54 | * **Queries:** For retrieving data (Export, GetAll, Pagination). 55 | * `{nameofPlural}/Queries/Export/Export{nameofPlural}Query.cs` 56 | * `{nameofPlural}/Queries/GetAll/GetAll{nameofPlural}Query.cs` 57 | * `{nameofPlural}/Queries/Pagination/{nameofPlural}PaginationQuery.cs` 58 | 59 | #### TypeScript Definition Generation 60 | Automatically generate TypeScript definition files (`.d.ts`) for your Data Transfer Objects (DTOs), enabling type-safe interaction with your frontend applications. 61 | * `{nameofPlural}/DTOs/{name}Dto.d.ts` 62 | 63 | ### CleanArchitecture for Blazor Server Application project 64 | Please use this in collaboration with this project. 65 | 66 | Github :[https://github.com/neozhu/RazorPageCleanArchitecture](https://github.com/neozhu/CleanArchitectureWithBlazorServer) 67 | ![Clean Architecture With Blazor Server](https://raw.githubusercontent.com/neozhu/CleanArchitectureWithBlazorServer/main/doc/page.png) 68 | 69 | ### How to use 70 | 71 | A new button is added to the context menu in Solution Explorer. 72 | 73 | ![Add new file dialog](art/menu1.png) 74 | 75 | You can either click that button or use the keybord shortcut **Shift+F2**. 76 | 77 | Select Entity Name from Domain Project 78 | 79 | ![Add new file dialog](art/dialog1.png) 80 | 81 | ### Create folders and namespace 82 | 83 | Create additional folders for your file by using forward-slash to 84 | specify the structure. 85 | 86 | For example, by typing **scripts/test.js** in the dialog, the 87 | folder **scripts** is created if it doesn't exist and the file 88 | **test.js** is then placed into it. 89 | 90 | ### Generate sourcecode 91 | ![Source code for application features](art/code.png) 92 | 93 | ### Generate to-do list 94 | ![to-do list](art/task-list.png) 95 | 96 | ### code templates 97 | You can modify these templates according to your own projects 98 | ![tempaltes](art/template.png) 99 | 100 | ## Contribute 101 | Check out the [contribution guidelines](.github/CONTRIBUTING.md) 102 | if you want to contribute to this project. 103 | 104 | For cloning and building this project yourself, make sure 105 | to install the 106 | [Extensibility Tools 2015](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.ExtensibilityTools) 107 | extension for Visual Studio which enables some features 108 | used by this project. 109 | 110 | ## **License** 111 | 112 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 113 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | 3 | install: 4 | - ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex 5 | 6 | before_build: 7 | - ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion 8 | - ps: Vsix-TokenReplacement src\source.extension.cs 'Version = "([0-9\\.]+)"' 'Version = "{version}"' 9 | 10 | build_script: 11 | - nuget restore -Verbosity quiet 12 | - msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m 13 | 14 | after_test: 15 | - ps: Vsix-PushArtifacts | Vsix-PublishToGallery 16 | 17 | before_deploy: 18 | - ps: Vsix-CreateChocolatyPackage -packageId webessentials2019 19 | 20 | deploy: 21 | - provider: Environment 22 | name: Chocolatey 23 | on: 24 | branch: master 25 | appveyor_repo_commit_message_extended: /\[release\]/ -------------------------------------------------------------------------------- /art/2022.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/2022.mp4 -------------------------------------------------------------------------------- /art/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/code.png -------------------------------------------------------------------------------- /art/dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/dialog.png -------------------------------------------------------------------------------- /art/dialog1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/dialog1.png -------------------------------------------------------------------------------- /art/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/menu.png -------------------------------------------------------------------------------- /art/menu1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/menu1.png -------------------------------------------------------------------------------- /art/nuget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/nuget.png -------------------------------------------------------------------------------- /art/task-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/task-list.png -------------------------------------------------------------------------------- /art/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neozhu/CleanArchitectureCodeGenerator/e776c86717a4a83fb08bd7e0bc9fdca9fc94c3c1/art/template.png -------------------------------------------------------------------------------- /src/CleanArchitectureCodeGenerator.GeneratedMSBuildEditorConfig.editorconfig: -------------------------------------------------------------------------------- 1 | is_global = true 2 | build_property.TargetFramework = 3 | build_property.TargetPlatformMinVersion = 4 | build_property.UsingMicrosoftNETSdkWeb = 5 | build_property.ProjectTypeGuids = {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 6 | build_property.PublishSingleFile = 7 | build_property.IncludeAllContentForSelfExtract = 8 | build_property._SupportedPlatformList = 9 | -------------------------------------------------------------------------------- /src/CleanArchitectureCodeGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(VisualStudioVersion) 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | Program 7 | $(DevEnvDir)\devenv.exe 8 | /rootsuffix Exp 9 | true 10 | publish\ 11 | true 12 | Disk 13 | false 14 | Foreground 15 | 7 16 | Days 17 | false 18 | false 19 | true 20 | 0 21 | 1.0.0.%2a 22 | false 23 | false 24 | true 25 | 26 | 27 | 28 | 29 | 30 | bin\CI\ 31 | TRACE 32 | true 33 | pdbonly 34 | AnyCPU 35 | false 36 | prompt 37 | MinimumRecommendedRules.ruleset 38 | False 39 | 40 | 41 | 42 | Debug 43 | AnyCPU 44 | 2.0 45 | {CAD947D3-06E2-4A76-8838-68115036B179} 46 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 47 | Library 48 | Properties 49 | CleanArchitecture.CodeGenerator 50 | CleanArchitectureCodeGenerator 51 | v4.7.2 52 | 53 | 54 | true 55 | full 56 | false 57 | bin\Debug\ 58 | DEBUG;TRACE 59 | prompt 60 | 4 61 | 62 | 63 | pdbonly 64 | true 65 | bin\Release\ 66 | TRACE 67 | prompt 68 | 4 69 | false 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | True 88 | True 89 | CodeGenerator.vsct 90 | 91 | 92 | FileNameDialog.xaml 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | True 110 | True 111 | source.extension.vsixmanifest 112 | 113 | 114 | 115 | true 116 | 117 | 118 | true 119 | 120 | 121 | true 122 | 123 | 124 | true 125 | 126 | 127 | true 128 | 129 | 130 | true 131 | 132 | 133 | true 134 | 135 | 136 | true 137 | 138 | 139 | true 140 | 141 | 142 | true 143 | 144 | 145 | true 146 | 147 | 148 | true 149 | 150 | 151 | true 152 | 153 | 154 | true 155 | 156 | 157 | true 158 | 159 | 160 | true 161 | 162 | 163 | true 164 | 165 | 166 | true 167 | 168 | 169 | true 170 | 171 | 172 | true 173 | 174 | 175 | true 176 | 177 | 178 | true 179 | 180 | 181 | true 182 | 183 | 184 | true 185 | 186 | 187 | true 188 | 189 | 190 | true 191 | 192 | 193 | true 194 | 195 | 196 | true 197 | 198 | 199 | true 200 | 201 | 202 | true 203 | 204 | 205 | true 206 | 207 | 208 | true 209 | 210 | 211 | true 212 | 213 | 214 | true 215 | 216 | 217 | true 218 | 219 | 220 | true 221 | 222 | 223 | true 224 | 225 | 226 | true 227 | 228 | 229 | true 230 | 231 | 232 | true 233 | 234 | 235 | true 236 | 237 | 238 | true 239 | 240 | 241 | true 242 | 243 | 244 | true 245 | 246 | 247 | 248 | 249 | 250 | Resources\LICENSE 251 | true 252 | 253 | 254 | Designer 255 | VsixManifestGenerator 256 | source.extension.cs 257 | 258 | 259 | true 260 | 261 | 262 | true 263 | 264 | 265 | 266 | 267 | Menus.ctmenu 268 | Designer 269 | VsctGenerator 270 | CodeGenerator.cs 271 | 272 | 273 | 274 | 275 | true 276 | 277 | 278 | 279 | 280 | Designer 281 | MSBuild:Compile 282 | 283 | 284 | 285 | 286 | False 287 | Microsoft .NET Framework 4.5 %28x86 and x64%29 288 | true 289 | 290 | 291 | False 292 | .NET Framework 3.5 SP1 293 | false 294 | 295 | 296 | 297 | 298 | 6.1.0 299 | 300 | 301 | 17.13.40008 302 | compile; build; native; contentfiles; analyzers; buildtransitive 303 | 304 | 305 | 17.8.8 306 | 307 | 308 | 17.13.2126 309 | runtime; build; native; contentfiles; analyzers; buildtransitive 310 | all 311 | 312 | 313 | 9.0.3 314 | 315 | 316 | 9.0.3 317 | 318 | 319 | 320 | 321 | true 322 | 323 | 324 | 325 | 332 | -------------------------------------------------------------------------------- /src/CodeGenerator.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by VSIX Synchronizer 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace CleanArchitecture.CodeGenerator 7 | { 8 | using System; 9 | 10 | /// 11 | /// Helper class that exposes all GUIDs used across VS Package. 12 | /// 13 | internal sealed partial class PackageGuids 14 | { 15 | public const string guidCodeGeneratorPkgString = "da3d4428-7855-4b22-a0e6-a7b9a6c7aa5e"; 16 | public static Guid guidCodeGeneratorPkg = new Guid(guidCodeGeneratorPkgString); 17 | 18 | public const string guidCodeGeneratorCmdSetString = "4c7a4f97-2988-45ec-af7c-8984fc72c5e1"; 19 | public static Guid guidCodeGeneratorCmdSet = new Guid(guidCodeGeneratorCmdSetString); 20 | } 21 | /// 22 | /// Helper class that encapsulates all CommandIDs uses across VS Package. 23 | /// 24 | internal sealed partial class PackageIds 25 | { 26 | public const int cmdidMyCommand = 0x0100; 27 | } 28 | } -------------------------------------------------------------------------------- /src/CodeGenerator.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/CodeGeneratorPackage.cs: -------------------------------------------------------------------------------- 1 | using CleanArchitecture.CodeGenerator.Helpers; 2 | using CleanArchitecture.CodeGenerator.Models; 3 | using EnvDTE; 4 | using EnvDTE80; 5 | using Microsoft; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Text; 9 | using Microsoft.VisualStudio.Threading; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.ComponentModel.Design; 13 | using System.IO; 14 | using System.Linq; 15 | using System.Runtime.InteropServices; 16 | using System.Text; 17 | using System.Text.RegularExpressions; 18 | using System.Threading; 19 | using System.Threading.Tasks; 20 | using System.Windows; 21 | 22 | namespace CleanArchitecture.CodeGenerator 23 | { 24 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 25 | [InstalledProductRegistration("#110", "#112", Vsix.Version, IconResourceID = 400)] 26 | [ProvideMenuResource("Menus.ctmenu", 1)] 27 | [Guid(PackageGuids.guidCodeGeneratorPkgString)] 28 | public sealed class CodeGeneratorPackage : AsyncPackage 29 | { 30 | public const string DOMAINPROJECT = "Domain"; 31 | public const string UIPROJECT = "Server.UI"; 32 | public const string INFRASTRUCTUREPROJECT = "Infrastructure"; 33 | public const string APPLICATIONPROJECT = "Application"; 34 | 35 | private const string _solutionItemsProjectName = "Solution Items"; 36 | private static readonly Regex _reservedFileNamePattern = new Regex($@"(?i)^(PRN|AUX|NUL|CON|COM\d|LPT\d)(\.|$)"); 37 | private static readonly HashSet _invalidFileNameChars = new HashSet(Path.GetInvalidFileNameChars()); 38 | 39 | public static DTE2 _dte; 40 | 41 | protected async override Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 42 | { 43 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 44 | 45 | _dte = await GetServiceAsync(typeof(DTE)) as DTE2; 46 | Assumes.Present(_dte); 47 | 48 | Logger.Initialize(this, Vsix.Name); 49 | 50 | if (await GetServiceAsync(typeof(IMenuCommandService)) is OleMenuCommandService mcs) 51 | { 52 | CommandID menuCommandID = new CommandID(PackageGuids.guidCodeGeneratorCmdSet, PackageIds.cmdidMyCommand); 53 | OleMenuCommand menuItem = new OleMenuCommand(ExecuteAsync, menuCommandID); 54 | mcs.AddCommand(menuItem); 55 | } 56 | } 57 | 58 | private void ExecuteAsync(object sender, EventArgs e) 59 | { 60 | NewItemTarget target = NewItemTarget.Create(_dte); 61 | NewItemTarget domain= NewItemTarget.Create(_dte,"Domain"); 62 | NewItemTarget infrastructure = NewItemTarget.Create(_dte, "Infrastructure"); 63 | NewItemTarget ui = NewItemTarget.Create(_dte, "Server.UI"); 64 | var includes = new string[] { "IEntity", "BaseEntity", "BaseAuditableEntity", "BaseAuditableSoftDeleteEntity", "AuditTrail", "OwnerPropertyEntity","KeyValue" }; 65 | 66 | var testlist = ProjectHelpers.GetEntities(domain.Project); 67 | var objectlist = ProjectHelpers.GetEntities(domain.Project) 68 | .Where(x => x.IsEnum || (includes.Contains(x.BaseName) && !includes.Contains(x.Name))); 69 | var entities = objectlist.Where(x=>x.IsEnum==false).Select(x=>x.Name).ToArray(); 70 | if (target == null && target.Project.Name == APPLICATIONPROJECT) 71 | { 72 | MessageBox.Show( 73 | "Unable to determine the location for creating the new file. Please select a folder within the Application Project in the Explorer and try again.", 74 | Vsix.Name, 75 | MessageBoxButton.OK, 76 | MessageBoxImage.Error); 77 | return; 78 | } 79 | 80 | string input = PromptForFileName(target.Directory,entities).TrimStart('/', '\\').Replace("/", "\\"); 81 | 82 | if (string.IsNullOrEmpty(input)) 83 | { 84 | return; 85 | } 86 | 87 | string[] parsedInputs = GetParsedInput(input); 88 | 89 | foreach (string inputname in parsedInputs) 90 | { 91 | try 92 | { 93 | var name = Path.GetFileNameWithoutExtension(inputname); 94 | var nameofPlural = ProjectHelpers.Pluralize(name); 95 | var objectClass = objectlist.Where(x => x.Name == name).First(); 96 | var events = new List() { 97 | $"Events/{name}CreatedEvent.cs", 98 | $"Events/{name}DeletedEvent.cs", 99 | $"Events/{name}UpdatedEvent.cs", 100 | }; 101 | foreach (var item in events) 102 | { 103 | AddItemAsync(objectClass,item, name, domain, objectlist).Forget(); 104 | } 105 | var configurations = new List() { 106 | $"Persistence/Configurations/{name}Configuration.cs", 107 | $"PermissionSet/{nameofPlural}.cs" 108 | }; 109 | foreach (var item in configurations) 110 | { 111 | AddItemAsync(objectClass, item, name, infrastructure, objectlist).Forget(); 112 | } 113 | 114 | var list = new List() 115 | { 116 | $"{nameofPlural}/Commands/AddEdit/AddEdit{name}Command.cs", 117 | $"{nameofPlural}/Commands/AddEdit/AddEdit{name}CommandValidator.cs", 118 | $"{nameofPlural}/Commands/Create/Create{name}Command.cs", 119 | $"{nameofPlural}/Commands/Create/Create{name}CommandValidator.cs", 120 | $"{nameofPlural}/Commands/Delete/Delete{name}Command.cs", 121 | $"{nameofPlural}/Commands/Delete/Delete{name}CommandValidator.cs", 122 | $"{nameofPlural}/Commands/Update/Update{name}Command.cs", 123 | $"{nameofPlural}/Commands/Update/Update{name}CommandValidator.cs", 124 | $"{nameofPlural}/Commands/Import/Import{nameofPlural}Command.cs", 125 | $"{nameofPlural}/Commands/Import/Import{nameofPlural}CommandValidator.cs", 126 | $"{nameofPlural}/Caching/{name}CacheKey.cs", 127 | $"{nameofPlural}/DTOs/{name}Dto.cs", 128 | /*$"{nameofPlural}/Mappers/{name}Mapper.cs",*/ 129 | $"{nameofPlural}/EventHandlers/{name}CreatedEventHandler.cs", 130 | $"{nameofPlural}/EventHandlers/{name}UpdatedEventHandler.cs", 131 | $"{nameofPlural}/EventHandlers/{name}DeletedEventHandler.cs", 132 | $"{nameofPlural}/Specifications/{name}AdvancedFilter.cs", 133 | $"{nameofPlural}/Specifications/{name}AdvancedSpecification.cs", 134 | $"{nameofPlural}/Specifications/{name}ByIdSpecification.cs", 135 | $"{nameofPlural}/Queries/Export/Export{nameofPlural}Query.cs", 136 | $"{nameofPlural}/Queries/GetAll/GetAll{nameofPlural}Query.cs", 137 | $"{nameofPlural}/Queries/GetById/Get{name}ByIdQuery.cs", 138 | $"{nameofPlural}/Queries/Pagination/{nameofPlural}PaginationQuery.cs", 139 | 140 | }; 141 | foreach (var item in list) 142 | { 143 | AddItemAsync(objectClass,item, name, target, objectlist).Forget(); 144 | } 145 | 146 | var pages = new List() 147 | { 148 | $"Pages/{nameofPlural}/Create{name}.razor", 149 | $"Pages/{nameofPlural}/Edit{name}.razor", 150 | $"Pages/{nameofPlural}/View{name}.razor", 151 | $"Pages/{nameofPlural}/{nameofPlural}.razor", 152 | $"Pages/{nameofPlural}/Components/{name}FormDialog.razor", 153 | $"Pages/{nameofPlural}/Components/{nameofPlural}AdvancedSearchComponent.razor" 154 | }; 155 | foreach (var item in pages) 156 | { 157 | AddItemAsync(objectClass,item, name, ui, objectlist).Forget(); 158 | } 159 | 160 | } 161 | catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) 162 | { 163 | Logger.Log(ex); 164 | MessageBox.Show( 165 | $"Error creating file '{inputname}':{Environment.NewLine}{ex.Message}", 166 | Vsix.Name, 167 | MessageBoxButton.OK, 168 | MessageBoxImage.Error); 169 | } 170 | } 171 | } 172 | 173 | private async Task AddItemAsync(IntellisenseObject classObject, string name,string itemname, NewItemTarget target,IEnumerable objectlist=null) 174 | { 175 | // The naming rules that apply to files created on disk also apply to virtual solution folders, 176 | // so regardless of what type of item we are creating, we need to validate the name. 177 | ValidatePath(name); 178 | 179 | if (name.EndsWith("\\", StringComparison.Ordinal)) 180 | { 181 | if (target.IsSolutionOrSolutionFolder) 182 | { 183 | GetOrAddSolutionFolder(name, target); 184 | } 185 | else 186 | { 187 | 188 | AddProjectFolder(name, target); 189 | } 190 | } 191 | else 192 | { 193 | await AddFileAsync(classObject,name, itemname, target, objectlist); 194 | } 195 | } 196 | 197 | private void ValidatePath(string path) 198 | { 199 | do 200 | { 201 | string name = Path.GetFileName(path); 202 | 203 | if (_reservedFileNamePattern.IsMatch(name)) 204 | { 205 | throw new InvalidOperationException($"The name '{name}' is a system reserved name."); 206 | } 207 | 208 | if (name.Any(c => _invalidFileNameChars.Contains(c))) 209 | { 210 | throw new InvalidOperationException($"The name '{name}' contains invalid characters."); 211 | } 212 | 213 | path = Path.GetDirectoryName(path); 214 | } while (!string.IsNullOrEmpty(path)); 215 | } 216 | 217 | private async Task AddFileAsync(IntellisenseObject classObject, string name,string itemname, NewItemTarget target,IEnumerable objectlist=null) 218 | { 219 | await JoinableTaskFactory.SwitchToMainThreadAsync(); 220 | FileInfo file; 221 | 222 | // If the file is being added to a solution folder, but that 223 | // solution folder doesn't have a corresponding directory on 224 | // disk, then write the file to the root of the solution instead. 225 | if (target.IsSolutionFolder && !Directory.Exists(target.Directory)) 226 | { 227 | file = new FileInfo(Path.Combine(Path.GetDirectoryName(_dte.Solution.FullName), Path.GetFileName(name))); 228 | } 229 | else 230 | { 231 | file = new FileInfo(Path.Combine(target.Directory, name)); 232 | } 233 | 234 | // Make sure the directory exists before we create the file. Don't use 235 | // `PackageUtilities.EnsureOutputPath()` because it can silently fail. 236 | Directory.CreateDirectory(file.DirectoryName); 237 | 238 | if (!file.Exists) 239 | { 240 | Project project; 241 | 242 | if (target.IsSolutionOrSolutionFolder) 243 | { 244 | project = GetOrAddSolutionFolder(Path.GetDirectoryName(name), target); 245 | } 246 | else 247 | { 248 | project = target.Project; 249 | } 250 | 251 | int position = await WriteFileAsync(project, classObject, file.FullName, itemname, target.Directory, objectlist); 252 | if (target.ProjectItem != null && target.ProjectItem.IsKind(Constants.vsProjectItemKindVirtualFolder)) 253 | { 254 | target.ProjectItem.ProjectItems.AddFromFile(file.FullName); 255 | } 256 | else 257 | { 258 | project.AddFileToProject(file); 259 | } 260 | 261 | VsShellUtilities.OpenDocument(this, file.FullName); 262 | 263 | // Move cursor into position. 264 | if (position > 0) 265 | { 266 | Microsoft.VisualStudio.Text.Editor.IWpfTextView view = ProjectHelpers.GetCurentTextView(); 267 | 268 | if (view != null) 269 | { 270 | view.Caret.MoveTo(new SnapshotPoint(view.TextBuffer.CurrentSnapshot, position)); 271 | } 272 | } 273 | 274 | ExecuteCommandIfAvailable("SolutionExplorer.SyncWithActiveDocument"); 275 | _dte.ActiveDocument.Activate(); 276 | } 277 | else 278 | { 279 | //MessageBox.Show($"The file '{file}' already exists.", Vsix.Name, MessageBoxButton.OK, MessageBoxImage.Information); 280 | Console.WriteLine($"The file '{file}' already exists."); 281 | } 282 | } 283 | 284 | private static async Task WriteFileAsync(Project project, IntellisenseObject classObject, string file,string itemname,string selectFolder,IEnumerable objectlist=null) 285 | { 286 | string template = await TemplateMap.GetTemplateFilePathAsync(project, classObject,file, itemname, selectFolder, objectlist); 287 | 288 | if (!string.IsNullOrEmpty(template)) 289 | { 290 | int index = template.IndexOf('$'); 291 | 292 | if (index > -1) 293 | { 294 | //template = template.Remove(index, 1); 295 | } 296 | 297 | await WriteToDiskAsync(file, template); 298 | return index; 299 | } 300 | 301 | await WriteToDiskAsync(file, string.Empty); 302 | 303 | return 0; 304 | } 305 | 306 | private static async Task WriteToDiskAsync(string file, string content) 307 | { 308 | using (StreamWriter writer = new StreamWriter(file, false, GetFileEncoding(file))) 309 | { 310 | await writer.WriteAsync(content); 311 | } 312 | } 313 | 314 | private static Encoding GetFileEncoding(string file) 315 | { 316 | string[] noBom = { ".cmd", ".bat", ".json" }; 317 | string ext = Path.GetExtension(file).ToLowerInvariant(); 318 | 319 | if (noBom.Contains(ext)) 320 | { 321 | return new UTF8Encoding(false); 322 | } 323 | 324 | return new UTF8Encoding(true); 325 | } 326 | 327 | private Project GetOrAddSolutionFolder(string name, NewItemTarget target) 328 | { 329 | if (target.IsSolution && string.IsNullOrEmpty(name)) 330 | { 331 | // An empty solution folder name means we are not creating any solution 332 | // folders for that item, and the file we are adding is intended to be 333 | // added to the solution. Files cannot be added directly to the solution, 334 | // so there is a "Solution Items" folder that they are added to. 335 | return _dte.Solution.FindSolutionFolder(_solutionItemsProjectName) 336 | ?? ((Solution2)_dte.Solution).AddSolutionFolder(_solutionItemsProjectName); 337 | } 338 | 339 | // Even though solution folders are always virtual, if the target directory exists, 340 | // then we will also create the new directory on disk. This ensures that any files 341 | // that are added to this folder will end up in the corresponding physical directory. 342 | if (Directory.Exists(target.Directory)) 343 | { 344 | // Don't use `PackageUtilities.EnsureOutputPath()` because it can silently fail. 345 | Directory.CreateDirectory(Path.Combine(target.Directory, name)); 346 | } 347 | 348 | Project parent = target.Project; 349 | 350 | foreach (string segment in SplitPath(name)) 351 | { 352 | // If we don't have a parent project yet, 353 | // then this folder is added to the solution. 354 | if (parent == null) 355 | { 356 | parent = _dte.Solution.FindSolutionFolder(segment) ?? ((Solution2)_dte.Solution).AddSolutionFolder(segment); 357 | } 358 | else 359 | { 360 | parent = parent.FindSolutionFolder(segment) ?? ((SolutionFolder)parent.Object).AddSolutionFolder(segment); 361 | } 362 | } 363 | 364 | return parent; 365 | } 366 | 367 | private void AddProjectFolder(string name, NewItemTarget target) 368 | { 369 | ThreadHelper.ThrowIfNotOnUIThread(); 370 | 371 | // Make sure the directory exists before we add it to the project. Don't 372 | // use `PackageUtilities.EnsureOutputPath()` because it can silently fail. 373 | Directory.CreateDirectory(Path.Combine(target.Directory, name)); 374 | 375 | // We can't just add the final directory to the project because that will 376 | // only add the final segment rather than adding each segment in the path. 377 | // Split the name into segments and add each folder individually. 378 | ProjectItems items = target.ProjectItem?.ProjectItems ?? target.Project.ProjectItems; 379 | string parentDirectory = target.Directory; 380 | 381 | foreach (string segment in SplitPath(name)) 382 | { 383 | parentDirectory = Path.Combine(parentDirectory, segment); 384 | 385 | // Look for an existing folder in case it's already in the project. 386 | ProjectItem folder = items 387 | .OfType() 388 | .Where(item => segment.Equals(item.Name, StringComparison.OrdinalIgnoreCase)) 389 | .Where(item => item.IsKind(Constants.vsProjectItemKindPhysicalFolder, Constants.vsProjectItemKindVirtualFolder)) 390 | .FirstOrDefault(); 391 | 392 | if (folder == null) 393 | { 394 | folder = items.AddFromDirectory(parentDirectory); 395 | } 396 | 397 | items = folder.ProjectItems; 398 | } 399 | } 400 | 401 | private static string[] SplitPath(string path) 402 | { 403 | return path.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); 404 | } 405 | 406 | private static string[] GetParsedInput(string input) 407 | { 408 | // var tests = new string[] { "file1.txt", "file1.txt, file2.txt", ".ignore", ".ignore.(old,new)", "license", "folder/", 409 | // "folder\\", "folder\\file.txt", "folder/.thing", "page.aspx.cs", "widget-1.(html,js)", "pages\\home.(aspx, aspx.cs)", 410 | // "home.(html,js), about.(html,js,css)", "backup.2016.(old, new)", "file.(txt,txt,,)", "file_@#d+|%.3-2...3^&.txt" }; 411 | Regex pattern = new Regex(@"[,]?([^(,]*)([\.\/\\]?)[(]?((?<=[^(])[^,]*|[^)]+)[)]?"); 412 | List results = new List(); 413 | Match match = pattern.Match(input); 414 | 415 | while (match.Success) 416 | { 417 | // Always 4 matches w. Group[3] being the extension, extension list, folder terminator ("/" or "\"), or empty string 418 | string path = match.Groups[1].Value.Trim() + match.Groups[2].Value; 419 | string[] extensions = match.Groups[3].Value.Split(','); 420 | 421 | foreach (string ext in extensions) 422 | { 423 | string value = path + ext.Trim(); 424 | 425 | // ensure "file.(txt,,txt)" or "file.txt,,file.txt,File.TXT" returns as just ["file.txt"] 426 | if (value != "" && !value.EndsWith(".", StringComparison.Ordinal) && !results.Contains(value, StringComparer.OrdinalIgnoreCase)) 427 | { 428 | results.Add(value); 429 | } 430 | } 431 | match = match.NextMatch(); 432 | } 433 | return results.ToArray(); 434 | } 435 | 436 | private string PromptForFileName(string folder,string[] entities) 437 | { 438 | DirectoryInfo dir = new DirectoryInfo(folder); 439 | FileNameDialog dialog = new FileNameDialog(dir.Name, entities); 440 | 441 | //IntPtr hwnd = new IntPtr(_dte.MainWindow.HWnd); 442 | //System.Windows.Window window = (System.Windows.Window)HwndSource.FromHwnd(hwnd).RootVisual; 443 | dialog.Owner = Application.Current.MainWindow; 444 | 445 | bool? result = dialog.ShowDialog(); 446 | return (result.HasValue && result.Value) ? dialog.Input : string.Empty; 447 | } 448 | 449 | private void ExecuteCommandIfAvailable(string commandName) 450 | { 451 | ThreadHelper.ThrowIfNotOnUIThread(); 452 | Command command; 453 | 454 | try 455 | { 456 | command = _dte.Commands.Item(commandName); 457 | } 458 | catch (ArgumentException) 459 | { 460 | // The command does not exist, so we can't execute it. 461 | return; 462 | } 463 | 464 | if (command.IsAvailable) 465 | { 466 | _dte.ExecuteCommand(commandName); 467 | } 468 | } 469 | } 470 | } -------------------------------------------------------------------------------- /src/FileNameDialog.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |