You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on May 4, 2023. It is now read-only.

2
2
3
3
# Development documentation
4
+
4
5
This documentation provides a high level view on the technical components of the extension project.
5
6
6
7
# Table of contents
7
-
1.[General](#general)
8
+
9
+
1.[General](#general)
8
10
2.[Releasing](#releasing)
9
11
3.[Project structure](#project-structure)
10
12
4.[Feature overview](#feature-overview)
11
-
1.[Shortcut snippets](#shortcut-snippets)
12
-
2.[Inline completion](#inline-completion)
13
-
3.[Snippet search](#snippet-search)
14
-
4.[Settings](#settings)
13
+
1.[Shortcut snippets](#shortcut-snippets)
14
+
2.[Inline completion](#inline-completion)
15
+
3.[Snippet search](#snippet-search)
16
+
4.[Rosie code analysis](#rosie-code-analysis)
17
+
5.[Settings](#settings)
15
18
5.[Frameworks/Packages](#frameworkspackages)
16
19
6.[Testing](#testing)
17
20
7.[Links/Help](#linkshelp)
18
21
19
22
# General
23
+
20
24
As Visual Studio has a long history, the SDK also has different APIs. The older one is referred to as [Legacy API](https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/legacy-language-service-extensibility?view=vs-2022) and the current one uses the [MEF](https://learn.microsoft.com/en-us/visualstudio/extensibility/managed-extensibility-framework-in-the-editor?view=vs-2022) and async API. Not all APIs were migrated to the new style yet, so we have a mix of both in the extension. For example, the API for inserting snippets is a Legacy API.
25
+
21
26
## MEF
27
+
22
28
The Managed Extensibility Framework is working like a dependency injection framework for Visual Studio components. Registering implementations of a type or interface is done via the `[Export]` attribute and receiving already registered components and services is done via `[Import]`. Depending on the context most exported components are instantiated as singletons.
23
29
24
30
# Releasing
25
31
32
+
The build and release process is automated via GitHub Actions. The release workflow is defined in [release.yml](.github/workflows/release.yml).
26
33
27
-
The build and release process is automated via GitHub Actions. The release workflow is defined in [release.yml](.github/workflows/release.yml).
28
34
## Versioning
29
35
36
+
The version of the extension is stored in the manifest file [`src/source.extension.vsixmanifest`](src/Extension/source.extension.vsixmanifest).
37
+
The vsix manifest editor in Visual Studio generates the C#-Class [`src/Extension/source.extension.cs`](src/Extension/source.extension.cs) that
38
+
is used to version the assemblies. To update the Version of the Extension I recommend doing it in the VSIX-editor.
30
39
31
-
The version of the extension is stored in the manifest file [`src/source.extension.vsixmanifest`](src/Extension/source.extension.vsixmanifest). The vsix manifest editor in Visual Studio generates the C#-Class [`src/Extension/source.extension.cs`](src/Extension/source.extension.cs) that is used to version the assemblies. To update the Version of the Extension I recommend doing it in the VSIX-editor.
32
40
## Releasing a new version to the marketplace
41
+
33
42
<imgalign="right"src="images/version.png"/>
34
43
35
44
To trigger a new release build follow these steps:
@@ -39,14 +48,16 @@ To trigger a new release build follow these steps:
39
48
40
49
>To further automate this process, we would need to update the manifest file AND the C#-File with the just generated version tag before the extension is compiled in the build step.
41
50
42
-
43
51
# Project structure
52
+
44
53
The project is divided into three projects:
45
54
*[`Extension.csproj`](src/Extension/Extension.csproj) - for the actual extension
46
55
*[`GraphQLClient.csproj`](src/GraphQLClient/GraphQLClient.csproj) - for handling the Codiga API
47
56
*[`Tests.csproj`](src/Tests/Tests.csproj) - for the unit tests
48
57
49
-
Visual Studio Extensions still need to target full .NET 4.8 Framework as Visual Studio itself is not migrated to .NET 6 or 7. That means for all libraries and packages we reference in our `Extension.csproj` we can only use [.NET Standard 2.0](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0).
58
+
Visual Studio Extensions still need to target full .NET 4.8 Framework as Visual Studio itself is not migrated to .NET 6 or 7.
59
+
That means for all libraries and packages we reference in our `Extension.csproj` we can only use
60
+
[.NET Standard 2.0](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0).
50
61
51
62
## Folder structure
52
63
@@ -62,20 +73,50 @@ Visual Studio Extensions still need to target full .NET 4.8 Framework as Visual
62
73
*`SnippetSearch` - The menu entry and the tool window for the snippet search
63
74
*`ExtensionPackage.cs` - registrations for settings dialog and search window
64
75
*`.vsixmanifest` - metadata for the extension that will show up in the marketplace
65
-
66
-
<br>
67
76
77
+
<br>
68
78
69
79
# Feature overview
70
80
81
+
## GraphQL client, queries and mutations
82
+
83
+
The extension uses a GraphQL client to send queries and mutations to Codiga.
84
+
85
+
The queries are used to fetch timestamp-, snippet- and ruleset related data from Codiga, while mutations are used to send metrics to Codiga
86
+
of the usage of certain functionality, for example when a Rosie fix is applied.
87
+
88
+
The query files are available under the [`GraphQLClient/Queries`](/src/GraphQLClient/Queries) folder,
89
+
and are loaded and provided via [`QueryProvider.cs`](/src/GraphQLClient/QueryProvider.cs).
90
+
91
+
### User-Agent
92
+
93
+
The User-Agent header is sent in order to identify the client application the GraphQL requests are sent from.
94
+
95
+
The `RosieClient` uses a value in the form "<productname> <majorversion> <minorversion>, e.g. *Microsoft Visual Studio Community 2022 17 4*,
96
+
while `CodigaClient` uses a simpler one in the form "VisualStudio/<version>, e.g. *"VisualStudio/17.4.33103.184 D17.4"*.
97
+
98
+
### User fingerprint
99
+
100
+
In general, the fingerprint is a unique string generated when the plugin is installed.
101
+
102
+
### Codiga API Token
103
+
104
+
Having a Codiga account registered, using this token, users can access and use to their private rulesets and rules in the IDE.
105
+
106
+
The configuration is provided in the Visual Studio settings via [`ExtensionOptions.cs`](/src/Extension/Settings/ExtensionOptions.cs) and the `Extension.Settings` namespace.
107
+
71
108
## Shortcut snippets
109
+
72
110
The shortcut snippets feature is triggered by typing `.` in the editor. This is the general workflow:
73
111
<imgsrc="images/snippet-workflow.png">
74
112
The key part here is that we prevent the normal code completion session from completing the keyword and start an Expansion session instead.
113
+
75
114
### Triggering
115
+
76
116
For the completion menu and triggering we use the standard API based on [this example](https://github.com/microsoft/VSSDK-Extensibility-Samples/tree/master/AsyncCompletion).
77
117
78
118
### Inserting
119
+
79
120
Snippet insertion sessions are called [*Expansion*](https://learn.microsoft.com/en-us/visualstudio/extensibility/walkthrough-implementing-code-snippets?view=vs-2022&tabs=csharp) on the Visual Studio API. The insertion process is the same for all three features and is done in [`AssistantCompletion/ExpansionClient.cs`](src/Extension/AssistantCompletion/ExpansionClient.cs). We bypass the regular `.snippet` files by calling:
80
121
```csharp
81
122
publicintIVsExpansion.InsertSpecificExpansion (
@@ -86,41 +127,50 @@ public int IVsExpansion.InsertSpecificExpansion (
See the [documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.textmanager.interop.ivsexpansion.insertspecificexpansion?view=visualstudiosdk-2022).
130
+
See the [documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.textmanager.interop.ivsexpansion.insertspecificexpansion?view=visualstudiosdk-2022).
90
131
91
-
This API allows us to insert snippets from in-memory XML using the [Visual Studio snippet XML schema](https://learn.microsoft.com/en-us/visualstudio/ide/code-snippets-schema-reference?view=vs-2022).
92
-
We just need to serialize the incoming Codiga Recipes into the required XML format:
132
+
This API allows us to insert snippets from in-memory XML using the [Visual Studio snippet XML schema](https://learn.microsoft.com/en-us/visualstudio/ide/code-snippets-schema-reference?view=vs-2022).
133
+
We just need to serialize the incoming Codiga Recipes into the required XML format:
93
134
94
135
<imgsrc="images/serializer-workflow.png">
95
-
136
+
96
137
During the expansion session, we use [`IOleCommandTarget`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.ole.interop.iolecommandtarget?view=visualstudiosdk-2022) to handle incoming keystrokes and commands to pass `Tab` keys to the expansion and enable navigation between the user variables. The snippet usage reporting is also done in this module.
97
138
98
139
>For more details look at the answer on [this thread](https://learn.microsoft.com/en-us/answers/questions/1018899/visual-studio-extensibility-addinginstalling-snipp.html).
99
140
100
141
For a detailed explanation on the async completion API, there is a great GitHub issue at [microsoft/vs-editor-api#Async Completion API discussion](https://github.com/microsoft/vs-editor-api/issues/9).
101
142
102
-
103
143
## Inline completion
144
+
104
145
The inline completion is triggered by starting a line comment on a new line.
146
+
105
147
### Triggering
148
+
106
149
To be able to trigger the inline completion we make use of another `IOleCommandTarget` in [`InlineCompletion/InlineCompletionClient.cs`](src/Extension/InlineCompletion/InlineCompletionClient.cs). Where we check if a session should be started based on the typed text of the current line.
150
+
107
151
### Preview
152
+
108
153
The preview is done by drawing on the editor Canvas by using [TextAdornments](https://learn.microsoft.com/de-de/archive/blogs/lucian/visual-studio-text-adornment-vsix-using-roslyn) which allows adding WPF controls in relation to editor text lines. The drawing of the instructions and the preview is done in [`InlineCompletionView.cs`](src/Extension/InlineCompletion/InlineCompletionView.cs).
109
154
> An approach inserting styled code directly to the editor was dismissed as scrolling through suggestions would add all of those to the undo/redo stack which resulted in a bad UX.
110
155
111
156
### Inserting
157
+
112
158
Inserting is handled the same way as with the shortcut snippets using `ExpansionClient.StartExpansion()`.
113
159
114
160
## Snippet search
115
-
The snippet search is implemented using a [ToolWindow](https://learn.microsoft.com/en-us/visualstudio/extensibility/adding-a-tool-window?view=vs-2022). Tool windows are written using WPF and the search window UI is defined in [`SnippetSearch/View/SnippetSearchControl.xaml`](src/Extension/SnippetSearch/View/SnippetSearchControl.xaml). We try to follow [MVVM](https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm) as much as possible, therefore the UI is mostly driven by the Binding on [`SnippetSearchViewModel.cs`](src/Extension/SnippetSearch/View/SnippetSearchViewModel.cs).
161
+
162
+
The snippet search is implemented using a [ToolWindow](https://learn.microsoft.com/en-us/visualstudio/extensibility/adding-a-tool-window?view=vs-2022). Tool windows are written using WPF and the search window UI is defined in [`SnippetSearch/View/SnippetSearchControl.xaml`](src/Extension/SnippetSearch/View/SnippetSearchControl.xaml). We try to follow [MVVM](https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm) as much as possible, therefore the UI is mostly driven by the Binding on [`SnippetSearchViewModel.cs`](src/Extension/SnippetSearch/View/SnippetSearchViewModel.cs).
116
163
117
164
### Preview
165
+
118
166
The preview for snippets from the Snippet Search is done by inserting the code in the editor and using a [Classifier](https://learn.microsoft.com/en-us/visualstudio/extensibility/language-service-and-editor-extension-points?view=vs-2022#extend-classification-types-and-classification-formats) to style the span in a way that makes it obvious to users that this is a preview. The classifier is polling the text spans to be classified on changes to the editor. While the preview is active we provide a static span to be used. When the preview ends, it is set to `null`. The whole preview and classification logic is grouped under [`SnippetSearch/Preview`](/src/Extension/SnippetSearch/Preview/).
119
167
120
168
### Inserting
169
+
121
170
When inserting the snippet the preview span is replaced by the new Expansion via the [`ExpansionClient`](src/Extension/AssistantCompletion/ExpansionClient.cs).
122
171
123
172
### Menu item
173
+
124
174
To be able to bring up the tool window via the menu, two parts are needed:
125
175
1. Define the menu item command in a [VS command table](https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/visual-studio-command-table-dot-vsct-files?view=vs-2022) ([`SnippetSearchPackage.vsct`](src/Extension/SnippetSearch/SnippetSearchPackage.vsct))
126
176
2. Implement the command that gets fired when clicking the menu item (done in [`SearchWindowMenuCommand.cs`](/src/Extension/SnippetSearch/SearchWindowMenuCommand.cs))
@@ -143,14 +193,22 @@ If what is opened is simply a folder, and not an actual solution, the `codiga.ym
143
193
Here comes in [`RosieRulesCache`](/src/Extension/Rosie/RosieRulesCache.cs) which provides a periodic background thread for polling the contents of this config file,
144
194
and looking for rule changes on Codiga Hub, as well as the caches the received rules per language.
145
195
196
+
The cache is initialized when the tagging is first invoked in an editor. (See **Tagging** section below)
197
+
198
+
The periodic update is executed in every 10 seconds, and updates the cache if either the `codiga.yml` file has changed,
199
+
or the configured rulesets (or underlying rules) have changed on Codiga Hub.
200
+
201
+
### Rosie client
202
+
146
203
The rules are retrieved via [`RosieClient`](/src/Extension/Rosie/RosieClient.cs), and this is where `RosieRulesCache` is initialized before sending the first
147
204
request to the Rosie server. This way, it is initialized only when code analysis is actually needed.
148
205
149
206
For response/request (de)serialization, you can find the model classes in the `Extension.Rosie.Model` namespace.
150
207
151
208
### Adding new rule AST types
152
209
153
-
You can add new constants and mappings for the new types in [`RosieRuleAstTypes`](/src/Extension/Rosie/Model/RosieRuleAstTypes.cs).
210
+
- Add new constants into the `ElementCheckedEnumeration` in [`schema.graphql`](/src/GraphQLClient/schema.graphql).
211
+
- Add new constants and mappings for the new types in [`RosieRuleAstTypes`](/src/Extension/Rosie/Model/RosieRuleAstTypes.cs).
154
212
155
213
### Adding support for new Rosie languages
156
214
@@ -159,6 +217,10 @@ and [`RosieLanguageSupport.GetRosieLanguage`](/src/Extension/Rosie/RosieLanguage
159
217
160
218
If a language needs special treatment on the caching part, make sure to update at least [`RosieRulesCache.GetCachedLanguageTypeOf`](/src/Extension/Rosie/RosieRulesCache).
161
219
220
+
If default ruleset suggestions are also needed for the language, then:
221
+
- create a new `Default*RulesetConfig` constant in [`CodigaRulesetConfigs`](/src/Extension/Rosie/CodigaRulesetConfigs.cs)
222
+
and hook it into `CreateCodigaConfigFile(LanguageEnumeration, SVsServiceProvider)` in `CodigaConfigFileUtil`.
223
+
162
224
### Tagging
163
225
164
226
#### Tagging in general in Visual Studio extensions
@@ -238,20 +300,48 @@ There are three lightbulb actions (quick fixes) available for each violation:
238
300
<br>
239
301
240
302
## Settings
303
+
241
304
The settings dialog is also divided into the settings model and the options dialog that shows up in the VS settings.
242
-
The definition and registration of the Codiga settings are done in [`Settings/ExtensionOptions.cs`](src/Extension/Settings/ExtensionOptions.cs). These settings are stored in the Windows registry and can be accessed via a singleton instance `CodigaOptions.Instance`. The UI for the settings is defined in [`OptionsPage.xaml`](src/Extension/Settings/OptionsPage.xaml). For the simple settings dialog, the minimal logic is done in the [code-behind](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/code-behind-and-xaml-in-wpf?view=netframeworkdesktop-4.8) file [`OptionsPage.xaml.cs`](src/Extension/Settings/OptionsPage.xaml.cs).
305
+
The definition and registration of the Codiga settings are done in [`Settings/ExtensionOptions.cs`](src/Extension/Settings/ExtensionOptions.cs).
306
+
307
+
These settings are stored in the Windows registry and can be accessed via a singleton instance `CodigaOptions.Instance`.
308
+
309
+
The UI for the settings is defined in [`OptionsPage.xaml`](src/Extension/Settings/OptionsPage.xaml).
310
+
For the simple settings dialog, the minimal logic is done in the
|[NUnit](https://nunit.org/)| Unit test framework |
250
320
|[Moq](https://github.com/Moq)| For mocking in unit tests |
251
321
|[GraphQL .NET](https://github.com/graphql-dotnet/graphql-dotnet)| For consuming the Codiga API |
252
-
|[Visual Studio Community Toolkit](https://github.com/VsixCommunity/Community.VisualStudio.Toolkit)| For easier development against the VS SDK |
322
+
|[Visual Studio Community Toolkit](https://github.com/VsixCommunity/Community.VisualStudio.Toolkit)| For easier development against the VS SDK |
323
+
324
+
## Environments
325
+
326
+
In case testing on different environments is necessary, you can use the following endpoints:
Rosie related features are test via unit testing, with some mock types when it comes to tagging.
338
+
339
+
Due to UI and threading related limitations, the `TextBuffer` is replaced with a mock implementation that uses simple string manipulation under the hood.
340
+
This makes is possible to test the tagging in a simpler way, and test the lightbulb actions in a "visual" way, being able to compare the before and after states
341
+
of the document easily.
342
+
343
+
## Extras
344
+
255
345
Some general overview of features and edge cases beyond the defined extension main features that should be tested with Visual Studio:
256
346
257
347
| Scenario | Expected |
@@ -261,12 +351,10 @@ Some general overview of features and edge cases beyond the defined extension ma
261
351
| Changing the font settings under Tools -> Options -> Environment -> Fonts and Colors | Should also affect the inline completion and snippet search preview. |
0 commit comments