Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Five projects wired together in `CSharpCodeAnalyst.sln`:
- **`Tests/`** (project name `CodeParserTests`) — NUnit suite. `ApprovalTests/` parses the `TestSuite/` C# solution once per fixture and asserts on the resulting graph; `UnitTests/` covers cycles, exploration, export, search, architectural rules, etc.
- **`ApprovalTestTool/`** — standalone console app that clones external repos listed in `Repositories.txt`, parses each at a pinned commit, hashes the graph dump, and diffs against references. Used to catch parser regressions on real codebases; not part of the CI test run.

`ThirdParty/DsmSuite/` holds a vendored subset (7 of ~38 projects) of the GPL-licensed DsmSuite, which provides the matrix view on the DSM tab. It is foreign code with its own rules — see **Vendored DsmSuite** below before touching anything in there.

`TestSuite/` is a handcrafted C# solution used purely as parser input for the approval tests. Do not consume it from production code — it is intentionally full of odd language constructs. `ReferencedAssemblies/` contains the MSAGL DLLs referenced directly by `CSharpCodeAnalyst.csproj` and `Tests.csproj` (MSAGL is not on NuGet for the versions used here).

## Architectural notes worth knowing before editing
Expand Down Expand Up @@ -112,6 +114,19 @@ Then wire up the edges: `RuleEngine.Execute` for the evaluation, `RuleViolationV
### Refactoring simulation is destructive
`Features/Refactoring/` mutates the in-memory `CodeGraph` directly (move / delete elements, cut edges) and there is no undo. Any code path that offers these operations should save or warn first — see existing command handlers before adding new ones.

### Vendored DsmSuite (the DSM tab)
The matrix on the DSM tab is not ours: `ThirdParty/DsmSuite/` is a modified subset of [DsmSuite](https://github.com/ernstaii/dsmsuite.sourcecode), pinned to a commit recorded in `ThirdParty/DsmSuite/README.md`, GPL-3.0-or-later (originally MIT). Our side is `Features/DsmMatrix/`: `CodeGraphToDsmModelBuilder` fills a DsmSuite `IDsmModel` straight from the `TypeGraph` — explicit `parentId`, no DSI file, no name splitting — and `DsmMatrixView` hosts their `MatrixView`.

**`ThirdParty/DsmSuite/Dsm.md` is the reference for what the matrix actually draws** — axes (row = provider, column = consumer), the depth-coloured blocks, the two row indicator bars, the weight deciles, and what the builder feeds in. Read it before changing anything about the view's semantics, and keep it in sync: it exists because none of that is discoverable from the UI and it otherwise has to be re-derived from the drawing code.

**Every change under `ThirdParty/DsmSuite/` costs two things, and neither is optional:** (1) a `Changed <YYYY-MM> for CSharpCodeAnalyst` comment at the site explaining *why*, and (2) a row in the change table in `ThirdParty/DsmSuite/README.md`. GPL §5(a) requires stating what was modified, and that table is the map for ever diffing against upstream again — an undocumented change silently becomes indistinguishable from upstream code. Same rule for the bug list in that README when you find (or fix) a defect in their code.

Two traps that are already documented there and worth knowing before you write code against their API:
- **`DsmApplication` binds `DsmQueries` to the model it is constructed with and never rebinds.** Populate the model *first*, then construct `DsmApplication`. Calling their `LoadModel` (i.e. the file-based import path) leaves every query running against the previous model. This is why `DsmMatrixView.Show` builds everything from scratch.
- **Their `MainViewModel` is the DataContext**, including its editing commands. Those mutate the DSM model only, never the `CodeGraph`, so they cannot corrupt a parse result — but they are live in the context menus.

Their resource dictionaries are merged in `App.xaml` at application scope because their controls resolve them via `StaticResource`. Everything in there is keyed; if you pull in more of their XAML, check for implicit (unkeyed) styles first — those would restyle the whole application.

## Code style

- `.editorconfig` is authoritative and ReSharper-tuned: braces required on all `if`/`foreach`/`while`, max line length 199, expression-bodied accessors preferred, `internal` first in modifier order. Analyzer severities default to `none` — do not add warning-as-error enforcement without discussion.
Expand Down
109 changes: 109 additions & 0 deletions CSharpCodeAnalyst.sln
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpCodeAnalyst.History",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpCodeAnalyst.Contracts", "CSharpCodeAnalyst.Contracts\CSharpCodeAnalyst.Contracts.csproj", "{9DE77B88-294B-45F2-B1A1-6642CC3B4137}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ThirdParty", "ThirdParty", "{356D9C46-07F5-7F13-4EEF-5DD22B885BE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.Analyzer.Model", "ThirdParty\DsmSuite\DsmSuite.Analyzer.Model\DsmSuite.Analyzer.Model.csproj", "{42652074-77DD-4502-9EF6-D51EBB72A918}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.Common.Model", "ThirdParty\DsmSuite\DsmSuite.Common.Model\DsmSuite.Common.Model.csproj", "{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.Common.Util", "ThirdParty\DsmSuite\DsmSuite.Common.Util\DsmSuite.Common.Util.csproj", "{FE8B79B8-497F-4939-B07A-6546994DC9EF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.DsmViewer.Application", "ThirdParty\DsmSuite\DsmSuite.DsmViewer.Application\DsmSuite.DsmViewer.Application.csproj", "{3C96864B-93C5-4D75-87FC-98D00A84E32F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.DsmViewer.Model", "ThirdParty\DsmSuite\DsmSuite.DsmViewer.Model\DsmSuite.DsmViewer.Model.csproj", "{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.DsmViewer.View", "ThirdParty\DsmSuite\DsmSuite.DsmViewer.View\DsmSuite.DsmViewer.View.csproj", "{9421A097-A4E6-496B-B782-DAB1E819DD0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DsmSuite.DsmViewer.ViewModel", "ThirdParty\DsmSuite\DsmSuite.DsmViewer.ViewModel\DsmSuite.DsmViewer.ViewModel.csproj", "{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -168,10 +184,103 @@ Global
{9DE77B88-294B-45F2-B1A1-6642CC3B4137}.Release|x64.Build.0 = Release|Any CPU
{9DE77B88-294B-45F2-B1A1-6642CC3B4137}.Release|x86.ActiveCfg = Release|Any CPU
{9DE77B88-294B-45F2-B1A1-6642CC3B4137}.Release|x86.Build.0 = Release|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Debug|x64.ActiveCfg = Debug|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Debug|x64.Build.0 = Debug|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Debug|x86.ActiveCfg = Debug|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Debug|x86.Build.0 = Debug|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Release|Any CPU.Build.0 = Release|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Release|x64.ActiveCfg = Release|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Release|x64.Build.0 = Release|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Release|x86.ActiveCfg = Release|Any CPU
{42652074-77DD-4502-9EF6-D51EBB72A918}.Release|x86.Build.0 = Release|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Debug|x64.ActiveCfg = Debug|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Debug|x64.Build.0 = Debug|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Debug|x86.ActiveCfg = Debug|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Debug|x86.Build.0 = Debug|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Release|Any CPU.Build.0 = Release|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Release|x64.ActiveCfg = Release|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Release|x64.Build.0 = Release|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Release|x86.ActiveCfg = Release|Any CPU
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8}.Release|x86.Build.0 = Release|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Debug|x64.ActiveCfg = Debug|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Debug|x64.Build.0 = Debug|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Debug|x86.ActiveCfg = Debug|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Debug|x86.Build.0 = Debug|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Release|Any CPU.Build.0 = Release|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Release|x64.ActiveCfg = Release|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Release|x64.Build.0 = Release|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Release|x86.ActiveCfg = Release|Any CPU
{FE8B79B8-497F-4939-B07A-6546994DC9EF}.Release|x86.Build.0 = Release|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Debug|x64.ActiveCfg = Debug|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Debug|x64.Build.0 = Debug|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Debug|x86.ActiveCfg = Debug|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Debug|x86.Build.0 = Debug|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Release|Any CPU.Build.0 = Release|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Release|x64.ActiveCfg = Release|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Release|x64.Build.0 = Release|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Release|x86.ActiveCfg = Release|Any CPU
{3C96864B-93C5-4D75-87FC-98D00A84E32F}.Release|x86.Build.0 = Release|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Debug|x64.ActiveCfg = Debug|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Debug|x64.Build.0 = Debug|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Debug|x86.ActiveCfg = Debug|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Debug|x86.Build.0 = Debug|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Release|Any CPU.Build.0 = Release|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Release|x64.ActiveCfg = Release|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Release|x64.Build.0 = Release|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Release|x86.ActiveCfg = Release|Any CPU
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB}.Release|x86.Build.0 = Release|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Debug|x64.ActiveCfg = Debug|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Debug|x64.Build.0 = Debug|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Debug|x86.ActiveCfg = Debug|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Debug|x86.Build.0 = Debug|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Release|Any CPU.Build.0 = Release|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Release|x64.ActiveCfg = Release|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Release|x64.Build.0 = Release|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Release|x86.ActiveCfg = Release|Any CPU
{9421A097-A4E6-496B-B782-DAB1E819DD0D}.Release|x86.Build.0 = Release|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Debug|x64.ActiveCfg = Debug|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Debug|x64.Build.0 = Debug|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Debug|x86.ActiveCfg = Debug|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Debug|x86.Build.0 = Debug|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Release|Any CPU.Build.0 = Release|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Release|x64.ActiveCfg = Release|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Release|x64.Build.0 = Release|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Release|x86.ActiveCfg = Release|Any CPU
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{42652074-77DD-4502-9EF6-D51EBB72A918} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
{4F8EA064-AF33-4973-A63A-6984CA7C5DF8} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
{FE8B79B8-497F-4939-B07A-6546994DC9EF} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
{3C96864B-93C5-4D75-87FC-98D00A84E32F} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
{D912F0E9-D8C1-43ED-B3DD-F4413AF366AB} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
{9421A097-A4E6-496B-B782-DAB1E819DD0D} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
{A9B11DC9-D36F-48DD-87E8-1C6E547EED5E} = {356D9C46-07F5-7F13-4EEF-5DD22B885BE8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2BBCF792-C019-4195-AF08-45CC642B8A18}
EndGlobalSection
Expand Down
25 changes: 23 additions & 2 deletions CSharpCodeAnalyst/App.xaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
<Application x:Class="CSharpCodeAnalyst.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:themes="clr-namespace:DsmSuite.DsmViewer.View.Resources.Themes;assembly=DsmSuite.DsmViewer.View">
<Application.Resources>

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--
Resources of the vendored DsmSuite matrix view (see ThirdParty/DsmSuite). Its controls
resolve these via StaticResource, so they have to live at application scope. Every style
in here is keyed, so nothing bleeds into the rest of the application.
-->
<ResourceDictionary Source="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Themes/LightTheme.xaml" />
<ResourceDictionary Source="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Converters.xaml" />
<ResourceDictionary Source="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Icons.xaml" />
<ResourceDictionary Source="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/ControlTemplates.xaml" />
<ResourceDictionary Source="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Style.xaml" />
<themes:ThemeResourceDictionary
DarkThemeSource="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Themes/DarkTheme.xaml"
PastelThemeSource="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Themes/PastelTheme.xaml"
LightThemeSource="pack://application:,,,/DsmSuite.DsmViewer.View;component/Resources/Themes/LightTheme.xaml" />

<!-- Our restyling of the matrix. Must stay last: it wins by overriding the keys above. -->
<ResourceDictionary Source="Features/DsmMatrix/DsmMatrixTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
4 changes: 4 additions & 0 deletions CSharpCodeAnalyst/CSharpCodeAnalyst.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@
<ProjectReference Include="..\CSharpCodeAnalyst.History\CSharpCodeAnalyst.History.csproj" />
<ProjectReference Include="..\CSharpCodeAnalyst.TreeMap\CSharpCodeAnalyst.TreeMap.csproj" />
<ProjectReference Include="..\CSharpCodeAnalyst.Contracts\CSharpCodeAnalyst.Contracts.csproj" />
<ProjectReference Include="..\ThirdParty\DsmSuite\DsmSuite.DsmViewer.View\DsmSuite.DsmViewer.View.csproj" />
<ProjectReference Include="..\ThirdParty\DsmSuite\DsmSuite.DsmViewer.ViewModel\DsmSuite.DsmViewer.ViewModel.csproj" />
<ProjectReference Include="..\ThirdParty\DsmSuite\DsmSuite.DsmViewer.Application\DsmSuite.DsmViewer.Application.csproj" />
<ProjectReference Include="..\ThirdParty\DsmSuite\DsmSuite.DsmViewer.Model\DsmSuite.DsmViewer.Model.csproj" />
</ItemGroup>


Expand Down
Loading