├── icon.png ├── videos ├── comment.gif ├── addProject.PNG ├── cliplistcom.gif ├── clippropdic.gif ├── classInterface.PNG ├── createproject.PNG └── clipvarclassfunc.gif ├── media ├── addProjectIcon.png ├── reset.css ├── sdks.txt ├── index.html ├── vscode.css ├── addProject.js └── main.js ├── models ├── class6.mdl ├── interface6.mdl ├── record6.mdl ├── struct6.mdl ├── class.mdl ├── record.mdl ├── struct.mdl └── interface.mdl ├── .vscodeignore ├── .vscode ├── extensions.json ├── tasks.json ├── launch.json └── settings.json ├── .gitignore ├── src ├── resource │ ├── createProjectWebView │ │ ├── GetNonce.ts │ │ └── CreateProject.ts │ ├── smartComments │ │ ├── SmartComments.ts │ │ └── Parser.ts │ ├── addProjectToSolution │ │ ├── AddProjectToSolution.ts │ │ └── Panel.ts │ ├── todoList │ │ └── icon.svg │ └── contextualMenu │ │ └── ContextualMenu.ts ├── extension.ts ├── utils │ ├── sdk.provider.ts │ └── terminal-cmd.provider.ts └── CommandRegister.ts ├── tsconfig.json ├── .eslintrc.json ├── LICENSE.md ├── snippets ├── designpattern.json ├── documentxml.json └── general.json ├── CHANGELOG.md ├── package.json └── README.md /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/icon.png -------------------------------------------------------------------------------- /videos/comment.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/comment.gif -------------------------------------------------------------------------------- /videos/addProject.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/addProject.PNG -------------------------------------------------------------------------------- /videos/cliplistcom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/cliplistcom.gif -------------------------------------------------------------------------------- /videos/clippropdic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/clippropdic.gif -------------------------------------------------------------------------------- /media/addProjectIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/media/addProjectIcon.png -------------------------------------------------------------------------------- /videos/classInterface.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/classInterface.PNG -------------------------------------------------------------------------------- /videos/createproject.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/createproject.PNG -------------------------------------------------------------------------------- /videos/clipvarclassfunc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsaz/csharp-snippet-productivity/HEAD/videos/clipvarclassfunc.gif -------------------------------------------------------------------------------- /models/class6.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace}; 4 | 5 | public class ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | -------------------------------------------------------------------------------- /models/interface6.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace}; 4 | 5 | public interface ${fileName} 6 | { 7 | ${cursor} 8 | } -------------------------------------------------------------------------------- /models/record6.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace}; 4 | 5 | public record ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | -------------------------------------------------------------------------------- /models/struct6.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace}; 4 | 5 | public struct ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | -------------------------------------------------------------------------------- /models/class.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace} 4 | { 5 | public class ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | } -------------------------------------------------------------------------------- /models/record.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace} 4 | { 5 | public record ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | } -------------------------------------------------------------------------------- /models/struct.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace} 4 | { 5 | public struct ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | } -------------------------------------------------------------------------------- /models/interface.mdl: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ${namespace} 4 | { 5 | public interface ${fileName} 6 | { 7 | ${cursor} 8 | } 9 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .eslintrc.json 2 | .vscode/** 3 | .vscode-test/** 4 | out/test/** 5 | test/** 6 | src/** 7 | .gitignore 8 | tsconfig.json 9 | tslint.json 10 | vsc-extension-quickstart.md 11 | *.yml -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode/** 3 | .vscode-test/** 4 | .gitignore 5 | vsc-extension-quickstart.md 6 | out/test/** 7 | /out/ 8 | test/** 9 | *.vsix 10 | .yarnrc 11 | **/tsconfig.json 12 | **/.eslintrc.json 13 | **/*.map 14 | 15 | # Ignore lock files 16 | yarn.lock 17 | package-lock.json -------------------------------------------------------------------------------- /src/resource/createProjectWebView/GetNonce.ts: -------------------------------------------------------------------------------- 1 | export function getNonce() { 2 | let text = ''; 3 | const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 4 | for (let i = 0; i < 32; i++) { 5 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 6 | } 7 | return text; 8 | } -------------------------------------------------------------------------------- /media/reset.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 13px; 4 | } 5 | 6 | *, 7 | *:before, 8 | *:after { 9 | box-sizing: inherit; 10 | } 11 | 12 | body, 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | ol, 21 | ul { 22 | margin: 0; 23 | padding: 0; 24 | font-weight: normal; 25 | } 26 | 27 | img { 28 | max-width: 100%; 29 | height: auto; 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": ["es6"], 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "strict": true /* enable all strict type-checking options */, 10 | "strictNullChecks": true, 11 | "strictPropertyInitialization": false 12 | }, 13 | "exclude": ["node_modules", ".vscode-test", "src/_test*"] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { CommandRegister } from "./CommandRegister"; 3 | 4 | /** 5 | * This method is called when the extension is activated 6 | */ 7 | export function activate(context: vscode.ExtensionContext) { 8 | const commands = CommandRegister.getInstance(context); 9 | commands.initializeCommands(); 10 | } 11 | 12 | /** 13 | * This method is called when the extension is deactivated 14 | */ 15 | export function deactivate() {} 16 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": [ 29 | "${workspaceFolder}/out/test/**/*.js" 30 | ], 31 | "preLaunchTask": "${defaultBuildTask}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /media/sdks.txt: -------------------------------------------------------------------------------- 1 | 2.1.2 [C:\Program Files\dotnet\sdk] 2 | 2.1.4 [C:\Program Files\dotnet\sdk] 3 | 2.1.201 [C:\Program Files\dotnet\sdk] 4 | 2.1.202 [C:\Program Files\dotnet\sdk] 5 | 2.1.402 [C:\Program Files\dotnet\sdk] 6 | 2.1.403 [C:\Program Files\dotnet\sdk] 7 | 2.1.526 [C:\Program Files\dotnet\sdk] 8 | 2.1.617 [C:\Program Files\dotnet\sdk] 9 | 2.1.700 [C:\Program Files\dotnet\sdk] 10 | 2.1.701 [C:\Program Files\dotnet\sdk] 11 | 2.1.818 [C:\Program Files\dotnet\sdk] 12 | 2.2.301 [C:\Program Files\dotnet\sdk] 13 | 2.2.401 [C:\Program Files\dotnet\sdk] 14 | 2.2.402 [C:\Program Files\dotnet\sdk] 15 | 3.1.426 [C:\Program Files\dotnet\sdk] 16 | 5.0.101 [C:\Program Files\dotnet\sdk] 17 | 5.0.104 [C:\Program Files\dotnet\sdk] 18 | 5.0.203 [C:\Program Files\dotnet\sdk] 19 | 5.0.214 [C:\Program Files\dotnet\sdk] 20 | 5.0.303 [C:\Program Files\dotnet\sdk] 21 | 5.0.408 [C:\Program Files\dotnet\sdk] 22 | 6.0.101 [C:\Program Files\dotnet\sdk] 23 | 6.0.118 [C:\Program Files\dotnet\sdk] 24 | 6.0.202 [C:\Program Files\dotnet\sdk] 25 | 6.0.203 [C:\Program Files\dotnet\sdk] 26 | 6.0.321 [C:\Program Files\dotnet\sdk] 27 | 8.0.100 [C:\Program Files\dotnet\sdk] 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Richard Zampieri 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 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off", 11 | "cSpell.ignoreWords": [ 12 | "backreference", 13 | "documentxml", 14 | "extention", 15 | "group", 16 | "json", 17 | "multiline", 18 | "snippets", 19 | "strikethrough", 20 | "to", 21 | "zampieri" 22 | ], 23 | "cSpell.words": [ 24 | "Blazor", 25 | "blazorserver", 26 | "blazorwasm", 27 | "classlib", 28 | "Creational", 29 | "designpattern", 30 | "forminline", 31 | "mauilib", 32 | "minwebapi", 33 | "mstest", 34 | "netcoreapp", 35 | "nunit", 36 | "paddding", 37 | "razorclasslib", 38 | "reactredux", 39 | "readlines", 40 | "richardzampieriprog", 41 | "submenu", 42 | "webapi", 43 | "xunit" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /media/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |",
55 | "/// int a = 5, b = 10;",
56 | "/// int sum = a + b;",
57 | "/// Console.WriteLine(\"Sum :\"sum)",
58 | "/// ",
59 | "/// tag"
62 | },
63 | "-> XML Para": {
64 | "prefix": "xml-para",
65 | "body": [
66 | "/// ${1:text} "
67 | ],
68 | "description": "You use the tag to format the content within its parent tag. is usually used inside a tag, such as or , to divide text into paragraphs. You can format the contents of the tag for your class definition"
69 | },
70 | "-> XML C": {
71 | "prefix": "xml-c",
72 | "body": [
73 | "/// ${1:text} "
74 | ],
75 | "description": "Still on the topic of formatting, you use the tag for marking part of text as code. It's like the tag but inline. It's useful when you want to show a quick code example as part of a tag's content"
76 | },
77 | "-> XML Exception": {
78 | "prefix": "xml-exception",
79 | "body": [
80 | "/// ${2:Param is max and the other greater than 0} "
81 | ],
82 | "description": "by using the tag, you let your developers know that a method can throw specific exceptions"
83 | },
84 | "-> XML See": {
85 | "prefix": "xml-see",
86 | "body": [
87 | "/// "
88 | ],
89 | "description": "the tag lets you create a clickable link to a documentation page for another code element"
90 | },
91 | "-> XML Seealso": {
92 | "prefix": "xml-seealso",
93 | "body": [
94 | "/// "
95 | ],
96 | "description": "you use the tag in the same way you do the tag. The only difference is that its content is typically placed in a \"See Also\" section"
97 | },
98 | "-> XML Param": {
99 | "prefix": "xml-param",
100 | "body": [
101 | "/// ${2:description}"
102 | ],
103 | "description": "you use the tag to describe a method's parameters"
104 | },
105 | "XML Typeparam": {
106 | "prefix": "xml-typeparam",
107 | "body": [
108 | "/// ${2:description} "
109 | ],
110 | "description": "You use tag just like the tag but for generic type or method declarations to describe a generic parameter"
111 | },
112 | "XML Paramref": {
113 | "prefix": "xml-paramref",
114 | "body": [
115 | "/// "
116 | ],
117 | "description": "sometimes you might be in the middle of describing what a method does in what could be a tag, and you might want to make a reference to a parameter"
118 | },
119 | "XML Typeparamref": {
120 | "prefix": "xml-typeparamref",
121 | "body": [
122 | "/// "
123 | ],
124 | "description": "you use tag just like the tag but for generic type or method declarations to describe a generic parameter"
125 | },
126 | "-> XML List": {
127 | "prefix": "xml-list",
128 | "body": [
129 | "/// ",
130 | "/// ",
131 | "/// ${1:term} ",
132 | "/// ${2:description} ",
133 | "/// ",
134 | "/// - ",
135 | "///
${3:term} ",
136 | "/// ${4:description} ",
137 | "/// ",
138 | "///
"
139 | ],
140 | "description": "you use the tag to format documentation information as an ordered list, unordered list, or table"
141 | },
142 | "-> XML Inheritdoc": {
143 | "prefix": "xml-inheritdoc",
144 | "body": [
145 | "/// "
146 | ],
147 | "description": "you can use the tag to inherit XML comments from base classes, interfaces, and similar methods"
148 | },
149 | "-> XML Include": {
150 | "prefix": "xml-include",
151 | "body": [
152 | "/// "
153 | ],
154 | "description": "the tag lets you refer to comments in a separate XML file that describe the types and members in your source code, as opposed to placing documentation comments directly in your source code file"
155 | }
156 | }
--------------------------------------------------------------------------------
/media/addProject.js:
--------------------------------------------------------------------------------
1 | // This script will be run within the webview itself
2 | // It cannot access the main VS Code APIs directly.
3 |
4 | (function () {
5 | // vscode api
6 | const vscode = acquireVsCodeApi();
7 |
8 | // Html elements bindings
9 | const buttonCreateProject = document.getElementById("create-project-button");
10 | const template = document.getElementById("custom-select");
11 | const project = document.getElementById("projectName");
12 | const framework = document.getElementById("custom-select2");
13 | const projectGroupSelect = document.getElementById("project-group-select");
14 |
15 | document.addEventListener("DOMContentLoaded", function (event) {
16 | populateTemplateSelect(projectGroupSelect.value);
17 | buttonCreateProject.disabled = "true";
18 | buttonCreateProject.style.backgroundColor = "#3C3C3C";
19 | fieldValidation();
20 | });
21 |
22 | /* Project group select */
23 | const projectGroupToTemplates = {
24 | api: [
25 | { templateName: ".NET Core Web API", shortName: "webapi" },
26 | { templateName: ".NET Core Web API (native AOT)", shortName: "webapiaot" },
27 | { templateName: "API Controller", shortName: "apicontroller" },
28 | ],
29 | blazor: [
30 | { templateName: ".NET MAUI Blazor Hybrid App", shortName: "maui-blazor" },
31 | { templateName: "Blazor Server App", shortName: "blazorserver" },
32 | { templateName: "Blazor Server App Empty", shortName: "blazorserver-empty" },
33 | { templateName: "Blazor Web App", shortName: "blazor" },
34 | { templateName: "Blazor WebAssembly App Empty", shortName: "blazorwasm-empty" },
35 | { templateName: "Blazor WebAssembly Standalone App", shortName: "blazorwasm" },
36 | ],
37 | cloud: [], // No specific templates for cloud in the given list
38 | console: [{ templateName: "Console App", shortName: "console" }],
39 | desktop: [
40 | { templateName: "Windows Forms App", shortName: "winforms" },
41 | { templateName: "Windows Forms Class Library", shortName: "winformslib" },
42 | { templateName: "Windows Forms Control Library", shortName: "winformscontrollib" },
43 | { templateName: "WPF Application", shortName: "wpf" },
44 | { templateName: "WPF Class Library", shortName: "wpflib" },
45 | { templateName: "WPF Custom Control Library", shortName: "wpfcustomcontrollib" },
46 | { templateName: "WPF User Control Library", shortName: "wpfusercontrollib" },
47 | ],
48 | extensions: [], // No specific templates for extensions in the given list
49 | game: [], // No specific templates for game in the given list
50 | iot: [], // No specific templates for IoT in the given list
51 | lib: [
52 | { templateName: "Class Library", shortName: "classlib" },
53 | { templateName: ".NET MAUI Class Library", shortName: "mauilib" },
54 | { templateName: "Android Class Library", shortName: "androidlib" },
55 | { templateName: "iOS Class Library", shortName: "ioslib" },
56 | { templateName: "Mac Catalyst Class Library", shortName: "maccatalystlib" },
57 | { templateName: "Razor Class Library", shortName: "razorclasslib" },
58 | ],
59 | machinelearning: [], // No specific templates for machine learning in the given list
60 | maui: [
61 | { templateName: ".NET MAUI App", shortName: "maui" },
62 | { templateName: ".NET MAUI ContentPage (C#)", shortName: "maui-page-csharp" },
63 | { templateName: ".NET MAUI ContentPage (XAML)", shortName: "maui-page-xaml" },
64 | { templateName: ".NET MAUI ContentView (C#)", shortName: "maui-view-csharp" },
65 | { templateName: ".NET MAUI ContentView (XAML)", shortName: "maui-view-xaml" },
66 | { templateName: ".NET MAUI ResourceDictionary (XAML)", shortName: "maui-dict-xaml" },
67 | ],
68 | mobile: [
69 | { templateName: "Android Application", shortName: "android" },
70 | { templateName: "Android Wear Application", shortName: "androidwear" },
71 | { templateName: "iOS Application", shortName: "ios" },
72 | { templateName: "iOS Tabbed Application", shortName: "ios-tabbed" },
73 | ],
74 | test: [
75 | { templateName: "MSTest Test Project", shortName: "mstest" },
76 | { templateName: "MSTest Playwright Test Project", shortName: "mstest-playwright" },
77 | { templateName: "NUnit 3 Test Project", shortName: "nunit" },
78 | { templateName: "NUnit 3 Test Item", shortName: "nunit-test" },
79 | { templateName: "NUnit Playwright Test Project", shortName: "nunit-playwright" },
80 | { templateName: "xUnit Test Project", shortName: "xunit" },
81 | ],
82 | web: [
83 | { templateName: "ASP.NET Core Empty", shortName: "web" },
84 | { templateName: "ASP.NET Core gRPC Service", shortName: "grpc" },
85 | { templateName: "ASP.NET Core Web App (Model-View-Controller)", shortName: "mvc" },
86 | { templateName: "ASP.NET Core Web App (Razor Pages)", shortName: "webapp" },
87 | { templateName: "ASP.NET Core with Angular", shortName: "angular" },
88 | { templateName: "ASP.NET Core with React.js", shortName: "react" },
89 | { templateName: "ASP.NET Core with React.js and Redux", shortName: "reactredux" },
90 | { templateName: "Razor Component", shortName: "razorcomponent" },
91 | { templateName: "Razor Page", shortName: "page" },
92 | { templateName: "Razor View", shortName: "view" },
93 | ],
94 | };
95 |
96 | function populateTemplateSelect(group) {
97 | const templates = projectGroupToTemplates[group] || [];
98 |
99 | template.innerHTML = templates
100 | .map(
101 | (template) =>
102 | ``
103 | )
104 | .join("");
105 | }
106 |
107 | /* Project group select */
108 | projectGroupSelect.addEventListener("change", () => {
109 | populateTemplateSelect(projectGroupSelect.value);
110 | });
111 |
112 | function fieldValidation() {
113 | if (project.value === "") {
114 | buttonCreateProject.disabled = true;
115 | } else {
116 | buttonCreateProject.disabled = false;
117 | buttonCreateProject.style.backgroundColor = "#0E639C";
118 | }
119 | }
120 |
121 | template.addEventListener("keydown" | "click", () => {
122 | project.focus();
123 | });
124 |
125 | project.addEventListener("change", () => {
126 | fieldValidation();
127 | solution.value = project.value;
128 | });
129 |
130 | // create console project
131 | buttonCreateProject.addEventListener("click", () => {
132 | let frameworkSelected = framework.options[framework.selectedIndex].value;
133 |
134 | // verify if project has white spaces
135 | let projectTrimmed = project.value.replace(/\s/g, "");
136 |
137 | vscode.postMessage({
138 | command: "addProject",
139 | template: template.options[template.selectedIndex].value,
140 | project: projectTrimmed,
141 | framework: frameworkSelected,
142 | });
143 | });
144 | })();
145 |
--------------------------------------------------------------------------------
/src/resource/contextualMenu/ContextualMenu.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from "vscode";
2 | import * as path from "path";
3 | import * as fs from "fs";
4 | import * as parentFinder from "find-parent-dir";
5 | import * as os from "os";
6 | const findUpGlob = require("find-up-glob");
7 | const lineByLine = require("n-readlines");
8 |
9 | class ContextualMenu {
10 | public static async init(uri: vscode.Uri, fileType: string, framework: string) {
11 | let pathSelected = "";
12 | let projectRoot: string | undefined;
13 |
14 | // If the command is called from the explorer context menu (true) or from the editor context menu (false)
15 | const formContextMenu = !!uri;
16 |
17 | if (formContextMenu && uri) {
18 | pathSelected = uri.fsPath;
19 | projectRoot = getProjectRootDirOrFilePath(pathSelected);
20 |
21 | if (!projectRoot) {
22 | vscode.window.showErrorMessage("No project selected");
23 | return;
24 | }
25 | } else {
26 | projectRoot = await selectProject();
27 |
28 | if (!projectRoot) {
29 | vscode.window.showErrorMessage("No project selected");
30 | return;
31 | }
32 |
33 | // Open folder picker at the root of the selected project
34 | const selectedFolder = await vscode.window.showOpenDialog({
35 | canSelectFolders: true,
36 | canSelectFiles: false,
37 | canSelectMany: false,
38 | defaultUri: vscode.Uri.file(projectRoot),
39 | openLabel: "Select folder",
40 | });
41 |
42 | if (!selectedFolder || selectedFolder.length === 0) {
43 | vscode.window.showErrorMessage("No folder selected");
44 | return;
45 | }
46 | pathSelected = selectedFolder[0].fsPath;
47 | }
48 |
49 | // Resource creation
50 | vscode.window
51 | .showInputBox({
52 | ignoreFocusOut: true,
53 | prompt: "Type the file name",
54 | value: "New " + fileType + ".cs",
55 | })
56 | .then((newFileName) => {
57 | if (typeof newFileName === undefined || newFileName === "") {
58 | vscode.window.showErrorMessage(
59 | "Please input a valid name or press Scape to cancel the operation!"
60 | );
61 | return this.init(uri, fileType, framework);
62 | }
63 |
64 | if (newFileName) {
65 | newFileName = newFileName.replace(/\s/g, "");
66 | } else {
67 | newFileName = "New" + fileType + ".cs";
68 | }
69 |
70 | let newFilePath = pathSelected + path.sep + newFileName;
71 |
72 | if (fs.existsSync(newFilePath)) {
73 | vscode.window.showErrorMessage(`File ${newFileName} already exist`);
74 | return;
75 | }
76 |
77 | newFilePath = correctFileNameExtension(newFilePath);
78 | let originalFilePath = newFilePath;
79 |
80 | let rootDir = getProjectRootDirOrFilePath(newFilePath);
81 |
82 | if (rootDir === null) {
83 | vscode.window.showErrorMessage("Unable to find *.csproj or project.json");
84 | return;
85 | }
86 |
87 | let rootNamespace: any = checkRootNameOnCsproj(newFilePath);
88 |
89 | if (rootDir !== undefined) {
90 | rootDir =
91 | rootDir[rootDir.length - 1] === path.sep
92 | ? rootDir.substring(0, rootDir.length - 1)
93 | : rootDir;
94 |
95 | let projRootDir = rootDir.substring(rootDir.lastIndexOf(path.sep) + 1);
96 |
97 | let childFilePath = newFilePath.substring(newFilePath.lastIndexOf(projRootDir));
98 |
99 | if (rootNamespace !== null) {
100 | childFilePath = childFilePath.replace(
101 | childFilePath.substring(0, childFilePath.indexOf("\\")),
102 | rootNamespace
103 | );
104 | }
105 |
106 | // set the regex pattern for path structure
107 | let pathSepRegEX = /\//g;
108 | if (os.platform() === "win32") {
109 | pathSepRegEX = /\\/g;
110 | }
111 |
112 | // replace \\ for . in following the namespace convention
113 | let namespace = path.dirname(childFilePath);
114 | namespace = namespace.replace(pathSepRegEX, ".");
115 |
116 | // replace file name empty space or dash by underscore
117 | namespace = namespace.replace(/\s+/g, "_");
118 | namespace = namespace.replace(/-/g, "_");
119 |
120 | newFilePath = path.basename(newFilePath, ".cs");
121 |
122 | loadTemplate(fileType, namespace, newFilePath, originalFilePath, framework);
123 | }
124 | });
125 | }
126 | }
127 |
128 | export { ContextualMenu };
129 |
130 | // function to fix the file extension in case user forget to input .cs
131 | function correctFileNameExtension(fileName: any) {
132 | if (path.extname(fileName) !== ".cs") {
133 | if (fileName.endsWith(".")) {
134 | fileName = fileName + "cs";
135 | } else {
136 | fileName = fileName + ".cs";
137 | }
138 | }
139 | return fileName;
140 | }
141 |
142 | // function to detect the root directory where the .csproj is included
143 | function getProjectRootDirOrFilePath(filePath: any): string | undefined {
144 | var projectRootDir = parentFinder.sync(path.dirname(filePath), "project.json");
145 | if (projectRootDir === null) {
146 | let csProjFiles = findUpGlob.sync("*.csproj", { cwd: path.dirname(filePath) });
147 |
148 | if (csProjFiles === null) {
149 | return undefined;
150 | }
151 | projectRootDir = path.dirname(csProjFiles[0]);
152 | }
153 | return projectRootDir;
154 | }
155 |
156 | // load the template, replace by current values and create the document in the folder selected
157 | function loadTemplate(
158 | templateType: string,
159 | namespace: string,
160 | newFilepath: string,
161 | originalFilePath: string,
162 | framework: string
163 | ) {
164 | let fileTemplate: string = "";
165 |
166 | if (framework === "net6.0") {
167 | fileTemplate = templateType + "6.mdl";
168 | } else {
169 | fileTemplate = templateType + ".mdl";
170 | }
171 |
172 | vscode.workspace
173 | .openTextDocument(
174 | vscode.extensions.getExtension("richardzampieriprog.csharp-snippet-productivity")
175 | ?.extensionPath +
176 | "/models/" +
177 | fileTemplate
178 | )
179 | .then((doc: vscode.TextDocument) => {
180 | let content = doc.getText();
181 | content = content.replace("${namespace}", namespace);
182 | content = content.replace("${fileName}", newFilepath);
183 | let cursorPos = findCursorPos(content);
184 | content = content.replace("${cursor}", "");
185 | fs.writeFileSync(originalFilePath, content);
186 |
187 | vscode.workspace.openTextDocument(originalFilePath).then((doc) => {
188 | vscode.window.showTextDocument(doc).then((editor) => {
189 | let selection = new vscode.Selection(cursorPos, cursorPos);
190 | editor.selection = selection;
191 | });
192 | });
193 | });
194 | }
195 |
196 | // find cursor position in the template file
197 | function findCursorPos(content: string) {
198 | let cursorPos = content.indexOf("${cursor}");
199 | let beforePos = content.substring(0, cursorPos);
200 | let line = beforePos.match(/\n/gi)?.length;
201 | let charId = beforePos.substring(beforePos.lastIndexOf("\n")).length;
202 | return new vscode.Position(line !== undefined ? line : 0, charId);
203 | }
204 |
205 | // function to check root namespace in the csproj file
206 | function checkRootNameOnCsproj(filePath: string) {
207 | let rootNamespace: string = findUpGlob.sync("*.csproj", { cwd: path.dirname(filePath) })[0];
208 | const liner = new lineByLine(rootNamespace);
209 | let line;
210 |
211 | while ((line = liner.next())) {
212 | let l = line.toString("ascii");
213 | let result: string = l.match(/(.*?)<\/RootNamespace>/g);
214 | if (result === null) {
215 | continue;
216 | }
217 | let content = result[0];
218 |
219 | let root: string = content.substring(content.indexOf(">") + 1, content.indexOf(""));
220 |
221 | if (root !== null && root !== "") {
222 | return root;
223 | }
224 | }
225 |
226 | return null;
227 | }
228 |
229 | // Updated function to list project folders
230 | async function selectProject(): Promise {
231 | const solutionFolders = vscode.workspace.workspaceFolders;
232 | if (!solutionFolders || solutionFolders.length === 0) {
233 | return undefined;
234 | }
235 |
236 | const csprojFiles = await vscode.workspace.findFiles("**/*.csproj");
237 | const projects = csprojFiles.map((file) => {
238 | return {
239 | label: path.basename(file.fsPath),
240 | detail: path.dirname(file.fsPath),
241 | };
242 | });
243 |
244 | const selectedProject = await vscode.window.showQuickPick(projects, {
245 | placeHolder: "Select a project",
246 | });
247 |
248 | return selectedProject?.detail;
249 | }
250 |
--------------------------------------------------------------------------------
/src/resource/addProjectToSolution/Panel.ts:
--------------------------------------------------------------------------------
1 | import {
2 | window,
3 | Uri,
4 | ViewColumn,
5 | WebviewPanel,
6 | ExtensionContext,
7 | WebviewOptions,
8 | workspace,
9 | } from "vscode";
10 | import * as path from "path";
11 | import * as fs from "fs";
12 | import { getTargetFrameworks } from "../../utils/sdk.provider";
13 | import { CommandFactory, TEMPLATE_COMPATIBILITY } from "../../utils/terminal-cmd.provider";
14 |
15 | type FrameworkCommand = {
16 | [key: string]: string;
17 | };
18 |
19 | const frameworkCommands: FrameworkCommand = {
20 | ["3.1"]: "netcoreapp3.1",
21 | ["5.0"]: "net5.0",
22 | ["6.0"]: "net6.0",
23 | ["7.0"]: "net7.0",
24 | ["8.0"]: "net8.0",
25 | };
26 |
27 | export class Panel {
28 | readonly _context: ExtensionContext;
29 | private _webViewPanel: WebviewPanel | undefined = undefined;
30 | private _vscodeCss!: Uri;
31 | private _resetCss!: Uri;
32 | private _script!: Uri;
33 | private _projectName: string;
34 | private _solution: string;
35 | private _sdksResources!: Uri;
36 | private _sdks!: string[];
37 |
38 | constructor(
39 | context: ExtensionContext,
40 | projectName: string,
41 | solution: string,
42 | title: string,
43 | iconPath: { folder: string; file: string } = { folder: "", file: "" },
44 | viewColumn: ViewColumn = ViewColumn.One,
45 | preserveFocus: boolean = false,
46 | enableFindWidget: boolean = false,
47 | retainContextWhenHidden: boolean = false
48 | ) {
49 | // context from the extension main entry point
50 | this._context = context;
51 | this._projectName = projectName;
52 | this._solution = solution;
53 |
54 | let _viewType: string = title.replace(/[^A-Z0-9]/gi, "-");
55 |
56 | if (this._webViewPanel) {
57 | this._webViewPanel.reveal(window.activeTextEditor?.viewColumn);
58 | } else {
59 | // creating the panel
60 | this._webViewPanel = window.createWebviewPanel(
61 | _viewType,
62 | title,
63 | { preserveFocus, viewColumn },
64 | { enableFindWidget, retainContextWhenHidden }
65 | );
66 | this._webViewPanel.iconPath = Uri.file(
67 | path.join(this._context.extensionPath, iconPath.folder, iconPath.file)
68 | );
69 |
70 | // webview options
71 | this._webViewPanel.webview.options = {
72 | enableCommandUris: false,
73 | enableScripts: true,
74 | localResourceRoots: [
75 | Uri.file(path.join(this._context.extensionPath, "media")),
76 | Uri.file(path.join(this._context.extensionPath, "out")),
77 | ],
78 | portMapping: [],
79 | };
80 |
81 | this._sdksResources = this._webViewPanel.webview.asWebviewUri(
82 | Uri.file(path.join(this._context.extensionPath, "media", "sdks.txt"))
83 | );
84 | this._sdks = getTargetFrameworks(this._sdksResources);
85 | this._resetCss = this._webViewPanel.webview.asWebviewUri(
86 | Uri.file(path.join(this._context.extensionPath, "media", "reset.css"))
87 | );
88 | this._vscodeCss = this._webViewPanel.webview.asWebviewUri(
89 | Uri.file(path.join(this._context.extensionPath, "media", "vscode.css"))
90 | );
91 | this._script = this._webViewPanel.webview.asWebviewUri(
92 | Uri.file(path.join(this._context.extensionPath, "media", "addProject.js"))
93 | );
94 | this._webViewPanel.webview.html = this.baseHtml(
95 | "index.html",
96 | this._resetCss,
97 | this._vscodeCss,
98 | this._script
99 | );
100 | }
101 |
102 | // control event listener
103 | this._webViewPanel.webview.onDidReceiveMessage(async (message) => {
104 | switch (message.command) {
105 | case "addProject":
106 | await this.addProject(message);
107 | return;
108 | }
109 | });
110 | }
111 |
112 | private async addProject(message: any): Promise {
113 | let root = workspace.workspaceFolders
114 | ?.map((folder) => folder.uri.path)[0]
115 | .replace(/\//g, "\\");
116 | root = root?.slice(1, root.length);
117 |
118 | // Get the framework command
119 | message.framework = frameworkCommands[message.framework];
120 |
121 | const terminal = window.createTerminal();
122 | terminal.show(true);
123 |
124 | if (!isFrameworkCompatible(message)) {
125 | // Format list of compatible frameworks
126 | const compatibleFrameworks = TEMPLATE_COMPATIBILITY[message.template]
127 | .map((f) => `'${f.substring(3)}'`)
128 | .join(", ");
129 |
130 | window.showWarningMessage(
131 | `Please select a compatible framework for ${message.template} - [${
132 | compatibleFrameworks || "None"
133 | }]`
134 | );
135 |
136 | setTimeout(() => {
137 | terminal.dispose();
138 | }, 5000);
139 |
140 | return;
141 | }
142 |
143 | terminal.sendText(
144 | "dotnet new " +
145 | message.template +
146 | " --language c# -n " +
147 | message.project +
148 | " -o " +
149 | root +
150 | "\\" +
151 | message.project +
152 | " -f " +
153 | message.framework +
154 | " --force"
155 | );
156 | terminal.sendText(
157 | "dotnet sln " +
158 | this._solution +
159 | " add " +
160 | root +
161 | "\\" +
162 | message.project +
163 | "\\" +
164 | message.project +
165 | ".csproj"
166 | );
167 |
168 | setTimeout(() => {
169 | terminal.dispose();
170 | }, 5000);
171 |
172 | // Close the WebView after the project is created
173 | if (this._webViewPanel) {
174 | this._webViewPanel.dispose();
175 | this._webViewPanel = undefined;
176 | }
177 | }
178 |
179 | private getNonce() {
180 | let text = "";
181 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
182 | for (let i: number = 0; i < 32; i++) {
183 | text += possible.charAt(Math.floor(Math.random() * possible.length));
184 | }
185 | return text;
186 | }
187 |
188 | public get webViewPanel(): WebviewPanel | undefined {
189 | return this._webViewPanel;
190 | }
191 | public set webViewPanel(panelInstance: WebviewPanel | undefined) {
192 | this._webViewPanel = panelInstance;
193 | }
194 |
195 | public set iconPath(
196 | icon: { folder: string; file: string } | { folder: string; file: string }[]
197 | ) {
198 | if (this._webViewPanel) {
199 | if (Array.isArray(icon)) {
200 | this._webViewPanel.iconPath = {
201 | dark: Uri.file(
202 | path.join(this._context.extensionPath, icon[0].folder, icon[0].file)
203 | ),
204 | light: Uri.file(
205 | path.join(this._context.extensionPath, icon[1].folder, icon[1].file)
206 | ),
207 | };
208 | } else {
209 | this._webViewPanel.iconPath = Uri.file(
210 | path.join(this._context.extensionPath, icon.folder, icon.file)
211 | );
212 | }
213 | }
214 | }
215 |
216 | public set options(options: WebviewOptions) {
217 | if (this._webViewPanel) {
218 | this._webViewPanel.webview.options = options;
219 | }
220 | }
221 | public allowedLocalResource(...folders: string[]) {
222 | if (this._webViewPanel) {
223 | let foldersRoot: Uri[] = [];
224 |
225 | for (let i: number = 0; i < folders.length; i++) {
226 | foldersRoot[i] = Uri.file(path.join(this._context.extensionPath, folders[i]));
227 | }
228 |
229 | this._webViewPanel.webview.options = {
230 | localResourceRoots: foldersRoot,
231 | };
232 | }
233 | }
234 |
235 | public set html(htmlDoc: string) {
236 | if (this._webViewPanel) {
237 | this._webViewPanel.webview.html = htmlDoc;
238 | }
239 | }
240 |
241 | public addResource(content: { folder: string; resource: string }): Uri | undefined {
242 | const diskResource = Uri.file(
243 | path.join(this._context.extensionPath, content.folder, content.resource)
244 | );
245 | return this._webViewPanel?.webview.asWebviewUri(diskResource);
246 | }
247 |
248 | private baseHtml(page: string, ...resource: Uri[]): string {
249 | let html = fs.readFileSync(path.join(this._context.extensionPath, "media", page), "utf-8");
250 | html = html.replace(`{{project}}`, this._projectName);
251 | html = html.replace(`{{stylesResetUri}}`, resource[0].toString());
252 | html = html.replace(`{{stylesMainUri}}`, resource[1].toString());
253 | html = html.replace(`{{script}}`, resource[2].toString());
254 |
255 | const sdkOptions = this._sdks
256 | .map((sdk) => ``)
257 | .join("");
258 |
259 | html = html.replace(`{{sdkOptions}}`, sdkOptions);
260 |
261 | if (this._webViewPanel) {
262 | html = html.split(`{{nonce}}`).join(this.getNonce());
263 | html = html.split(`{{cspSource}}`).join(this._webViewPanel.webview.cspSource);
264 | }
265 | return html.toString();
266 | }
267 | }
268 |
269 | function isFrameworkCompatible(message: any) {
270 | // Verify if template is not undefined
271 | if (!message.template || !(message.template in TEMPLATE_COMPATIBILITY)) {
272 | return true;
273 | }
274 | return TEMPLATE_COMPATIBILITY[message.template].includes(message.framework);
275 | }
276 |
--------------------------------------------------------------------------------
/media/main.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable curly */
2 | // This script will be run within the webview itself
3 | // It cannot access the main VS Code APIs directly.
4 |
5 | (function () {
6 | // vscode api
7 | const vscode = acquireVsCodeApi();
8 |
9 | // Html elements bindings
10 | const buttonCreateProject = document.getElementById("create-project-button");
11 | const buttonFilePicker = document.getElementById("selectFolder");
12 | const projectGroupSelect = document.getElementById("project-group-select");
13 | const template = document.getElementById("custom-select");
14 | const project = document.getElementById("projectName");
15 | const filePath = document.getElementById("inputLocal");
16 | const solution = document.getElementById("solution");
17 | const framework = document.getElementById("custom-select2");
18 |
19 | document.addEventListener("DOMContentLoaded", function (event) {
20 | buttonCreateProject.disabled = "true";
21 | buttonCreateProject.style.backgroundColor = "#3C3C3C";
22 |
23 | // Event listener for project group selection
24 | projectGroupSelect.addEventListener("change", function () {
25 | loadTemplateSelect(this.value, "");
26 | });
27 |
28 | fieldValidation();
29 | });
30 |
31 | function fieldValidation() {
32 | if (project.value === "" || solution.value === "") {
33 | buttonCreateProject.disabled = true;
34 | } else {
35 | buttonCreateProject.disabled = false;
36 | buttonCreateProject.style.backgroundColor = "#0E639C";
37 | }
38 | }
39 |
40 | /* Project group select */
41 | const projectGroupToTemplates = {
42 | api: [
43 | { templateName: ".NET Core Web API", shortName: "webapi" },
44 | { templateName: ".NET Core Web API (native AOT)", shortName: "webapiaot" },
45 | { templateName: "API Controller", shortName: "apicontroller" },
46 | ],
47 | blazor: [
48 | { templateName: ".NET MAUI Blazor Hybrid App", shortName: "maui-blazor" },
49 | { templateName: "Blazor Server App", shortName: "blazorserver" },
50 | { templateName: "Blazor Server App Empty", shortName: "blazorserver-empty" },
51 | { templateName: "Blazor Web App", shortName: "blazor" },
52 | { templateName: "Blazor WebAssembly App Empty", shortName: "blazorwasm-empty" },
53 | { templateName: "Blazor WebAssembly Standalone App", shortName: "blazorwasm" },
54 | ],
55 | cloud: [], // No specific templates for cloud in the given list
56 | console: [{ templateName: "Console App", shortName: "console" }],
57 | desktop: [
58 | { templateName: "Windows Forms App", shortName: "winforms" },
59 | { templateName: "Windows Forms Class Library", shortName: "winformslib" },
60 | { templateName: "Windows Forms Control Library", shortName: "winformscontrollib" },
61 | { templateName: "WPF Application", shortName: "wpf" },
62 | { templateName: "WPF Class Library", shortName: "wpflib" },
63 | { templateName: "WPF Custom Control Library", shortName: "wpfcustomcontrollib" },
64 | { templateName: "WPF User Control Library", shortName: "wpfusercontrollib" },
65 | ],
66 | extensions: [], // No specific templates for extensions in the given list
67 | game: [], // No specific templates for game in the given list
68 | iot: [], // No specific templates for IoT in the given list
69 | lib: [
70 | { templateName: "Class Library", shortName: "classlib" },
71 | { templateName: ".NET MAUI Class Library", shortName: "mauilib" },
72 | { templateName: "Android Class Library", shortName: "androidlib" },
73 | { templateName: "iOS Class Library", shortName: "ioslib" },
74 | { templateName: "Mac Catalyst Class Library", shortName: "maccatalystlib" },
75 | { templateName: "Razor Class Library", shortName: "razorclasslib" },
76 | ],
77 | machinelearning: [], // No specific templates for machine learning in the given list
78 | maui: [
79 | { templateName: ".NET MAUI App", shortName: "maui" },
80 | { templateName: ".NET MAUI ContentPage (C#)", shortName: "maui-page-csharp" },
81 | { templateName: ".NET MAUI ContentPage (XAML)", shortName: "maui-page-xaml" },
82 | { templateName: ".NET MAUI ContentView (C#)", shortName: "maui-view-csharp" },
83 | { templateName: ".NET MAUI ContentView (XAML)", shortName: "maui-view-xaml" },
84 | { templateName: ".NET MAUI ResourceDictionary (XAML)", shortName: "maui-dict-xaml" },
85 | ],
86 | mobile: [
87 | { templateName: "Android Application", shortName: "android" },
88 | { templateName: "Android Wear Application", shortName: "androidwear" },
89 | { templateName: "iOS Application", shortName: "ios" },
90 | { templateName: "iOS Tabbed Application", shortName: "ios-tabbed" },
91 | ],
92 | test: [
93 | { templateName: "MSTest Test Project", shortName: "mstest" },
94 | { templateName: "MSTest Playwright Test Project", shortName: "mstest-playwright" },
95 | { templateName: "NUnit 3 Test Project", shortName: "nunit" },
96 | { templateName: "NUnit 3 Test Item", shortName: "nunit-test" },
97 | { templateName: "NUnit Playwright Test Project", shortName: "nunit-playwright" },
98 | { templateName: "xUnit Test Project", shortName: "xunit" },
99 | ],
100 | web: [
101 | { templateName: "ASP.NET Core Empty", shortName: "web" },
102 | { templateName: "ASP.NET Core gRPC Service", shortName: "grpc" },
103 | { templateName: "ASP.NET Core Web App (Model-View-Controller)", shortName: "mvc" },
104 | { templateName: "ASP.NET Core Web App (Razor Pages)", shortName: "webapp" },
105 | { templateName: "ASP.NET Core with Angular", shortName: "angular" },
106 | { templateName: "ASP.NET Core with React.js", shortName: "react" },
107 | { templateName: "ASP.NET Core with React.js and Redux", shortName: "reactredux" },
108 | { templateName: "Razor Component", shortName: "razorcomponent" },
109 | { templateName: "Razor Page", shortName: "page" },
110 | { templateName: "Razor View", shortName: "view" },
111 | ],
112 | };
113 |
114 | // Load template select based on selected project group
115 | function loadTemplateSelect(group, selectedTemplate) {
116 | template.innerHTML = ""; // Clear existing options
117 |
118 | // Add default 'Select Template' option
119 | const defaultOption = document.createElement("option");
120 | defaultOption.value = "";
121 | defaultOption.textContent = "Select Template";
122 | template.appendChild(defaultOption);
123 |
124 | // Check if a project group is selected before populating templates
125 | if (group && projectGroupToTemplates[group]) {
126 | const templates = projectGroupToTemplates[group];
127 | templates.forEach((tmpl) => {
128 | const option = document.createElement("option");
129 | option.value = tmpl.shortName;
130 | option.textContent = tmpl.templateName;
131 | if (tmpl.shortName === selectedTemplate) option.selected = true;
132 | template.appendChild(option);
133 | });
134 | }
135 | }
136 |
137 | // Receive message from the extension
138 | window.addEventListener("message", (event) => {
139 | const message = event.data; // The JSON data our extension sent
140 |
141 | switch (message.command) {
142 | case "updateState":
143 | projectGroupSelect.value = message.projectGroup;
144 | loadTemplateSelect(message.projectGroup, message.selectedTemplate);
145 | break;
146 | }
147 | });
148 | /* Project group select End of Implementation */
149 |
150 | template.addEventListener("keydown" | "click", () => {
151 | project.focus();
152 | });
153 |
154 | project.addEventListener("change", () => {
155 | solution.value = project.value;
156 | fieldValidation();
157 | });
158 |
159 | solution.addEventListener("keyup", () => {
160 | fieldValidation();
161 | });
162 |
163 | filePath.addEventListener("keyup" | "focus", () => {
164 | fieldValidation();
165 | });
166 |
167 | filePath.addEventListener("keydown", () => {
168 | buttonFilePicker.focus();
169 | });
170 |
171 | // create console project
172 | buttonCreateProject.addEventListener("click", () => {
173 | let frameworkSelected = framework.options[framework.selectedIndex].value;
174 | let frameworkRun = "";
175 |
176 | if (frameworkSelected === "2.0") frameworkRun = "netcoreapp2.0";
177 | else if (frameworkSelected === "2.1") frameworkRun = "netcoreapp2.1";
178 | else if (frameworkSelected === "2.2") frameworkRun = "netcoreapp2.2";
179 | else if (frameworkSelected === "3.0") frameworkRun = "netcoreapp3.0";
180 | else if (frameworkSelected === "3.1") frameworkRun = "netcoreapp3.1";
181 | else if (frameworkSelected === "5.0") frameworkRun = "net5.0";
182 | else if (frameworkSelected === "6.0") frameworkRun = "net6.0";
183 | else if (frameworkSelected === "7.0") frameworkRun = "net7.0";
184 | else if (frameworkSelected === "8.0") frameworkRun = "net8.0";
185 |
186 | vscode.postMessage({
187 | command: "createProject",
188 | template: template.options[template.selectedIndex].value,
189 | project: project.value,
190 | filePath: filePath.value,
191 | solution: solution.value,
192 | framework: frameworkRun,
193 | });
194 | });
195 |
196 | // file picker to save the project in a specific location
197 | buttonFilePicker.addEventListener("click", () => {
198 | solution.focus();
199 | vscode.postMessage({
200 | command: "selectDirectory",
201 | projectGroupSelect:
202 | projectGroupSelect.options[projectGroupSelect.selectedIndex].textContent,
203 | templateName: template.options[template.selectedIndex].text,
204 | template: template.options[template.selectedIndex].value,
205 | project: project.value,
206 | solution: solution.value,
207 | framework: framework.options[framework.selectedIndex].value,
208 | });
209 | });
210 | })();
211 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "csharp-snippet-productivity",
3 | "displayName": "C# Toolbox of Productivity",
4 | "description": "The complete set of tools for C# development",
5 | "version": "2.1.1",
6 | "icon": "icon.png",
7 | "publisher": "richardzampieriprog",
8 | "license": "SEE LICENSE IN LICENSE.md",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/rsaz/csharp-snippet-productivity"
12 | },
13 | "engines": {
14 | "vscode": "^1.85.0"
15 | },
16 | "categories": [
17 | "Programming Languages",
18 | "Snippets",
19 | "Formatters"
20 | ],
21 | "keywords": [
22 | ".NET",
23 | ".NET Core",
24 | "C#",
25 | "Visual Studio",
26 | "snippet",
27 | "productivity",
28 | "keybindings"
29 | ],
30 | "extensionKind": [
31 | "ui"
32 | ],
33 | "activationEvents": [
34 | "onLanguage:csharp",
35 | "onLanguage:markdown",
36 | "onLanguage:plaintext"
37 | ],
38 | "contributes": {
39 | "snippets": [
40 | {
41 | "language": "csharp",
42 | "path": "./snippets/general.json"
43 | },
44 | {
45 | "language": "csharp",
46 | "path": "./snippets/documentxml.json"
47 | },
48 | {
49 | "language": "csharp",
50 | "path": "./snippets/designpattern.json"
51 | }
52 | ],
53 | "commands": [
54 | {
55 | "command": "csharp-snippet-productivity.createProject",
56 | "title": "Create Project",
57 | "category": "C# Toolbox"
58 | },
59 | {
60 | "command": "csharp-snippet-productivity.createClass",
61 | "title": "Create Class",
62 | "category": "C# Toolbox"
63 | },
64 | {
65 | "command": "csharp-snippet-productivity.createInterface",
66 | "title": "Create Interface",
67 | "category": "C# Toolbox"
68 | },
69 | {
70 | "command": "csharp-snippet-productivity.createStruct",
71 | "title": "Create Struct",
72 | "category": "C# Toolbox"
73 | },
74 | {
75 | "command": "csharp-snippet-productivity.createRecord",
76 | "title": "Create Record",
77 | "category": "C# Toolbox"
78 | },
79 | {
80 | "command": "csharp-snippet-productivity.addProjectToSolution",
81 | "title": "Add Project",
82 | "category": "C# Toolbox"
83 | },
84 | {
85 | "command": "csharp-snippet-productivity.scaffoldResources",
86 | "title": "Scaffold Resources",
87 | "category": "C# Toolbox"
88 | }
89 | ],
90 | "keybindings": [
91 | {
92 | "command": "csharp-snippet-productivity.createProject",
93 | "key": "ctrl+alt+/ p"
94 | },
95 | {
96 | "command": "csharp-snippet-productivity.addProjectToSolution",
97 | "key": "ctrl+alt+/ a"
98 | },
99 | {
100 | "command": "csharp-snippet-productivity.createClass",
101 | "key": "ctrl+alt+/ c",
102 | "when": "!editorReadonly"
103 | },
104 | {
105 | "command": "csharp-snippet-productivity.createInterface",
106 | "key": "ctrl+alt+/ i",
107 | "when": "!editorReadonly"
108 | },
109 | {
110 | "command": "csharp-snippet-productivity.createStruct",
111 | "key": "ctrl+alt+/ s",
112 | "when": "!editorReadonly"
113 | },
114 | {
115 | "command": "csharp-snippet-productivity.createRecord",
116 | "key": "ctrl+alt+/ r",
117 | "when": "!editorReadonly"
118 | },
119 | {
120 | "command": "csharp-snippet-productivity.scaffoldResources",
121 | "key": "ctrl+alt+/ ctrl+e",
122 | "when": "!editorReadonly"
123 | }
124 | ],
125 | "menus": {
126 | "explorer/context": [
127 | {
128 | "submenu": "cSharp.subMenu",
129 | "group": "navigation@1"
130 | }
131 | ],
132 | "cSharp.subMenu": [
133 | {
134 | "command": "csharp-snippet-productivity.createClass"
135 | },
136 | {
137 | "command": "csharp-snippet-productivity.createInterface"
138 | },
139 | {
140 | "command": "csharp-snippet-productivity.createStruct"
141 | },
142 | {
143 | "command": "csharp-snippet-productivity.createRecord"
144 | },
145 | {
146 | "command": "csharp-snippet-productivity.addProjectToSolution"
147 | },
148 | {
149 | "command": "csharp-snippet-productivity.scaffoldResources"
150 | }
151 | ],
152 | "commandPalette": [
153 | {
154 | "command": "csharp-snippet-productivity.createProject",
155 | "when": "true"
156 | },
157 | {
158 | "command": "csharp-snippet-productivity.createClass",
159 | "when": "true"
160 | },
161 | {
162 | "command": "csharp-snippet-productivity.createInterface",
163 | "when": "true"
164 | },
165 | {
166 | "command": "csharp-snippet-productivity.createStruct",
167 | "when": "true"
168 | },
169 | {
170 | "command": "csharp-snippet-productivity.createRecord",
171 | "when": "true"
172 | },
173 | {
174 | "command": "csharp-snippet-productivity.addProjectToSolution",
175 | "when": "true"
176 | },
177 | {
178 | "command": "csharp-snippet-productivity.scaffoldResources",
179 | "when": "true"
180 | }
181 | ]
182 | },
183 | "submenus": [
184 | {
185 | "id": "cSharp.subMenu",
186 | "label": "C# Toolbox: Options"
187 | }
188 | ],
189 | "configuration": {
190 | "title": "C# Toolbox of Productivity",
191 | "properties": {
192 | "csharp-snippet-productivity.defaultFolderForProjectCreation": {
193 | "type": [
194 | "string",
195 | "null"
196 | ],
197 | "description": "Set the default folder for project creation",
198 | "default": null
199 | },
200 | "csharp-snippet-productivity.multilineComments": {
201 | "type": "boolean",
202 | "description": "Whether the multiline comment highlighter should be active",
203 | "default": true
204 | },
205 | "csharp-snippet-productivity.highlightPlainText": {
206 | "type": "boolean",
207 | "description": "Whether the plaintext comment highlighter should be active",
208 | "default": false
209 | },
210 | "csharp-snippet-productivity.tags": {
211 | "type": "array",
212 | "description": "Colored comments. Select your favorite colors. Changes require a restart of VS Code to take effect",
213 | "default": [
214 | {
215 | "tag": "bug",
216 | "color": "#FF2D00",
217 | "strikethrough": false,
218 | "underline": false,
219 | "backgroundColor": "transparent",
220 | "bold": false,
221 | "italic": false
222 | },
223 | {
224 | "tag": "research",
225 | "color": "#3498DB",
226 | "strikethrough": false,
227 | "underline": false,
228 | "backgroundColor": "transparent",
229 | "bold": false,
230 | "italic": false
231 | },
232 | {
233 | "tag": "//",
234 | "color": "#474747",
235 | "strikethrough": true,
236 | "underline": false,
237 | "backgroundColor": "transparent",
238 | "bold": false,
239 | "italic": false
240 | },
241 | {
242 | "tag": "todo",
243 | "color": "#FF8C00",
244 | "strikethrough": false,
245 | "underline": false,
246 | "backgroundColor": "transparent",
247 | "bold": false,
248 | "italic": false
249 | },
250 | {
251 | "tag": "review",
252 | "color": "#B429A9",
253 | "strikethrough": false,
254 | "underline": false,
255 | "backgroundColor": "transparent",
256 | "bold": false,
257 | "italic": false
258 | }
259 | ]
260 | }
261 | }
262 | }
263 | },
264 | "galleryBanner": {
265 | "color": "#e3f4ff",
266 | "theme": "light"
267 | },
268 | "main": "./out/extension.js",
269 | "scripts": {
270 | "vscode:prepublish": "npm run compile",
271 | "compile": "tsc -p ./",
272 | "watch": "tsc -watch -p ./",
273 | "pretest": "npm run compile && npm run lint",
274 | "lint": "eslint src --ext ts",
275 | "test": "node ./out/test/runTest.js",
276 | "package": "vsce package"
277 | },
278 | "devDependencies": {
279 | "@types/find-parent-dir": "^0.3.3",
280 | "@types/glob": "^8.1.0",
281 | "@types/mocha": "^10.0.6",
282 | "@types/node": "^20.11.0",
283 | "@types/vscode": "^1.85.0",
284 | "@typescript-eslint/eslint-plugin": "^6.18.1",
285 | "@typescript-eslint/parser": "^6.18.1",
286 | "eslint": "^8.56.0",
287 | "glob": "^10.3.10",
288 | "mocha": "^10.2.0",
289 | "typescript": "^5.3.3",
290 | "vscode-test": "^1.5.0"
291 | },
292 | "dependencies": {
293 | "find-parent-dir": "^0.3.1",
294 | "find-up-glob": "^1.0.0",
295 | "n-readlines": "^1.0.1",
296 | "os": "^0.1.2"
297 | }
298 | }
299 |
--------------------------------------------------------------------------------
/src/resource/createProjectWebView/CreateProject.ts:
--------------------------------------------------------------------------------
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | import * as vscode from "vscode";
4 | import { getTargetFrameworks } from "../../utils/sdk.provider";
5 | import { getNonce } from "./GetNonce";
6 | import { CommandFactory, Message } from "../../utils/terminal-cmd.provider";
7 |
8 | export class CreateProjectPanel {
9 | private static context: vscode.ExtensionContext;
10 | private static _filepath: any = "";
11 | private static _panel: vscode.WebviewPanel;
12 | private static _disposables: vscode.Disposable[] = [];
13 | private static _sdks: string[] = [];
14 | private static _defaultFolder: vscode.WorkspaceConfiguration | undefined;
15 | private static _terminal: vscode.Terminal;
16 |
17 | // To avoid direct instantiation use the createOrShow method
18 | private constructor() {}
19 |
20 | // Main method to create or show the panel
21 | public static createOrShow(context: vscode.ExtensionContext): void {
22 | this.context = context;
23 |
24 | const column = vscode.window.activeTextEditor
25 | ? vscode.window.activeTextEditor.viewColumn
26 | : undefined;
27 |
28 | // If we already have a panel, show it.
29 | if (CreateProjectPanel._panel) {
30 | CreateProjectPanel._panel.reveal(column);
31 | CreateProjectPanel.update();
32 | return;
33 | }
34 |
35 | // Otherwise, create a new panel.
36 | const panel = vscode.window.createWebviewPanel(
37 | "create-project",
38 | "Create Project",
39 | column || vscode.ViewColumn.One,
40 | {
41 | enableScripts: true,
42 | localResourceRoots: [
43 | vscode.Uri.joinPath(context.extensionUri, "media"),
44 | vscode.Uri.joinPath(context.extensionUri, "out/compiled"),
45 | ],
46 | }
47 | );
48 |
49 | // adding panel icon
50 | panel.iconPath = vscode.Uri.file(
51 | path.join(this.context.extensionPath, "media", "addProjectIcon.png")
52 | );
53 |
54 | CreateProjectPanel.defaultConstructor(panel);
55 | }
56 |
57 | private static async defaultConstructor(panel: vscode.WebviewPanel) {
58 | this._panel = panel;
59 |
60 | this._defaultFolder = vscode.workspace
61 | .getConfiguration("csharp-snippet-productivity")
62 | .get("defaultFolderForProjectCreation");
63 |
64 | if (!this._defaultFolder) {
65 | vscode.window.showInformationMessage(
66 | "Please set a default folder for project creation"
67 | );
68 | }
69 |
70 | this._filepath = this._defaultFolder;
71 | this._terminal =
72 | vscode.window.activeTerminal === undefined
73 | ? vscode.window.createTerminal()
74 | : vscode.window.activeTerminal;
75 | this._terminal.show();
76 |
77 | // OnPanel Close
78 | this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
79 |
80 | this._panel.webview.onDidReceiveMessage(
81 | async (message) => {
82 | switch (message.command) {
83 | case "createProject":
84 | await this.projectCreation(message);
85 | return;
86 |
87 | case "selectDirectory":
88 | const options: vscode.OpenDialogOptions = {
89 | canSelectMany: false,
90 | openLabel: "Select",
91 | canSelectFiles: false,
92 | canSelectFolders: true,
93 | };
94 | vscode.window.showOpenDialog(options).then((fileUri) => {
95 | if (fileUri && fileUri[0]) {
96 | this._filepath = fileUri[0].fsPath;
97 | this.update(
98 | message.projectGroupSelect,
99 | message.projectGroupSelect,
100 | message.templateName,
101 | message.template,
102 | message.project,
103 | message.solution,
104 | message.framework
105 | );
106 | }
107 | });
108 |
109 | this._panel.webview.postMessage({
110 | command: "updateState",
111 | projectGroupSelect: message.projectGroupSelect,
112 | selectedTemplate: message.template,
113 | });
114 | return;
115 | }
116 | },
117 | null,
118 | this._disposables
119 | );
120 |
121 | // Set the Webview initial html content
122 | this.update();
123 | }
124 |
125 | private static dispose() {
126 | // Clean up our resources
127 | this._panel.dispose();
128 |
129 | CreateProjectPanel._panel = undefined as any;
130 |
131 | while (this._disposables.length) {
132 | const x = this._disposables.pop();
133 | if (x) {
134 | x.dispose();
135 | }
136 | }
137 | }
138 |
139 | private static async projectCreation(message: Message) {
140 | // Adjust filepath from the default or selection
141 | message.filepath = this._filepath;
142 |
143 | // Remove spaces from project and solution names
144 | message.solution = message.solution.replace(/\s+/g, "");
145 | message.project = message.project.replace(/\s+/g, "");
146 |
147 | if (fs.existsSync(this._filepath + "\\" + message.solution)) {
148 | vscode.window.showErrorMessage("Solution folder already exist");
149 | return;
150 | }
151 |
152 | const command = CommandFactory.getCommand(this._terminal, message);
153 | command.execute();
154 |
155 | // setting the current project framework to define the template namespace to be used
156 | CreateProjectPanel.context.globalState.update("framework", message.framework);
157 | }
158 |
159 | private static async update(
160 | projectGroupName: any = "Select Project Type",
161 | projectGroup: any = "api",
162 | templateName: any = "Select Template",
163 | template: any = "console",
164 | project: any = "",
165 | solution: any = "",
166 | framework: any = ""
167 | ) {
168 | const webview = this._panel.webview;
169 |
170 | // list of sdk's
171 | const sdksResource: vscode.Uri = webview.asWebviewUri(
172 | vscode.Uri.joinPath(this.context.extensionUri, "media", "sdks.txt")
173 | );
174 | this._sdks = getTargetFrameworks(sdksResource);
175 |
176 | this._panel.webview.html = this.getHtmlForWebview(
177 | webview,
178 | projectGroupName,
179 | projectGroup,
180 | templateName,
181 | template,
182 | project,
183 | solution,
184 | framework
185 | );
186 | }
187 |
188 | private static getHtmlForWebview(
189 | webview: vscode.Webview,
190 | projectGroupName: any,
191 | projectGroup: any,
192 | templateName: any,
193 | template: any,
194 | project: any,
195 | solution: any,
196 | framework: any
197 | ) {
198 | // main script integration
199 | const scriptUri = webview.asWebviewUri(
200 | vscode.Uri.joinPath(this.context.extensionUri, "media", "main.js")
201 | );
202 |
203 | // Local path to css styles
204 | const styleResetPath = vscode.Uri.joinPath(this.context.extensionUri, "media", "reset.css");
205 | const stylesPathMainPath = vscode.Uri.joinPath(
206 | this.context.extensionUri,
207 | "media",
208 | "vscode.css"
209 | );
210 |
211 | // Uri to load styles into webview
212 | const stylesResetUri = webview.asWebviewUri(styleResetPath);
213 | const stylesMainUri = webview.asWebviewUri(stylesPathMainPath);
214 |
215 | // Use a nonce to only allow specific scripts to be run
216 | const nonce = getNonce();
217 |
218 | // Post message transformation before sending to the webview
219 | const frameworkPostMessage = this._sdks
220 | .map((sdk: string) => {
221 | return ``;
224 | })
225 | .join("");
226 |
227 | return `
228 |
229 |
230 |
231 |
234 |
235 |
236 |
237 |
238 |
239 | Create a new Solution or Project
240 |
241 |
242 | Select the project type
243 |
262 |
263 |
264 | Select the project template
265 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
290 |
291 |
292 |
293 | `;
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/snippets/general.json:
--------------------------------------------------------------------------------
1 | {
2 | "-> TODO Comments": {
3 | "prefix": "todo",
4 | "body": ["// TODO: ${0: comments}"],
5 | "description": "TODO Comments"
6 | },
7 | "-> REVIEW Comments": {
8 | "prefix": "review",
9 | "body": ["// REVIEW: ${0: comments}"],
10 | "description": "To Review Comments"
11 | },
12 | "-> BUG Comments": {
13 | "prefix": "bug",
14 | "body": ["// BUG: ${0: comments}"],
15 | "description": "Bug Comments"
16 | },
17 | "-> RESEARCH Comments": {
18 | "prefix": "research",
19 | "body": ["// RESEARCH: ${0: comments}"],
20 | "description": "Research Comments"
21 | },
22 | "-> Console WriteLine": {
23 | "prefix": "cw",
24 | "body": ["Console.${1|WriteLine,Write|}($0);"],
25 | "description": "Console WriteLine();"
26 | },
27 | "-> WriteLine Interpolation": {
28 | "prefix": "cwi",
29 | "body": ["Console.${1|WriteLine,Write|}($\"${0:Text}\");"],
30 | "description": "Console WriteLine with Interpolation"
31 | },
32 | "-> Console ReadLine": {
33 | "prefix": "cr",
34 | "body": ["Console.ReadLine();", "$0"],
35 | "description": "Console ReadLine"
36 | },
37 | "-> Console ReadKey": {
38 | "prefix": "crk",
39 | "body": ["Console.ReadKey(true);", "$0"],
40 | "description": "Console ReadKey"
41 | },
42 | "-> Console Clear": {
43 | "prefix": "clr",
44 | "body": ["Console.Clear();", "$0"],
45 | "description": "Console Clear"
46 | },
47 | "-> Variable Declaration": {
48 | "prefix": "var",
49 | "body": ["${1:int} ${2:variable} = ${3:0};", "$0"],
50 | "description": "Variable declaration"
51 | },
52 | "-> if statement": {
53 | "prefix": "if",
54 | "body": ["if (${1:condition})", "{", "\t$0", "}"],
55 | "description": "Creates an if statement"
56 | },
57 | "-> Else statement": {
58 | "prefix": "else",
59 | "body": ["else", "{", " $0", "}"],
60 | "description": "Else statement"
61 | },
62 | "-> if else statement": {
63 | "prefix": "ifelse",
64 | "body": ["if (${1:condition})", "{", "\t$0", "}", "else", "{", "\t", "}"],
65 | "description": "Creates an if else statement"
66 | },
67 | "-> Conditional operator": {
68 | "prefix": "iif",
69 | "body": [
70 | "var ${1:variable} = ${2:true};",
71 | "var ${3:result} = (${1} ? ${4:true} : ${5:false});",
72 | "$0"
73 | ],
74 | "description": "Creates a conditional operator"
75 | },
76 | "-> Enum": {
77 | "prefix": "enum",
78 | "body": ["enum ${1:Name}", "{", " $0", "}"],
79 | "description": "Create a Enum Type"
80 | },
81 | "-> Switch statement": {
82 | "prefix": "switch",
83 | "body": ["switch (${1:condition})", "{", " $0", " default:", " break;", "}"],
84 | "description": "Create a Switch statement"
85 | },
86 | "-> Using statement": {
87 | "prefix": "using",
88 | "body": ["using (${1:resource})", "{", "\t$0", "}"],
89 | "description": "Using statement"
90 | },
91 | "-> While loop": {
92 | "prefix": "while",
93 | "body": ["while (${1:condition})", "{", "\t$0", "}"],
94 | "description": "While loop"
95 | },
96 | "-> Do while loop": {
97 | "prefix": "dowhile",
98 | "body": ["do", "{", "\t$0", "} while (${1:condition})"],
99 | "description": "Creates a do while loop"
100 | },
101 | "-> for loop": {
102 | "prefix": "for",
103 | "body": ["for (var ${1:i} = ${2:0}; $1 < ${3:length}; $1++)", "{", "\t$0", "}"],
104 | "description": "Creates a for loop"
105 | },
106 | "-> reverse for loop": {
107 | "prefix": "forr",
108 | "body": ["for (int ${1:i} = ${2:length}; ${1:i} >= ${3:0} ; ${1:i}--)", "{", "\t$0", "}"],
109 | "description": "Creates a reverse for loop"
110 | },
111 | "-> foreach statement": {
112 | "prefix": "foreach",
113 | "body": ["foreach (var ${1:item} in ${2:collection})", "{", "\t$0", "}"],
114 | "description": "Creates a foreach statement"
115 | },
116 | "-> Array": {
117 | "prefix": "arr",
118 | "body": ["${1:type}[] ${2:arrayName} = new ${1}[${3:size}];", "$0"],
119 | "description": "Creates an array"
120 | },
121 | "-> Var Array": {
122 | "prefix": "varr",
123 | "body": ["var ${1:arrayName} = new ${2:type}[${3:size}];", "$0"],
124 | "description": "Creates an array using var"
125 | },
126 | "-> List": {
127 | "prefix": "lst",
128 | "body": ["List<${1:type}> ${2:arrayName} = new List<${1}>();", "$0"],
129 | "description": "Creates a list"
130 | },
131 | "-> Var List": {
132 | "prefix": "vlst",
133 | "body": ["var ${1:arrayName} = new List<${2}>();", "$0"],
134 | "description": "Creates a list with var"
135 | },
136 | "-> IList": {
137 | "prefix": "ilst",
138 | "body": ["IList<${1:type}> ${2:arrayName} = new List<${1}>();", "$0"],
139 | "description": "Creates a generic list"
140 | },
141 | "-> Dictionary": {
142 | "prefix": "dic",
143 | "body": [
144 | "Dictionary<${1:key}, ${2:value}> ${3:dictionaryName} = new Dictionary<${1},${2}>();",
145 | "$0"
146 | ],
147 | "description": "Creates a dictionary"
148 | },
149 | "-> Var Dictionary": {
150 | "prefix": "vdic",
151 | "body": ["var ${1:dictionaryName} = new Dictionary<${2},${3}>();", "$0"],
152 | "description": "Creates a dictionary with var"
153 | },
154 | "-> Concurrent Dictionary": {
155 | "prefix": "cdic",
156 | "body": [
157 | "ConcurrentDictionary<${1:key}, ${2:value}> ${3:dictionaryName} = new ConcurrentDictionary<${1},${2}>();",
158 | "$0"
159 | ],
160 | "description": "Creates a concurrent dictionary"
161 | },
162 | "-> IDictionary": {
163 | "prefix": "idic",
164 | "body": [
165 | "IDictionary<${1:key}, ${2:value}> ${3:dictionaryName} = new Dictionary<${1},${2}>();",
166 | "$0"
167 | ],
168 | "description": "Creates a idictionary"
169 | },
170 | "-> Function": {
171 | "prefix": "func",
172 | "body": ["public ${1:void} ${2:functionName}()", "{", "\t$0", "}"],
173 | "description": "Creates a standard function"
174 | },
175 | "-> Virtual Function": {
176 | "prefix": "vfunc",
177 | "body": ["public virtual ${1:void} ${2:functionName}()", "{", "\t$0", "}"],
178 | "description": "Creates a virtual function"
179 | },
180 | "-> Abstract Function": {
181 | "prefix": "afunc",
182 | "body": ["public abstract ${1:void} ${2:functionName}();", "\t$0"],
183 | "description": "Creates a virtual function"
184 | },
185 | "-> Return Function": {
186 | "prefix": "rfunc",
187 | "body": ["public ${1:int} ${2:functionName}()", "{", "\t$0", "\treturn 0;", "}"],
188 | "description": "Creates a function with return type"
189 | },
190 | "-> Static Function": {
191 | "prefix": "sfunc",
192 | "body": ["public static ${1:void} ${2:functionName}()", "{", "\t$0", "}"],
193 | "description": "Creates a static function"
194 | },
195 | "-> Params Function": {
196 | "prefix": "pfunc",
197 | "body": ["public ${1:void} ${2:functionName}(params ${3:type}[] list)", "{", "\t$0", "}"],
198 | "description": "Creates a static function"
199 | },
200 | "-> Exception Try Catch": {
201 | "prefix": "try",
202 | "body": ["try", "{", "\t$0", "}", "catch (${1:Exception} ${2:ex})", "{", "\t // TODO", "}"],
203 | "description": "Creates a try catch block"
204 | },
205 | "-> Namespace": {
206 | "prefix": "namespace",
207 | "body": ["namespace ${1:name}", "{", "\t$0", "}"],
208 | "description": "Add namespace based on file directory"
209 | },
210 | "-> Struct": {
211 | "prefix": "struct",
212 | "body": ["struct ${1:structName}", "{", "\t$0", "}"],
213 | "description": "Creates a struct"
214 | },
215 | "-> Class": {
216 | "prefix": "class",
217 | "body": ["public class ${TM_FILENAME_BASE}", "{", "\t$0", "}"],
218 | "description": "Creates a basic class"
219 | },
220 | "-> Class Constructor": {
221 | "prefix": "ctor",
222 | "body": ["public ${TM_FILENAME_BASE}()", "{", "\t$0", "}"],
223 | "description": "Creates a constructor"
224 | },
225 | "-> Object Instantiation": {
226 | "prefix": "instantiate",
227 | "body": ["${1:class} ${2:objectName} = new ${1}($3);", "$0"],
228 | "description": "Creates an object"
229 | },
230 | "-> Full Class": {
231 | "prefix": "fclass",
232 | "body": [
233 | "public class ${TM_FILENAME_BASE}",
234 | "{",
235 | "\tpublic ${TM_FILENAME_BASE}(){}",
236 | "\tpublic override string ToString(){throw new NotImplementedException();}",
237 | "\tpublic override bool Equals(object obj){throw new NotImplementedException();}",
238 | "\tpublic override int GetHashCode(){throw new NotImplementedException();}",
239 | "\t$0",
240 | "}"
241 | ],
242 | "description": "Creates a complete class implementation"
243 | },
244 | "-> Static Class": {
245 | "prefix": "sclass",
246 | "body": ["public static class ${TM_FILENAME_BASE}", "{", "\t$0", "}"],
247 | "description": "Creates a basic static class"
248 | },
249 | "-> Abstract Class": {
250 | "prefix": "aclass",
251 | "body": ["public abstract class ${TM_FILENAME_BASE}", "{", "\t$0", "}"],
252 | "description": "Creates an abstract class"
253 | },
254 | "-> Interface": {
255 | "prefix": "interface",
256 | "body": ["public interface I${TM_FILENAME_BASE}", "{", "\t$0", "}"],
257 | "description": "Creates an interface"
258 | },
259 | "-> Properties": {
260 | "prefix": "prop",
261 | "body": ["public ${1:type} ${2:Property} { get; set; }"],
262 | "description": "Creates property"
263 | },
264 | "-> Expanded Properties": {
265 | "prefix": "prope",
266 | "body": [
267 | "private ${1} _${3:property};",
268 | "public ${1:type} ${2:Property}",
269 | "{",
270 | "\tget => _${3}; ",
271 | "\tset => _${3} = value;",
272 | "}"
273 | ],
274 | "description": "Creates property"
275 | },
276 | "-> Record": {
277 | "prefix": "record",
278 | "body": ["public record ${1:RecordName}($0);"],
279 | "description": "Creates a record model"
280 | },
281 | "-> Regex": {
282 | "prefix": "regex",
283 | "body": [
284 | "// ********** Character Class **********",
285 | "// . any character except newline",
286 | "// \\w\\d\\s word, digit, whitespace.",
287 | "// \\W\\D\\S not word, digit, whitespace",
288 | "// [abc] any of a, b, or c",
289 | "// [^abc] not a, b, or c",
290 | "// [a-g] character between a & g",
291 | "// ********** Anchors **********",
292 | "// ^abc$ start / end of the string",
293 | "// \\b\\B word, not-word boundary",
294 | "// ********** Escaped Characters **********",
295 | "// \\.\\*\\\\ escaped special characters",
296 | "// \\t\\n\\r tab, linefeed, carriage return",
297 | "// ********** Groups & Lookaround **********",
298 | "// (abc) capture group",
299 | "// \\1 backreference to group #1",
300 | "// (?:abc) non-capturing group",
301 | "// (?=abc) positive lookahead,",
302 | "// (?!abc) negative lookahead",
303 | "// ********** Quantifiers & Alternations **********",
304 | "// a*a+a? 0 or more, 1 or more, 0 or 1",
305 | "// a{5}a{2,} exactly five, two or more",
306 | "// a{1,3} between one & three",
307 | "// a+?a{2,}? match as few as possible",
308 | "// ab|cd match ab or cd"
309 | ],
310 | "description": "Regex cheat sheet"
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | C# Snippet Productivity
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ## Goal
21 |
22 | > - C# Snippet Productivity aims to increase the use of vscode editor as the main tool for console, web and game development in C# Programming Language providing the same shortcuts, efficiency, intellisense that is offered by Visual Studio Community.
23 | > - One of the first objectives is to reduce the amount of extensions downloaded for C# reducing the time and effort on configuration that has to be done by the user as well as to avoid extensions conflicts. Also great features to speed up your development workflow.
24 |
25 | ## Changelog
26 |
27 | > [Click here](https://github.com/rsaz/csharp-snippet-productivity/blob/main/CHANGELOG.md)
28 |
29 | ## What's new in 2.1.1
30 |
31 | > - **_New Feature added_**: Added all current scaffold commands from context menu available in the command palette.
32 | > - **_New Feature added_**: Added template validation against the .NET SDK installed on the machine.
33 | > - **_Fix_**: Adjusted the AddProject command to work with the new template validation and project group selection.
34 |
35 | Observations:
36 |
37 | ```diff
38 | - Not all templates from Project Creation are tested and validated. Please report any issues with the following information to help us to improve the extension:
39 | - - Template Name
40 | - - Template Framework
41 | ```
42 |
43 | The commands available in the context menu follow a different workflow than the commands available in the command palette. The commands in the context menu will create the project or resource in the same clicked folder.
44 |
45 | The commands in the command palette will ask the user to select the project, create or select the folder, and then create the project.
46 |
47 | Expect a different interaction when using the commands in the context menu and the command palette.
48 |
49 | All commands are available via shortcut keys. You can find the shortcut keys in the command palette.
50 |
51 | - `Ctrl + alt + /` + `p` - Create Project
52 | - `Ctrl + alt + /` + `c` - Create Class
53 | - `Ctrl + alt + /` + `i` - Create Interface
54 | - `Ctrl + alt + /` + `r` - Create Record
55 | - `Ctrl + alt + /` + `s` - Create Struct
56 | - `Ctrl + alt + /` + `a` - Add Project to Solution
57 |
58 | ## What's new in 2.0.1
59 |
60 | > - **_Fix_**: Fixed issues related to design patterns snippets. Added a more modern code approach to the snippets.
61 |
62 | ## What's new in 2.0.0
63 |
64 | > - **_All Project Types_**: Added support for all project types and templates under project creation.
65 | > - **_Support for .NET 7.0 and .NET 8.0_**
66 | > - **_Performance improvements_**: Extension loading time decreased and command execution time decreased.
67 | > - **_Snippet improvements_**: Fixed snippet conflicts and non standard snippets.
68 | > - **_Project Template and Framework Compatibility Validation_**: Validates the project template and framework compatibility based on the .NET SDK installed on the machine.
69 | > - **_Add or Create Project with empty space_**: Added validation to avoid creating projects with empty spaces.
70 | > - **_Suggests the user to add the default folder_**: Reinforce the use of the default folder for project creation.
71 |
72 | ## What's new in 1.3.0
73 |
74 | > - **_New Feature added_**: Minimal Web API, MStest, xUnit, NUnit project template added.
75 | > - **_Fix_**: Creating Solution with the same name in the same directory.
76 | > - **_Improvement_**: Extension loading time decreased.
77 |
78 | ## What's new in 1.2.9
79 |
80 | > - **_New Feature added_**: Scoped namespaces in the .NET 6.0
81 | > - **_Improvement_**: Project creation highlighting the `create project button` after the project name is typed and tab is pressed.
82 |
83 | ## What's new in 1.2.8
84 |
85 | > - **_New Feature added_**: Project support for C# .NET Core 6.0
86 |
87 | ## Current features
88 |
89 | > - **_Fix_**: Classes, Interfaces, and other types created correctly even when the user type incorrect names.
90 | > - **_New Features added_**: Added a default folder for project creation. Add this configuration to your settings with your path: `"csharp-snippet-productivity.defaultFolderForProjectCreation": "D:\\"` **{Your path}**
91 | > - **_New Features added_**:
92 | > - **_Add Project to a Solution_** : Capability to add projects to the same solution with a click of a button. You can select a different project framework as well as the template.
93 | >
94 | > 
95 | >
96 | > - **_Submenu With Options_** :
97 | > - Create Class
98 | > - Create Interface
99 | > - Create Record
100 | > - Create Struct
101 | > - **_Fix_**: .NET target frameworks list on project creation are based on OS and SDKs installed.
102 | > - **_Enhancement_**: Design patterns snippets added. It will create a commented pattern code to be used as reference
103 | > - **_singleton_** : Creational singleton pattern
104 | > - **_factoryMethod_** : Creational factory method pattern
105 | > - **_adapter_** : Structural adapter pattern
106 | > - **_observer_**: Structural observer pattern
107 | > - **_Enhancement_**: Regex snippet cheat sheet added.
108 | > - **_regex_** : Regex cheat sheet
109 | > - When creating classes or interfaces system will consider if you have a ``YourUniqueNamespace` ` tag on your **_.csproj_**. If the tag is not found system will use your project name as your root namespace
110 | > - Added command to create Class from the context/menu
111 | > - Added command to create Interface from the context/menu
112 | > - How to use:
113 | > - Right click in the project folder or any folder inside of your project folder and select either Create Class or Create Interface
114 | > - Give it a name of your file and class or interface will be created automatically in the selected folder
115 | >
116 | > 
117 |
118 | ### Command to Create Solution or Project
119 |
120 | > Command to create projects
121 | >
122 | > Press CTRL + SHIFT + P: Then type: Create Project
123 | > [ C# Toolbox: Create Project ]
124 | >
125 | > > 
126 | >
127 | > - Projects templates supported:
128 | > - Blazor Server App
129 | > - Blazor WebAssembly App
130 | > - Console Application
131 | > - Class Library
132 | > - .NET Core: Empty, MVC, Razor Page, Angular SPA, React SPA, React/Redux SPA, Web Api, GRPC Services, Razor Class Library
133 | >
134 | > - Added snippets for creating arrays, lists and dictionaries using var
135 | > - var myArray = new type[size];
136 | > - var myList = new List\();
137 | > - var myDictionary = new Dictionary\();
138 |
139 | > ### Smart Comments
140 | >
141 | > - Colorful and configurable comments to better emphasize your work
142 | > - Snippets:
143 | > - **_todo_** : comments
144 | > - **_review_** : comments
145 | > - **_bug_** : comments
146 | > - **_research_** : comments
147 | > > 
148 |
149 | > ### General Snippets
150 | >
151 | > - **_cw_** : console write/writeline
152 | > - **_cwi_** : console writeline interpolation
153 | > - **_cr_** : console readline
154 | > - **_crk_**: console readkey
155 | > - **_clr_**: console clear
156 | > - **_var_**: variable declaration
157 | > - **_if_**: if statement
158 | > - **_else_**: else statement
159 | > - **_ifelse_**: if/else statement
160 | > - **_iif_**: conditional operator
161 | > - **_enum_**: enum type
162 | > - **_switch_**: switch statement
163 | > - **_using_**: using statement
164 | > - **_while_**: while loop
165 | > - **_dowhile_**: do/while loop
166 | > - **_for_**: for loop
167 | > - **_foreach_**: foreach loop
168 | > - **_arr_**: array structure
169 | > - **_varr_**: array structure using var
170 | > - **_lst_**: list structure
171 | > - **_vlst_**: list structure using var
172 | > - **_ilst_**: Ilist structure
173 | > - **_dic_**: dictionary structure
174 | > - **_vdic_**: dictionary structure using var
175 | > - **_cdic_**: concurrent dictionary structure
176 | > - **_idic_**: idictionary structure
177 | > - **_func_**: create a void function
178 | > - **_vfunc_**: create a virtual function
179 | > - **_afunc_**: create an abstract function
180 | > - **_rfunc_**: create a function with return type
181 | > - **_sfunc_**: create a static function
182 | > - **_pfunc_**: create a function using params
183 | > - **_try_**: create a try/catch block
184 | > - **_namespace_**: add namespace
185 | > - **_struct_**: create a struct
186 | > - **_class_**: create a class based on the file name
187 | > - **_ctor_**: class constructor
188 | > - **_instantiate_**: object instantiation
189 | > - **_fclass_**: class created with a default constructor and three overrides [ToString, Equals, GetHashCode]
190 | > - **_sclass_**: create a static class
191 | > - **_aclass_**: create an abstract class
192 | > - **_interface_**: create an interface based on the file name
193 | > - **_prop_**: create a property
194 | > - **_prope_**: create an expanded property
195 | > - **_record_**: create a record
196 | >
197 | > ### XML Documentation Snippets
198 | >
199 | > - **_xml-summary_**: this tag adds brief information about a type or member
200 | > - **_xml-remarks_**: the [remarks] tag supplements the information about types or members that the [summary] tag provides
201 | > - **_xml-returns_**: the [returns] tag describes the return value of a method declaration
202 | > - **_xml-value_**: the [value] tag is similar to the [returns] tag, except that you use it for properties
203 | > - **_xml-example_**: You use the [example] tag to include an example in your XML documentation. This involves using the child [code] tag
204 | > - **_xml-para_**: you use the [para] tag to format the content within its parent tag. [para] is usually used inside a tag, such as [remarks] or [returns], to divide text into paragraphs. You can format the contents of the [remarks] tag for your class definition
205 | > - **_xml-c_**: still on the topic of formatting, you use the [c] tag for marking part of text as code. It's like the [code] tag but inline. It's useful when you want to show a quick code example as part of a tag's content
206 | > - **_xml-exception_**: by using the [exception] tag, you let your developers know that a method can throw specific exceptions
207 | > - **_xml-see_**: the [see] tag lets you create a clickable link to a documentation page for another code element
208 | > - **_xml-seealso_**: you use the [seealso] tag in the same way you do the [see] tag. The only difference is that its content is typically placed in a \"See Also\" section
209 | > - **_xml-param_**: you use the [param] tag to describe a method's parameters
210 | > - **_xml-typeparam_**: You use [typeparam] tag just like the [param] tag but for generic type or method declarations to describe a generic parameter
211 | > - **_xml-paramref_**: sometimes you might be in the middle of describing what a method does in what could be a [summary] tag, and you might want to make a reference to a parameter
212 | > - **_xml-typeparamref_**: you use [typeparamref] tag just like the [paramref] tag but for generic type or method declarations to describe a generic parameter
213 | > - **_xml-list_**: you use the [list] tag to format documentation information as an ordered list, unordered list, or table
214 | > - **_xml-inheritdoc_**: you can use the [inheritdoc] tag to inherit XML comments from base classes, interfaces, and similar methods
215 | > - **_xml-include_**: the [include] tag lets you refer to comments in a separate XML file that describe the types and members in your source code, as opposed to placing documentation comments directly in your source code file
216 |
217 | ## How to use
218 |
219 | > - All the snippets comments are shown as -> snippet name
220 | > - Snippets were created thinking on productivity and the extensive use of tab key
221 | >
222 | > 
223 | >
224 | > 
225 | >
226 | > - Colored comments were created to increase visibility of todo's, reviews, bugs and research
227 | >
228 | > 
229 |
230 | ## Do you want to contribute?
231 |
232 | ### Guidelines
233 |
234 | > 1. **Fork** the original repository to your own repository
235 | > 2. **Clone** it to your local
236 | > 3. **Contribute to it**
237 | > 4. **Push** it to your remote repo
238 | > 5. Send a **PR** `[Pull Request]` to the main repo
239 | > 6. Your contribution will be evaluated then we will merge your changes with the original repository. ❤
240 |
241 | ### For more information
242 |
243 | - [Richard Zampieri](https://github.com/rsaz)
244 |
245 | **Enjoy!**
246 |
--------------------------------------------------------------------------------
/src/resource/smartComments/Parser.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 |
3 | interface CommentTag {
4 | tag: string;
5 | escapedTag: string;
6 | decoration: vscode.TextEditorDecorationType;
7 | ranges: Array;
8 | }
9 |
10 | interface Contributions {
11 | multilineComments: boolean;
12 | useJSDocStyle: boolean;
13 | highlightPlainText: boolean;
14 | tags: [{
15 | tag: string;
16 | color: string;
17 | strikethrough: boolean;
18 | underline: boolean;
19 | bold: boolean;
20 | italic: boolean;
21 | backgroundColor: string;
22 | }];
23 | }
24 |
25 | export class Parser {
26 | private tags: CommentTag[] = [];
27 | private expression: string = "";
28 |
29 | private delimiter: string = "";
30 | private blockCommentStart: string = "";
31 | private blockCommentEnd: string = "";
32 |
33 | private highlightSingleLineComments = true;
34 | private highlightMultilineComments = false;
35 | private highlightJSDoc = false;
36 |
37 | // this will allow plaintext files to show comment highlighting if switched on
38 | private isPlainText = false;
39 |
40 | // this is used to prevent the first line of the file (specifically python) from coloring like other comments
41 | private ignoreFirstLine = false;
42 |
43 | // this is used to trigger the events when a supported language code is found
44 | public supportedLanguage = true;
45 |
46 | // Read from the package.json
47 | private contributions: Contributions = vscode.workspace.getConfiguration('csharp-snippet-productivity') as any;
48 |
49 | public constructor() {
50 | this.setTags();
51 | }
52 |
53 | /**
54 | * Sets the regex to be used by the matcher based on the config specified in the package.json
55 | * @param languageCode The short code of the current language
56 | * https://code.visualstudio.com/docs/languages/identifiers
57 | */
58 | // eslint-disable-next-line @typescript-eslint/naming-convention
59 | public SetRegex(languageCode: string) {
60 | this.setDelimiter(languageCode);
61 |
62 | // if the language isn't supported, we don't need to go any further
63 | if (!this.supportedLanguage) {
64 | return;
65 | }
66 |
67 | let characters: Array = [];
68 | for (let commentTag of this.tags) {
69 | characters.push(commentTag.escapedTag);
70 | }
71 |
72 | if (this.isPlainText && this.contributions.highlightPlainText) {
73 | // start by tying the regex to the first character in a line
74 | this.expression = "(^)+([ \\t]*[ \\t]*)";
75 | } else {
76 | // start by finding the delimiter (//, --, #, ') with optional spaces or tabs
77 | this.expression = "(" + this.delimiter.replace(/\//ig, "\\/") + ")+( |\t)*";
78 | }
79 |
80 | // Apply all configurable comment start tags
81 | this.expression += "(";
82 | this.expression += characters.join("|");
83 | this.expression += ")+(.*)";
84 | }
85 |
86 | /**
87 | * Finds all single line comments delimited by a given delimiter and matching tags specified in package.json
88 | * @param activeEditor The active text editor containing the code document
89 | */
90 | // eslint-disable-next-line @typescript-eslint/naming-convention
91 | public FindSingleLineComments(activeEditor: vscode.TextEditor): void {
92 |
93 | // If highlight single line comments is off, single line comments are not supported for this language
94 | if (!this.highlightSingleLineComments) {return;}
95 |
96 | let text = activeEditor.document.getText();
97 |
98 | // if it's plain text, we have to do multiline regex to catch the start of the line with ^
99 | let regexFlags = (this.isPlainText) ? "igm" : "ig";
100 | let regEx = new RegExp(this.expression, regexFlags);
101 |
102 | let match: any;
103 | while (match = regEx.exec(text)) {
104 | let startPos = activeEditor.document.positionAt(match.index);
105 | let endPos = activeEditor.document.positionAt(match.index + match[0].length);
106 | let range = { range: new vscode.Range(startPos, endPos) };
107 |
108 | // Required to ignore the first line of .py files (#61)
109 | if (this.ignoreFirstLine && startPos.line === 0 && startPos.character === 0) {
110 | continue;
111 | }
112 |
113 | // Find which custom delimiter was used in order to add it to the collection
114 | let matchTag = this.tags.find(item => item.tag.toLowerCase() === match[3].toLowerCase());
115 |
116 | if (matchTag) {
117 | matchTag.ranges.push(range);
118 | }
119 | }
120 | }
121 |
122 | /**
123 | * Finds block comments as indicated by start and end delimiter
124 | * @param activeEditor The active text editor containing the code document
125 | */
126 | // eslint-disable-next-line @typescript-eslint/naming-convention
127 | public FindBlockComments(activeEditor: vscode.TextEditor): void {
128 |
129 | // If highlight multiline is off in package.json or doesn't apply to his language, return
130 | if (!this.highlightMultilineComments) {return;}
131 |
132 | let text = activeEditor.document.getText();
133 |
134 | // Build up regex matcher for custom delimiter tags
135 | let characters: Array = [];
136 | for (let commentTag of this.tags) {
137 | characters.push(commentTag.escapedTag);
138 | }
139 |
140 | // Combine custom delimiters and the rest of the comment block matcher
141 | let commentMatchString = "(^)+([ \\t]*[ \\t]*)(";
142 | commentMatchString += characters.join("|");
143 | commentMatchString += ")([ ]*|[:])+([^*/][^\\r\\n]*)";
144 |
145 | // Use start and end delimiters to find block comments
146 | let regexString = "(^|[ \\t])(";
147 | regexString += this.blockCommentStart;
148 | regexString += "[\\s])+([\\s\\S]*?)(";
149 | regexString += this.blockCommentEnd;
150 | regexString += ")";
151 |
152 | let regEx = new RegExp(regexString, "gm");
153 | let commentRegEx = new RegExp(commentMatchString, "igm");
154 |
155 | // Find the multiline comment block
156 | let match: any;
157 | while (match = regEx.exec(text)) {
158 | let commentBlock = match[0];
159 |
160 | // Find the line
161 | let line;
162 | while (line = commentRegEx.exec(commentBlock)) {
163 | let startPos = activeEditor.document.positionAt(match.index + line.index + line[2].length);
164 | let endPos = activeEditor.document.positionAt(match.index + line.index + line[0].length);
165 | let range: vscode.DecorationOptions = { range: new vscode.Range(startPos, endPos) };
166 |
167 | // Find which custom delimiter was used in order to add it to the collection
168 | let matchString = line[3] as string;
169 | let matchTag = this.tags.find(item => item.tag.toLowerCase() === matchString.toLowerCase());
170 |
171 | if (matchTag) {
172 | matchTag.ranges.push(range);
173 | }
174 | }
175 | }
176 | }
177 |
178 | /**
179 | * Finds all multiline comments starting with "*"
180 | * @param activeEditor The active text editor containing the code document
181 | */
182 | // eslint-disable-next-line @typescript-eslint/naming-convention
183 | public FindJSDocComments(activeEditor: vscode.TextEditor): void {
184 |
185 | // If highlight multiline is off in package.json or doesn't apply to his language, return
186 | if (!this.highlightMultilineComments && !this.highlightJSDoc) {return;}
187 |
188 | let text = activeEditor.document.getText();
189 |
190 | // Build up regex matcher for custom delimiter tags
191 | let characters: Array = [];
192 | for (let commentTag of this.tags) {
193 | characters.push(commentTag.escapedTag);
194 | }
195 |
196 | // Combine custom delimiters and the rest of the comment block matcher
197 | let commentMatchString = "(^)+([ \\t]*\\*[ \\t]*)("; // Highlight after leading *
198 | let regEx = /(^|[ \t])(\/\*\*)+([\s\S]*?)(\*\/)/gm; // Find rows of comments matching pattern /** */
199 |
200 | commentMatchString += characters.join("|");
201 | commentMatchString += ")([ ]*|[:])+([^*/][^\\r\\n]*)";
202 |
203 | let commentRegEx = new RegExp(commentMatchString, "igm");
204 |
205 | // Find the multiline comment block
206 | let match: any;
207 | while (match = regEx.exec(text)) {
208 | let commentBlock = match[0];
209 |
210 | // Find the line
211 | let line;
212 | while (line = commentRegEx.exec(commentBlock)) {
213 | let startPos = activeEditor.document.positionAt(match.index + line.index + line[2].length);
214 | let endPos = activeEditor.document.positionAt(match.index + line.index + line[0].length);
215 | let range: vscode.DecorationOptions = { range: new vscode.Range(startPos, endPos) };
216 |
217 | // Find which custom delimiter was used in order to add it to the collection
218 | let matchString = line[3] as string;
219 | let matchTag = this.tags.find(item => item.tag.toLowerCase() === matchString.toLowerCase());
220 |
221 | if (matchTag) {
222 | matchTag.ranges.push(range);
223 | }
224 | }
225 | }
226 | }
227 |
228 | /**
229 | * Apply decorations after finding all relevant comments
230 | * @param activeEditor The active text editor containing the code document
231 | */
232 | // eslint-disable-next-line @typescript-eslint/naming-convention
233 | public ApplyDecorations(activeEditor: vscode.TextEditor): void {
234 | for (let tag of this.tags) {
235 | activeEditor.setDecorations(tag.decoration, tag.ranges);
236 |
237 | // clear the ranges for the next pass
238 | tag.ranges.length = 0;
239 | }
240 | }
241 |
242 | /**
243 | * Sets the comment delimiter [//, #, --, '] of a given language
244 | * @param languageCode The short code of the current language
245 | * https://code.visualstudio.com/docs/languages/identifiers
246 | */
247 | private setDelimiter(languageCode: string): void {
248 | this.supportedLanguage = true;
249 | this.ignoreFirstLine = false;
250 | this.isPlainText = false;
251 |
252 | switch (languageCode) {
253 | case "asciidoc":
254 | this.setCommentFormat("//", "////", "////");
255 | break;
256 |
257 | case "apex":
258 | case "javascript":
259 | case "javascriptreact":
260 | case "typescript":
261 | case "typescriptreact":
262 | this.setCommentFormat("//", "/*", "*/");
263 | this.highlightJSDoc = true;
264 | break;
265 |
266 | case "al":
267 | case "c":
268 | case "cpp":
269 | case "csharp":
270 | case "dart":
271 | case "flax":
272 | case "fsharp":
273 | case "go":
274 | case "groovy":
275 | case "haxe":
276 | case "java":
277 | case "jsonc":
278 | case "kotlin":
279 | case "less":
280 | case "pascal":
281 | case "objectpascal":
282 | case "php":
283 | case "rust":
284 | case "scala":
285 | case "sass":
286 | case "scss":
287 | case "shaderlab":
288 | case "stylus":
289 | case "swift":
290 | case "verilog":
291 | case "vue":
292 | this.setCommentFormat("//", "/*", "*/");
293 | break;
294 |
295 | case "css":
296 | this.setCommentFormat("/*", "/*", "*/");
297 | break;
298 |
299 | case "coffeescript":
300 | case "dockerfile":
301 | case "gdscript":
302 | case "graphql":
303 | case "julia":
304 | case "makefile":
305 | case "perl":
306 | case "perl6":
307 | case "puppet":
308 | case "r":
309 | case "ruby":
310 | case "shellscript":
311 | case "tcl":
312 | case "yaml":
313 | this.delimiter = "#";
314 | break;
315 |
316 | case "tcl":
317 | this.delimiter = "#";
318 | this.ignoreFirstLine = true;
319 | break;
320 |
321 | case "elixir":
322 | case "python":
323 | this.setCommentFormat("#", '"""', '"""');
324 | this.ignoreFirstLine = true;
325 | break;
326 |
327 | case "nim":
328 | this.setCommentFormat("#", "#[", "]#");
329 | break;
330 |
331 | case "powershell":
332 | this.setCommentFormat("#", "<#", "#>");
333 | break;
334 |
335 | case "ada":
336 | case "hive-sql":
337 | case "pig":
338 | case "plsql":
339 | case "sql":
340 | this.delimiter = "--";
341 | break;
342 |
343 | case "lua":
344 | this.setCommentFormat("--", "--[[", "]]");
345 | break;
346 |
347 | case "elm":
348 | case "haskell":
349 | this.setCommentFormat("--", "{-", "-}");
350 | break;
351 |
352 | case "brightscript":
353 | case "diagram": // ? PlantUML is recognized as Diagram (diagram)
354 | case "vb":
355 | this.delimiter = "'";
356 | break;
357 |
358 | case "bibtex":
359 | case "erlang":
360 | case "latex":
361 | case "matlab":
362 | this.delimiter = "%";
363 | break;
364 |
365 | case "clojure":
366 | case "racket":
367 | case "lisp":
368 | this.delimiter = ";";
369 | break;
370 |
371 | case "terraform":
372 | this.setCommentFormat("#", "/*", "*/");
373 | break;
374 |
375 | case "COBOL":
376 | this.delimiter = this.escapeRegExp("*>");
377 | break;
378 |
379 | case "fortran-modern":
380 | this.delimiter = "c";
381 | break;
382 |
383 | case "SAS":
384 | case "stata":
385 | this.setCommentFormat("*", "/*", "*/");
386 | break;
387 |
388 | case "html":
389 | case "markdown":
390 | case "xml":
391 | this.setCommentFormat("");
392 | break;
393 |
394 | case "twig":
395 | this.setCommentFormat("{#", "{#", "#}");
396 | break;
397 |
398 | case "genstat":
399 | this.setCommentFormat("\\", '"', '"');
400 | break;
401 |
402 | case "cfml":
403 | this.setCommentFormat("");
404 | break;
405 |
406 | case "plaintext":
407 | this.isPlainText = true;
408 |
409 | // If highlight plaintext is enabeld, this is a supported language
410 | this.supportedLanguage = this.contributions.highlightPlainText;
411 | break;
412 |
413 | default:
414 | this.supportedLanguage = false;
415 | break;
416 | }
417 | }
418 |
419 | /**
420 | * Sets the highlighting tags up for use by the parser
421 | */
422 | private setTags(): void {
423 | let items = this.contributions.tags;
424 | for (let item of items) {
425 | let options: vscode.DecorationRenderOptions = { color: item.color, backgroundColor: item.backgroundColor };
426 |
427 | // ? the textDecoration is initialized to empty so we can concat a preceding space on it
428 | options.textDecoration = "";
429 |
430 | if (item.strikethrough) {
431 | options.textDecoration += "line-through";
432 | }
433 |
434 | if (item.underline) {
435 | options.textDecoration += " underline";
436 | }
437 |
438 | if (item.bold) {
439 | options.fontWeight = "bold";
440 | }
441 |
442 | if (item.italic) {
443 | options.fontStyle = "italic";
444 | }
445 |
446 | let escapedSequence = item.tag.replace(/([()[{*+.$^\\|?])/g, '\\$1');
447 | this.tags.push({
448 | tag: item.tag,
449 | escapedTag: escapedSequence.replace(/\//gi, "\\/"), // ! hardcoded to escape slashes
450 | ranges: [],
451 | decoration: vscode.window.createTextEditorDecorationType(options)
452 | });
453 | }
454 | }
455 |
456 | /**
457 | * Escapes a given string for use in a regular expression
458 | * @param input The input string to be escaped
459 | * @returns {string} The escaped string
460 | */
461 | private escapeRegExp(input: string): string {
462 | return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
463 | }
464 |
465 | /**
466 | * Set up the comment format for single and multiline highlighting
467 | * @param singleLine The single line comment delimiter. If NULL, single line is not supported
468 | * @param start The start delimiter for block comments
469 | * @param end The end delimiter for block comments
470 | */
471 | private setCommentFormat(singleLine: string | null, start: string, end: string): void {
472 |
473 | // If no single line comment delimiter is passed, single line comments are not supported
474 | if (singleLine) {
475 | this.delimiter = this.escapeRegExp(singleLine);
476 | }
477 | else {
478 | this.highlightSingleLineComments = false;
479 | }
480 |
481 | this.blockCommentStart = this.escapeRegExp(start);
482 | this.blockCommentEnd = this.escapeRegExp(end);
483 | this.highlightMultilineComments = this.contributions.multilineComments;
484 | }
485 | }
--------------------------------------------------------------------------------