Introduce the structural disposal pattern (#625)

* add interfaces

* parted added disposal pattern

* add analyzer

* refine structural disposal chain

* add guideline md

* update style
This commit is contained in:
Dylech30th 2025-01-15 15:49:14 +08:00 committed by GitHub
parent 20360b034d
commit 04d7155f95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 1271 additions and 68 deletions

View File

@ -1,5 +1,5 @@
# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行
root = true
# 如果要从更高级别的目录继承 .editorconfig 设置,请删除以下行
[*]
charset = utf-8
@ -7,8 +7,11 @@ end_of_line = crlf
insert_final_newline = true
# CA2208: Instantiate argument exceptions correctly
dotnet_diagnostic.CA2208.severity = none
dotnet_diagnostic.CS8305.severity = none
dotnet_diagnostic.ca2208.severity = none
dotnet_diagnostic.cs8305.severity = none
# Microsoft .NET properties
csharp_space_after_cast = true
[*.cs]
@ -24,7 +27,7 @@ tab_width = 4
# 组织 Using
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
file_header_template = unset
file_header_template =
# this. 和 Me. 首选项
dotnet_style_qualification_for_event = false:suggestion
@ -139,7 +142,7 @@ csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# 空格键首选项
csharp_space_after_cast = false
csharp_space_after_cast = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false

View File

@ -20,6 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.ru.md = README.ru.md
SECURITY.md = SECURITY.md
Settings.XamlStyler = Settings.XamlStyler
StructuralDisposal.md = StructuralDisposal.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{CF6471C8-02F1-4B25-9207-43312C206A45}"
@ -45,16 +46,29 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval.SourceGen", "src\Pi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pixeval.Controls", "src\Pixeval.Controls\Pixeval.Controls.csproj", "{97EB18FC-3948-465F-B7DC-D059B03CA5C4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pixeval.Analyzer", "src\Pixeval.Analyzer\Pixeval.Analyzer\Pixeval.Analyzer.csproj", "{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pixeval.Analyzer.Package", "src\Pixeval.Analyzer\Pixeval.Analyzer.Package\Pixeval.Analyzer.Package.csproj", "{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pixeval.Analyzer.Vsix", "src\Pixeval.Analyzer\Pixeval.Analyzer.Vsix\Pixeval.Analyzer.Vsix.csproj", "{D76B0877-D163-46BE-88B1-D2FF02745DDC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzer", "Analyzer", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|arm64 = Release|arm64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|Any CPU.ActiveCfg = Debug|x64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|Any CPU.Build.0 = Debug|x64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|Any CPU.Deploy.0 = Debug|x64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|arm64.ActiveCfg = Debug|arm64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|arm64.Build.0 = Debug|arm64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|arm64.Deploy.0 = Debug|arm64
@ -64,6 +78,9 @@ Global
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|x86.ActiveCfg = Debug|x86
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|x86.Build.0 = Debug|x86
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Debug|x86.Deploy.0 = Debug|x86
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|Any CPU.ActiveCfg = Release|x64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|Any CPU.Build.0 = Release|x64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|Any CPU.Deploy.0 = Release|x64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|arm64.ActiveCfg = Release|arm64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|arm64.Build.0 = Release|arm64
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|arm64.Deploy.0 = Release|arm64
@ -73,54 +90,118 @@ Global
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|x86.ActiveCfg = Release|x86
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|x86.Build.0 = Release|x86
{97004D5A-21BE-4137-A529-A9E150AEB3CE}.Release|x86.Deploy.0 = Release|x86
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|Any CPU.ActiveCfg = Debug|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|Any CPU.Build.0 = Debug|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|arm64.ActiveCfg = Debug|arm64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|arm64.Build.0 = Debug|arm64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|x64.ActiveCfg = Debug|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|x64.Build.0 = Debug|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|x86.ActiveCfg = Debug|x86
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Debug|x86.Build.0 = Debug|x86
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|Any CPU.ActiveCfg = Release|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|Any CPU.Build.0 = Release|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|arm64.ActiveCfg = Release|arm64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|arm64.Build.0 = Release|arm64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|x64.ActiveCfg = Release|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|x64.Build.0 = Release|x64
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|x86.ActiveCfg = Release|x86
{8DDC779F-33E3-43CF-BEAC-3A9355F13C55}.Release|x86.Build.0 = Release|x86
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|Any CPU.ActiveCfg = Debug|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|Any CPU.Build.0 = Debug|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|arm64.ActiveCfg = Debug|arm64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|arm64.Build.0 = Debug|arm64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|x64.ActiveCfg = Debug|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|x64.Build.0 = Debug|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|x86.ActiveCfg = Debug|x86
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Debug|x86.Build.0 = Debug|x86
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|Any CPU.ActiveCfg = Release|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|Any CPU.Build.0 = Release|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|arm64.ActiveCfg = Release|arm64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|arm64.Build.0 = Release|arm64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|x64.ActiveCfg = Release|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|x64.Build.0 = Release|x64
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|x86.ActiveCfg = Release|x86
{AC5EAFCE-09A5-4BF1-91B4-B23D0110FCFF}.Release|x86.Build.0 = Release|x86
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|arm64.ActiveCfg = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|arm64.Build.0 = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|x64.ActiveCfg = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|x64.Build.0 = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|x86.ActiveCfg = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Debug|x86.Build.0 = Debug|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|Any CPU.Build.0 = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|arm64.ActiveCfg = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|arm64.Build.0 = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|x64.ActiveCfg = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|x64.Build.0 = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|x86.ActiveCfg = Release|Any CPU
{1FA40122-7618-45F0-ABC5-37EB3218422C}.Release|x86.Build.0 = Release|Any CPU
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|Any CPU.ActiveCfg = Debug|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|Any CPU.Build.0 = Debug|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|arm64.ActiveCfg = Debug|arm64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|arm64.Build.0 = Debug|arm64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|x64.ActiveCfg = Debug|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|x64.Build.0 = Debug|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|x86.ActiveCfg = Debug|x86
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Debug|x86.Build.0 = Debug|x86
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|Any CPU.ActiveCfg = Release|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|Any CPU.Build.0 = Release|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|arm64.ActiveCfg = Release|arm64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|arm64.Build.0 = Release|arm64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|x64.ActiveCfg = Release|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|x64.Build.0 = Release|x64
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|x86.ActiveCfg = Release|x86
{97EB18FC-3948-465F-B7DC-D059B03CA5C4}.Release|x86.Build.0 = Release|x86
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|arm64.ActiveCfg = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|arm64.Build.0 = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|x64.ActiveCfg = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|x64.Build.0 = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|x86.ActiveCfg = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Debug|x86.Build.0 = Debug|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|Any CPU.Build.0 = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|arm64.ActiveCfg = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|arm64.Build.0 = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|x64.ActiveCfg = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|x64.Build.0 = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|x86.ActiveCfg = Release|Any CPU
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994}.Release|x86.Build.0 = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|arm64.ActiveCfg = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|arm64.Build.0 = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|x64.ActiveCfg = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|x64.Build.0 = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|x86.ActiveCfg = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Debug|x86.Build.0 = Debug|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|Any CPU.Build.0 = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|arm64.ActiveCfg = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|arm64.Build.0 = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|x64.ActiveCfg = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|x64.Build.0 = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|x86.ActiveCfg = Release|Any CPU
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA}.Release|x86.Build.0 = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|arm64.ActiveCfg = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|arm64.Build.0 = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|x64.ActiveCfg = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|x64.Build.0 = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|x86.ActiveCfg = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Debug|x86.Build.0 = Debug|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|Any CPU.Build.0 = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|arm64.ActiveCfg = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|arm64.Build.0 = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|x64.ActiveCfg = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|x64.Build.0 = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|x86.ActiveCfg = Release|Any CPU
{D76B0877-D163-46BE-88B1-D2FF02745DDC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -129,6 +210,9 @@ Global
{CF6471C8-02F1-4B25-9207-43312C206A45} = {BE94830A-603A-4C95-9FAC-1B69CE8ABA47}
{F041EE9B-A618-438D-A16D-1265421E22FC} = {BE94830A-603A-4C95-9FAC-1B69CE8ABA47}
{BE94830A-603A-4C95-9FAC-1B69CE8ABA47} = {1663D266-BE8A-4035-BE44-AB6D2BAFE62A}
{89ACAEAF-26C4-4314-A1EA-683DF4BC9994} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{3AA6F6BD-A8F7-4FBE-A00B-A19741388BCA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{D76B0877-D163-46BE-88B1-D2FF02745DDC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
RESX_ConfirmAddLanguageFile = True

39
StructuralDisposal.md Normal file
View File

@ -0,0 +1,39 @@
# Structural Disposal
This is the design document of the structural disposal pattern, in this document we briefly discuss about this pattern and the guideline of using it.
The UI components, mostly the pages, requires itself or the resources that it manages to be disposed when it's unloaded, out of life, from the visual tree, moreover, since the resources they manage do not exploit a clear hierarchical structure, even the unload of one page will cause all of its children to be also unload, the resources held by children will, however, not necessarily be disposed alongside it, in order to handle this, we introduce the structural disposal pattern in Pixeval, and all subsequent scenarios should strictly follow this pattern.
The heart of the structural disposal pattern is an interface
```csharp
public interface IStructuralDisposalCompleter
{
void CompleteDisposal();
List<Action> ChildrenCompleters { get; }
}
```
The key is, **every page that requires such lifetime resource management should implement this interface**, if the page is derived from `EnhancedPage`, then nothing needs to be worried since all works are already done inside `EnhancedPage`, however, if such a component is a control other than page, or not a subclass of `EnhancedPage`, this interface needs thus to be implemented manually. The above code only reveals the most essential **abstract** functions of this interface, however, this interface also comes with several default-implemented functions, one of which is the `Hook()` function.
When deriving such a component, first, you should define the `Loaded` event of that component, which is supposed to be triggered when the content of the component is already rendered and ready to be mounted on the visual tree, inside the `Loaded` event, call `IStructuralDisposalCompleter.Hook()` method, then, implement `CompleteDisposal()` method, inside which is your custom disposal logic.
The mechanism is explained as follow: Every component will
1. Handle its own disposal by `CompleteDisposal` function.
2. Invoke the disposal logic of all its direct descendants.
the disposal logics of the direct descendants are stored in `ChildrenCompleters`, so the point of `Hook()` is to register the `CompleteDisposal()` function of current component to its parent component.
By using this pattern, the disposal logics are chained, once a component disposes, all of its direct children are disposed and then all of its indirect children, however, this flow only propagates downward, if a child is disposed, nothing will happen to its parents.
Of course, all this chained needs a single source to invoke, the source of this chain will be some top-level components, currently, there are only two such components
1. `TabPage`, inside which the tab closed event will trigger the chain of disposal of itself and all its contents.
2. `EnhancedWindow`, when the window is closed, the chain of disposal of its content will be triggered.
Notice that the `IStructuralDisposalCompleter` comes with other two extra properties, `CompleterDisposed` and `CompleterRegistered`, so *there's no need to worry about the idempotency, the debounce is handled automatically by `IStructuralDisposalCompleter`.*
### Analyzer
It's noticed that the `Hook()` function **must be called manually each time you implementing such a component**, so Pixeval comes with an analyzer in `Pixeval.Analyzer` project, this analyzer monitors every class that implements `IStructuralDisposalCompleter`, and will issue an error if no call to `Hook()` is found in such a class.

BIN
sign.exe Normal file

Binary file not shown.

View File

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup>
<PackageId>Pixeval.Analyzer</PackageId>
<PackageVersion>1.0.0.0</PackageVersion>
<Authors>26532</Authors>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<Description>Pixeval.Analyzer</Description>
<PackageReleaseNotes>Summary of changes made in this release of the package.</PackageReleaseNotes>
<Copyright>Copyright</Copyright>
<PackageTags>Pixeval.Analyzer, analyzers</PackageTags>
<DevelopmentDependency>true</DevelopmentDependency>
<NoPackageAnalysis>true</NoPackageAnalysis>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Pixeval.Analyzer\Pixeval.Analyzer.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="tools\*.ps1" CopyToOutputDirectory="PreserveNewest" Pack="true" PackagePath="" />
</ItemGroup>
<Target Name="_AddAnalyzersToOutput">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\Pixeval.Analyzer.dll" PackagePath="analyzers/dotnet/cs" />
</ItemGroup>
</Target>
</Project>

View File

@ -0,0 +1,275 @@
param($installPath, $toolsPath, $package, $project)
if($project.Object.SupportsPackageDependencyResolution)
{
if($project.Object.SupportsPackageDependencyResolution())
{
# Do not install analyzers via install.ps1, instead let the project system handle it.
return
}
}
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
foreach($analyzersPath in $analyzersPaths)
{
if (Test-Path $analyzersPath)
{
# Install the language agnostic analyzers.
foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
}
}
}
}
# $project.Type gives the language name like (C# or VB.NET)
$languageFolder = ""
if($project.Type -eq "C#")
{
$languageFolder = "cs"
}
if($project.Type -eq "VB.NET")
{
$languageFolder = "vb"
}
if($languageFolder -eq "")
{
return
}
foreach($analyzersPath in $analyzersPaths)
{
# Install language specific analyzers.
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
if (Test-Path $languageAnalyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
}
}
}
}
# SIG # Begin signature block
# MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s
# FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDXYwggX0MIID3KADAgECAhMzAAADTrU8
# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEY4Ow3COroWH11sAEOoStJj
# 1u4sq9rcx0dAx0gyZLHCMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAFlSLlk17KQp2AwLbr5e4T5zyE44MGZOdNejIip+Mg8co8qho16qdNSvg
# AhvoJYxPJJr70DSCU9BIUoWY7hXoi9S4P08YlAid7BUT5OciIgHlrb8I900LaE+S
# 83rSvfVU1CZCjiSwcgng5DD2VPRo0Lu4G9p2Ky14dyOPwtPvrpsib5s9kewZLdiy
# /KxEDLKX8P+cHat1xH7RaZLDNxweRS6GSomjE2vjOlQHNSW879XR8bSoAt/m4uR1
# WyrAxTGZb4miEYX+I5HsrWvbZLw9NSCJ/crbbap3LIobfQtK5binjY7v4MQp/5Oq
# y4S/4FAfwhWDXfaQfq6YTeOjHRVQbKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC
# F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCCV3irbWIoYz2Llfx9YQhUNtcP6GOrL7+YTUXQ4y5qzWAIGZNTJrsAW
# GBMyMDIzMDgzMTAwMTI1OC45ODNaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHqMIIHIDCCBQigAwIBAgITMwAAAc9SNr5xS81IygABAAABzzANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy
# MTFaFw0yNDAyMDExOTEyMTFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC4Pct+15TYyrUje553lzBQodgmd5Bz7WuH8SdHpAoW
# z+01TrHExBSuaMKnxvVMsyYtas5h6aopUGAS5WKVLZAvUtH62TKmAE0JK+i1hafi
# CSXLZPcRexxeRkOqeZefLBzXp0nudMOXUUab333Ss8LkoK4l3LYxm1Ebsr3b2OTo
# 2ebsAoNJ4kSxmVuPM7C+RDhGtVKR/EmHsQ9GcwGmluu54bqiVFd0oAFBbw4txTU1
# mruIGWP/i+sgiNqvdV/wah/QcrKiGlpWiOr9a5aGrJaPSQD2xgEDdPbrSflYxsRM
# dZCJI8vzvOv6BluPcPPGGVLEaU7OszdYjK5f4Z5Su/lPK1eST5PC4RFsVcOiS4L0
# sI4IFZywIdDJHoKgdqWRp6Q5vEDk8kvZz6HWFnYLOlHuqMEYvQLr6OgooYU9z0A5
# cMLHEIHYV1xiaBzx2ERiRY9MUPWohh+TpZWEUZlUm/q9anXVRN0ujejm6OsUVFDs
# sIMszRNCqEotJGwtHHm5xrCKuJkFr8GfwNelFl+XDoHXrQYL9zY7Np+frsTXQpKR
# NnmI1ashcn5EC+wxUt/EZIskWzewEft0/+/0g3+8YtMkUdaQE5+8e7C8UMiXOHkM
# K25jNNQqLCedlJwFIf9ir9SpMc72NR+1j6Uebiz/ZPV74do3jdVvq7DiPFlTb92U
# KwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFDaeKPtp0eTSVdG+gZc5BDkabTg4MB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBQgm4pnA0xkd/9uKXJMzdMYyxUfUm/ZusU
# Ba32MEZXQuMGp20pSuX2VW9/tpTMo5bkaJdBVoUyd2DbDsNb1kjr/36ntT0jvL3A
# oWStAFhZBypmpPbx+BPK49ZlejlM4d5epX668tRRGfFip9Til9yKRfXBrXnM/q64
# IinN7zXEQ3FFQhdJMzt8ibXClO7eFA+1HiwZPWysYWPb/ZOFobPEMvXie+GmEbTK
# bhE5tze6RrA9aejjP+v1ouFoD5bMj5Qg+wfZXqe+hfYKpMd8QOnQyez+Nlj1ityn
# OZWfwHVR7dVwV0yLSlPT+yHIO8g+3fWiAwpoO17bDcntSZ7YOBljXrIgad4W4gX+
# 4tp1eBsc6XWIITPBNzxQDZZRxD4rXzOB6XRlEVJdYZQ8gbXOirg/dNvS2GxcR50Q
# dOXDAumdEHaGNHb6y2InJadCPp2iT5QLC4MnzR+YZno1b8mWpCdOdRs9g21QbbrI
# 06iLk9KD61nx7K5ReSucuS5Z9nbkIBaLUxDesFhr1wmd1ynf0HQ51Swryh7YI7TX
# T0jr81mbvvI9xtoqjFvIhNBsICdCfTR91ylJTH8WtUlpDhEgSqWt3gzNLPTSvXAx
# XTpIM583sZdd+/2YGADMeWmt8PuMce6GsIcLCOF2NiYZ10SXHZS5HRrLrChuzedD
# RisWpIu5uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN
# MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDq
# 8xzVXwLguauAQj1rrJ4/TyEMm6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpNsjAiGA8yMDIzMDgzMDIzMjIy
# NloYDzIwMjMwODMxMjMyMjI2WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDomk2y
# AgEAMAcCAQACAi7oMAcCAQACAhLnMAoCBQDom58yAgEAMDYGCisGAQQBhFkKBAIx
# KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI
# hvcNAQELBQADggEBADE16CHIs3WeXYKQG9djja+StAB7gsNJxV9p+CETL3847UL3
# +DIeoj6p5g0FkS8PJK2xc+UbcNx6XJO+WUXEbU9GL4mrOCQjYOM+i3r8FEU+l3Gh
# 6ZG/9ygsIYRXEfDKK4lsbrrUkFQs9nDISHT3f8JZEJJXSsGmwcHWlNC0LC8bv0Jp
# e2Bw2+SNc6SlGD8Vv45r4WFPHhfSioCz4HSsF1He3/2Wku7OH85FKvugBlsca7+F
# bpGsDSL4LO9bv60DxuD+8xBZuyTB8s64ifCGlOXCNpK5VaHND48PhoJbuD0COwlM
# Rn5NlT6T4hhtkPOqNscMlzYHmTOKc5NhWK8PyrIxggQNMIIECQIBATCBkzB8MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAc9SNr5xS81IygABAAABzzAN
# BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G
# CSqGSIb3DQEJBDEiBCAjGiC3/PscCMBvPZjpZqbYcL2WRZ+Ecf74oiIPQKkjSzCB
# +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EILPpsLqeNS4NuYXE2VJlMuvQeWVA
# 80ZDFhpOPjSzhPa/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
# MTACEzMAAAHPUja+cUvNSMoAAQAAAc8wIgQgWdbLA3Co0zf3nE5086HU0tdn0vK7
# yXyU9aAFpszvWFIwDQYJKoZIhvcNAQELBQAEggIANrHN06oaP0N1lUSxxoJteJgI
# fQjN82xV5VSE0cwNCBy05fg1VydPERF59+MIwIlJHhikSo3YOj5tt3AohC78U46P
# 2IdrLHKlA3rjiLUGwQvGUKJUSsEH4uueA1mmh9jwUBJhY3NjTcMQaQqp/oxZTaya
# l7NqbubBIZvsDD126SUr8jtVWtZZzw8pnWCFb4Rijii4fY1UiQzfQLFwqQuid6tE
# I0AaY3IoTlp7U9K2wfAPWcP1G7n3qv+990GEiQlGJlCfIJSSJQodzL2QZF5HCn/K
# SfgkRPn3y/Aax8683mWCT8zricYzO3MZ9j0T7tAcqOiWb7PFCsk5Va44lq4Gv0u+
# +60FYCAA/Qn6eMuNkqIpBeIK0+NYUcMSwPdY/riKXdgkVLwChEJC5WWznD/Iqsil
# Jj9XYailhXzYx7Pa2MLays62LPwCnUxRQBTD9/rL3XQMj69iA4lisb51dKAtrqAU
# 53aRXFn+twYYFTAUQ/oNK14nZEQE5H53xAfhphMokJOu+CnQQKeCMeYlex6Q4zfw
# TQxP/xXZ+QW2cSZTwh1d5iE0XMhKCZxhxIOF/rRA+75L5GUz60reRZPeH/7USYZL
# VPc0+kxIdSbNFJAhAp0u59wSMQdBofor3+HfDfmxmoSjfCSH4TvOkNIulX1PPJPX
# UB7H4n7XHWJPkUU0cUw=
# SIG # End signature block

View File

@ -0,0 +1,282 @@
param($installPath, $toolsPath, $package, $project)
if($project.Object.SupportsPackageDependencyResolution)
{
if($project.Object.SupportsPackageDependencyResolution())
{
# Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
return
}
}
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
foreach($analyzersPath in $analyzersPaths)
{
# Uninstall the language agnostic analyzers.
if (Test-Path $analyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
}
}
}
}
# $project.Type gives the language name like (C# or VB.NET)
$languageFolder = ""
if($project.Type -eq "C#")
{
$languageFolder = "cs"
}
if($project.Type -eq "VB.NET")
{
$languageFolder = "vb"
}
if($languageFolder -eq "")
{
return
}
foreach($analyzersPath in $analyzersPaths)
{
# Uninstall language specific analyzers.
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
if (Test-Path $languageAnalyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
{
if($project.Object.AnalyzerReferences)
{
try
{
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
}
catch
{
}
}
}
}
}
# SIG # Begin signature block
# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL
# yXrxJhYfmibzcOh8caqC0uZprfczDaCCDXYwggX0MIID3KADAgECAhMzAAADTrU8
# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIBdcqRcs5QL71hlQnl3M636V
# 5iTZvb6co3MHeMuIr36qMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEARbhO/zqm6SWBf6DSJ3P1r82VjSyMGXaLtMfTOq/bqHOXPOqC25R1v5uO
# zu4ri+UOS8dU6EfW6C9Xf/Z1Ue/oxrvxn5j8mPvsmcs5OyDO0hW0Wv6pYEy5Z5up
# mfcqvfUfl3+ir29lgPuz0f1mLpz0XxqhjqElEi5RfZD1k1YVg65f0qHroP2txql5
# TfC77DXJ8verzVm1wqXBHTAERQD94TJobahYTCmyaudMjLUVFakv2lTMv0YTnrQR
# So006ZQg3i1jcVCJt/bRDGKh3xUo1IHgoh3NjMEkxT3iWt8rnX8Us6T6Zg8B2OxC
# 0EnuIu/eYYUlYTLQxO9eks7w1kVcUqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBQ1HvJFFsT0ICl6oEZlSNN8DlUeQobBHt/oTUGN6iKBgIGZNTKS0Bc
# GBMyMDIzMDgzMTAwMTI1OC4zODVaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHtMIIHIDCCBQigAwIBAgITMwAAAdWpAs/Fp8npWgABAAAB1TANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy
# MzBaFw0yNDAyMDExOTEyMzBaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDFfak57Oph9vuxtloABiLc6enT+yKH619b+OhGdkyh
# gNzkX80KUGI/jEqOVMV4Sqt/UPFFidx2t7v2SETj2tAzuVKtDfq2HBpu80vZ0vyQ
# DydVt4MDL4tJSKqgYofCxDIBrWzJJjgBolKdOJx1ut2TyOc+UOm7e92tVPHpjdg+
# Omf31TLUf/oouyAOJ/Inn2ih3ASP0QYm+AFQjhYDNDu8uzMdwHF5QdwsscNa9PVS
# GedLdDLo9jL6DoPF4NYo06lvvEQuSJ9ImwZfBGLy/8hpE7RD4ewvJKmM1+t6eQuE
# sTXjrGM2WjkW18SgUZ8n+VpL2uk6AhDkCa355I531p0Jkqpoon7dHuLUdZSQO40q
# mVIQ6qQCanvImTqmNgE/rPJ0rgr0hMPI/uR1T/iaL0mEq4bqak+3sa8I+FAYOI/P
# C7V+zEek+sdyWtaX+ndbGlv/RJb5mQaGn8NunbkfvHD1Qt5D0rmtMOekYMq7QjYq
# E3FEP/wAY4TDuJxstjsa2HXi2yUDEg4MJL6/JvsQXToOZ+IxR6KT5t5fB5FpZYBp
# VLMma3pm5z6VXvkXrYs33NXJqVWLwiswa7NUFV87Es2sou9Idw3yAZmHIYWgOQ+D
# IY1nY3aG5DODiwN1rJyEb+mbWDagrdVxcncr6UKKO49eoNTXEW+scUf6GwXG0KEy
# mQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFK/QXKNO35bBMOz3R5giX7Ala2OaMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBmRddqvQuyjRpx0HGxvOqffFrbgFAg0j82
# v0v7R+/8a70S2V4t7yKYKSsQGI6pvt1A8JGmuZyjmIXmw23AkI5bZkxvSgws8rrB
# tJw9vakEckcWFQb7JG6b618x0s9Q3DL0dRq46QZRnm7U6234lecvjstAow30dP0T
# nIacPWKpPc3QgB+WDnglN2fdT1ruQ6WIVBenmpjpG9ypRANKUx5NRcpdJAQW2FqE
# HTS3Ntb+0tCqIkNHJ5aFsF6ehRovWZp0MYIz9bpJHix0VrjdLVMOpe7wv62t90E3
# UrE2KmVwpQ5wsMD6YUscoCsSRQZrA5AbwTOCZJpeG2z3vDo/huvPK8TeTJ2Ltu/I
# tXgxIlIOQp/tbHAiN8Xptw/JmIZg9edQ/FiDaIIwG5YHsfm2u7TwOFyd6OqLw18Z
# 5j/IvDPzlkwWJxk6RHJF5dS4s3fnyLw3DHBe5Dav6KYB4n8x/cEmD/R44/8gS5Pf
# uG1srjLdyyGtyh0KiRDSmjw+fa7i1VPoemidDWNZ7ksNadMad4ZoDvgkqOV4A6a+
# N8HIc/P6g0irrezLWUgbKXSN8iH9RP+WJFx5fBHE4AFxrbAUQ2Zn5jDmHAI3wYcQ
# DnnEYP51A75WFwPsvBrfrb1+6a1fuTEH1AYdOOMy8fX8xKo0E0Ys+7bxIvFPsUpS
# zfFjBolmhzCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ
# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBO
# Ei+S/ZVFe6w1Id31m6Kge26lNKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpN3jAiGA8yMDIzMDgzMDIzMjMx
# MFoYDzIwMjMwODMxMjMyMzEwWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDomk3e
# AgEAMAoCAQACAiLLAgH/MAcCAQACAhLTMAoCBQDom59eAgEAMDYGCisGAQQBhFkK
# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
# KoZIhvcNAQELBQADggEBAC/zAZ9IEqD3nHBUWwxFTfnDSRErnCJ6XGxH5jOVtR0t
# 9pi7yCpApLpA2D12g1lcv4ugnwGpsbwmFjrcF4WlHemRa77qv409xNhNKrnh3H+U
# X2hvy9Utp9LiJiqS7lOW5VN1Uv+LbnA+FWt//4J+YLv44D/dliUGjYX623X7KiEX
# dbdXPR/Sn+W2YVQ19O8liKaFDnDnIAz+WLCfL6EaoGu4Te/Mr65Khy3YWTwQfXxr
# gR/JMDzLzWossnGszYCN8S8d9X6mfzWuYv4JHLEiThW++WbMLeT2hhKPomcbvqU4
# wPb/ylDrTrWuAr/fVndECXVjCIzYJiFwOWn/ZfN9FpQxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdWpAs/Fp8npWgABAAAB
# 1TANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCAHS+EjBkJ/YPugQQv1D7eXVQOI4DWPIYpUxpssheDN
# lTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EINm/I4YM166JMM7EKIcYvlcb
# r2CHjKC0LUOmpZIbBsH/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAHVqQLPxafJ6VoAAQAAAdUwIgQgBVj4mNvMkXn0xEeEe3i6urr/
# L5Eh2p4uPk3zOcR/jHIwDQYJKoZIhvcNAQELBQAEggIAiKbnM5VQAwi5gWDevpEJ
# oHiZRrb0HApEUkca54Oye2+5tyRfWo3ruZvLZueHWsbEzYXclOscHVPjyR1t6s+p
# W13utx4mDab1BaJCBq1q9IH1HDfCf/kSMb0H02UDNBumrt1s5/oaiiY75S3kigpt
# XHCKV/AKbi+4vORYdBwJhWvy4kug+zZHWAp7+K4rVeAaMbWOitriFPQaSdZlOkNT
# VKMbYUtX6VEnhyfr5O390TxPJCdkDznT6rAf5FlQEkim7wb2gb/Osi73KF0dtnRd
# 0ePNT4GxulYTrAhHsZmyuinh/FIyqJDtW3C+2eVt/lx6GJfAtDA/gVCe9mAL4bYv
# 7vjocHeLvWJ8bc0PYMCRZXMsU2zkJqVJ7pZWq+z5hGQAHemrQj9hUZBXCEFVZ8dd
# jEZbIsg2nwZOFakhvdAvvGlTXPbRMOCHblToXAKA8ksRbLob8CDL6Cstoy5VL3al
# fOAWLj3FYITlvGvAYCtJzHrHdqyphL805Co1syR6YDopR8tDrxgWzJKAby5fIolP
# 7SfunXsCa0n3xx80aaxR0apljIXZWBSMGZdJmVzASQFxlexNsU1PmaLmmpn0fih/
# 567e+kyzFG1wiw5btwttSW9hKgCX+yze1B4IK4yquTQSfPikrCpbBaFZ0OlWmOnL
# r0eKAKY3WDyxYgNyeg6KeCc=
# SIG # End signature block

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<RootNamespace>Pixeval.Analyzer.Vsix</RootNamespace>
<AssemblyName>Pixeval.Analyzer.Vsix</AssemblyName>
</PropertyGroup>
<PropertyGroup>
<GeneratePkgDefFile>false</GeneratePkgDefFile>
<IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
<IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
<IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
<CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory>
<CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
<VSSDKTargetPlatformRegRootSuffix>Roslyn</VSSDKTargetPlatformRegRootSuffix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.12.2069" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<StartAction>Program</StartAction>
<StartProgram>$(DevEnvDir)devenv.exe</StartProgram>
<StartArguments>/rootsuffix $(VSSDKTargetPlatformRegRootSuffix)</StartArguments>
</PropertyGroup>
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="Exists('$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets')" />
<ItemGroup>
<ProjectReference Include="..\Pixeval.Analyzer\Pixeval.Analyzer.csproj" />
</ItemGroup>
<ItemGroup>
<!-- https://github.com/dotnet/sdk/issues/433 -->
<ProjectReference Update="@(ProjectReference)" AdditionalProperties="TargetFramework=netstandard2.0" />
<!-- https://github.com/Microsoft/extendvs/issues/57 -->
<ProjectReference Update="@(ProjectReference)" Name="%(Filename)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net472\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Pixeval.Analyzer.4cdf3e41-a0f3-4358-9e19-ee93691fb9e3" Version="1.1" Language="en-US" Publisher="26532"/>
<DisplayName>Pixeval.Analyzer</DisplayName>
<Description xml:space="preserve">An analyzer specifically for Pixeval proj.</Description>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0,)">
<ProductArchitecture>amd64</ProductArchitecture>
</InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Professional" Version="[15.0,)">
<ProductArchitecture>amd64</ProductArchitecture>
</InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Enterprise" Version="[15.0,)">
<ProductArchitecture>amd64</ProductArchitecture>
</InstallationTarget>
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
</Dependencies>
<Assets>
<Asset Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="Pixeval.Analyzer" Path="|Pixeval.Analyzer|"/>
<Asset Type="Microsoft.VisualStudio.Analyzer" d:Source="Project" d:ProjectName="Pixeval.Analyzer" Path="|Pixeval.Analyzer|"/>
</Assets>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0,)" DisplayName="Visual Studio core editor" />
<Prerequisite Id="Microsoft.VisualStudio.Component.Roslyn.LanguageServices" Version="[15.0,)" DisplayName="Roslyn Language Services" />
</Prerequisites>
</PackageManifest>

View File

@ -0,0 +1,6 @@
## Release 1.0
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|--------------------
PE1001 | Design | Error | PE1001_StructuralDisposalHookAnalyzer

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<!-- Avoid ID conflicts with the package project. -->
<PackageId>*$(MSBuildProjectFile)*</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
<PackageReference Include="PolySharp" Version="1.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Pixeval.Analyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class StructuralDisposalHookAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "PE1001";
private const string InterfaceFqName = "Pixeval.Controls.IStructuralDisposalCompleter";
private const string HookFunctionName = "Hook";
private const string Category = "Design";
private const string Title = "Implementation of IStructuralDisposalCompleter does not call Hook() function";
private const string Description = "The implementor of IStructuralDisposalCompleter must call its Hook() function in order for the structural control of disposal to be chained";
private static readonly DiagnosticDescriptor _Rule = new(DiagnosticId, Title, Description, Category, DiagnosticSeverity.Error, true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [_Rule];
public override void Initialize(AnalysisContext context)
{
// Debugger.Launch();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSemanticModelAction(AnalyzeDocument);
}
private static void AnalyzeDocument(SemanticModelAnalysisContext context)
{
var descendantTypes = context.SemanticModel.SyntaxTree.GetRoot().DescendantNodes().Where(node => node.IsKind(SyntaxKind.ClassDeclaration)
|| node.IsKind(SyntaxKind.RecordDeclaration)
|| node.IsKind(SyntaxKind.StructDeclaration)
|| node.IsKind(SyntaxKind.RecordStructDeclaration))
.Where(typeDecl => context.SemanticModel.GetDeclaredSymbol(typeDecl) is INamedTypeSymbol symbol &&
symbol.Interfaces.Any(sym => sym.GetFullMetadataName() == InterfaceFqName))
.Cast<TypeDeclarationSyntax>();
foreach (var typeDecl in descendantTypes)
{
var dictionary = new Dictionary<TypeDeclarationSyntax, (bool, Location)>();
if (!dictionary.ContainsKey(typeDecl))
{
dictionary[typeDecl] = (false, typeDecl.Identifier.GetLocation());
}
var invocationNodes = typeDecl.DescendantNodes()
.Where(node => node is InvocationExpressionSyntax)
.Cast<InvocationExpressionSyntax>();
foreach (var expr in invocationNodes)
{
if (expr.Expression is MemberAccessExpressionSyntax memberAccess)
{
var lhs = memberAccess.Expression;
var rhs = memberAccess.Name;
var typeInfo = context.SemanticModel.GetTypeInfo(lhs);
if (typeInfo.Type?.OriginalDefinition.GetFullMetadataName() == InterfaceFqName
&& expr.ArgumentList.Arguments.Count == 0
&& rhs.Identifier.Text == HookFunctionName)
{
dictionary[typeDecl] = (true, null);
}
}
}
foreach (var pair in dictionary.Where(tuple => !tuple.Value.Item1))
{
context.ReportDiagnostic(Diagnostic.Create(_Rule, pair.Value.Item2));
}
}
}
}
}

View File

@ -0,0 +1,63 @@
#region Copyright (c) Pixeval/Pixeval.Analyzer
// GPL v3 License
//
// Pixeval/Pixeval.Analyzer
// Copyright (c) 2025 Pixeval.Analyzer/SymbolHelper.cs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#endregion
using Microsoft.CodeAnalysis;
using System.Text;
namespace Pixeval.Analyzer;
public static class SymbolHelper
{
public static string GetFullMetadataName(this ISymbol s)
{
if (s == null || IsRootNamespace(s))
{
return string.Empty;
}
var sb = new StringBuilder(s.MetadataName);
var last = s;
s = s.ContainingSymbol;
while (!IsRootNamespace(s))
{
if (s is ITypeSymbol && last is ITypeSymbol)
{
sb.Insert(0, '+');
}
else
{
sb.Insert(0, '.');
}
sb.Insert(0, s.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
s = s.ContainingSymbol;
}
return sb.ToString();
}
private static bool IsRootNamespace(ISymbol symbol)
{
INamespaceSymbol s;
return ((s = symbol as INamespaceSymbol) != null) && s.IsGlobalNamespace;
}
}

View File

@ -2,6 +2,8 @@
// Licensed under the GPL v3 License.
using System;
using System.Collections.Generic;
using System.Runtime;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
@ -10,8 +12,14 @@ using Pixeval.Controls.Windowing;
namespace Pixeval.Controls;
public partial class EnhancedPage : Page
public partial class EnhancedPage : Page, IStructuralDisposalCompleter
{
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
public EnhancedWindow Window => WindowFactory.GetWindowForElement(this);
public int ActivationCount { get; private set; }
@ -23,6 +31,15 @@ public partial class EnhancedPage : Page
base.OnNavigatedTo(e);
++ActivationCount;
OnPageActivated(e, e.Parameter);
Loaded += (_, _) =>
{
if (this is IStructuralDisposalCompleter completer)
{
// Hook the disposal event of current page to its parent
completer.Hook();
}
};
}
protected sealed override void OnNavigatingFrom(NavigatingCancelEventArgs e)
@ -84,4 +101,11 @@ public partial class EnhancedPage : Page
{
Navigate(tag.NavigateTo, frame, tag.Parameter, info);
}
public virtual void CompleteDisposal()
{
Content = null;
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
}
}

View File

@ -0,0 +1,63 @@
#region Copyright (c) Pixeval/Pixeval.Controls
// GPL v3 License
//
// Pixeval/Pixeval.Controls
// Copyright (c) 2025 Pixeval.Controls/IStructuralDisposalCompleter.cs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml;
namespace Pixeval.Controls;
public interface IStructuralDisposalCompleter : IPageDisposalCompleter
{
IStructuralDisposalCompleter? ParentCompleter => FindParentCompleterRecursively(this as FrameworkElement);
List<Action> ChildrenCompletes { get; }
bool CompleterRegistered { get; set; }
bool CompleterDisposed { get; set; }
void CompleteDisposalRecursively()
{
if (CompleterDisposed) return;
CompleterDisposed = true;
CompleteDisposal();
foreach (var childrenComplete in ChildrenCompletes)
{
childrenComplete();
}
}
/// <summary>
/// Call this after the UI is loaded
/// </summary>
public void Hook()
{
if (CompleterRegistered) return;
CompleterRegistered = true;
ParentCompleter?.ChildrenCompletes.Add(CompleteDisposalRecursively);
}
private static IStructuralDisposalCompleter? FindParentCompleterRecursively(DependencyObject? uiElement)
{
return uiElement?.FindAscendant<FrameworkElement>(x => x is IStructuralDisposalCompleter) as IStructuralDisposalCompleter;
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the GPL v3 License.
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@ -35,7 +36,7 @@ public sealed partial class EnhancedWindow : Window
VerticalAlignment = VerticalAlignment.Bottom
};
Growl.SetGrowlParent(stackPanel, true);
Growl.SetToken(stackPanel, this.HWnd);
Growl.SetToken(stackPanel, HWnd);
Content = new Grid
{
Children =
@ -64,6 +65,8 @@ public sealed partial class EnhancedWindow : Window
if (_owner is not null)
_owner.AppWindow.Closing -= OnOwnerOnClosing;
WeakReferenceMessenger.Default.UnregisterAll(this);
var completer = Content.FindDescendant<FrameworkElement>(element => element is IStructuralDisposalCompleter) as IStructuralDisposalCompleter;
completer?.CompleteDisposalRecursively();
}
private void OnOwnerOnClosing(AppWindow sender, AppWindowClosingEventArgs e) => Close();

View File

@ -35,6 +35,7 @@ public sealed partial class TabPage
private void TabPage_OnLoaded(object sender, RoutedEventArgs e)
{
if (_ownsWindow)
{
Window.SetTitleBar(CustomDragRegion);
@ -82,9 +83,9 @@ public sealed partial class TabPage
private void TabView_OnTabCloseRequested(TabView sender, TabViewTabCloseRequestedEventArgs e)
{
if (e.Tab.Content is Frame { Content: IPageDisposalCompleter completer })
if (e.Tab.Content is FrameworkElement element && element.FindDescendant<FrameworkElement>(ele => ele is IStructuralDisposalCompleter) is IStructuralDisposalCompleter completer)
{
completer.CompleteDisposal();
completer.CompleteDisposalRecursively();
}
RemoveTab(e.Tab);
}
@ -162,12 +163,6 @@ public sealed partial class TabPage
*/
private void TabPage_OnUnloaded(object sender, RoutedEventArgs e)
{
foreach (var tabViewTabItem in TabView.TabItems)
{
if (tabViewTabItem is TabViewItem { Content: Frame { Content: IPageDisposalCompleter completer } })
{
completer.CompleteDisposal();
}
}
((IStructuralDisposalCompleter) this).CompleteDisposalRecursively();
}
}

View File

@ -59,12 +59,12 @@ public static class WindowFactory
?? ThrowHelper.ArgumentOutOfRange<UIElement, EnhancedWindow>(element, $"Specified {nameof(element)} is not existed in any of {nameof(ForkedWindows)}.");
}
public static EnhancedWindow Create(EnhancedPage content, out EnhancedWindow window)
public static EnhancedWindow Create(EnhancedPage content)
{
RootWindow = window = new EnhancedWindow(content);
window.Closed += (sender, _) => _ForkedWindowsInternal.Remove(sender.To<EnhancedWindow>().HWnd);
_ForkedWindowsInternal[window.HWnd] = window;
return window;
RootWindow = new EnhancedWindow(content);
RootWindow.Closed += (sender, _) => _ForkedWindowsInternal.Remove(sender.To<EnhancedWindow>().HWnd);
_ForkedWindowsInternal[RootWindow.HWnd] = RootWindow;
return RootWindow;
}
public static EnhancedWindow Fork(this EnhancedWindow owner, EnhancedPage content, out ulong hWnd)

View File

@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"
Loaded="ZoomableImage_OnLoaded"
mc:Ignorable="d">
<win2d:CanvasControl
x:Name="CanvasControl"

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Pixeval.Utilities;
using WinUI3Utilities.Attributes;
@ -27,7 +28,7 @@ namespace Pixeval.Controls;
[DependencyProperty<ZoomableImageMode>("InitMode", "ZoomableImageMode.Fit")]
[DependencyProperty<ZoomableImagePosition>("InitPosition", "ZoomableImagePosition.AbsoluteCenter")]
[ObservableObject]
public sealed partial class ZoomableImage : UserControl
public sealed partial class ZoomableImage : UserControl, IStructuralDisposalCompleter
{
public bool IsDisposed { get; private set; }
@ -101,7 +102,13 @@ public sealed partial class ZoomableImage : UserControl
}
}
~ZoomableImage()
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
public void CompleteDisposal()
{
IsDisposed = true;
CanvasControl.Draw -= CanvasControlOnDraw;
@ -110,4 +117,9 @@ public sealed partial class ZoomableImage : UserControl
frame.Dispose();
_frames.Clear();
}
private void ZoomableImage_OnLoaded(object sender, RoutedEventArgs e)
{
((IStructuralDisposalCompleter) this).Hook();
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>

View File

@ -30,7 +30,7 @@ public class ReadonlyInterfaceGenerator : IIncrementalGenerator
var list = typeSymbol.GetProperties(attributeList[0].AttributeClass!)
// .Where(symbol => !symbol.IsReadOnly)
.Select(symbol => (MemberDeclarationSyntax)PropertyDeclaration(symbol.Type.GetTypeSyntax(false), symbol.Name)
.Select(MemberDeclarationSyntax (symbol) => PropertyDeclaration(symbol.Type.GetTypeSyntax(false), symbol.Name)
.AddAccessorListAccessors(getter));
var member = InterfaceDeclaration("I" + typeSymbol.Name)

View File

@ -26,12 +26,12 @@ public class SettingsEntryGenerator : IIncrementalGenerator
{
var list = new List<(IPropertySymbol Property, (int, string, string) Attribute)>();
foreach (var ga in gas)
if (ga is
{
TargetSymbol: IPropertySymbol symbol,
Attributes: [{ ConstructorArguments: [{ Value: int a }, { Value: string b }, { Value: string c }] }]
})
{
if (ga is { TargetSymbol: IPropertySymbol symbol, Attributes: [{ ConstructorArguments: [{ Value: int a }, { Value: string b }, { Value: string c }] }] })
{
list.Add((symbol, (a, b, c)));
}
}
const string metadataTable = "MetadataTable";
@ -55,8 +55,8 @@ public class SettingsEntryGenerator : IIncrementalGenerator
$"global::System.Collections.Generic.Dictionary<string, global::{AttributeFullName}>"),
null,
InitializerExpression(SyntaxKind.ObjectInitializerExpression,
SeparatedList(list.Select(elem =>
(ExpressionSyntax)AssignmentExpression(
SeparatedList(list.Select(ExpressionSyntax (elem) =>
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
ImplicitElementAccess(BracketedArgumentList(SeparatedList(
[

View File

@ -0,0 +1,25 @@
#region Copyright (c) Pixeval/Pixeval.SourceGen
// GPL v3 License
//
// Pixeval/Pixeval.SourceGen
// Copyright (c) 2025 Pixeval.SourceGen/InterceptsLocationAttribute.cs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#endregion
// ReSharper disable once CheckNamespace
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class InterceptsLocationAttribute(string filePath, int line, int column) : Attribute;

View File

@ -68,7 +68,7 @@ public partial class App
await AppViewModel.InitializeAsync(isProtocolActivated);
WindowFactory.Create(new LoginPage(), out var w)
WindowFactory.Create(new LoginPage())
.WithInitialized(onLoaded: OnLoaded)
.WithClosing((_, _) => AppInfo.SaveContextWhenExit()) // TODO: 从运行打开应用的时候不会ExitApp就算是调用App.Current.Exit();
.WithSizeLimit(800, 360)

View File

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Pixeval.Controls"
Loaded="CommentView_OnLoaded"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:EntryView HasNoItem="{x:Bind HasNoItem, Mode=OneWay}" IsLoadingMore="{x:Bind CommentsList.IsLoadingMore, Mode=OneWay}">

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using WinUI3Utilities.Attributes;
namespace Pixeval.Controls;
@ -10,7 +11,7 @@ namespace Pixeval.Controls;
[DependencyProperty<object>("ItemsSource")]
[DependencyProperty<bool>("HasNoItem", "true")]
[DependencyProperty<bool>("IsLoadingMore", "false")]
public sealed partial class CommentView
public sealed partial class CommentView : IStructuralDisposalCompleter
{
public CommentView() => InitializeComponent();
@ -28,10 +29,21 @@ public sealed partial class CommentView
DeleteHyperlinkButtonClick?.Invoke(viewModel);
}
~CommentView()
public void CompleteDisposal()
{
if (CommentsList.ItemsSource is IEnumerable<CommentItemViewModel> list)
if (CommentsList.ItemsSource is IEnumerable<CommentItemViewModel> list)
foreach (var commentBlockViewModel in list)
commentBlockViewModel.Dispose();
}
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
private void CommentView_OnLoaded(object sender, RoutedEventArgs e)
{
((IStructuralDisposalCompleter) this).Hook();
}
}

View File

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Pixeval.Controls"
Loaded="DownloadView_OnLoaded"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:EntryView HasNoItem="{x:Bind ViewModel.HasNoItem, Mode=OneWay}" IsLoadingMore="{x:Bind ItemsView.IsLoadingMore, Mode=OneWay}">

View File

@ -1,7 +1,10 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Pixeval.CoreApi.Model;
using Pixeval.Pages.IllustrationViewer;
@ -9,7 +12,7 @@ using Pixeval.Pages.NovelViewer;
namespace Pixeval.Controls;
public sealed partial class DownloadView : UserControl
public sealed partial class DownloadView : UserControl, IStructuralDisposalCompleter
{
public DownloadViewViewModel ViewModel { get; } = new(App.AppViewModel.DownloadManager.QueuedTasks);
@ -38,5 +41,19 @@ public sealed partial class DownloadView : UserControl
_ = await viewModel.TryLoadThumbnailAsync(ViewModel);
}
~DownloadView() => ViewModel.Dispose();
public void CompleteDisposal()
{
ViewModel.Dispose();
}
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
private void DownloadView_OnLoaded(object sender, RoutedEventArgs e)
{
((IStructuralDisposalCompleter) this).Hook();
}
}

View File

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Pixeval.Controls"
Loaded="IllustratorView_OnLoaded"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:EntryView

View File

@ -1,13 +1,16 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System;
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Pixeval.Pages.IllustratorViewer;
using WinUI3Utilities;
namespace Pixeval.Controls;
public sealed partial class IllustratorView : IScrollViewHost
public sealed partial class IllustratorView : IScrollViewHost, IStructuralDisposalCompleter
{
public IllustratorViewViewModel ViewModel { get; } = new();
@ -25,7 +28,21 @@ public sealed partial class IllustratorView : IScrollViewHost
await this.CreateIllustratorPageAsync(e.InvokedItem.To<IllustratorItemViewModel>().UserId);
}
~IllustratorView() => ViewModel.Dispose();
public ScrollView ScrollView => AdvancedItemsView.ScrollView;
public void CompleteDisposal()
{
ViewModel.Dispose();
}
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
private void IllustratorView_OnLoaded(object sender, RoutedEventArgs e)
{
((IStructuralDisposalCompleter) this).Hook();
}
}

View File

@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Pixeval.Controls"
Loaded="SpotlightView_OnLoaded"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<local:EntryView HasNoItem="{x:Bind ViewModel.HasNoItem, Mode=OneWay}" IsLoadingMore="{x:Bind AdvancedItemsView.IsLoadingMore, Mode=OneWay}">

View File

@ -5,10 +5,12 @@ using Windows.System;
using Microsoft.UI.Xaml.Controls;
using WinUI3Utilities;
using System;
using System.Collections.Generic;
using Microsoft.UI.Xaml;
namespace Pixeval.Controls;
public sealed partial class SpotlightView : IScrollViewHost
public sealed partial class SpotlightView : IScrollViewHost, IStructuralDisposalCompleter
{
public SpotlightViewViewModel ViewModel { get; } = new();
@ -27,12 +29,23 @@ public sealed partial class SpotlightView : IScrollViewHost
await Launcher.LaunchUriAsync(new Uri(e.InvokedItem.To<SpotlightItemViewModel>().Entry.ArticleUrl));
}
~SpotlightView()
public ScrollView ScrollView => AdvancedItemsView.ScrollView;
public void CompleteDisposal()
{
foreach (var viewModel in ViewModel.DataProvider.Source)
viewModel.UnloadThumbnail(ViewModel);
ViewModel.Dispose();
}
public ScrollView ScrollView => AdvancedItemsView.ScrollView;
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
private void SpotlightView_OnLoaded(object sender, RoutedEventArgs e)
{
((IStructuralDisposalCompleter) this).Hook();
}
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Windows.Foundation;
@ -24,7 +26,7 @@ namespace Pixeval.Controls;
/// todo
/// </summary>
[ObservableObject]
public sealed partial class WorkView : IEntryView<ISortableEntryViewViewModel>
public sealed partial class WorkView : IEntryView<ISortableEntryViewViewModel>, IStructuralDisposalCompleter
{
public const double LandscapeHeight = 180;
public const double PortraitHeight = 250;
@ -168,17 +170,6 @@ public sealed partial class WorkView : IEntryView<ISortableEntryViewViewModel>
private (ThumbnailDirection ThumbnailDirection, double DesiredHeight) IllustrationItem_OnRequiredParam() => (ThumbnailDirection, DesiredHeight);
~WorkView()
{
if (ViewModel == null!)
return;
var viewModel = ViewModel;
ViewModel = null!;
foreach (var vm in viewModel.Source)
vm.UnloadThumbnail(viewModel);
viewModel.Dispose();
}
private void AddToBookmarkTeachingTip_OnCloseButtonClick(TeachingTip sender, object e)
{
sender.GetTag<IWorkViewModel>().AddToBookmarkCommand.Execute((BookmarkTagSelector.SelectedTags, BookmarkTagSelector.IsPrivate, null as object));
@ -196,4 +187,21 @@ public sealed partial class WorkView : IEntryView<ISortableEntryViewViewModel>
{
await this.CreateIllustratorPageAsync(e.User.Id);
}
public void CompleteDisposal()
{
if (ViewModel == null!)
return;
var viewModel = ViewModel;
ViewModel = null!;
foreach (var vm in viewModel.Source)
vm.UnloadThumbnail(viewModel);
viewModel.Dispose();
}
public List<Action> ChildrenCompletes { get; } = [];
public bool CompleterRegistered { get; set; }
public bool CompleterDisposed { get; set; }
}

View File

@ -27,7 +27,7 @@ public class IllustrationDownloadTaskFactory : IDownloadTaskFactory<Illustration
{
{ IsUgoira: true } => new UgoiraDownloadTaskGroup(context.Entry, path),
{ IsManga: true, MangaIndex: -1 } => new MangaDownloadTaskGroup(context.Entry, path),
_ => (IImageDownloadTaskGroup)new SingleImageDownloadTaskGroup(context.Entry, path)
_ => (IImageDownloadTaskGroup) new SingleImageDownloadTaskGroup(context.Entry, path)
};
manager.Insert(task.DatabaseEntry);

View File

@ -2,6 +2,7 @@
// Licensed under the GPL v3 License.
using System;
using System.Runtime;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Pixeval.Controls;

View File

@ -1,11 +1,8 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Pixeval.Controls;
using Pixeval.CoreApi.Global.Enum;
namespace Pixeval.Pages.Capability;

View File

@ -1,7 +1,9 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System;
using System.Linq;
using System.Runtime;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Pixeval.Controls;

View File

@ -1,6 +1,8 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System.Runtime;
using System;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Pixeval.Controls;

View File

@ -1,6 +1,8 @@
// Copyright (c) Pixeval.
// Licensed under the GPL v3 License.
using System.Runtime;
using System;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
using Pixeval.Controls;

View File

@ -221,7 +221,11 @@ public sealed partial class IllustrationViewerPage
teachingTip.Target = appBarButton.IsInOverflow ? null : appBarButton;
}
~IllustrationViewerPage() => _viewModel.Dispose();
public override void CompleteDisposal()
{
base.CompleteDisposal();
_viewModel.Dispose();
}
private void IllustrationViewerPage_OnLoaded(object sender, RoutedEventArgs e)
{
@ -249,7 +253,7 @@ public sealed partial class IllustrationViewerPage
deferral.Complete();
return;
async void FileDispose(DataPackage dataPackage, object o) => await file?.DeleteAsync(StorageDeleteOption.PermanentDelete);
async void FileDispose(DataPackage dataPackage, object o) => await file.DeleteAsync(StorageDeleteOption.PermanentDelete);
}
}
}

View File

@ -73,8 +73,6 @@ public sealed partial class SettingsPage : IDisposable, INotifyPropertyChanged
}
}
~SettingsPage() => Dispose();
private void CheckForUpdateButton_OnClicked(object sender, RoutedEventArgs e)
{
ViewModel.CheckForUpdate();
@ -190,7 +188,12 @@ public sealed partial class SettingsPage : IDisposable, INotifyPropertyChanged
Bindings.StopTracking();
ViewModel.Dispose();
ViewModel = null!;
GC.SuppressFinalize(this);
}
public override void CompleteDisposal()
{
base.CompleteDisposal();
Dispose();
}
public event PropertyChangedEventHandler? PropertyChanged;

View File

@ -75,8 +75,6 @@ public sealed partial class NovelViewerPage
DocumentViewer_OnTapped(null!, null!);
}
~NovelViewerPage() => _viewModel.Dispose();
private async void FrameworkElement_OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
var viewModel = sender.GetDataContext<NovelItemViewModel>();
@ -160,4 +158,10 @@ public sealed partial class NovelViewerPage
}
public (FrameworkElement, DocumentViewerViewModel?) DownloadParameter(DocumentViewerViewModel? viewModel) => (this, viewModel);
public override void CompleteDisposal()
{
base.CompleteDisposal();
_viewModel.Dispose();
}
}

View File

@ -30,5 +30,9 @@ public sealed partial class TagsPage
_viewModel.WorkingDirectory = folder.Path;
}
~TagsPage() => _viewModel.Dispose();
public override void CompleteDisposal()
{
base.CompleteDisposal();
_viewModel.Dispose();
}
}