Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(clangd) add_subdirectory(include-fixer) add_subdirectory(pp-trace) +add_subdirectory(remark-viewer-vs) add_subdirectory(tool-template) # Add the common testsuite after all the tools. Index: CODE_OWNERS.TXT =================================================================== --- CODE_OWNERS.TXT +++ CODE_OWNERS.TXT @@ -19,3 +19,7 @@ N: Alexander Kornienko E: alexfh@google.com D: clang-tidy + +N: River Riddle +E: riddleriver@gmail.com +D: remark-viewer-vs Index: remark-viewer-vs/.gitignore =================================================================== --- /dev/null +++ remark-viewer-vs/.gitignore @@ -0,0 +1,42 @@ +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates +.vs/ + +# Build results +[Dd]ebug*/ +[Rr]elease*/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +*.ilk +*.obj +*.pdb +*.dll + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# NuGet Packages +*.nupkg +**/packages/* Index: remark-viewer-vs/CMakeLists.txt =================================================================== --- /dev/null +++ remark-viewer-vs/CMakeLists.txt @@ -0,0 +1,18 @@ +option(BUILD_OPT_REMARK_VIEWER_VS_PLUGIN "Build opt-remark-viewer VS plugin" OFF) +if (BUILD_OPT_REMARK_VIEWER_VS_PLUGIN) + add_custom_target(opt_remark_viewer_license + ${CMAKE_COMMAND} -E copy_if_different + "${CLANG_SOURCE_DIR}/LICENSE.TXT" + "${CMAKE_CURRENT_SOURCE_DIR}/RemarkPackage/license.txt") + + if (NOT OPT_REMARK_VIEWER_VS_VERSION) + set(OPT_REMARK_VIEWER_VS_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + endif() + + add_custom_target(opt_remark_viewer_vsix ALL + devenv "${CMAKE_CURRENT_SOURCE_DIR}/OptRemarkViewer.sln" /Build "Release - VS2015" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_SOURCE_DIR}/RemarkPackage/bin/Release - VS2015/RemarkPackage.vsix" + "${LLVM_TOOLS_BINARY_DIR}/OptRemarkViewer.vsix" + DEPENDS opt_remark_viewer_license) +endif() Index: remark-viewer-vs/OptRemarkViewer.sln =================================================================== --- /dev/null +++ remark-viewer-vs/OptRemarkViewer.sln @@ -0,0 +1,183 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2006 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemarkInterface", "RemarkInterface\RemarkInterface.csproj", "{6A858A68-448F-4B27-93FC-3EBBC3D3FC07}" + ProjectSection(ProjectDependencies) = postProject + {D539632C-BFBC-4191-ABA0-A98F8F172A01} = {D539632C-BFBC-4191-ABA0-A98F8F172A01} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemarkTagger", "RemarkTagger\RemarkTagger.csproj", "{E6C96829-D87B-489D-9C05-38110DA50296}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demangler", "demangler\demangler.vcxproj", "{D539632C-BFBC-4191-ABA0-A98F8F172A01}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemarkPackage", "RemarkPackage\RemarkPackage.csproj", "{D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - VS2015|Any CPU = Debug - VS2015|Any CPU + Debug - VS2015|x64 = Debug - VS2015|x64 + Debug - VS2015|x86 = Debug - VS2015|x86 + Debug - VS2017|Any CPU = Debug - VS2017|Any CPU + Debug - VS2017|x64 = Debug - VS2017|x64 + Debug - VS2017|x86 = Debug - VS2017|x86 + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release - VS2015|Any CPU = Release - VS2015|Any CPU + Release - VS2015|x64 = Release - VS2015|x64 + Release - VS2015|x86 = Release - VS2015|x86 + Release - VS2017|Any CPU = Release - VS2017|Any CPU + Release - VS2017|x64 = Release - VS2017|x64 + Release - VS2017|x86 = Release - VS2017|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2015|Any CPU.ActiveCfg = Debug - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2015|Any CPU.Build.0 = Debug - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2015|x64.ActiveCfg = Debug - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2015|x64.Build.0 = Debug - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2015|x86.ActiveCfg = Debug - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2015|x86.Build.0 = Debug - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2017|Any CPU.ActiveCfg = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2017|Any CPU.Build.0 = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2017|x64.ActiveCfg = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2017|x64.Build.0 = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug - VS2017|x86.ActiveCfg = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug|Any CPU.ActiveCfg = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug|Any CPU.Build.0 = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug|x64.ActiveCfg = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug|x64.Build.0 = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug|x86.ActiveCfg = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Debug|x86.Build.0 = Debug - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2015|Any CPU.ActiveCfg = Release - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2015|Any CPU.Build.0 = Release - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2015|x64.ActiveCfg = Release - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2015|x64.Build.0 = Release - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2015|x86.ActiveCfg = Release - VS2015|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2017|Any CPU.ActiveCfg = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2017|Any CPU.Build.0 = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2017|x64.ActiveCfg = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2017|x64.Build.0 = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release - VS2017|x86.ActiveCfg = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release|Any CPU.ActiveCfg = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release|Any CPU.Build.0 = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release|x64.ActiveCfg = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release|x64.Build.0 = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release|x86.ActiveCfg = Release - VS2017|Any CPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07}.Release|x86.Build.0 = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2015|Any CPU.ActiveCfg = Debug - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2015|Any CPU.Build.0 = Debug - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2015|x64.ActiveCfg = Debug - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2015|x64.Build.0 = Debug - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2015|x86.ActiveCfg = Debug - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2015|x86.Build.0 = Debug - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2017|Any CPU.ActiveCfg = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2017|Any CPU.Build.0 = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2017|x64.ActiveCfg = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2017|x64.Build.0 = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug - VS2017|x86.ActiveCfg = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug|Any CPU.ActiveCfg = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug|Any CPU.Build.0 = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug|x64.ActiveCfg = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug|x64.Build.0 = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug|x86.ActiveCfg = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Debug|x86.Build.0 = Debug - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2015|Any CPU.ActiveCfg = Release - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2015|Any CPU.Build.0 = Release - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2015|x64.ActiveCfg = Release - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2015|x64.Build.0 = Release - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2015|x86.ActiveCfg = Release - VS2015|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2017|Any CPU.ActiveCfg = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2017|Any CPU.Build.0 = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2017|x64.ActiveCfg = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2017|x64.Build.0 = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release - VS2017|x86.ActiveCfg = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release|Any CPU.ActiveCfg = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release|Any CPU.Build.0 = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release|x64.ActiveCfg = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release|x64.Build.0 = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release|x86.ActiveCfg = Release - VS2017|Any CPU + {E6C96829-D87B-489D-9C05-38110DA50296}.Release|x86.Build.0 = Release - VS2017|Any CPU + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|Any CPU.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|Any CPU.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|Any CPU.Deploy.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|x64.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|x64.Build.0 = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|x86.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2015|x86.Build.0 = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|Any CPU.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|Any CPU.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|Any CPU.Deploy.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|x64.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|x64.Build.0 = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|x86.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug - VS2017|x86.Build.0 = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug|x64.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug|x64.Build.0 = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug|x86.ActiveCfg = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Debug|x86.Build.0 = Debug|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|Any CPU.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|Any CPU.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|Any CPU.Deploy.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|x64.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|x64.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|x86.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2015|x86.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|Any CPU.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|Any CPU.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|Any CPU.Deploy.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|x64.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|x64.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|x86.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release - VS2017|x86.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release|Any CPU.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release|x64.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release|x64.Build.0 = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release|x86.ActiveCfg = Release|Win32 + {D539632C-BFBC-4191-ABA0-A98F8F172A01}.Release|x86.Build.0 = Release|Win32 + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2015|Any CPU.ActiveCfg = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2015|Any CPU.Build.0 = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2015|x64.ActiveCfg = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2015|x64.Build.0 = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2015|x86.ActiveCfg = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2015|x86.Build.0 = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2017|Any CPU.ActiveCfg = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2017|Any CPU.Build.0 = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2017|x64.ActiveCfg = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2017|x64.Build.0 = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug - VS2017|x86.ActiveCfg = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug|Any CPU.ActiveCfg = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug|Any CPU.Build.0 = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug|x64.ActiveCfg = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug|x64.Build.0 = Debug - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug|x86.ActiveCfg = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Debug|x86.Build.0 = Debug - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2015|Any CPU.ActiveCfg = Release - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2015|Any CPU.Build.0 = Release - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2015|x64.ActiveCfg = Release - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2015|x64.Build.0 = Release - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2015|x86.ActiveCfg = Release - VS2015|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2017|Any CPU.ActiveCfg = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2017|Any CPU.Build.0 = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2017|x64.ActiveCfg = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2017|x64.Build.0 = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release - VS2017|x86.ActiveCfg = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release|Any CPU.ActiveCfg = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release|Any CPU.Build.0 = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release|x64.ActiveCfg = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release|x64.Build.0 = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release|x86.ActiveCfg = Release - VS2017|Any CPU + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239}.Release|x86.Build.0 = Release - VS2017|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2A6988C9-7C0C-4F21-AD5E-5DE443F9F56E} + EndGlobalSection +EndGlobal Index: remark-viewer-vs/README.txt =================================================================== --- /dev/null +++ remark-viewer-vs/README.txt @@ -0,0 +1,19 @@ +This directory contains a VSPackage project to generate a Visual Studio extension +for remark-viewer. + +Build prerequisites are: +- Visual Studio 2017 Professional +- Visual Studio 2017 SDK +- Visual Studio 2015 Professional +- Visual Studio 2015 SDK + +The extension is built using CMake by setting BUILD_OPT_REMARK_VIEWER_VS_PLUGIN=ON +when configuring a Clang build, and building the remark_viewer_vsix target. + +The CMake build will copy LICENSE.TXT into the RemarkPackage/ +directory so they can be bundled with the plug-in, as well as creating +RemarkPackage/source.extension.vsixmanifest. Once the plug-in has been built with +CMake once, it can be built manually from the OptRemarkViewer.sln solution in Visual +Studio. + +The sln also contains a configuration for building the vsix for Visual Studio 2017. Index: remark-viewer-vs/RemarkInterface/Async/RemarkEvent.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Async/RemarkEvent.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Threading; + +namespace RemarkInterface { + public enum EvtCode { + // When the cache of remarks was recently invalidated. + CacheInvalidated + } + public class RemarkEvent { + public RemarkEvent(EvtCode C, object O = null) { + Code = C; + Data = O; + } + public EvtCode Code; + public object Data; + } + + public static class AsyncUtil { + // Token to help cancel all async tasks we may be computing for remarks. + static CancellationTokenSource CancelTokenSource = new CancellationTokenSource(); + + // Source of async tasks to be handled by a single thread. + static ConcurrentQueue AsyncNonTaskActions = new ConcurrentQueue(); + + // Synchronized dispatcher. + static Dispatcher DispatchSink; + + /// Initialization and diposal + public static void init() { + startActionManager(); + DispatchSink = Application.Current.Dispatcher; + } + public static void dispose() => cancelAllActions(true); + private static void startActionManager() { + Action Create = () => { + while (true) { + if (AsyncNonTaskActions.TryDequeue(out Action Item)) + Item(); + else + Thread.Sleep(125); + throwOnCancel(); + } + }; + for (int i = 0, e = Environment.ProcessorCount / 2; i < e; ++i) + dispatchAsync(Create, ThreadPriority.Highest); + } + + /// Utilities. + private static CancellationToken CancelToken => CancelTokenSource.Token; + public static void throwOnCancel() => CancelToken.ThrowIfCancellationRequested(); + internal static void cancelAllActions(bool Dispose = false) { + CancelTokenSource.Cancel(); + CancelTokenSource = new CancellationTokenSource(); + + // Reset the task queue. + while (AsyncNonTaskActions.TryDequeue(out Action item)) ; + + // If we aren't disposing then restart the action manager. + if (!Dispose) + startActionManager(); + } + + /// Event dispatchers. + //// Async with a X-milisecond delay. + public static void dispatchAsyncDelay(int Mili, Action Act) { + dispatchAsync(async delegate { + try { + await Task.Delay(Mili, CancelToken); + } catch (OperationCanceledException) { + return; + } + Act(); + }, ThreadPriority.AboveNormal); + } + //// Async. + public static void dispatchAsync(Action Act, ThreadPriority Prio = ThreadPriority.Normal) { + if (Prio == ThreadPriority.Normal) { + AsyncNonTaskActions.Enqueue(Act); + return; + } + // Run the task and ignore any exceptions. + Task.Run(() => { + try { Act(); } catch { } + }, CancelToken); + } + //// Sync on main ui thread. + public static void dispatchSyncInvoke(Action Act) => DispatchSink.Invoke(Act); + } +} Index: remark-viewer-vs/RemarkInterface/Core/ExternalConfigDirWatcher.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Core/ExternalConfigDirWatcher.cs @@ -0,0 +1,53 @@ +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RemarkInterface { + class ExternalConfigDirWatcher { + private RemarkManager Par; + // Set of filesystem watchers for each external config directory. + Dictionary Watchers = new Dictionary(); + + public ExternalConfigDirWatcher(RemarkManager P) => Par = P; + + // Setup the file system watchers. + public void reset(Dictionary Dirs) { + // Reset the existing watchers. + Watchers.Clear(); + if (Dirs == null) + return; + + // Setup new watchers for each of the sources. + foreach (var Kvp in Dirs) { + // Create the watcher for opt files. + FileSystemWatcher FSW = new FileSystemWatcher { + Path = Kvp.Value.Dir, + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName, + Filter = "*.opt.yaml" + }; + + // Add the change event. + FSW.Changed += FSW_Changed; + FSW.Renamed += FSW_Renamed; + FSW.Created += FSW_Created; + FSW.IncludeSubdirectories = true; + FSW.EnableRaisingEvents = true; + Watchers.Add(Kvp.Key, FSW); + } + } + + private void FSW_Created(object sender, FileSystemEventArgs e) { + string BaseActiveDocName = Path.GetFileNameWithoutExtension(ServiceLocator.DTE.ActiveDocument?.Name); + string BaseFileName = Path.GetFileNameWithoutExtension(e.Name.Substring(0, e.Name.Length - 9)); + if (BaseActiveDocName == BaseFileName) + Par.loadActiveDocument(); + } + private void FSW_Renamed(object sender, RenamedEventArgs e) => FSW_Created(sender, e); + private void FSW_Changed(object sender, FileSystemEventArgs e) => Par.loadYamlDocument(e.FullPath); + } +} Index: remark-viewer-vs/RemarkInterface/Core/RemarkFilter.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Core/RemarkFilter.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq; + +namespace RemarkInterface { + public class RemarkFilter { + RemarkSettings Settings; + internal RemarkFilter(RemarkSettings Settings) => this.Settings = Settings; + + // Check to see if we should ignore this remark when displaying. + private bool shouldIgnore(Remark R, RemarkConfig RC) { + // Global type ignore. + if (Settings.IgnoreMask[(int)R.Type]) + return true; + // Per pass ignore. + var PassIgnoreMask = Settings.getPassVisibilityMask(R.Pass); + if (PassIgnoreMask[(int)R.Type]) + return true; + // Hotness check. + uint HotnessFilter = Settings.HotnessFilter; + if (HotnessFilter != 0) { + double Pctg = (double)HotnessFilter / 100; + uint MinHotness = (uint)(RC.MaxHotness * Pctg); + if (R.Hotness < MinHotness) + return true; + } + // Pass subtype ignore. + if (Settings.getPassSubTypeIgnored(R.Pass, R.Name)) + return true; + return false; + } + + // Filter the list of remarks with the rules for the given config. + public List filter(List ToFilter, RemarkConfig RC) { + return ToFilter.Where(R => !shouldIgnore(R, RC)).ToList(); + } + } +} Index: remark-viewer-vs/RemarkInterface/Core/RemarkManager.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Core/RemarkManager.cs @@ -0,0 +1,407 @@ +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using RemarkInterface.Utils; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace RemarkInterface { + public class RemarkVisibilityData { + public RemarkVisibilityData(List RL, RemarkConfig RC, int ID) { + RemarkList = RL; + Config = RC; + ConfigID = ID; + } + public List RemarkList; + public readonly RemarkConfig Config; + public readonly int ConfigID; + } + public class RemarkManager { + // Settings for viewing remarks. + public RemarkSettings Settings; + + // Demangler for function names. + public Demangler Mangler; + + // Filter. + public RemarkFilter Filter; + + // Reader for optimization reports. + internal readonly RemarkReader Reader; + + // Different solution configurations. + readonly Dictionary Configs + = new Dictionary(); + + // Active configs other than the current config. + readonly HashSet ActiveConfigs + = new HashSet(); + + // Watcher for external directory changes. + private ExternalConfigDirWatcher ExternalDirWatcher; + + public RemarkManager() { + ExternalDirWatcher = new ExternalConfigDirWatcher(this); + Mangler = new Demangler(); + Reader = new RemarkReader(this); + Settings = new RemarkSettings(); + Filter = new RemarkFilter(Settings); + AsyncUtil.init(); + } + ~RemarkManager() { + AsyncUtil.dispose(); + } + + // Reset all of the internal data for the manager. + public void clear() { + AsyncUtil.cancelAllActions(); + ProjectUtils.reset(); + Configs.Clear(); + ActiveConfigs.Clear(); + Settings.ExternalConfigSources.Clear(); + ExternalDirWatcher.reset(null); + } + + // Loading document utilities. + public void loadActiveDocument() { + if (!Settings.IsEnabled) + return; + RemarkConfig RC = getCurrentConfig(); + if (RC != null) + Reader.loadActiveDocument(RC); + foreach (RemarkConfig ActiveRC in ActiveConfigs) + Reader.loadActiveDocument(ActiveRC); + } + public void loadAllDocumentsInCurrentProject() { + if (!Settings.IsEnabled) + return; + RemarkConfig RC = getCurrentConfig(); + if (RC != null) + Reader.loadAllDocumentsInCurrentProject(RC); + foreach (RemarkConfig ActiveRC in ActiveConfigs) + Reader.loadAllDocumentsInCurrentProject(ActiveRC); + } + internal void loadYamlDocument(string Yaml) { + System.Action ProcessConfig = + RC => { + YamlFile YF = RC?.getYamlFile(Yaml); + if (YF != null) Reader.loadRemarks(RC, YF); + }; + ProcessConfig(getCurrentConfig()); + foreach (RemarkConfig ActiveRC in ActiveConfigs) + ProcessConfig(ActiveRC); + } + + // Update/Remove/Rename document utilities. + public void updateAllDocuments() { + if (!Settings.IsEnabled) + return; + RemarkConfig RC = getCurrentConfig(); + if (RC != null) + Reader.updateAllDocuments(RC); + foreach (RemarkConfig ActiveRC in ActiveConfigs) + Reader.updateAllDocuments(ActiveRC); + } + public void removeDocument(string Document, bool Delay = false) { + foreach (var RC in Configs.Values) { + if (Delay) + RC.delayRemoveRemarkFile(Document); + else + RC.removeRemarkFile(Document); + } + } + public void renameDocument(ProjectItem ProjectItem, string OldName) { + foreach (var RC in Configs.Values) + RC.renameRemarkFile(ProjectItem, OldName); + } + + // Visible Remark utilities. + public IEnumerable> getVisibleRemarks(string FullFileName, + IEnumerable LinesToCheck) { + // Check to see if it was modified. + System.DateTime FileAccess = System.DateTime.MinValue; + if (!Settings.AllowOutOfDateRemarks) + FileAccess = File.GetLastWriteTime(FullFileName); + + // Create a unique ID for this config. + Dictionary ConfigIDs = new Dictionary(); + + // Get the configs that we will actually be able to see. + IEnumerable ActiveConfigs = getActiveConfigs(); + var VisibleConfigs = new List>>>(); + foreach (RemarkConfig RC in ActiveConfigs) { + RemarkFile RF = RC.getRemarkFile(FullFileName); + if (RF == null) + continue; + Dictionary> Remarks; + // Check the last write time. + if (!Settings.AllowOutOfDateRemarks) { + if(!RF.hasUpToDateRemarks(FileAccess)) + continue; + Remarks = RF.UpToDateRemarks; + } else + Remarks = RF.Remarks; + VisibleConfigs.Add(new KeyValuePair>>(RC, Remarks)); + ConfigIDs.Add(RC, ConfigIDs.Count); + } + foreach (int LineNumber in LinesToCheck) { + List DataList = new List(); + foreach (var ConfigFilePair in VisibleConfigs) { + if (!ConfigFilePair.Value.TryGetValue((uint)LineNumber, out List RemarksOnCurLine)) + continue; + RemarkConfig RC = ConfigFilePair.Key; + List Remarks = Filter.filter(RemarksOnCurLine, RC); + if (Remarks.Count != 0) + DataList.Add(new RemarkVisibilityData(Remarks, RC, ConfigIDs[RC])); + } + if (DataList.Count == 0) + continue; + + // If we only show diffs then we remove any duplicates. + if (Settings.OnlyShowDiffs && DataList.Count > 1) { + // Get a set of lists to interesect. + List[] VisibleRemarkLists = new List[DataList.Count]; + for (int i = 0, e = DataList.Count; i < e; ++i) + VisibleRemarkLists[i] = DataList[i].RemarkList; + + // Intersect each of the configs into a hashset of remarks. + System.Func[], IEnumerable> IntersectLists + = Lists => Lists.Aggregate((l1, l2) => l1.Intersect(l2)); + HashSet UniqueRemarks = new HashSet(IntersectLists(VisibleRemarkLists)); + + // ReSelect the lists such that each remark is unique. + foreach (RemarkVisibilityData Data in DataList) + Data.RemarkList = Data.RemarkList.Where(R => !UniqueRemarks.Contains(R)).ToList(); + // ReSelect lists such that each remark is not a subset of others. + for (int i = 0, e = DataList.Count; i < e;) { + RemarkVisibilityData Data = DataList[i]; + Data.RemarkList = Data.RemarkList.Where(R => !DataList.Any(D => D != Data && D.RemarkList.Any(DR => R.isSubsetOf(DR)))).ToList(); + if (Data.RemarkList.Count == 0) { + DataList.RemoveAt(i); + --e; + } else + ++i; + } + } + if(DataList.Count != 0) + yield return DataList; + } + } + private bool displayRemarks(string FullFile, IEnumerable Lines, ref bool CheckedSanity, ref int CurrentCount) { + // Walk each of the lines provided to dump the visible remarks. + foreach (var VisibleRemarks in getVisibleRemarks(FullFile, Lines)) { + foreach (RemarkVisibilityData RVD in VisibleRemarks) { + string ConfigMsg = RVD.Config.ToString(); + foreach (Remark R in RVD.RemarkList) { + string ProfMsg = ""; + if (RVD.Config.MaxHotness != 0) { + double ConfigPct = 100.0 * ((double)R.Hotness / (double)RVD.Config.MaxHotness); + ProfMsg += ((int)ConfigPct).ToString() + "%"; + } + var Error = new ErrorList.Error(R.DebugLoc) { + Category = R.GetType().Name, + Description = R.ToString(), + Id = ProfMsg, + Project = ConfigMsg, + Severity = TaskErrorCategory.Message + }; + ErrorListUtils.addTaskToErrorList(Error, "RemarkDisplay"); + } + + // Check for a large amount of remarks. + if (!CheckedSanity && (CurrentCount += RVD.RemarkList.Count) >= 1000) { + string CheckMsg = "The amount of loaded remarks has exceeded 1000!! " + + "Do you want to continue displaying? " + + "(You can reduce the amount of visible remarks through filtering.)"; + int result = VsShellUtilities.ShowMessageBox(ServiceLocator.PackageServiceProvider, CheckMsg, + null /*title*/, OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_OKCANCEL, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + if (result == (int)Microsoft.VisualStudio.VSConstants.MessageBoxResult.IDCANCEL) + return false; + CheckedSanity = true; + } + } + } + return true; + } + public void displayRemarksInCurrentDoc() { + // Reset the current error list. + ErrorListUtils.clean("RemarkDisplay"); + + // Check for active document. + string FullFile = ServiceLocator.DTE.ActiveDocument?.FullName; + if (FullFile == null) + return; + + // Display the remarks for the current doc. + bool CheckedSanity = false; + int CurrentCount = 0; + int NumLines = File.ReadLines(FullFile).Count(); + displayRemarks(FullFile, Enumerable.Range(1, NumLines), ref CheckedSanity, ref CurrentCount); + } + public void displayAllRemarks() { + bool CheckedSanity = false; + int CurrentCount = 0; + + // Reset the current error list. + ErrorListUtils.clean("RemarkDisplay"); + + // Check each of the configs for the active documents. + HashSet Seen = new HashSet(); + foreach (RemarkConfig RC in getActiveConfigs()) { + foreach (RemarkFile RF in RC.RemarkFiles) { + if (!Seen.Add(RF.FullName)) + continue; + if (!displayRemarks(RF.FullName, RF.Remarks.Keys.Select(i => (int)i), ref CheckedSanity, ref CurrentCount)) + return; + } + } + } + private void getRemarkCounts(Dictionary> Counts, string FullFile, IEnumerable Lines) { + + // Walk each of the lines provided to dump the visible remarks. + foreach (List VisibleRemarks in getVisibleRemarks(FullFile, Lines)) { + foreach (RemarkVisibilityData RVD in VisibleRemarks) { + if (!Counts.TryGetValue(RVD.Config, out Dictionary FnCounts)) + Counts.Add(RVD.Config, FnCounts = new Dictionary()); + foreach (Remark R in RVD.RemarkList) { + foreach(FunctionReference FR in R.SourceFunctions) { + if (!FnCounts.TryGetValue(FR, out int[] RemarkTyCounts)) + FnCounts.Add(FR, RemarkTyCounts = new int[(int)RemarkType.Count]); + ++RemarkTyCounts[(int)R.Type]; + } + } + } + } + } + private void displayRemarkCounts(Dictionary> Counts) { + string[] RemarkTypeNames = System.Enum.GetNames(typeof(RemarkType)); + + // Helper for emitting count message. + System.Action CreateAndEmitError + = (FRef, TotalCountMsg, ProjMsg, RemarkTyMsgs) => { + string Msg = FRef.BaseName + ": " + TotalCountMsg + " visible remarks."; + for (int i = 0, e = (int)RemarkType.Count; i < e; ++i) + if (RemarkTyMsgs[i] != "") + Msg += " " + RemarkTyMsgs[i] + ' ' + RemarkTypeNames[i] + " remarks."; + var Error = new ErrorList.Error(FRef.DebugLoc) { + Description = Msg, + Id = TotalCountMsg + " Remarks", + Project = ProjMsg, + Severity = TaskErrorCategory.Message + }; + ErrorListUtils.addTaskToErrorList(Error, "RemarkCount"); + }; + + // If there is a single config we only display that config. + if(Counts.Count == 1) { + var CountsBegin = Counts.First(); + string ConfigMsg = CountsBegin.Key.ToString(); + foreach (var FnRefPair in CountsBegin.Value) + CreateAndEmitError(FnRefPair.Key, FnRefPair.Value.Sum().ToString(), ConfigMsg, + FnRefPair.Value.Select(C => C == 0 ? "" : C.ToString()).ToArray()); + return; + } + + // Otherwise we display all of the configs in each source function. + HashSet Functions = new HashSet(); + foreach (var FnList in Counts.Values) + Functions.UnionWith(FnList.Keys); + string MultiConfigMsg = '[' + string.Join(",", Counts.Keys) + ']'; + foreach (FunctionReference FRef in Functions) { + var ConfigCounts = Counts.Values.Select(FnDict => FnDict.TryGetValue(FRef, out int[] TmpCounts) + ? TmpCounts : new int[(int)RemarkType.Count]).ToArray(); + int TotalCount = ConfigCounts.Sum(CountArr => CountArr.Sum()); + string[] TyMsgs = new string[(int)RemarkType.Count]; + for (int i = 0, e = TyMsgs.Length; i < e; ++i) + if (ConfigCounts.All(Iter => Iter[i] == 0)) + TyMsgs[i] = ""; + else + TyMsgs[i] = '[' + string.Join(",", ConfigCounts.Select(Iter => Iter[i].ToString())) + ']'; + CreateAndEmitError(FRef, TotalCount.ToString(), MultiConfigMsg, TyMsgs); + } + } + public void displayRemarkCountsInCurrentDoc() { + // Reset the current error list. + ErrorListUtils.clean("RemarkCount"); + + // Check for active document. + string FullFile = ServiceLocator.DTE.ActiveDocument?.FullName; + if (FullFile == null) + return; + + // Get the counts for the current doc. + var Counts = new Dictionary>(); + int NumLines = File.ReadLines(FullFile).Count(); + getRemarkCounts(Counts, FullFile, Enumerable.Range(1, NumLines)); + displayRemarkCounts(Counts); + } + public void displayAllRemarkCounts() { + // Reset the current error list. + ErrorListUtils.clean("RemarkCount"); + + // Check each of the configs for the active documents. + HashSet Seen = new HashSet(); + var Counts = new Dictionary>(); + foreach (RemarkConfig RC in getActiveConfigs()) + foreach (RemarkFile RF in RC.RemarkFiles) + if (Seen.Add(RF.FullName)) + getRemarkCounts(Counts, RF.FullName, RF.Remarks.Keys.Select(i => (int)i)); + displayRemarkCounts(Counts); + } + // Config utilities. + public IEnumerable getActiveConfigs() { + HashSet Configs = new HashSet(); + RemarkConfig RC = getCurrentConfig(); + if (RC == null) + return Configs; + Configs.Add(RC); + Configs.UnionWith(ActiveConfigs); + return Configs; + } + private RemarkConfig getCurrentConfig() { + return ProjectUtils.getCurrentConfigName(out string Config, out string Platform) + ? getAddConfig(Config, Platform) : null; + } + private RemarkConfig getAddConfig(string Config, string Platform) { + string Key = Config + '|' + Platform; + if (!Configs.TryGetValue(Key, out RemarkConfig RC)) + Configs.Add(Key, RC = new RemarkConfig(Config, Platform)); + return RC; + } + + // Add or remove an active configuration. + public void addActiveConfig(string Config, string Platform) { + RemarkConfig RC = getAddConfig(Config, Platform); + ActiveConfigs.Add(RC); + Reader.loadActiveDocument(RC); + IRemarkService ORS = ServiceLocator.getGlobalService(); + ORS.dispatchRemarkCacheInvalidateEvent(); + } + public void removeActiveConfig(string Config, string Platform) { + RemarkConfig RC = getAddConfig(Config, Platform); + ActiveConfigs.Remove(RC); + IRemarkService ORS = ServiceLocator.getGlobalService(); + ORS.dispatchRemarkCacheInvalidateEvent(); + } + + /// Solution Specific Utilities. + // Solution Settings. + public void loadSolutionSettings(Stream IOStream) { + Settings.loadSolutionSettings(IOStream); + ExternalDirWatcher.reset(Settings.ExternalConfigSources); + } + public void saveSolutionSettings(Stream IOStream) { + Settings.saveSolutionSettings(IOStream); + } + // External Config Directory Utilities. + public Dictionary getExternalConfigDirectories() { + return Settings.ExternalConfigSources; + } + public void setExternalConfigDirectories(Dictionary Dirs) { + Settings.ExternalConfigSources = Dirs; + ExternalDirWatcher.reset(Dirs); + } + } +} Index: remark-viewer-vs/RemarkInterface/Core/RemarkReader.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Core/RemarkReader.cs @@ -0,0 +1,435 @@ +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using RemarkInterface.Utils; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; +using System.Collections; +using Path = System.IO.Path; +using Microsoft.VisualStudio.VCProjectEngine; +using System.Linq; + +namespace RemarkInterface { + internal class RemarkReader { + public RemarkReader(RemarkManager Par) => Parent = Par; + // Parent access. + readonly RemarkManager Parent; + + // Update any loaded remarks. + public void updateAllDocuments(RemarkConfig RC) { + loadActiveDocument(RC); + loadLTODocument(RC); + foreach (var YF in RC.YamlFiles) + loadRemarks(RC, YF.Value); + } + + // Try to load remarks for an LTO remark file. + private void loadLTODocument(RemarkConfig RC) { + Document ActiveDoc = ServiceLocator.DTE.ActiveDocument; + + // Check for active project. + Project ActiveDocProj = ActiveDoc?.ProjectItem?.ContainingProject; + if (ActiveDocProj == null) + return; + + // Check for an already loaded LTO file. + if (RC.isLTO(ActiveDocProj.Object as VCProject)) + return; + + string LTODocName = ActiveDocProj.Name + ".vcxproj"; + performActionOnActiveProjectDirs(ActiveDocProj, RC, + Dir => loadDocumentFromDirectory(Dir, LTODocName, RC) + ); + } + + // Try to load/update remarks for the current active document. + public void loadActiveDocument(RemarkConfig RC) { + Document ActiveDoc = ServiceLocator.DTE.ActiveDocument; + + // Check for active document. + if (ActiveDoc?.ProjectItem == null || ActiveDoc.FullName == null) + return; + + // Check for an existing mapping for this source file that isn't just LTO files. + RemarkFile RF = RC.getRemarkFile(ActiveDoc.FullName); + if (RF != null && RF.hasNonLTOParentYaml()) { + List ParentYamls = new List(RF.ParentYamlFiles); + foreach (YamlFile YF in ParentYamls) + loadRemarks(RC, YF); + return; + } + + // If this is the first doc load we also check for an LTO doc. + if (RC.YamlFiles.Count == 0) + loadLTODocument(RC); + + // Otherwise we need to locate a yaml file that contains information + // for this source file. + string DocName = ActiveDoc.Name; + Project ActiveDocProj = ActiveDoc.ProjectItem.ContainingProject; + performActionOnActiveProjectDirs(ActiveDocProj, RC, + Dir => loadDocumentFromDirectory(Dir, DocName, RC) + ); + } + // Try to load in any remarks for all files in this project. + public void loadAllDocumentsInCurrentProject(RemarkConfig RC) { + try { + Document ActiveDoc = ServiceLocator.DTE.ActiveDocument; + if (ActiveDoc?.ProjectItem == null) + return; + Project ActiveDocProj = ActiveDoc.ProjectItem.ContainingProject; + performActionOnActiveProjectDirs(ActiveDocProj, RC, Dir => { + // Search for remark files and load them. + var Files = Directory.GetFiles(Dir, "*.opt.yaml", + SearchOption.AllDirectories); + foreach (var File in Files) + loadRemarks(RC, RC.getCreateYamlFile(File)); + return Files.Length != 0; + }); + } + catch { } + } + + // Loading remark from the specified file. + internal void loadRemarks(RemarkConfig RC, YamlFile YF) { + // Create a new load task. + if (!YF.updateAccessTime()) return; + string OutputMsg = "Loading Optimization Remark File: " + YF.FullName; + ErrorListUtils.addTaskToOutputWindow(OutputMsg); + AsyncUtil.dispatchAsync(() => { + asyncLoadYamlDoc(RC, YF); + }); + } + + // Perform a load action on all of the active project directories. + private void performActionOnActiveProjectDirs(Project ActiveProj, RemarkConfig RC, Func DirAction) { + // Get the current project intermediate dir. + string OutDir = ProjectUtils.getProjectIntermediateDir(ActiveProj, RC); + if (Directory.Exists(OutDir) && DirAction(OutDir)) + return; + + // Check for an external source for our config. + ExternalRemarkSource ERS = Parent.Settings.getExternalSource(RC); + if (ERS != null && Directory.Exists(ERS.Dir)) { + List ExternalDirsToSearch = new List(); + ExternalDirsToSearch.AddRange(Directory.GetDirectories(ERS.Dir, '*' + ActiveProj.Name + '*', SearchOption.AllDirectories)); + ExternalDirsToSearch.Add(ERS.Dir); + + // Search for this document inside of the found directories. + foreach (string DirIter in ExternalDirsToSearch) { + // Check for a wild card for the config in this directory. + string Dir; + if (ERS.WildCard != "") { + var ConfigDir = Directory.GetDirectories(DirIter, ERS.WildCard, SearchOption.AllDirectories); + if (ConfigDir.Length == 0) + continue; + Dir = ConfigDir[0]; + } else + Dir = DirIter; + if (DirAction(Dir)) + return; + } + } + } + + // Try to load a YamlFile for /p Doc from the provided directory. + private bool loadDocumentFromDirectory(string Dir, string DocName, RemarkConfig RC) { + // Check for a yaml file with an extension. + var Files = Directory.GetFiles(Dir, DocName + ".opt.yaml", SearchOption.AllDirectories); + + // Check for the file without an extension. + if (Files.Length == 0) { + DocName = Path.GetFileNameWithoutExtension(DocName); + Files = Directory.GetFiles(Dir, DocName + ".opt.yaml", SearchOption.AllDirectories); + if (Files.Length == 0) return false; + } + Array.ForEach(Files, File => loadRemarks(RC, RC.getCreateYamlFile(File))); + return true; + } + + // Load in the Remarks for the provided YamlFile. + void asyncLoadYamlDoc(RemarkConfig RC, YamlFile YF) { + // Statistics for the loaded file. + int RemarkCount; + Stopwatch OperationTimer = Stopwatch.StartNew(); + + // The remarks loaded for the file. + var RemarkFiles = new List(); + var ReferencedFiles = new Dictionary>>(); + var RemarkLocations = new List(); + + // Get the yaml file associated with our file path. + lock (YF) { + // Clear out any current remark files. + YF.clear(); + + // Wrap in a try in the case of malformed yaml file. + try { + // Parsing classes. + DeserializerBuilder DB = new DeserializerBuilder(); + DB.WithTagMapping("!Analysis", typeof(Analysis)); + DB.WithTagMapping("!AnalysisFPCommute", typeof(AnalysisFPCommute)); + DB.WithTagMapping("!AnalysisAliasing", typeof(AnalysisAliasing)); + DB.WithTagMapping("!Passed", typeof(Passed)); + DB.WithTagMapping("!Missed", typeof(Missed)); + DB.WithNodeDeserializer(new RemarkNodeDeserializer(Parent)); + + byte[] LoadedFile = File.ReadAllBytes(YF.FullName); + RemarkLocations.Add(0); + for (int i = 0, e = LoadedFile.Length - 1; i < e; ++i) { + if ((char) LoadedFile[i] != '\n') + continue; + + if ((char) LoadedFile[i + 1] == 'D') { + i += 26; + + // Helper for getting a string value for a debug loc member. + Func GetStrFromLoc = Start => { + while (i < e && (char)LoadedFile[i] != ',') + ++i; + if (i == e) + throw new Exception("Invalid debug location."); + + // Add the file to the store. + return + System.Text.Encoding.Default.GetString(LoadedFile, Start, i - Start); + }; + + // Get the debug file. + string File = GetStrFromLoc(i); + if (File[0] == '\'') + File = File.Substring(1, File.Length - 2); + RemarkFile RF = RC.addRemarkFile(File); + if (!ReferencedFiles.TryGetValue(RF, out Dictionary> YamlRemarkDict)) + ReferencedFiles.Add(RF, YamlRemarkDict = new Dictionary>()); + RemarkFiles.Add(RF); + + // Make sure a list for this line number exists. + while ((char)LoadedFile[i] != ':') + ++i; + uint Line = UInt32.Parse(GetStrFromLoc(++i)); + if (!YamlRemarkDict.ContainsKey(Line)) + YamlRemarkDict.Add(Line, new List()); + } + else if ((char) LoadedFile[i + 1] == '-') { + // Check to see if the last location had a debug file. + if (RemarkLocations.Count != RemarkFiles.Count) + RemarkLocations[RemarkLocations.Count - 1] = i; + else + RemarkLocations.Add(i); + i += 4; + } + } + // Remove the last remark if it didn't have a location. + if (RemarkLocations.Count != RemarkFiles.Count) + RemarkLocations.RemoveAt(RemarkLocations.Count - 1); + RemarkLocations.Add(LoadedFile.Length); + + // Resize our remark list by the known remark count. + RemarkCount = RemarkFiles.Count; + System.Threading.Tasks.Parallel.For(0, RemarkCount, (int i) => { + // Check for cancel. + AsyncUtil.throwOnCancel(); + + // Generate the doc loaders. + int Idx = RemarkLocations[i]; + int Count = RemarkLocations[i + 1] - Idx; + TextReader TR = new StreamReader(new MemoryStream(LoadedFile, Idx, Count)); + var P = new Parser(TR); + Deserializer D = DB.Build(); + + // Consume the stream start event "manually". + P.Expect(); + P.Accept(); + + // Deserialize a remark. + RemarkFile RF = RemarkFiles[i]; + Remark R = (Remark)D.Deserialize(P); + + // Canonicalize the function internals. + R.SourceFunctions.Add(Parent.Mangler.demangle(R.Function)); + R.canonicalize(); + + // Make sure there is an existing line table for the remark. + List RemarksInFile = ReferencedFiles[RF][R.DebugLoc.Line]; + + // Check the list to see if we can merge with an existing remark. + lock (RemarksInFile) + if (!RemarksInFile.Exists(FileR => FileR.merge(R))) + RemarksInFile.Add(R); + }); + OperationTimer.Stop(); + + // After we are done then initialize the yamlfile source items. + YF.initializeSourceItems(ReferencedFiles.Keys); + + // Sort the remark lists. + System.Threading.Tasks.Parallel.ForEach(ReferencedFiles, (RFPair) => { + System.Threading.Tasks.Parallel.ForEach(RFPair.Value, + RList => RList.Value.Sort((L, R) => L.compareTo(R))); + RFPair.Key.addParentYaml(YF, RFPair.Value); + RFPair.Key.reMergeRemarks(); + }); + + // Dispatch a cache invalidate event. + IRemarkService ORS = ServiceLocator.getGlobalService(); + ORS.dispatchRemarkCacheInvalidateEvent(ReferencedFiles.Keys); + + // Update the global settings. + lock (RC) { + foreach (var RFPair in ReferencedFiles) { + foreach (var RList in RFPair.Value) { + foreach (var R in RList.Value) { + if (R.Hotness > RC.MaxHotness) + RC.MaxHotness = R.Hotness; + Parent.Settings.logVisiblePass(R.Pass, R.Name); + } + } + } + } + + // Log the load message. + TimeSpan Elapsed = OperationTimer.Elapsed; + string SuccessLoadMsg = $"Finished Loading {YF.Name}: Completed in " + + $"{Elapsed.Seconds + (Elapsed.Milliseconds / 1000.0)} seconds, {ReferencedFiles.Count}" + + $" source file(s) referenced, {RemarkCount} unique remarks loaded."; + ErrorListUtils.addTaskToOutputWindow(SuccessLoadMsg); + + // Preserve cancel exceptions. + } catch (OperationCanceledException) { + throw; + // Otherwise it's a load failure of some kind. + } catch(Exception E) { + ErrorListUtils.addTaskToOutputWindow($"Failure Loading: {YF.Name} : {E.Message}."); + } + } + } + + // Special Deserializer for Remark document objects. + sealed class RemarkNodeDeserializer : INodeDeserializer { + private readonly RemarkManager Parent; + public RemarkNodeDeserializer(RemarkManager Par) => Parent = Par; + + bool INodeDeserializer.Deserialize(IParser Parser, Type ExpectedTy, + Func NestedObjectDeserializer, out object Result) { + Type KeyTy, ValueTy; + var GenericDictionaryTy = ReflectionUtility.getImplementedGenericInterface(ExpectedTy, typeof(IDictionary<,>)); + if (GenericDictionaryTy != null) { + var GenericArguments = GenericDictionaryTy.GetGenericArguments(); + KeyTy = GenericArguments[0]; + ValueTy = GenericArguments[1]; + } else if (typeof(IDictionary).IsAssignableFrom(ExpectedTy)) + KeyTy = ValueTy = typeof(object); + else { + Result = null; + return false; + } + Result = deserializeMapHelper(KeyTy, ValueTy, Parser, NestedObjectDeserializer); + return true; + } + + private object deserializeMapHelper(Type TKey, Type TValue, IParser Parser, Func NestedObjectDeserializer) { + object Result = null; + Parser.Expect(); + while (!Parser.Accept()) { + var Key = NestedObjectDeserializer(Parser, TKey); + + // Check for a debug location, we handle this now to add it + // to any references. + if ((string)Key == "DebugLoc") { + NameReference Ref = Result as NameReference; + if (Ref == null) + Result = Ref = new NameReference(Result as string, null); + Ref.DebugLoc = NestedObjectDeserializer(Parser, typeof(DebugLocation)) as DebugLocation; + continue; + } + + var Value = NestedObjectDeserializer(Parser, TValue); + DeserializeStoreValue(Key, Value, (K, V) => { + // Check the key for the deserialization type. + var StringKey = K as string; + switch (StringKey) { + case "Caller": + case "Callee": + // Get the string value. + Result = Parent.Mangler.demangle(V as string); + return; + case "Type": + var TypeVal = V as string; + TypeVal = TypeVal.Replace("struct.", ""); + TypeVal = TypeVal.Replace("class.", ""); + TypeVal = TypeVal.Replace("\"", ""); + TypeVal = TypeVal.Replace("%", ""); + Result = TypeVal; + return; + default: + Result = V; + return; + } + }); + } + Parser.Expect(); + return Result; + } + + private static void DeserializeStoreValue(object Key, object Value, Action StoreVal) { + var KeyPromise = Key as IValuePromise; + var ValuePromise = Value as IValuePromise; + if (KeyPromise == null) { + if (ValuePromise == null) + // Happy path: both Key and Value are known + StoreVal(Key, Value); + else + // Key is known, Value is pending + ValuePromise.ValueAvailable += V => StoreVal(Key, V); + return; + } + if (ValuePromise == null) { + // Key is pending, Value is known + KeyPromise.ValueAvailable += K => StoreVal(K, Value); + return; + } + + // Both Key and Value are pending. We need to wait until both of them becom available. + var HasFirstPart = false; + KeyPromise.ValueAvailable += K => { + if (HasFirstPart) + StoreVal(K, Value); + else { + Key = K; + HasFirstPart = true; + } + }; + + ValuePromise.ValueAvailable += v => { + if (HasFirstPart) + StoreVal(Key, v); + else { + Value = v; + HasFirstPart = true; + } + }; + } + } + + // Utility used in the above NodeDeserializer. + static class ReflectionUtility { + public static Type getImplementedGenericInterface(Type Ty, Type GenericInterfaceType) { + foreach (var InterfaceTy in getImplementedInterfaces(Ty)) + if (InterfaceTy.IsGenericType && InterfaceTy.GetGenericTypeDefinition() == GenericInterfaceType) + return InterfaceTy; + return null; + } + public static IEnumerable getImplementedInterfaces(Type Ty) { + if (Ty.IsInterface) + yield return Ty; + foreach (var ImplementedInterface in Ty.GetInterfaces()) + yield return ImplementedInterface; + } + } + } +} Index: remark-viewer-vs/RemarkInterface/Core/RemarkSettings.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Core/RemarkSettings.cs @@ -0,0 +1,226 @@ +using Microsoft.VisualStudio.Settings; +using Microsoft.VisualStudio.Shell.Settings; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +namespace RemarkInterface { + // Location info for external remark locations. + [Serializable] + public class ExternalRemarkSource { + public string Dir = ""; + public string WildCard = ""; + } + + public class RemarkSettings { + // Main setting location. + private static string CollectionPath = "OptRemarkViewer"; + // Settings store. + private readonly WritableSettingsStore WSettingStore; + // Mask of RemarkType. + public BitArray IgnoreMask { get; internal set; } + // Visibility mask per pass. + private Dictionary PassMasks = new Dictionary(); + // Visibility mask per pass subtype. + private Dictionary PassSubTypeMasks = new Dictionary(); + // A list of passes that we have encountered. + public Dictionary> PassList + = new Dictionary>(); + // External source locations for configurations. + internal Dictionary ExternalConfigSources + = new Dictionary(); + + public RemarkSettings() { + // Get the settings store from visual studios. + var ShellSettingsMgr = new ShellSettingsManager(ServiceLocator.PackageServiceProvider); + WSettingStore = ShellSettingsMgr.GetWritableSettingsStore(SettingsScope.UserSettings); + + // Load in the initial settings. + loadSettings(); + } + + // Set whether we display remarks on out of date files. + public bool AllowOutOfDateRemarks { get; set; } + // Whether to show the full line or not. + public bool ShowFullSourceFileLine { get; set; } + // If we should only show the diffs when comparing configs. + public bool OnlyShowDiffs { get; set; } + // Filter by hotness percentage. + public uint HotnessFilter { get; set; } + // If we should auto emit the option to emit remarks. + public bool AutoEmitRemarks { get; set; } + // If the remark tagger is enabled. + public bool IsEnabled { get; set; } + + public ExternalRemarkSource getExternalSource(RemarkConfig RC) { + return ExternalConfigSources.TryGetValue(RC.ToString(), out ExternalRemarkSource Out) ? Out : null; + } + + /// Save and load solution specific settings. + internal void loadSolutionSettings(Stream IOStream) { + try { + if (IOStream.Length <= 0) return; + BinaryFormatter Formatter = new BinaryFormatter(); + ExternalConfigSources = Formatter.Deserialize(IOStream) as Dictionary; + } catch { } + } + internal void saveSolutionSettings(Stream IOStream) { + try { + // External source configurations for configs. + BinaryFormatter BF = new BinaryFormatter(); + BF.Serialize(IOStream, ExternalConfigSources); + } catch { } + } + + /// Save and load global remark settings. + public void loadSettings() { + // Try to load in each of the settings. + try { + if (!WSettingStore.CollectionExists(CollectionPath)) + WSettingStore.CreateCollection(CollectionPath); + + // Global ignore mask. + IgnoreMask = loadBitArraySetting("IgnoreMask", (int)RemarkType.Count); + + // IsEnabled. + IsEnabled = WSettingStore.GetBoolean(CollectionPath, "IsEnabled", false); + + // Out-of-Date. + AllowOutOfDateRemarks = WSettingStore.GetBoolean(CollectionPath, "AllowOoD", false); + + // Full source line. + ShowFullSourceFileLine = WSettingStore.GetBoolean(CollectionPath, "FullSourceLine", false); + + // Diffs. + OnlyShowDiffs = WSettingStore.GetBoolean(CollectionPath, "OnlyShowDiffs", false); + + // Hotness. + HotnessFilter = WSettingStore.GetUInt32(CollectionPath, "Hotness", 0); + + // Remark Option. + AutoEmitRemarks = WSettingStore.GetBoolean(CollectionPath, "AutoEmitRemarks", false); + + // Gather all of the settings we don't have an exact name for. + var Collection = WSettingStore.GetPropertyNames(CollectionPath); + foreach (string Prop in Collection) { + // Only get the properties that don't have exact names. + if (Prop[0] != '.') + continue; + // Global visibility mask. + if (Prop.StartsWith(".gi")) { + BitArray Value = loadBitArraySetting(Prop, (int)RemarkType.Count); + PassMasks.Add(Prop.Substring(4), Value); + continue; + } + // Sub type visibility mask. + if (Prop.StartsWith(".si")) { + string Key = Prop.Substring(4); + bool Val = WSettingStore.GetBoolean(CollectionPath, Prop); + if (PassSubTypeMasks.ContainsKey(Key)) + PassSubTypeMasks[Key] = Val; + else + PassSubTypeMasks.Add(Key, Val); + } + } + } catch { } + } + BitArray loadBitArraySetting(string Name, int DefaultCount) { + if (WSettingStore.PropertyExists(CollectionPath, Name)) { + int I32IgnoreMask = WSettingStore.GetInt32(CollectionPath, Name); + return new BitArray(new int[] { I32IgnoreMask }); + } + return new BitArray(DefaultCount); + } + + public void saveSettings() { + try { + // Make sure our store exists. + if (!WSettingStore.CollectionExists(CollectionPath)) + WSettingStore.CreateCollection(CollectionPath); + + // Global ignore mask. + saveBitArraySetting("IgnoreMask", IgnoreMask); + + // IsEnabled. + WSettingStore.SetBoolean(CollectionPath, "IsEnabled", IsEnabled); + + // Out-of-Date. + WSettingStore.SetBoolean(CollectionPath, "AllowOoD", AllowOutOfDateRemarks); + + // Full source line. + WSettingStore.SetBoolean(CollectionPath, "FullSourceLine", ShowFullSourceFileLine); + + // Diffs. + WSettingStore.SetBoolean(CollectionPath, "OnlyShowDiffs", OnlyShowDiffs); + + // Hotness. + WSettingStore.SetUInt32(CollectionPath, "Hotness", HotnessFilter); + + // Remark option. + WSettingStore.SetBoolean(CollectionPath, "AutoEmitRemarks", AutoEmitRemarks); + + // PerPass global ignore mask. + foreach (KeyValuePair PMask in PassMasks) + saveBitArraySetting(".gi." + PMask.Key, PMask.Value); + + // PerPass sub type ignore mask. + foreach (KeyValuePair PMask in PassSubTypeMasks) + WSettingStore.SetBoolean(CollectionPath, ".si." + PMask.Key, PMask.Value); + } catch { } + } + void saveBitArraySetting(string Name, BitArray Value) { + int[] array = new int[1]; + Value.CopyTo(array, 0); + WSettingStore.SetInt32(CollectionPath, Name, array[0]); + } + + /// Main settings checks and utilities. + internal void logVisiblePass(string Pass, string SubType) { + lock (PassList) { + if (!PassList.TryGetValue(Pass, out HashSet SubList)) + PassList.Add(Pass, SubList = new HashSet()); + SubList.Add(SubType); + } + } + + // Get the current global visibility mask for a pass. + public BitArray getPassVisibilityMask(string Pass) { + if (!PassMasks.TryGetValue(Pass, out BitArray PassMask)) + PassMasks.Add(Pass, PassMask = new BitArray((int)RemarkType.Count)); + return PassMask; + } + public bool setPassVisibilityMask(string Pass, BitArray Mask) { + if ((BitArray)PassMasks[Pass] == Mask) + return false; + PassMasks[Pass] = Mask; + return true; + } + + // Get the pass subtype visibility mask. + public bool getPassSubTypeIgnored(string Pass, string SubTy) { + string Key = Pass + "<>" + SubTy; + if (!PassSubTypeMasks.TryGetValue(Key, out bool Ignored)) + PassSubTypeMasks.Add(Key, Ignored = false); + return Ignored; + } + public bool setPassSubTypeIgnored(string RawKey, bool Ignore) { + if (PassSubTypeMasks[RawKey] == Ignore) + return false; + PassSubTypeMasks[RawKey] = Ignore; + return true; + } + public bool setPassSubTypeIgnored(string Pass, string SubTy, bool Ignore) { + return setPassSubTypeIgnored(Pass + "<>" + SubTy, Ignore); + } + + // Set a type to be ignored or not. + public bool ignoreType(RemarkType RTy, bool Ignore = true) { + if (IgnoreMask[(int)RTy] == Ignore) + return false; + IgnoreMask.Set((int)RTy, Ignore); + return true; + } + } +} Index: remark-viewer-vs/RemarkInterface/ErrorList/ErrorList.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/ErrorList/ErrorList.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.TableControl; +using Microsoft.VisualStudio.Shell.TableManager; +using Microsoft.VisualStudio.Shell.Interop; +using EnvDTE; +using EnvDTE80; + +namespace RemarkInterface.ErrorList { + class Error { + public Error() { } + public Error(DebugLocation Loc) { + if (Loc == null) + return; + FileName = Loc.File; + Line = (int)Loc.Line - 1; + Column = (int)Loc.Column; + } + public string Category { get; set; } + public int Column { get; set; } + public string Description { get; set; } + public string FileName { get; set; } + public string Help { get; set; } + public string Id { get; set; } + public int Line { get; set; } + public string Project { get; set; } = ""; + public TaskErrorCategory Severity { get; set; } + public List Tags { get; set; } + + public __VSERRORCATEGORY GetSeverity() => (__VSERRORCATEGORY)Severity; + + public override bool Equals(object Obj) + => Obj is Error RE ? GetHashCode() == RE.GetHashCode() : false; + + public override int GetHashCode() + => $"{Id}{FileName}{Line}{Column}".GetHashCode(); + } + class SinkManager : IDisposable { + private readonly ITableDataSink TableSink; + private TableDataSource ErrorList; + private List SnapShots = new List(); + + internal SinkManager(TableDataSource PList, ITableDataSink PSink) { + TableSink = PSink; + ErrorList = PList; + PList.AddSinkManager(this); + } + + internal void clear() => TableSink.RemoveAllSnapshots(); + public void Dispose() => ErrorList.RemoveSinkManager(this); + internal void removeSnapShots(string Category) { + SnapShots.RemoveAll(S => { + if(S.Category == Category) { + TableSink.RemoveSnapshot(S); + return true; + } + return false; + }); + } + internal void update(IEnumerable Snaps) { + foreach (var SnapShot in Snaps) { + var Existing = SnapShots.FirstOrDefault(S => S.Category == SnapShot.Category); + if (Existing != null) { + SnapShots.Remove(Existing); + TableSink.ReplaceSnapshot(Existing, SnapShot); + } else + TableSink.AddSnapshot(SnapShot); + SnapShots.Add(SnapShot); + } + } + } + class TableEntriesSnapshot : WpfTableEntriesSnapshotBase { + internal TableEntriesSnapshot(string Category) => this.Category = Category; + + public List Errors { get; } = new List(); + + public override int VersionNumber { get; } = 1; + + public override int Count => Errors.Count; + + public string Category { get; set; } + + public override bool TryGetValue(int Index, string ColName, out object Content) { + Content = null; + if (Index < 0 || Index >= Errors.Count) + return false; + + Error E = Errors[Index]; + switch (ColName) { + case StandardTableKeyNames.DocumentName: + Content = E.FileName; + return true; + case StandardTableKeyNames.ErrorCategory: + Content = E.Category; + return true; + case StandardTableKeyNames.BuildTool: + Content = "OptRemarkViewer"; + return true; + case StandardTableKeyNames.Line: + if (!string.IsNullOrEmpty(E.FileName)) { + Content = E.Line; + return true; + } + return false; + case StandardTableKeyNames.Column: + if (!string.IsNullOrEmpty(E.FileName)) { + Content = E.Column; + return true; + } + return false; + case StandardTableKeyNames.Text: + Content = E.Description; + return true; + case StandardTableKeyNames.PriorityImage: + case StandardTableKeyNames.ErrorSeverityImage: + return false; + case StandardTableKeyNames.ErrorSeverity: + Content = E.GetSeverity(); + return true; + case StandardTableKeyNames.Priority: + Content = vsTaskPriority.vsTaskPriorityMedium; + return true; + case StandardTableKeyNames.ErrorSource: + Content = ErrorSource.Other; + return true; + case StandardTableKeyNames.ErrorCode: + Content = E.Id; + return true; + case StandardTableKeyNames.ProjectName: + Content = E.Project; + return true; + case StandardTableKeyNames.ErrorCodeToolTip: + Content = E.Help; + return true; + case StandardTableKeyNames.HelpLink: + return false; + default: + return false; + } + } + + public override bool CanCreateDetailsContent(int index) => false; + + public override bool TryCreateDetailsStringContent(int Idx, out string Content) { + Content = null; + return false; + } + } + class TableDataSource : ITableDataSource { + private static TableDataSource StaticInstance; + private readonly List Managers = new List(); + private static Dictionary SnapShots + = new Dictionary(); + + [Import] + private ITableManagerProvider TableManagerProvider { get; set; } = null; + + private TableDataSource() { + var CompositionService = ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)) as IComponentModel; + CompositionService.DefaultCompositionService.SatisfyImportsOnce(this); + + var Manager = TableManagerProvider.GetTableManager(StandardTables.ErrorsTable); + Manager.AddSource(this, StandardTableColumnDefinitions.DetailsExpander, StandardTableColumnDefinitions.BuildTool, + StandardTableColumnDefinitions.ErrorSeverity, StandardTableColumnDefinitions.ErrorCode, + StandardTableColumnDefinitions.ErrorSource, StandardTableColumnDefinitions.ErrorCategory, + StandardTableColumnDefinitions.Text, StandardTableColumnDefinitions.DocumentName, + StandardTableColumnDefinitions.Line, StandardTableColumnDefinitions.Column); + } + + public static TableDataSource Instance => StaticInstance ?? (StaticInstance = new TableDataSource()); + + public bool HasErrors => SnapShots.Any(); + + public string SourceTypeIdentifier => StandardTableDataSources.ErrorTableDataSource; + + public string Identifier => GuidStrings.GuidPackage; + + public string DisplayName => "OptRemarkViewer"; + + public IDisposable Subscribe(ITableDataSink S) => new SinkManager(this, S); + + public void AddSinkManager(SinkManager manager) { + lock (Managers) Managers.Add(manager); + } + + public void RemoveSinkManager(SinkManager manager) { + lock (Managers) Managers.Remove(manager); + } + + public void UpdateAllSinks() { + lock (Managers) + foreach (var Man in Managers) + Man.update(SnapShots.Values); + } + + public void addError(string Category, Error E) { + if (Category == "" || E == null) + return; + if (!SnapShots.TryGetValue(Category, out TableEntriesSnapshot Snap)) + SnapShots.Add(Category, Snap = new TableEntriesSnapshot(Category)); + Snap.Errors.Add(E); + UpdateAllSinks(); + } + public void cleanErrors(string Category) { + if(SnapShots.TryGetValue(Category, out TableEntriesSnapshot Snap)) { + Snap.Dispose(); + SnapShots.Remove(Category); + } + lock (Managers) + foreach (var Man in Managers) + Man.removeSnapShots(Category); + UpdateAllSinks(); + } + public void cleanAllErrors() { + foreach (var Snap in SnapShots.Values) + Snap?.Dispose(); + SnapShots.Clear(); + lock (Managers) + foreach (var Man in Managers) + Man.clear(); + } + } +} Index: remark-viewer-vs/RemarkInterface/ErrorList/ErrorListUtils.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/ErrorList/ErrorListUtils.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.VisualStudio.Shell.Interop; +using RemarkInterface.ErrorList; + +namespace RemarkInterface.Utils { + internal static class ErrorListUtils { + public static void clean() => TableDataSource.Instance.cleanAllErrors(); + public static void clean(string SnapShotCategory) => TableDataSource.Instance.cleanErrors(SnapShotCategory); + public static void addTaskToOutputWindow(string Msg, DebugLocation Loc = null) { + AsyncUtil.dispatchSyncInvoke(() => emitToOutputWindow(Msg, Loc)); + } + + private static void emitToOutputWindow(string Msg, DebugLocation Loc) { + // Now get the SVsOutputWindow service from the service provider. + IVsOutputWindow OutputWindow = ServiceLocator.getGlobalService(); + if (OutputWindow == null) + return; + // We can not write on the Output window itself, but only on one of its panes. + // Here we try to use the "General" pane. + Guid GuidGeneral = Microsoft.VisualStudio.VSConstants.GUID_OutWindowGeneralPane; + if (Microsoft.VisualStudio.ErrorHandler.Failed(OutputWindow.GetPane(ref GuidGeneral, out IVsOutputWindowPane WindowPane)) + || WindowPane == null) { + if (Microsoft.VisualStudio.ErrorHandler.Failed(OutputWindow.CreatePane(ref GuidGeneral, "General", 1, 0))) + return; + if (Microsoft.VisualStudio.ErrorHandler.Failed(OutputWindow.GetPane(ref GuidGeneral, out WindowPane)) + || WindowPane == null) + return; + if (Microsoft.VisualStudio.ErrorHandler.Failed(WindowPane.Activate())) + return; + } + + // Finally we can write on the window pane. + if (Loc != null) + Msg = Loc.toMSVCString(true) + Msg; + WindowPane.OutputString("OptRemarkViewer: " + Msg + "\n"); + } + + // Add a task to the task window. + public static void addTaskToErrorList(Error E, string SnapShotCategory) => TableDataSource.Instance.addError(SnapShotCategory, E); + } +} Index: remark-viewer-vs/RemarkInterface/Properties/AssemblyInfo.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6a858a68-448f-4b27-93fc-3ebbc3d3fc07")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] Index: remark-viewer-vs/RemarkInterface/Properties/GuidStrings.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Properties/GuidStrings.cs @@ -0,0 +1,5 @@ +namespace RemarkInterface { + public static class GuidStrings { + public const string GuidPackage = "B0002DC2-56EE-4931-93F7-70D6E9863940"; + } +} Index: remark-viewer-vs/RemarkInterface/Properties/Resources.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RemarkInterface.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RemarkInterface.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Demangler { + get { + object obj = ResourceManager.GetObject("Demangler", resourceCulture); + return ((byte[])(obj)); + } + } + } +} Index: remark-viewer-vs/RemarkInterface/Properties/Resources.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Demangler.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file Index: remark-viewer-vs/RemarkInterface/Remark/DebugLocation.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Remark/DebugLocation.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace RemarkInterface { + // Remark debug location. + public class DebugLocation { + /// Construction. + public DebugLocation() => File = ""; + + /// Location Members. + public string File { get; internal set; } + public uint Line { get; internal set; } + public uint Column { get; internal set; } + + // Display Util. + public string toMSVCString(bool ShowFullLine) { + string Out = ShowFullLine ? File : Path.GetFileName(File); + Out += '(' + Line.ToString(); + if (Column != 0) + Out += "," + Column; + return Out + ")"; + } + + // Class Util. + public override bool Equals(Object Obj) { + if (Obj is DebugLocation RLoc) + return Line == RLoc.Line && Column == RLoc.Column + && File == RLoc.File; + return false; + } + public override int GetHashCode() => Line.GetHashCode(); + } + + // Reference to a named symbol. + public class NameReference { + /// Construction. + public NameReference() => DebugLoc = null; + public NameReference(string NewName, DebugLocation Loc) { + Name = NewName; + DebugLoc = Loc; + } + + // Class Util. + public override bool Equals(Object Obj) { + if(Obj is NameReference RLoc) + return DebugLoc.Equals(RLoc.DebugLoc) && Name == RLoc.Name; + return false; + } + public override int GetHashCode() => Name.GetHashCode() + DebugLoc.GetHashCode(); + public override string ToString() => Name; + + /// Reference Members. + public string Name; + public DebugLocation DebugLoc; + } + + // Reference to a function symbol. + public class FunctionReference : NameReference { + /// Reference Members. + public string DemangledName { get; internal set; } = ""; + public string BaseName { get; internal set; } = ""; + public override bool Equals(Object Obj) { + if (Obj is FunctionReference FRef) + return Name == FRef.Name; + return false; + } + + // Class Util. + public override int GetHashCode() => Name.GetHashCode(); + public override string ToString() => BaseName; + } +} Index: remark-viewer-vs/RemarkInterface/Remark/Remark.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Remark/Remark.cs @@ -0,0 +1,241 @@ +using System.Collections.Generic; +using System.Linq; +using System.Windows.Media; + +namespace RemarkInterface { + // Different types of optimization remarks. + public enum RemarkType { + Analysis, + AnalysisAliasing, + AnalysisFPCommute, + Missed, + Passed, + Count + } + + // Base Remark implementation. + public abstract class Remark { + /// Remark Type Colors + public static Color AnalysisColor = Color.FromArgb(55, 255, 255, 255); + public static Color MissedColor = Color.FromArgb(55, 255, 0, 0); + public static Color PassedColor = Color.FromArgb(55, 0, 255, 0); + + /// Remark properties. + public HashSet SourceFunctions + = new HashSet(); + public string Pass { get; set; } + public string Name { get; set; } + public DebugLocation DebugLoc { get; set; } + public string Function { get; set; } + public abstract Color Color { get; } + public List Args { get; set; } + public uint Hotness { get; set; } + public abstract RemarkType Type { get; } + + // Key to see if two Remarks can be merged. + private string MergeKey = null; + + /// Class Util. + private bool Equals(Remark Other) { + if (Args.Count != Other.Args.Count + || !string.Equals(Pass, Other.Pass) + || !string.Equals(Name, Other.Name)) + return false; + return Args.SequenceEqual(Other.Args, new EqualityComparer((LA, RA) => { + return (!(LA is string) || LA.Equals(RA)); + })); + } + public bool isSubsetOf(Remark R) { + if (R.GetType() != GetType()) return false; + if (!Equals(R)) return false; + + // Check non string args. + return Args.SequenceEqual(R.Args, new EqualityComparer((LA, RA) => { + if (LA is string) + return true; + var RList = RA as HashSet; + if (LA is HashSet LList) + return RList != null ? LList.IsSubsetOf(RList) : false; + return RList?.Contains(LA) ?? LA.Equals(RA); + })); + } + public override bool Equals(object Other) { + if (ReferenceEquals(null, Other)) return false; + if (ReferenceEquals(this, Other)) return true; + if (Other.GetType() != GetType()) return false; + Remark R = (Remark)Other; + if (!Equals(R)) return false; + + // Check non string args. + return Args.SequenceEqual(R.Args, new EqualityComparer((LA, RA) => { + if (LA.GetType() != RA.GetType()) + return false; + if (LA is HashSet LList) + return LList.SetEquals(RA as IEnumerable); + return LA is string ? true : LA.Equals(RA); + })); + } + public int compareTo(Remark R) { + if (Type != R.Type) + return (int)Type > (int)R.Type ? -1 : 1; + int Compare = Pass.CompareTo(R.Pass); + if (Compare == 0) + Compare = Name.CompareTo(R.Name); + return Compare; + } + public override int GetHashCode() { + unchecked { + var HashCode = 0; + HashCode = (HashCode * 397) ^ GetType().GetHashCode(); + HashCode = (HashCode * 397) ^ Pass.GetHashCode(); + HashCode = (HashCode * 397) ^ Name.GetHashCode(); + HashCode = (HashCode * 397) ^ DebugLoc.GetHashCode(); + return HashCode; + } + } + public override string ToString() { + string Out = string.Join(",", SourceFunctions.Select(Fn => Fn.BaseName)); + + // Pass details. + Out += " : " + Pass + " - " + Name + " : "; + + // Arguments. + foreach (object Arg in Args) { + // Name reference. + if (Arg.GetType() == typeof(HashSet)) + Out += '{' + string.Join(",", (Arg as HashSet).ToArray()) + '}'; + else + Out += Arg.ToString(); + } + return Out + '.'; + } + + // Checks to see if this remark is skipped iff it is from a perTU file in an LTO build. + public bool isPTUSkippedDuringLTO() => Type != RemarkType.Passed && Pass == "inline"; + + public Remark clone() { + Remark New = (Remark)System.Activator.CreateInstance(GetType()); + New.SourceFunctions = new HashSet(SourceFunctions); + New.Pass = Pass; + New.Name = Name; + New.DebugLoc = DebugLoc; + New.Function = Function; + New.Hotness = Hotness; + New.MergeKey = MergeKey; + New.Args = Args.Select(Arg => Arg is HashSet List ? new HashSet(List) : Arg).ToList(); + return New; + } + + // Canonicalize the internals of this remark. + public void canonicalize() { + // Merge string arguments. + for (int i = 1, e = Args.Count; i < e;) { + object Value = Args[i]; + if (Value.GetType() != typeof(string)) { + ++i; + continue; + } + var BackEle = Args[i - 1]; + if (BackEle.GetType() == typeof(string)) { + Args[i - 1] += Value as string; + Args.RemoveAt(i); + --e; + } + else + ++i; + } + + // Generate the merge key. + MergeKey = Pass + Name; + foreach (var Arg in Args) { + if (typeof(FunctionReference) == Arg.GetType()) + MergeKey += (char)1; + else if (typeof(NameReference) == Arg.GetType()) + MergeKey += (char)2; + else + MergeKey += (string)Arg; + } + } + + // Try to merge the R's contents into the current remark. + public bool merge(Remark R) { + if (!MergeKey.Equals(R.MergeKey)) + return false; + + // Merge source functions. + int PrevNumSourceFns = SourceFunctions.Count; + SourceFunctions.UnionWith(R.SourceFunctions); + + // Check subtle differences in the msg. + if (mergeArgs(R) || PrevNumSourceFns != SourceFunctions.Count) + if (R.Hotness > Hotness) + Hotness = R.Hotness; + return true; + } + + // Try to merge the arguments of the provided remark into the current. + private bool mergeArgs(Remark R) { + var Merged = false; + for (int i = 0, e = Args.Count; i < e; ++i) { + object Arg = Args[i], RArg = R.Args[i]; + + // Skip strings. + if (Arg is string) + continue; + // If the arg is a name reference. + if (typeof(NameReference).IsAssignableFrom(Arg.GetType())) { + var NewArgList = new HashSet() { Arg }; + + // Check to see if we merge into the right list. + if (RArg is HashSet NRMergeList) + NewArgList.UnionWith(NRMergeList); + // Don't merge if the references are identical. + else if (Arg.Equals(RArg)) + continue; + else + NewArgList.Add(RArg); + Merged = true; + Args[i] = NewArgList; + continue; + } + + // If the right object is a list then we merge the two. + var MergeList = (HashSet)Arg; + if (RArg is HashSet RMergeList) { + int PrevCount = MergeList.Count; + MergeList.UnionWith(RMergeList); + Merged |= MergeList.Count != PrevCount; + continue; + } + // Otherwise try to add the arg to our list. + Merged |= MergeList.Add(RArg); + } + return Merged; + } + } + + // Remark Types. + public class Analysis : Remark { + // yaml_tag = '!Analysis' + public override RemarkType Type => RemarkType.Analysis; + public override Color Color => Remark.AnalysisColor; + } + public class AnalysisAliasing : Analysis { + // yaml_tag = '!AnalysisAliasing' + public override RemarkType Type => RemarkType.AnalysisAliasing; + } + public class AnalysisFPCommute : Analysis { + // yaml_tag = '!AnalysisFPCommute' + public override RemarkType Type => RemarkType.AnalysisFPCommute; + } + public class Missed : Remark { + // yaml_tag = '!Missed' + public override RemarkType Type => RemarkType.Missed; + public override Color Color => Remark.MissedColor; + } + public class Passed : Remark { + // yaml_tag = '!Passed' + public override RemarkType Type => RemarkType.Passed; + public override Color Color => Remark.PassedColor; + } +} Index: remark-viewer-vs/RemarkInterface/Remark/RemarkFile.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Remark/RemarkFile.cs @@ -0,0 +1,345 @@ +using ConcurrentCollections; +using EnvDTE; +using Microsoft.VisualStudio.VCProjectEngine; +using RemarkInterface.Utils; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace RemarkInterface { + // Shared file implementation. + public abstract class BaseFile { + public string FullName { get; internal set; } + public string Name => System.IO.Path.GetFileName(FullName); + internal object FileLock = new object(); + } + + // A source file that contains optimization remarks. + public class RemarkFile : BaseFile { + internal RemarkFile(RemarkConfig ParentCfg, string FileName) { + ParentConfig = ParentCfg; + FullName = FileName; + Item = null; + AsyncUtil.dispatchSyncInvoke(() => { + Item = ProjectUtils.getProjectItemFromFullName(FileName); + }); + } + + // Parent utilities. + internal void addParentYaml(YamlFile YF, Dictionary> Remarks) { + lock (FileLock) + RemarksPerYamlFile.TryAdd(YF, Remarks); + IsLTO |= YF.IsLTO; + } + internal void removeParentYaml(YamlFile YF) { + lock (FileLock) { + RemarksPerYamlFile.TryRemove(YF, out Dictionary> Temp); + if (IsLTO && YF.IsLTO) + IsLTO = ParentYamlFiles.Any(PYF => PYF.IsLTO); + } + } + internal bool hasNonLTOParentYaml() { + lock (FileLock) + return ParentYamlFiles.Any(YF => !YF.IsLTO); + } + + // Util for merging in a yaml files remarks into a dictionary of remarks. + private void merge(IEnumerable Files, bool ContainsLTO, ref Dictionary> Dest) { + if(Files.Count() == 0) { + Dest.Clear(); + return; + } + // Checks for an LTO yaml file for this source.= + var NewDest = new Dictionary>(); + foreach (YamlFile YF in Files) { + if (!RemarksPerYamlFile.TryGetValue(YF, out Dictionary> Src)) + continue; + foreach (var LinePair in Src) { + uint Line = LinePair.Key; + List Remarks = LinePair.Value; + bool IsLTOPerTU = ContainsLTO && !YF.IsLTO; + if (!NewDest.ContainsKey(Line)) { + if (IsLTOPerTU) + NewDest.Add(Line, Remarks.Where(R => !R.isPTUSkippedDuringLTO()).Select(R => R.clone()).ToList()); + else + NewDest.Add(Line, Remarks.Select(R => R.clone()).ToList()); + continue; + } + List DestRemarks = NewDest[Line]; + bool InitialAdd = DestRemarks.Count == 0; + foreach (Remark R in Remarks) + if ((!IsLTOPerTU || !R.isPTUSkippedDuringLTO()) && (InitialAdd || !DestRemarks.Exists(DestR => DestR.merge(R)))) + DestRemarks.Add(R.clone()); + } + } + + // Sort the list of remarks. + if(Files.Count() != 1) + System.Threading.Tasks.Parallel.ForEach(NewDest, + RList => RList.Value.Sort((L, R) => L.compareTo(R))); + Dest = NewDest; + } + + // Recreate the merge list from a new parent yaml file. + internal void reMergeRemarks() { + lock (FileLock) { + merge(ParentYamlFiles, IsLTO, ref Remarks); + if (LastWriteTime == DateTime.MinValue) + LastWriteTime = System.IO.File.GetLastWriteTime(FullName); + unCheckedReMergeUpToDateRemarks(); + } + } + private void unCheckedReMergeUpToDateRemarks() { + var Files = ParentYamlFiles.Where(YF => YF.LastLoadTime.CompareTo(LastWriteTime) >= 0); + merge(Files, Files.Any(YF => YF.IsLTO), ref UpToDateRemarks); + } + private void reMergeUpToDateRemarks(DateTime Access) { + if (LastWriteTime == Access) + return; + lock (FileLock) { + LastWriteTime = Access; + unCheckedReMergeUpToDateRemarks(); + } + } + public bool hasUpToDateRemarks(DateTime Access) { + reMergeUpToDateRemarks(Access); + return UpToDateRemarks.Count != 0; + } + + // If this Remark file is part of an LTO build. + private bool IsLTO = false; + + // The latest time that we have constructed the outofdate remarks list. + private DateTime LastWriteTime = DateTime.MinValue; + + // The parent yaml file the remarks from this file were spawned from. + internal IEnumerable ParentYamlFiles => RemarksPerYamlFile.Keys; + internal ConcurrentDictionary>> RemarksPerYamlFile + = new ConcurrentDictionary>>(); + + // The project item attached to this file. + internal ProjectItem Item; + + // Remarks in this file separated by line number. + public Dictionary> UpToDateRemarks = new Dictionary>(); + public Dictionary> Remarks = new Dictionary>(); + + // Parent configuration. + RemarkConfig ParentConfig; + } + + // A yaml file loaded from disk. + internal class YamlFile : BaseFile { + public YamlFile(string Name) { + FullName = Name; + IsLTO = Name.EndsWith(".vcxproj.opt.yaml"); + } + + // Check if we can destroy this yaml file. + internal bool canDestroy() { + lock (FileLock) + return SourceItems.Count == 0; + } + internal bool canDelayDestroy() { + lock (FileLock) { + foreach (RemarkFile RF in SourceItems) + if (RF.Item?.IsOpen ?? false) + return false; + return true; + } + } + + /// Source Item Utils. + // Clear any held source items. + internal void clear() { + foreach (RemarkFile RF in SourceFiles) + RF.removeParentYaml(this); + SourceItems.Clear(); + } + internal void initializeSourceItems(IEnumerable Sources) { + SourceItems = Sources.ToList(); + } + internal void removeSourceItem(RemarkFile RF) { + lock (SourceItems) + SourceItems.Remove(RF); + } + internal IEnumerable SourceFiles => SourceItems; + + // Update the last access time + internal bool updateAccessTime() { + // Get the timestamp for the yaml file. + DateTime LastWrite = LastWriteTime; + + // Check for a cached access. + if (LastLoadTime != DateTime.MinValue && + LastLoadTime.CompareTo(LastWrite) >= 0) + return false; + LastLoadTime = LastWrite; + return true; + } + + // Last know file write time. + public DateTime LastWriteTime + => System.IO.File.GetLastWriteTime(FullName); + + // If this is an LTO remark file. + internal bool IsLTO { get; private set; } + + // Set of source files attached to this yaml file. + List SourceItems = new List(); + + // Cached last load time. + internal DateTime LastLoadTime = DateTime.MinValue; + } + + // Represents a build configuration Remark cache. + public class RemarkConfig { + internal RemarkConfig(string Config, string Platform) { + ConfigName = Config; + PlatformName = Platform; + } + public override string ToString() => ConfigName + '|' + PlatformName; + + /// Configuration descriptors + public string ConfigName { get; } + public string PlatformName { get; } + public bool isLTO(VCProject P) { + if (P == null) + return false; + string File = P.ProjectFile.Substring(P.ProjectDirectory.Length) + ".opt.yaml"; + return YamlFiles.Values.Any(YF => YF.FullName.EndsWith(File)); + } + + // The max hotness value seen for this config. + // FIXME: Maybe this should be per project, + // but then what do we do about header files? + public uint MaxHotness { get; internal set; } = 0; + + // Add/Access held RemarkFiles + public RemarkFile getRemarkFile(string File) { + return Remarks.TryGetValue(FileUtils.normalizeFilePath(File), out RemarkFile F) ? F : null; + } + internal RemarkFile addRemarkFile(string File) { + string NormalizedFile = FileUtils.normalizeFilePath(File); + if (Remarks.TryGetValue(NormalizedFile, out RemarkFile F)) + return F; + Remarks.TryAdd(NormalizedFile, new RemarkFile(this, File)); + return Remarks[NormalizedFile]; + } + internal IEnumerable RemarkFiles => Remarks.Values; + + // Remove RemarkFiles. + private void eraseRemarkFile(string File) { + File = FileUtils.normalizeFilePath(File); + Remarks.TryRemove(File, out RemarkFile F); + } + internal void removeRemarkFile(string File) { + RemarkFile RF = getRemarkFile(File); + if (RF == null || (RF.Item != null && RF.Item.IsOpen)) + return; + // Remove our file from the parent yaml files. + foreach(YamlFile YF in RF.ParentYamlFiles) { + lock (YF) + YF.removeSourceItem(RF); + if (YF.canDestroy()) + YamlFiles.TryRemove(YF.FullName, out YamlFile Temp); + } + // Finally delete the file. + eraseRemarkFile(File); + } + internal void delayRemoveRemarkFile(string File) { + RemarkFile RF = getRemarkFile(File); + if (RF == null) + return; + + // If our parent yaml can delay destroy we set up a timer to remove it. + AsyncUtil.dispatchAsync(() => { + bool CanDelayDestroy + = RF.ParentYamlFiles.Any(YF => { lock (YF) return !YF.canDelayDestroy(); }); + if (!CanDelayDestroy) + return; + AsyncUtil.dispatchAsyncDelay(15000, () => { + bool CanDestroy + = RF.ParentYamlFiles.Any(YF => { lock (YF) return !YF.canDelayDestroy(); }); + if (!CanDestroy) + return; + foreach (YamlFile YF in RF.ParentYamlFiles) + lock (YF) + removeYamlFile(YF.FullName); + }); + }); + } + + // Rename files. + internal void renameRemarkFile(ProjectItem Item, string OldName) { + OldName = FileUtils.normalizeFilePath(OldName); + if (!Remarks.TryRemove(OldName, out RemarkFile F)) + return; + // This shouldn't happen. + if (Item.FileCount == 0) + return; + + // Change the name. + F.FullName = Item.FileNames[0]; + Remarks.TryAdd(FileUtils.normalizeFilePath(F.FullName), F); + + // Check to see if we are an include. + if (F.ParentYamlFiles.Count() != 1) + return; + + // Rename the parent yaml file if necessary. + YamlFile ParentYamlFile = F.ParentYamlFiles.ElementAt(0); + string NewBaseName = F.Name; + string OldItemYamlName = NewBaseName + ".opt.yaml"; + string ParentYamlFullName = ParentYamlFile.FullName; + string ParentYamlName = System.IO.Path.GetFileName(ParentYamlFullName); + if (OldItemYamlName != ParentYamlName) { + OldItemYamlName = System.IO.Path.GetFileNameWithoutExtension(OldName) + ".opt.yaml"; + if (OldItemYamlName != ParentYamlName) + return; + F.FullName = System.IO.Path.GetFileNameWithoutExtension(NewBaseName); + } + ParentYamlFile.FullName = ParentYamlFullName.Substring(0, + ParentYamlFullName.Length - ParentYamlName.Length) + NewBaseName + ".opt.yaml"; + + // Attempt to rename the file to the new name. + try { + System.IO.File.Move(ParentYamlFullName, ParentYamlFile.FullName); + } catch { } + } + + // Get/Create/Remove YamlFiles. + internal YamlFile getYamlFile(string Yaml) { + Yaml = FileUtils.normalizeFilePath(Yaml); + return YamlFiles.TryGetValue(Yaml, out YamlFile YF) ? YF : null; + } + internal YamlFile getCreateYamlFile(string Yaml) { + string NormalizedYaml = FileUtils.normalizeFilePath(Yaml); + if (YamlFiles.TryGetValue(NormalizedYaml, out YamlFile YF)) + return YF; + YamlFiles.TryAdd(NormalizedYaml, new YamlFile(Yaml)); + return YamlFiles[NormalizedYaml]; + } + private void removeYamlFile(string Yaml) { + if (!YamlFiles.TryRemove(FileUtils.normalizeFilePath(Yaml), out YamlFile YF)) + return; + foreach (RemarkFile RF in YF.SourceFiles) { + lock (RF) + if (RF.ParentYamlFiles.Count() == 1) + eraseRemarkFile(RF.FullName); + else + RF.removeParentYaml(YF); + } + } + + // The remark files that we have seen. + readonly ConcurrentDictionary Remarks + = new ConcurrentDictionary(); + + // A mapping between source and remark files that we have found. + internal ConcurrentDictionary YamlFiles + = new ConcurrentDictionary(); + } +} Index: remark-viewer-vs/RemarkInterface/RemarkInterface.csproj =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/RemarkInterface.csproj @@ -0,0 +1,160 @@ + + + + + Debug + AnyCPU + {6A858A68-448F-4B27-93FC-3EBBC3D3FC07} + Library + Properties + RemarkInterface + RemarkInterface + v4.6 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\Debug - VS2015\ + DEBUG;TRACE + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + bin\Release - VS2015\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\ConcurrentHashSet.1.0.2\lib\netstandard1.0\ConcurrentCollections.dll + + + False + + + True + + + + + + + + + + + + + True + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCCodeModel.dll + + + True + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCProject.dll + + + True + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCProjectEngine.dll + + + True + + + True + + + True + + + + + + + + + + + + + + + + + ..\packages\YamlDotNet.4.2.2\lib\net35\YamlDotNet.dll + True + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Designer + Resources.Designer.cs + + + + + + + + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkInterface/Service/IRemarkService.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Service/IRemarkService.cs @@ -0,0 +1,67 @@ +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System; +using System.Collections.Generic; + +namespace RemarkInterface { + public static class ServiceLocator { + // Set of cached services so that we don't need to constantly re-search. + public static Dictionary CachedServices = new Dictionary(); + // A registered service provider. + public static IServiceProvider PackageServiceProvider { get; private set; } + public static void initializePackageServiceProvider(IServiceProvider Provider) => PackageServiceProvider = Provider; + + // Try to force load the remark package. + static bool loadRemarkPackage() { + IVsShell Shell = Package.GetGlobalService(typeof(SVsShell)) as IVsShell; + if (Shell == null) return false; + Guid PackageToBeLoadedGuid = new Guid(GuidStrings.GuidPackage); + Shell.LoadPackage(ref PackageToBeLoadedGuid, out IVsPackage P); + if (P != null) { + PackageServiceProvider = P as IServiceProvider; + return true; + } + return false; + } + + // Get a service from our registered provider. + static TInterface getFromProvider() where TInterface : class { + // If we don't have one then try and force load the remark package. + if (PackageServiceProvider == null) + if (!loadRemarkPackage()) + return null; + // Check to see if we have this service cached. + if (CachedServices.TryGetValue(typeof(TService), out object CInterface)) + return CInterface as TInterface; + lock(PackageServiceProvider) { + if (CachedServices.TryGetValue(typeof(TService), out object PInterface)) + return PInterface as TInterface; + + // If not then get this package for the provider. + TInterface TI = PackageServiceProvider.GetService(typeof(TService)) as TInterface; + if(TI != null) + CachedServices.Add(typeof(TService), TI); + return TI; + } + } + + // Get a service from the global package interface. + public static TInterface + getGlobalService() where TInterface : class { + TInterface Res = getFromProvider(); + return Res ?? (TInterface)Package.GetGlobalService(typeof(TService)); + } + + public static EnvDTE.DTE DTE => (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); + } + + // Base Interfaces for the RemarkService + public interface IRemarkService { + RemarkManager getOptRemarkManager(); + void registerEventHandler(EventHandler Sink); + void unRegisterEventHandler(EventHandler Sink); + void dispatchRemarkCacheInvalidateEvent(IEnumerable Invalidated); + void dispatchRemarkCacheInvalidateEvent(); + } + public interface SRemarkService {} +} Index: remark-viewer-vs/RemarkInterface/Utils/Demangler.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Utils/Demangler.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace RemarkInterface { + public class Demangler { + + static class NativeMethods { + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr LoadLibrary(string libname); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + public static extern bool FreeLibrary(IntPtr hModule); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); + } + + // Import declarations. + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.LPStr)] + delegate string DemanglerDel([MarshalAs(UnmanagedType.LPStr)]string Fn); + + + // The demangler function from our dll. + DemanglerDel DemangleCPPFunction; + + // The Demangler dll name. + string DLLName = ""; + + // A handle to the loaded dll. + IntPtr LoadedDLL = IntPtr.Zero; + + // Cache of demangled names. + readonly ConcurrentDictionary Cache + = new ConcurrentDictionary(); + + // Demangle the provided function name. + public FunctionReference demangle(string Fn) { + if (Cache.TryGetValue(Fn, out FunctionReference Cached)) + return Cached; + FunctionReference FRef = new FunctionReference { Name = Fn }; + if (!Cache.TryAdd(Fn, FRef)) + return Cache[Fn]; + AsyncUtil.dispatchAsync(() => { + FRef.DemangledName = DemangleCPPFunction(Fn); + FRef.BaseName = constructBaseName(FRef.DemangledName); + }, ThreadPriority.Highest); + return FRef; + } + + // Load in the demangle function. + void loadDemanglerFn() { + if (DLLName != "") + return; + + // Create the local dll. + DLLName = Path.GetTempFileName() + ".dll"; + File.WriteAllBytes(DLLName, Properties.Resources.Demangler); + + // Load the library. + LoadedDLL = NativeMethods.LoadLibrary(DLLName); + if (LoadedDLL == IntPtr.Zero) { + int errorCode = Marshal.GetLastWin32Error(); + throw new Exception(string.Format("Failed to load library (ErrorCode: {0})", errorCode)); + } + + // Get our demangler function. + IntPtr FuncAddr = NativeMethods.GetProcAddress(LoadedDLL, "get_demangled"); + DemangleCPPFunction = Marshal.GetDelegateForFunctionPointer(FuncAddr); + } + + // Valid cpp identifier character. + bool isValidNameCharacted(char C) => char.IsLetterOrDigit(C) || C == '_'; + + // Get the basename from a demangled c++ function name. + string constructBaseName(string Mangled) { + int NameEnd = Mangled.Length - 1; + while (NameEnd != 0 && Mangled[NameEnd] != ')') + --NameEnd; + + // Check to see if we have a c mangled name. + if (NameEnd == 0) + return Mangled; + + // Step past the params. + int ParenNum = 1; + uint TemplateNum = 0; + --NameEnd; + while (ParenNum > 0) { + if (Mangled[NameEnd] == '<') { + if (TemplateNum != 0) + --TemplateNum; + --NameEnd; + continue; + } if (Mangled[NameEnd] == '>') { + ++TemplateNum; + --NameEnd; + continue; + } if (TemplateNum != 0) { + --NameEnd; + continue; + } if(Mangled[NameEnd] == '(') { + if (--ParenNum == 0) { + --NameEnd; + break; + } + } + if (Mangled[NameEnd] == ')') + ++ParenNum; + --NameEnd; + } + // Skip any template params. + if (Mangled[NameEnd] == '>') { + int InitialNameEnd = NameEnd; + + // Try to find the start point of this template. + --NameEnd; + TemplateNum = 1; + while (NameEnd != 0 && TemplateNum > 0) { + if (Mangled[NameEnd] == '<') + --TemplateNum; + else if (Mangled[NameEnd] == '>') + ++TemplateNum; + --NameEnd; + } + + // If we still have a template num, then this is likely + // part of something like operator->. + if (TemplateNum != 0) + NameEnd = InitialNameEnd; + } + + // Get the base name. + ++NameEnd; + int NameStart = NameEnd - 1; + while (!isValidNameCharacted(Mangled[NameStart])) + --NameStart; + // Walk the name identifier. + while (NameStart != 0 && isValidNameCharacted(Mangled[NameStart - 1])) + --NameStart; + // Check for '~' on destructor. + if (NameStart != 0 && Mangled[NameStart - 1] == '~') + --NameStart; + + // Get the base name. + return Mangled.Substring(NameStart, NameEnd - NameStart); + } + + // Create the demangler dll. + public Demangler() => loadDemanglerFn(); + + // Dispose of the temp dll we created. + ~Demangler() { + if (LoadedDLL != IntPtr.Zero) + NativeMethods.FreeLibrary(LoadedDLL); + if (DLLName != "" && File.Exists(DLLName)) + File.Delete(DLLName); + } + } +} Index: remark-viewer-vs/RemarkInterface/Utils/FileUtils.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Utils/FileUtils.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace RemarkInterface.Utils { + static class FileUtils { + static public string normalizeFilePath(string P) { + if(Path.IsPathRooted(P)) { + int e = P.IndexOf(':'); + if (e == -1) + return P; + return P.Substring(0, e).ToUpper() + P.Substring(e); + } + return P; + } + } +} Index: remark-viewer-vs/RemarkInterface/Utils/IEnumeratorUtils.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Utils/IEnumeratorUtils.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RemarkInterface { + + public class EqualityComparer : IEqualityComparer { + public EqualityComparer(Func Functor) => this.Functor = Functor; + public bool Equals(T x, T y) => Functor(x, y); + public int GetHashCode(T obj) => obj.GetHashCode(); + Func Functor { get; set; } + } +} Index: remark-viewer-vs/RemarkInterface/Utils/ProjectUtils.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/Utils/ProjectUtils.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using EnvDTE; +using EnvDTE80; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.VCProjectEngine; + +namespace RemarkInterface { + class IntermediateDirCache { + VCProject Par; + Dictionary ConfigCache = new Dictionary(); + public IntermediateDirCache(VCProject P) => Par = P; + public string getIntermediateDir(RemarkConfig RC) { + string Key = RC.ToString(); + if (ConfigCache.TryGetValue(Key, out string ExistingIntermediateDir)) + return ExistingIntermediateDir; + + // Get the intermediate directory name. + IEnumerable VCConfigs = ((IVCCollection)Par.Configurations).OfType(); + VCConfiguration Config = VCConfigs.FirstOrDefault(C => C.Name == Key); + if (Config == null) + return ""; + + // Check to see if this is only a partial path and add the directory. + string IntermediateDir = Config.GetEvaluatedPropertyValue("IntDir"); + if (!Path.IsPathRooted(IntermediateDir)) + IntermediateDir = Par.ProjectDirectory + "\\" + IntermediateDir; + ConfigCache.Add(Key, IntermediateDir); + return IntermediateDir; + } + } + public static class ProjectUtils { + // Cache certain project attributes. + static Dictionary ProjectIntermediateDir + = new Dictionary(); + static object UtilLock = new object(); + + // Initialize a new solution. + internal static void reset() => ProjectIntermediateDir.Clear(); + + // Utils. + static SolutionConfiguration2 getCurrentConfig() { + return ServiceLocator.DTE.Solution?.SolutionBuild?.ActiveConfiguration as SolutionConfiguration2; + } + + // Get the current config and platform name. + internal static bool getCurrentConfigName(out string ConfigName, out string PformName) { + SolutionConfiguration2 Config = getCurrentConfig(); + PformName = Config?.PlatformName ?? ""; + ConfigName = Config?.Name ?? ""; + return true; + } + + // Get the current projects intermediate output directory. + internal static string getProjectIntermediateDir(Project P, RemarkConfig RC) { + // Get the project as a c++ project. + if (!(P.Object is VCProject)) + return ""; + if (!ProjectIntermediateDir.TryGetValue(P, out IntermediateDirCache Cache)) + ProjectIntermediateDir.Add(P, Cache = new IntermediateDirCache(P.Object as VCProject)); + return Cache.getIntermediateDir(RC); + } + + // Get the full name for the provided project item name. + public static ProjectItem getProjectItemFromFullName(string Name) { + lock (UtilLock) { + return ServiceLocator.DTE.Solution.FindProjectItem(Name) + ?? ServiceLocator.DTE.Solution.FindProjectItem(Path.GetFileName(Name)); + } + } + + public static bool isValidRemarkSolution() { + IEnumerable Projects = ServiceLocator.DTE.Solution.Projects.OfType(); + + // If this check is at the beginning of a load it isn't guaranteed that the + // project will be fully loaded or not. + return Projects.Any(P => P.Object is VCProject || P.UniqueName.EndsWith(".vcxproj")); + } + + // Navigate to the provided debug location. + public static void navigateTo(DebugLocation Loc) { + // Try to find a matching project item. + ProjectItem PrjItem = getProjectItemFromFullName(Loc.File); + if (PrjItem == null) + return; + Window PrjWindow = PrjItem.Open(); + PrjWindow.Visible = true; + var TS = (TextSelection)PrjWindow.Document.Selection; + TS?.MoveToLineAndOffset((int)Loc.Line, (int)Loc.Column + 1); + } + } + public static class RuleUtils { + public static bool GetEvaluatedBoolPropertyValue(this IVCRulePropertyStorage Rule, string Property) { + try { + return Rule.GetEvaluatedPropertyValue(Property) == "true"; + } catch { + return false; + } + } + } +} Index: remark-viewer-vs/RemarkInterface/packages.config =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkInterface/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/LTORemarkViewerProps.props =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/LTORemarkViewerProps.props @@ -0,0 +1,15 @@ + + + + + + + + -fsave-optimization-record %(AdditionalOptions) + + + --opt-remarks-with-hotness --opt-remarks-filename "$(IntermediateOutputPath)$(ProjectFileName).opt.yaml" %(AdditionalOptions) + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/PkgCmd.vsct =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/PkgCmd.vsct @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + Optimization Remark Viewer + + + + + + + + + + + + + + + + + IconAndText + CommandWellOnly + + Compare To Platform: + Compare With Config: + Select Platform To Compare To + Compare To Platform + Compare To Platform + Compare To Platform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkPropertyPageProvider.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkPropertyPageProvider.cs @@ -0,0 +1,97 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using EnvDTE; +using RemarkInterface; + +namespace RemarkPackage.ProjectOptions { + [ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)] + [Guid(GuidStrings.GuidSolutionPageString)] + [ProvideObject(typeof(RemarkViewerPropertyPage))] + public class RemarkViewerPropertyPage : IPropertyPage { + private RemarkSolutionPropPageControl PropControl; + private bool Active; + private IPropertyPageSite PageSite; + private bool IsDirty => PropControl?.isDirty() ?? false; + public string Title => "Opt Remark Viewer"; + + // Update the objects in the control. + protected void updateObjects() { + if (ServiceLocator.DTE.Solution != null) + PropControl.initialize(); + } + + /// IPropertyPage Methods. + public virtual void Activate(IntPtr parent, RECT[] pRect, int bModal) { + if (PropControl == null) { + if (pRect == null) + throw new ArgumentNullException("pRect"); + PropControl = new RemarkSolutionPropPageControl(this, pRect); + NativeMethods.SetParent(PropControl.Handle, parent); + } + Active = true; + updateObjects(); + } + public virtual int Apply() { + // Only write changes. + if (IsDirty) + PropControl.apply(); + return VSConstants.S_OK; + } + public virtual void Deactivate() { + if (PropControl != null) { + PropControl.Dispose(); + PropControl = null; + } + Active = false; + } + public virtual void GetPageInfo(PROPPAGEINFO[] PageInfoArr) { + if (PageInfoArr == null) + throw new ArgumentNullException("PageInfoArr"); + PROPPAGEINFO Info = new PROPPAGEINFO { + cb = (uint)Marshal.SizeOf(typeof(PROPPAGEINFO)), + dwHelpContext = 0, + pszDocString = null, + pszHelpFile = null, + pszTitle = Title + }; + Info.SIZE.cx = 550; + Info.SIZE.cy = 300; + PageInfoArr[0] = Info; + } + public virtual void Help(string helpDir) {} + public virtual int IsPageDirty() { + // Note this returns an HRESULT not a Bool. + return IsDirty ? VSConstants.S_OK : VSConstants.S_FALSE; + } + public virtual void Move(RECT[] ArrRect) { + if (ArrRect == null) + throw new ArgumentNullException("ArrRect"); + RECT R = ArrRect[0]; + PropControl.Location = new Point(R.left, R.top); + PropControl.Size = new Size(R.right - R.left, R.bottom - R.top); + } + public virtual void SetObjects(uint count, object[] punk) { + if (Active && punk != null) + updateObjects(); + } + public virtual void SetPageSite(IPropertyPageSite Site) => PageSite = Site; + public virtual void Show(uint cmd) { + PropControl.Visible = true; // TODO: pass SW_SHOW* flags through + PropControl.Show(); + } + public virtual int TranslateAccelerator(MSG[] ArrMsg) { + if (ArrMsg == null) + throw new ArgumentNullException("ArrMsg"); + MSG Msg = ArrMsg[0]; + if ((Msg.message < NativeMethods.WM_KEYFIRST || Msg.message > NativeMethods.WM_KEYLAST) + && (Msg.message < NativeMethods.WM_MOUSEFIRST || Msg.message > NativeMethods.WM_MOUSELAST)) + return 1; + return (NativeMethods.IsDialogMessageA(PropControl.Handle, ref Msg)) ? 0 : 1; + } + } +} + Index: remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkSolutionPropPageControl.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkSolutionPropPageControl.Designer.cs @@ -0,0 +1,220 @@ +namespace RemarkPackage.ProjectOptions { + partial class RemarkSolutionPropPageControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.FileBox = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.WildcardBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.textBox4 = new System.Windows.Forms.TextBox(); + this.PlatformCombo = new System.Windows.Forms.ComboBox(); + this.ConfigCombo = new System.Windows.Forms.ComboBox(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.BackColor = System.Drawing.SystemColors.Control; + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 39.56522F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 60.43478F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 51F)); + this.tableLayoutPanel1.Controls.Add(this.textBox2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.FileBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.button1, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.textBox1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.WildcardBox, 1, 1); + this.tableLayoutPanel1.Location = new System.Drawing.Point(30, 58); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(6); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 52F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(972, 110); + this.tableLayoutPanel1.TabIndex = 7; + // + // textBox2 + // + this.textBox2.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.textBox2.BackColor = System.Drawing.SystemColors.Control; + this.textBox2.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox2.Location = new System.Drawing.Point(6, 72); + this.textBox2.Margin = new System.Windows.Forms.Padding(6); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + this.textBox2.Size = new System.Drawing.Size(326, 24); + this.textBox2.TabIndex = 3; + this.textBox2.Text = "Configuration Sub-Directory"; + // + // FileBox + // + this.FileBox.Location = new System.Drawing.Point(370, 6); + this.FileBox.Margin = new System.Windows.Forms.Padding(6); + this.FileBox.Name = "FileBox"; + this.FileBox.Size = new System.Drawing.Size(540, 31); + this.FileBox.TabIndex = 1; + this.FileBox.TextChanged += new System.EventHandler(this.FileBox_TextChanged); + // + // button1 + // + this.button1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.button1.BackColor = System.Drawing.Color.Transparent; + this.button1.BackgroundImage = global::RemarkPackage.Resources.CompareFolder_16x; + this.button1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; + this.button1.ImageKey = "(none)"; + this.button1.Location = new System.Drawing.Point(922, 9); + this.button1.Margin = new System.Windows.Forms.Padding(2); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(42, 40); + this.button1.TabIndex = 2; + this.button1.UseVisualStyleBackColor = false; + this.button1.Click += new System.EventHandler(this.DirButton_Click); + // + // textBox1 + // + this.textBox1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.textBox1.BackColor = System.Drawing.SystemColors.Control; + this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox1.Location = new System.Drawing.Point(6, 17); + this.textBox1.Margin = new System.Windows.Forms.Padding(6); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(260, 24); + this.textBox1.TabIndex = 0; + this.textBox1.Text = "External Output Directory"; + // + // WildcardBox + // + this.WildcardBox.Enabled = false; + this.WildcardBox.Location = new System.Drawing.Point(370, 64); + this.WildcardBox.Margin = new System.Windows.Forms.Padding(6); + this.WildcardBox.Name = "WildcardBox"; + this.WildcardBox.Size = new System.Drawing.Size(540, 31); + this.WildcardBox.TabIndex = 4; + this.WildcardBox.TextChanged += new System.EventHandler(this.WildcardBox_TextChanged); + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 4; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 296F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 126F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 372F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel2.Controls.Add(this.textBox4, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.PlatformCombo, 3, 0); + this.tableLayoutPanel2.Controls.Add(this.ConfigCombo, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.textBox3, 0, 0); + this.tableLayoutPanel2.Location = new System.Drawing.Point(30, 0); + this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(6); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(972, 52); + this.tableLayoutPanel2.TabIndex = 8; + // + // textBox4 + // + this.textBox4.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox4.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.textBox4.Location = new System.Drawing.Point(480, 6); + this.textBox4.Margin = new System.Windows.Forms.Padding(6); + this.textBox4.Name = "textBox4"; + this.textBox4.ReadOnly = true; + this.textBox4.Size = new System.Drawing.Size(114, 29); + this.textBox4.TabIndex = 3; + this.textBox4.Text = "Platform:"; + // + // PlatformCombo + // + this.PlatformCombo.FormattingEnabled = true; + this.PlatformCombo.Location = new System.Drawing.Point(606, 6); + this.PlatformCombo.Margin = new System.Windows.Forms.Padding(6); + this.PlatformCombo.Name = "PlatformCombo"; + this.PlatformCombo.Size = new System.Drawing.Size(238, 33); + this.PlatformCombo.TabIndex = 4; + this.PlatformCombo.SelectedIndexChanged += new System.EventHandler(this.PlatformCombo_SelectedIndexChanged); + // + // ConfigCombo + // + this.ConfigCombo.FormattingEnabled = true; + this.ConfigCombo.Location = new System.Drawing.Point(184, 6); + this.ConfigCombo.Margin = new System.Windows.Forms.Padding(6); + this.ConfigCombo.Name = "ConfigCombo"; + this.ConfigCombo.Size = new System.Drawing.Size(238, 33); + this.ConfigCombo.TabIndex = 2; + this.ConfigCombo.SelectedIndexChanged += new System.EventHandler(this.ConfigCombo_SelectedIndexChanged); + // + // textBox3 + // + this.textBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox3.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox3.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.textBox3.Location = new System.Drawing.Point(6, 6); + this.textBox3.Margin = new System.Windows.Forms.Padding(6); + this.textBox3.Name = "textBox3"; + this.textBox3.ReadOnly = true; + this.textBox3.Size = new System.Drawing.Size(166, 29); + this.textBox3.TabIndex = 1; + this.textBox3.Text = "Configuration:"; + // + // RemarkSolutionPropPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.Controls.Add(this.tableLayoutPanel2); + this.Controls.Add(this.tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(6); + this.Name = "RemarkSolutionPropPageControl"; + this.Size = new System.Drawing.Size(1040, 606); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox FileBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TextBox textBox3; + private System.Windows.Forms.TextBox textBox4; + private System.Windows.Forms.ComboBox PlatformCombo; + private System.Windows.Forms.ComboBox ConfigCombo; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.TextBox WildcardBox; + private System.Windows.Forms.Button button1; + } +} Index: remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkSolutionPropPageControl.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkSolutionPropPageControl.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using Microsoft.VisualStudio.OLE.Interop; +using RemarkInterface; +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using EnvDTE80; + +namespace RemarkPackage.ProjectOptions { + public partial class RemarkSolutionPropPageControl : UserControl { + RemarkViewerPropertyPage ParentPage = null; + FolderBrowserDialog BrowserDiag = new FolderBrowserDialog(); + + // Current store of external source locations. + public Dictionary ExternalLocations + = new Dictionary(); + + public RemarkSolutionPropPageControl(RemarkViewerPropertyPage Par, RECT[] pRect) { + InitializeComponent(); + ParentPage = Par; + + // Initialize the settings for the control. + Size = new Size(pRect[0].right - pRect[0].left, pRect[0].bottom - pRect[0].top); + Visible = false; + CreateControl(); + } + + public void initialize() { + // Initial external locations. + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + ExternalLocations = new Dictionary(RM.getExternalConfigDirectories()); + + // Get the active configs and platforms. + HashSet Configs = new HashSet(); + HashSet Platforms = new HashSet(); + SolutionBuild SBuild = ServiceLocator.DTE.Solution.SolutionBuild; + foreach (var ConfigRaw in SBuild.SolutionConfigurations) { + var Config = ConfigRaw as SolutionConfiguration2; + if (Config == null) + continue; + Configs.Add(Config.Name); + Platforms.Add(Config.PlatformName); + } + ConfigCombo.Items.Clear(); + PlatformCombo.Items.Clear(); + ConfigCombo.Items.AddRange(Configs.ToArray()); + PlatformCombo.Items.AddRange(Platforms.ToArray()); + ConfigCombo.SelectedIndex = 0; + PlatformCombo.SelectedIndex = 0; + } + + public bool isDirty() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + return ExternalLocations != RM.getExternalConfigDirectories(); + } + + public void apply() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + foreach (var Item in ExternalLocations.Where(KVP => KVP.Value.Dir == "").ToList()) + ExternalLocations.Remove(Item.Key); + RM.setExternalConfigDirectories(ExternalLocations); + } + + private void DirButton_Click(object sender, EventArgs e) { + // Default dir. + if (BrowserDiag.SelectedPath == "") + BrowserDiag.SelectedPath = System.IO.Path.GetDirectoryName(ServiceLocator.DTE.Solution.FullName); + + // Open the dialog. + string OldDir = BrowserDiag.SelectedPath; + DialogResult Res = BrowserDiag.ShowDialog(); + // Failure. + if (Res != DialogResult.OK) + return; + // No new path. + string Dir = BrowserDiag.SelectedPath; + if (Dir == OldDir) + return; + FileBox.Text = Dir; + } + + private void FileBox_TextChanged(object sender, EventArgs e) { + string Dir = FileBox.Text; + string Config = ConfigCombo.Text; + string Platform = PlatformCombo.Text; + string Key = Config + '|' + Platform; + + if (!ExternalLocations.TryGetValue(Key, out ExternalRemarkSource ExtSource)) { + if (Dir != "") + ExternalLocations.Add(Key, new ExternalRemarkSource() { Dir = Dir }); + } else if (Dir == "") + ExternalLocations.Remove(Key); + else + ExtSource.Dir = Dir; + checkEnableWildcardBox(); + } + + private void ConfigCombo_SelectedIndexChanged(object sender, EventArgs e) { + if (PlatformCombo.Text != "") + changeConfig(); + } + + private void PlatformCombo_SelectedIndexChanged(object sender, EventArgs e) { + if (ConfigCombo.Text != "") + changeConfig(); + } + + private void changeConfig() { + string Config = ConfigCombo.Text; + string Platform = PlatformCombo.Text; + string Key = Config + '|' + Platform; + + // Get the stored info for this config. + string Dir = "", Wildcard = ""; + if (ExternalLocations.TryGetValue(Key, out ExternalRemarkSource ExtSource)) { + Dir = ExtSource.Dir; + Wildcard = ExtSource.WildCard; + } + + // Apply it. + FileBox.Text = BrowserDiag.SelectedPath = Dir; + WildcardBox.Text = Wildcard; + } + + private void checkEnableWildcardBox() => WildcardBox.Enabled = FileBox.Text != ""; + private void WildcardBox_TextChanged(object sender, EventArgs e) { + string Config = ConfigCombo.Text; + string Platform = PlatformCombo.Text; + string Key = Config + '|' + Platform; + ExternalLocations[Key].WildCard = WildcardBox.Text; + } + } +} Index: remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkSolutionPropPageControl.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ProjectOptions/RemarkSolutionPropPageControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/Properties/AssemblyInfo.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OptRemarkViewer")] +[assembly: AssemblyDescription("View optimization remarks generated by the llvm compiler framework.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyCompany("LLVM")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyProduct("OptRemarkViewer")] +[assembly: AssemblyTrademark("")] +[assembly: CLSCompliant(false)] +[assembly: ComVisible(true)] +[assembly: NeutralResourcesLanguage("en-US")] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("62652e5b-7dfe-44d1-920e-37e449b87855")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] Index: remark-viewer-vs/RemarkPackage/Properties/GlobalSuppressions.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Properties/GlobalSuppressions.cs @@ -0,0 +1,3 @@ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased", Scope = "namespace", Target = "Microsoft.Samples.VisualStudio.IDE.Package")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "Microsoft.Samples.VisualStudio.IDE.Package.VSPackage.resources")] Index: remark-viewer-vs/RemarkPackage/Properties/GuidStrings.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Properties/GuidStrings.cs @@ -0,0 +1,36 @@ +using System; + +namespace RemarkPackage { + /// + /// This class contains a list of GUIDs specific to this sample. + /// + public static class GuidStrings { + /// + /// Guid for the OptionsPagePackage class. + /// + public const string GuidPackage = RemarkInterface.GuidStrings.GuidPackage; + /// + /// Guid for the OptionsPageGeneral class. + /// + public const string GuidPageGeneral = "E6717D0B-111E-4a5b-9834-076CA319ED59"; + /// + /// Guid for the OptionsPagePerRemarkStatus class. + /// + public const string GuidOptionsPagePerRemarkStatus = "0A9F3920-3881-4f50-8986-9EDEC7B33566"; + /// + /// Guid for the OptionsPagePerRemarkSubStatus class. + /// + public const string GuidOptionsPagePerRemarkSubStatus = "0B9F3920-3881-4f50-8986-9EDEC7B33566"; + /// + /// Guid for the command set. + /// + public const string GuidRemarkCmdSetString = "36A0B180-F23F-4D96-A1A0-5928B6F7497D"; + public static readonly Guid GuidRemarkCmdSet = new Guid(GuidRemarkCmdSetString); + public static readonly Guid GuidRemarkPkg = new Guid(GuidPackage); + /// + /// Guid for the solution property page. + /// + public const string GuidSolutionPageString = "A7214157-9126-4516-AF64-C5E7F34DB825"; + public static readonly Guid GuidSolutionPage = new Guid(GuidSolutionPageString); + } +} Index: remark-viewer-vs/RemarkPackage/RemarkPackage.csproj =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/RemarkPackage.csproj @@ -0,0 +1,352 @@ + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + 15.0 + + + true + bin\Debug - VS2015\ + DEBUG;TRACE + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + bin\Release - VS2015\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {D7EC3D14-8BA3-40B0-81FC-56A4FA8D7239} + Library + v3 + Properties + RemarkPackage + RemarkPackage + v4.6 + 512 + 14.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + Program + $(DevEnvDir)devenv.exe + /rootsuffix Exp + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + UserControl + + + RemarkSolutionPropPageControl.cs + + + + + + True + True + Resources.resx + + + UserControl + + + GeneralPageControl.cs + + + Component + + + Component + + + Component + + + UserControl + + + PerRemarkStatusPageControl.cs + + + UserControl + + + PerRemarkSubStatusPageControl.cs + + + + + + + Component + + + + True + True + VSPackage.resx + + + + + + Designer + + + + + Designer + + + Designer + + + Menus.ctmenu + Designer + + + + + + Always + true + + + + + + RemarkSolutionPropPageControl.cs + Designer + + + GeneralPageControl.cs + + + PerRemarkStatusPageControl.cs + + + PerRemarkSubStatusPageControl.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + Designer + true + ResXFileCodeGenerator + VSPackage.Designer.cs + + + + + {6a858a68-448f-4b27-93fc-3ebbc3d3fc07} + RemarkInterface + + + {E6C96829-D87B-489D-9C05-38110DA50296} + RemarkTagger + BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b + DebugSymbolsProjectOutputGroup%3b + False + + + + + False + + + True + + + + + + ..\packages\Microsoft.VisualStudio.CoreUtility.15.4.27004\lib\net45\Microsoft.VisualStudio.CoreUtility.dll + True + + + True + + + + ..\packages\Microsoft.VisualStudio.Imaging.15.4.27004\lib\net45\Microsoft.VisualStudio.Imaging.dll + True + + + ..\packages\Microsoft.VisualStudio.ManagedInterfaces.8.0.50727\lib\Microsoft.VisualStudio.ManagedInterfaces.dll + True + + + + ..\packages\Microsoft.VisualStudio.Project.1.0.0.0\lib\net40\Microsoft.VisualStudio.Project.dll + True + + + + ..\packages\Microsoft.VisualStudio.Shell.Design.15.4.27004\lib\net45\Microsoft.VisualStudio.Shell.Design.dll + True + + + ..\packages\Microsoft.VisualStudio.Shell.Framework.15.4.27004\lib\net45\Microsoft.VisualStudio.Shell.Framework.dll + True + + + + + + + + True + + + True + + + True + + + + + + + + + True + ..\packages\Microsoft.VisualStudio.TextManager.Interop.10.0.10.0.30319\lib\Microsoft.VisualStudio.TextManager.Interop.10.0.dll + True + + + True + ..\packages\Microsoft.VisualStudio.TextManager.Interop.11.0.11.0.61030\lib\Microsoft.VisualStudio.TextManager.Interop.11.0.dll + True + + + True + ..\packages\Microsoft.VisualStudio.TextManager.Interop.12.0.12.0.30110\lib\Microsoft.VisualStudio.TextManager.Interop.12.0.dll + True + + + + + ..\packages\Microsoft.VisualStudio.Utilities.15.4.27004\lib\net46\Microsoft.VisualStudio.Utilities.dll + True + + + True + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCCodeModel.dll + + + True + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCProject.dll + + + True + ..\..\..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCProjectEngine.dll + + + True + + + True + + + True + + + + + + + + + + + + + + False + Microsoft .NET Framework 4.6 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/RemarkService/GlobalSuppressions.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/RemarkService/GlobalSuppressions.cs @@ -0,0 +1 @@ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")] Index: remark-viewer-vs/RemarkPackage/RemarkService/PkgCmdId.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/RemarkService/PkgCmdId.cs @@ -0,0 +1,19 @@ +namespace RemarkPackage +{ + /// + /// This class is used to expose the list of the IDs of the commands implemented + /// by the remark package. + /// + static class ClientPkgCmdIDList + { + // Define the list a set of public static members. + public const int CmdIDToggleEnabled = 0x2001; + public const int CmdIDComparePlatform = 0x101; + public const int CmdIDComparePlatformGetList = 0x102; + public const int CmdIDLoadAllDocsInCurProj = 0x103; + public const int CmdIDDisplayRemarksInCurDoc = 0x104; + public const int CmdIDDisplayRemarks = 0x105; + public const int CmdIDDisplayRemarkCountsInCurDoc = 0x106; + public const int CmdIDDisplayRemarkCounts = 0x107; + } +} Index: remark-viewer-vs/RemarkPackage/RemarkService/RemarkPackage.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/RemarkService/RemarkPackage.cs @@ -0,0 +1,248 @@ +using System; +using System.ComponentModel.Design; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System.Runtime.InteropServices; +using RemarkInterface; +using RemarkPackage.RemarkOptions; +using EnvDTE; +using System.Collections.Generic; +using EnvDTE80; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.Internal.VisualStudio.Shell; + +namespace RemarkPackage { + [PackageRegistration(UseManagedResourcesOnly = true)] + [InstalledProductRegistration("#110", "#111", "1.0", IconResourceID = 400)] + [ProvideOptionPage(typeof(OptionsPageGeneral), "Optimization Remark Viewer", "General", 100, 101, true, new string[] { "Change general remark options" })] + [ProvideProfile(typeof(OptionsPageGeneral), "Optimization Remark Viewer", "General Options", 100, 101, true, DescriptionResourceID = 100)] + [ProvideOptionPage(typeof(OptionsPagePerRemarkStatus), "Optimization Remark Viewer", "Global View Status", 100, 102, true, new string[] { "Change global view status per optimization pass" })] + [ProvideOptionPage(typeof(OptionsPagePerRemarkSubStatus), "Optimization Remark Viewer", "SubType View Status", 100, 103, true, new string[] { "Change view status per optimization pass sub type" })] + [ProvideAutoLoad(UIContextGuids80.SolutionExists)] + [ProvideMenuResource("Menus.ctmenu", 1)] + [ProvideService(typeof(SRemarkService))] + [Guid(GuidStrings.GuidPackage)] + [ProvideBindingPath] + public class RemarkPackage : Package, IVsPersistSolutionProps { + IRemarkService RemarkService; + + protected override void Initialize() { + // Call the base implementation to finish the initialization of the package. + base.Initialize(); + ServiceLocator.initializePackageServiceProvider(this); + + // Start the remark service. + IServiceContainer ISC = this; + ISC.AddService(typeof(SRemarkService), RemarkService = new RemarkService(this)); + + // Add the toggle button to the command window. + OleMenuCommandService MCS = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + if (MCS == null) + return; + + // Define the toggle command. + CommandID CID = new CommandID(GuidStrings.GuidRemarkCmdSet, ClientPkgCmdIDList.CmdIDToggleEnabled); + MenuCommand Cmd = new MenuCommand(ToggleRemarkTaggerEnabled, CID); + MCS.AddCommand(Cmd); + + // Load all remarks command. + CommandID LoadRemarksCID = new CommandID(GuidStrings.GuidRemarkCmdSet, ClientPkgCmdIDList.CmdIDLoadAllDocsInCurProj); + MenuCommand LoadRemarksCmd = new MenuCommand(LoadRemarksInCurrentProj, LoadRemarksCID); + MCS.AddCommand(LoadRemarksCmd); + + // Display remarks in current document command. + CommandID DisplayRemarksInCurDocCID = new CommandID(GuidStrings.GuidRemarkCmdSet, ClientPkgCmdIDList.CmdIDDisplayRemarksInCurDoc); + MenuCommand DisplayRemarksInCurDocCmd = new MenuCommand(DisplayRemarksInCurDoc, DisplayRemarksInCurDocCID); + MCS.AddCommand(DisplayRemarksInCurDocCmd); + + // Display all remarks command. + CommandID DisplayRemarksCID = new CommandID(GuidStrings.GuidRemarkCmdSet, ClientPkgCmdIDList.CmdIDDisplayRemarks); + MenuCommand DisplayRemarksCmd = new MenuCommand(DisplayAllRemarks, DisplayRemarksCID); + MCS.AddCommand(DisplayRemarksCmd); + + // Display remark counts in current document command. + CommandID DisplayRemarkCountsInCurDocCID = new CommandID(GuidStrings.GuidRemarkCmdSet, ClientPkgCmdIDList.CmdIDDisplayRemarkCountsInCurDoc); + MenuCommand DisplayRemarkCountsInCurDocCmd = new MenuCommand(DisplayRemarkCountsInCurDoc, DisplayRemarkCountsInCurDocCID); + MCS.AddCommand(DisplayRemarkCountsInCurDocCmd); + + // Display all remark counts command. + CommandID DisplayRemarkCountsCID = new CommandID(GuidStrings.GuidRemarkCmdSet, ClientPkgCmdIDList.CmdIDDisplayRemarkCounts); + MenuCommand DisplayRemarkCountsCmd = new MenuCommand(DisplayAllRemarkCounts, DisplayRemarkCountsCID); + MCS.AddCommand(DisplayRemarkCountsCmd); + + // Define the CompareTo commands. + CommandID menuMyDropDownComboCommandID = new CommandID(GuidStrings.GuidRemarkCmdSet, (int)ClientPkgCmdIDList.CmdIDComparePlatform); + OleMenuCommand menuMyDropDownComboCommand = new OleMenuCommand(new EventHandler(OnMenuCompareTo), menuMyDropDownComboCommandID); + MCS.AddCommand(menuMyDropDownComboCommand); + CommandID menuMyDropDownComboGetListCommandID = new CommandID(GuidStrings.GuidRemarkCmdSet, (int)ClientPkgCmdIDList.CmdIDComparePlatformGetList); + MenuCommand menuMyDropDownComboGetListCommand = new OleMenuCommand(new EventHandler(OnMenuOnMenuCompareToGetList), menuMyDropDownComboGetListCommandID); + MCS.AddCommand(menuMyDropDownComboGetListCommand); + + IVsSolutionBuildManager3 solutionUpdateInterface3 = (IVsSolutionBuildManager3)GetService(typeof(SVsSolutionBuildManager)); + ConfigChangeHandler UpdateHdlr = new ConfigChangeHandler() { Parent = this }; + solutionUpdateInterface3.AdviseUpdateSolutionEvents3(UpdateHdlr, out SinkUpdateCookie); + } + protected override void Dispose(bool disposing) { + if(disposing) { + IVsSolutionBuildManager3 solutionUpdateInterface3 = (IVsSolutionBuildManager3)GetService(typeof(SVsSolutionBuildManager)); + solutionUpdateInterface3.UnadviseUpdateSolutionEvents3(SinkUpdateCookie); + } + base.Dispose(disposing); + } + + /// Saving/Loading Solution Settings. + // The name of the section in the solution user options file. + private const string _strSolutionUserOptionsKey = "OptimizationRemarkViewer"; + + public int SaveUserOptions(IVsSolutionPersistence pPersistence) { + // Check to see if we have any properties to save. + pPersistence.SavePackageUserOpts(this, _strSolutionUserOptionsKey); + return VSConstants.S_OK; + } + public int WriteUserOptions(IStream pOptionsStream, string pszKey) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + DataStreamFromComStream pStream = new DataStreamFromComStream(pOptionsStream); + RM.saveSolutionSettings(pStream); + return VSConstants.S_OK; + } + public int LoadUserOptions(IVsSolutionPersistence pPersistence, uint grfLoadOpts) { + pPersistence.LoadPackageUserOpts(this, _strSolutionUserOptionsKey); + return VSConstants.S_OK; + } + public int ReadUserOptions(IStream pOptionsStream, string pszKey) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + DataStreamFromComStream pStream = new DataStreamFromComStream(pOptionsStream); + RM.loadSolutionSettings(pStream); + return VSConstants.S_OK; + } + public int QuerySaveSolutionProps(IVsHierarchy pHierarchy, VSQUERYSAVESLNPROPS[] pqsspSave) => VSConstants.S_OK; + public int SaveSolutionProps(IVsHierarchy pHierarchy, IVsSolutionPersistence pPersistence) => VSConstants.S_OK; + public int WriteSolutionProps(IVsHierarchy pHierarchy, string pszKey, IPropertyBag pPropBag) => VSConstants.S_OK; + public int ReadSolutionProps(IVsHierarchy pHierarchy, string pszProjectName, string pszProjectMk, string pszKey, int fPreLoad, IPropertyBag pPropBag) => VSConstants.S_OK; + public int OnProjectLoadFailure(IVsHierarchy pStubHierarchy, string pszProjectName, string pszProjectMk, string pszKey) => VSConstants.S_OK; + + + /// Opt Remark Toolbar handling. + private string[] CompareToChoices = { }; + private string CurrentCompareToChoice = ""; + uint SinkUpdateCookie; + + class ConfigChangeHandler : IVsUpdateSolutionEvents3 { + public RemarkPackage Parent; + + public int OnAfterActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) { + Parent.constructCurrentCompareToChoices(); + return VSConstants.S_OK; + } + public int OnBeforeActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) => VSConstants.S_OK; + } + + internal void resetSolutionSettings() { + CompareToChoices = new string[0]; + CurrentCompareToChoice = ""; + } + private void OnMenuCompareTo(object sender, EventArgs E) { + OleMenuCmdEventArgs EvtArgs = E as OleMenuCmdEventArgs; + if (EvtArgs == null) + return; + string NewChoice = EvtArgs.InValue as string; + IntPtr vOut = EvtArgs.OutValue; + + if (vOut != IntPtr.Zero) { + // when vOut is non-NULL, the IDE is requesting the current value for the combo + Marshal.GetNativeVariantForObject(CurrentCompareToChoice, vOut); + } else if (NewChoice != null) { + // New value was selected or typed in see if it is one of our items + Func CheckIfValid = () => { + for (int i = 0, e = CompareToChoices.Length; i < e; ++i) + if (string.Compare(CompareToChoices[i], NewChoice, StringComparison.CurrentCultureIgnoreCase) == 0) + return i; + return -1; + }; + int Choice = CheckIfValid(); + if (Choice != -1) { + // Remove the current. + if(CurrentCompareToChoice != "") + removeActiveConfig(CurrentCompareToChoice); + CurrentCompareToChoice = CompareToChoices[Choice]; + addActiveConfig(CurrentCompareToChoice); + } + } + } + private void OnMenuOnMenuCompareToGetList(object sender, EventArgs E) { + OleMenuCmdEventArgs EvtArgs = E as OleMenuCmdEventArgs; + if (EvtArgs == null) + return; + IntPtr VOut = EvtArgs.OutValue; + if (EvtArgs.InValue != null || VOut == null) + return; + + // Get the current config list. + if(CompareToChoices.Length == 0) + constructCurrentCompareToChoices(); + Marshal.GetNativeVariantForObject(CompareToChoices, VOut); + } + private void constructCurrentCompareToChoices() { + SolutionBuild SBuild = ServiceLocator.DTE?.Solution?.SolutionBuild; + if (SBuild == null) { + removeActiveConfig(CurrentCompareToChoice); + CurrentCompareToChoice = ""; + CompareToChoices = new string[0]; + return; + } + + // Get the active configs and platforms. + List Choices = new List { "" }; + foreach (var ConfigRaw in SBuild.SolutionConfigurations) + if (ConfigRaw is SolutionConfiguration2 Config && ConfigRaw != SBuild.ActiveConfiguration) + Choices.Add(Config.PlatformName + " " + Config.Name); + Choices.Sort(); + CompareToChoices = Choices.ToArray(); + } + private void removeActiveConfig(string FullName) { + string[] Split = FullName.Split(' '); + if (Split.Length != 2) + return; + RemarkManager RM = RemarkService.getOptRemarkManager(); + RM.removeActiveConfig(Split[1], Split[0]); + } + private void addActiveConfig(string FullName) { + string[] Split = FullName.Split(' '); + if (Split.Length != 2) + return; + RemarkManager RM = RemarkService.getOptRemarkManager(); + RM.addActiveConfig(Split[1], Split[0]); + } + private void LoadRemarksInCurrentProj(object sender, EventArgs args) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + RM.loadAllDocumentsInCurrentProject(); + } + private void DisplayRemarksInCurDoc(object sender, EventArgs args) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + if(RM.Settings.IsEnabled) + RM.displayRemarksInCurrentDoc(); + } + private void DisplayAllRemarks(object sender, EventArgs args) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + if(RM.Settings.IsEnabled) + RM.displayAllRemarks(); + } + private void DisplayRemarkCountsInCurDoc(object sender, EventArgs args) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + if (RM.Settings.IsEnabled) + RM.displayRemarkCountsInCurrentDoc(); + } + private void DisplayAllRemarkCounts(object sender, EventArgs args) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + if (RM.Settings.IsEnabled) + RM.displayAllRemarkCounts(); + } + private void ToggleRemarkTaggerEnabled(object sender, EventArgs args) { + RemarkManager RM = RemarkService.getOptRemarkManager(); + RM.Settings.IsEnabled = !RM.Settings.IsEnabled; + RM.Settings.saveSettings(); + RemarkService.dispatchRemarkCacheInvalidateEvent(); + } + } +} Index: remark-viewer-vs/RemarkPackage/RemarkService/RemarkService.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/RemarkService/RemarkService.cs @@ -0,0 +1,297 @@ +using System; +using Microsoft.VisualStudio.Shell.Interop; +using System.Runtime.InteropServices; +using RemarkInterface; +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using System.Collections.Generic; +using Microsoft.VisualStudio.VCProjectEngine; +using System.Linq; + +namespace RemarkPackage { + public class RemarkService : IRemarkService { + /// Evt sinks. + class RemarkSolutionEvtHandler : IVsSolutionEvents { + // Parent service. + RemarkService ORS; + + public RemarkSolutionEvtHandler(RemarkService Par) => ORS = Par; + + public int OnAfterCloseSolution(object pUnkReserved) { + ORS.IsEnabled = false; + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) { + // Load in any remark files. + ORS.Manager.loadActiveDocument(); + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int OnAfterOpenProject(IVsHierarchy pHierarchy, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fAdded) { + // Load in any remark files. + ORS.Manager.loadActiveDocument(); + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int OnAfterOpenSolution(object pUnkReserved, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fNewSolution) { + // Check to see if we will operate on this solution, if so then add + // our solution property page. + ORS.IsEnabled = ProjectUtils.isValidRemarkSolution(); + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int OnBeforeCloseProject(IVsHierarchy pHierarchy, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fRemoved) + => Microsoft.VisualStudio.VSConstants.S_OK; + + public int OnBeforeCloseSolution(object pUnkReserved) { + ORS.IsEnabled = false; + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) + => Microsoft.VisualStudio.VSConstants.S_OK; + + public int OnQueryCloseProject(IVsHierarchy pHierarchy, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fRemoving, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] ref int pfCancel) + => Microsoft.VisualStudio.VSConstants.S_OK; + + public int OnQueryCloseSolution(object pUnkReserved, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] ref int pfCancel) + => Microsoft.VisualStudio.VSConstants.S_OK; + + public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] ref int pfCancel) + => Microsoft.VisualStudio.VSConstants.S_OK; + } + class RemarkSolutionUpdateEvtHandler : IVsUpdateSolutionEvents, IVsUpdateSolutionEvents3 { + RemarkService ORS; + public RemarkSolutionUpdateEvtHandler(RemarkService Par) => ORS = Par; + + public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) => Microsoft.VisualStudio.VSConstants.S_OK; + + public int OnAfterActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) { + ORS.Manager.updateAllDocuments(); + ORS.dispatchRemarkCacheInvalidateEvent(); + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int OnBeforeActiveSolutionCfgChange(IVsCfg pOldActiveSlnCfg, IVsCfg pNewActiveSlnCfg) => Microsoft.VisualStudio.VSConstants.S_OK; + + public int UpdateSolution_Begin([ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] ref int pfCancelUpdate) + => Microsoft.VisualStudio.VSConstants.S_OK; + + public int UpdateSolution_Cancel() { + ORS.Manager.updateAllDocuments(); + ORS.dispatchRemarkCacheInvalidateEvent(); + return Microsoft.VisualStudio.VSConstants.S_OK; + } + public int UpdateSolution_Done([ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fSucceeded, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fModified, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] int fCancelCommand) { + ORS.Manager.updateAllDocuments(); + ORS.dispatchRemarkCacheInvalidateEvent(); + return Microsoft.VisualStudio.VSConstants.S_OK; + } + + public int UpdateSolution_StartUpdate([ComAliasName("Microsoft.VisualStudio.OLE.Interop.BOOL")] ref int pfCancelUpdate) => Microsoft.VisualStudio.VSConstants.S_OK; + } + + // Store in this variable the service provider that will be used to query for other services. + private IServiceProvider Parent; + + // The main remark loader will be run via our service. + private RemarkManager Manager = new RemarkManager(); + + // Documents we are subscribed to. + private Dictionary SubscribedWindows = new Dictionary(); + + // Event sink for solution events. + private uint SinkCookie; + private uint SinkUpdateCookie; + private uint SinkUpdateCookie3; + + // Property Sheet data. + string TempRemarkPropSheetFile = System.IO.Path.GetTempFileName() + ".props"; + string TempLTORemarkPropSheetFile = System.IO.Path.GetTempFileName() + ".props"; + List> ActivePropSheets + = new List>(); + + // If the remark service is active or not. + private bool Enabled = false; + public bool IsEnabled { + get { return Enabled; } + set { + if (value == Enabled) + return; + Enabled = value; + + // Hookup to any important events. + EnvDTE80.Events2 Events = (EnvDTE80.Events2)ServiceLocator.DTE.Events; + IVsSolution SolutionInterface = (IVsSolution)Parent.GetService(typeof(SVsSolution)); + IVsSolutionBuildManager SolutionUpdateInterface = (IVsSolutionBuildManager)Parent.GetService(typeof(SVsSolutionBuildManager)); + IVsSolutionBuildManager3 SolutionUpdateInterface3 = SolutionUpdateInterface as IVsSolutionBuildManager3; + + // Solution Property pages. + const string SolutionPageGuid = "{" + GuidStrings.GuidSolutionPageString + "};"; + object CLSIDs; + SolutionInterface.GetProperty((int)__VSPROPID.VSPROPID_SolutionPropertyPages, out CLSIDs); + + if (value) { + /// Solution property page. + CLSIDs += SolutionPageGuid; + + /// Window events. + Events.WindowEvents.WindowActivated += WindowEvents_WindowActivated; + Events.WindowEvents.WindowCreated += WindowEvents_WindowCreated; + Events.WindowEvents.WindowClosing += WindowEvents_WindowClosing; + + /// Solution events. + RemarkSolutionUpdateEvtHandler UpdateHdlr = new RemarkSolutionUpdateEvtHandler(this); + SolutionUpdateInterface.AdviseUpdateSolutionEvents(UpdateHdlr, out SinkUpdateCookie); + SolutionUpdateInterface3.AdviseUpdateSolutionEvents3(UpdateHdlr, out SinkUpdateCookie3); + + /// Project item events. + Events.ProjectItemsEvents.ItemRemoved += ProjectItemsEvents_ItemRemoved; + Events.ProjectItemsEvents.ItemRenamed += ProjectItemsEvents_ItemRenamed; + + // Build events. + Events.BuildEvents.OnBuildBegin += BuildEvents_OnBuildBegin; + Events.BuildEvents.OnBuildDone += BuildEvents_OnBuildDone; + } else { + /// Solution property page. + CLSIDs = ((string)CLSIDs).Replace(SolutionPageGuid, ""); + + // Clear the remark manager and any solution settings. + Manager.clear(); + SubscribedWindows.Clear(); + ((RemarkPackage)Parent).resetSolutionSettings(); + + /// Window events. + Events.WindowEvents.WindowActivated -= WindowEvents_WindowActivated; + Events.WindowEvents.WindowCreated -= WindowEvents_WindowCreated; + Events.WindowEvents.WindowClosing -= WindowEvents_WindowClosing; + + /// Solution events. + SolutionUpdateInterface.UnadviseUpdateSolutionEvents(SinkUpdateCookie); + SolutionUpdateInterface3.UnadviseUpdateSolutionEvents3(SinkUpdateCookie3); + + /// Project item events. + Events.ProjectItemsEvents.ItemRemoved -= ProjectItemsEvents_ItemRemoved; + Events.ProjectItemsEvents.ItemRenamed -= ProjectItemsEvents_ItemRenamed; + + // Build events. + Events.BuildEvents.OnBuildBegin -= BuildEvents_OnBuildBegin; + Events.BuildEvents.OnBuildDone -= BuildEvents_OnBuildDone; + } + + /// Solution property page. + SolutionInterface.SetProperty((int)__VSPROPID.VSPROPID_SolutionPropertyPages, CLSIDs); + } + } + + public RemarkService(RemarkPackage Pkg) { + Parent = Pkg; + IsEnabled = false; + + // Solution events. + IVsSolution SolutionInterface = (IVsSolution)Parent.GetService(typeof(SVsSolution)); + SolutionInterface.AdviseSolutionEvents(new RemarkSolutionEvtHandler(this), out SinkCookie); + + // Create the temp prop sheet files. + System.IO.File.WriteAllBytes(TempRemarkPropSheetFile, Resources.RemarkViewerProps); + System.IO.File.WriteAllBytes(TempLTORemarkPropSheetFile, Resources.LTORemarkViewerProps); + } + ~RemarkService() { + IsEnabled = false; + + // Solution events. + IVsSolution SolutionInterface = (IVsSolution)Parent.GetService(typeof(SVsSolution)); + SolutionInterface?.UnadviseSolutionEvents(SinkCookie); + + // Delete the prop sheet files. + System.IO.File.Delete(TempRemarkPropSheetFile); + System.IO.File.Delete(TempLTORemarkPropSheetFile); + } + + private void BuildEvents_OnBuildDone(vsBuildScope Scope, vsBuildAction Action) { + // Don't add on clean. + if (Action == vsBuildAction.vsBuildActionClean || ActivePropSheets.Count == 0) + return; + // Remove the property sheets. + foreach (var CfgAndSheet in ActivePropSheets) { + CfgAndSheet.Key.RemovePropertySheet(CfgAndSheet.Value); + (CfgAndSheet.Key.project as VCProject).Save(); + } + ActivePropSheets.Clear(); + } + + private void BuildEvents_OnBuildBegin(vsBuildScope Scope, vsBuildAction Action) { + // Don't remove on clean. + if (Action == vsBuildAction.vsBuildActionClean) + return; + + // Check for auto flag add. + if (!Manager.Settings.IsEnabled || !Manager.Settings.AutoEmitRemarks) + return; + foreach (BuildDependency BD in ServiceLocator.DTE.Solution.SolutionBuild.BuildDependencies) { + // Get the compiler tool for the project. + VCProject P = BD.Project.Object as VCProject; + if (P == null) + continue; + + // WholeProgramOptimization needs support for LTO flag otherwise it will error + // instead of give a warning like the cl flag does. + IVCRulePropertyStorage CompilerRule = P.ActiveConfiguration.Rules.Item("CL"); + bool IsLTO = CompilerRule.GetEvaluatedBoolPropertyValue("LinkTimeOptimization"); + // || CompilerRule.GetEvaluatedBoolPropertyValue("WholeProgramOptimization"); + string PropertySheet = IsLTO ? TempLTORemarkPropSheetFile : TempRemarkPropSheetFile; + VCPropertySheet VCPS = P.ActiveConfiguration.AddPropertySheet(PropertySheet); + ActivePropSheets.Add(new KeyValuePair(P.ActiveConfiguration, VCPS)); + } + } + + private void ProjectItemsEvents_ItemRenamed(ProjectItem ProjectItem, string OldName) { + Manager.renameDocument(ProjectItem, OldName); + } + + private void WindowEvents_WindowCreated(Window Window) { + // Check the window type. + if (Window?.Type != vsWindowType.vsWindowTypeDocument) + return; + if (Window.Document != null && !SubscribedWindows.ContainsKey(Window)) + SubscribedWindows.Add(Window, Window.Document.FullName); + } + + private void WindowEvents_WindowClosing(Window Window) { + if (!SubscribedWindows.TryGetValue(Window, out string FullName)) + return; + Manager.removeDocument(FullName, /*Delay*/true); + SubscribedWindows.Remove(Window); + } + + private void ProjectItemsEvents_ItemRemoved(ProjectItem Item) { + foreach (ProjectItem CItem in Item.ProjectItems) + ProjectItemsEvents_ItemRemoved(CItem); + if (Item.FileCount == 1) + Manager.removeDocument(Item.FileNames[0]); + } + + private void WindowEvents_WindowActivated(Window GotFocus, Window LostFocus) { + // Check the window type. + if (GotFocus?.Type == vsWindowType.vsWindowTypeDocument) + Manager.loadActiveDocument(); + } + + // Interface functions. + public RemarkManager getOptRemarkManager() => Manager; + + // Event handling. + private event EventHandler RemarkCacheEvents; + public void registerEventHandler(EventHandler Sink) => RemarkCacheEvents += Sink; + public void unRegisterEventHandler(EventHandler Sink) => RemarkCacheEvents -= Sink; + + // Dispatch events for Remark cache invalidation. + public void dispatchRemarkCacheInvalidateEvent() { + RemarkCacheEvents?.Invoke(this, new RemarkEvent(EvtCode.CacheInvalidated)); + } + public void dispatchRemarkCacheInvalidateEvent(IEnumerable Invalidated) { + RemarkCacheEvents?.Invoke(this, new RemarkEvent(EvtCode.CacheInvalidated, Invalidated)); + } + } +} Index: remark-viewer-vs/RemarkPackage/RemarkViewerProps.props =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/RemarkViewerProps.props @@ -0,0 +1,12 @@ + + + + + + + + -fsave-optimization-record %(AdditionalOptions) + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/Resources.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Resources.Designer.cs @@ -0,0 +1,165 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RemarkPackage { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RemarkPackage.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to &Clear Image. + /// + internal static string ButtonClearImageText { + get { + return ResourceManager.GetString("ButtonClearImageText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose &Image. + /// + internal static string ChooseImageButtonText { + get { + return ResourceManager.GetString("ChooseImageButtonText", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CompareFolder_16x { + get { + object obj = ResourceManager.GetObject("CompareFolder_16x", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] LTORemarkViewerProps { + get { + object obj = ResourceManager.GetObject("LTORemarkViewerProps", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized string similar to Information. + /// + internal static string MessageCaption { + get { + return ResourceManager.GetString("MessageCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Press Cancel to cancel this activation. OK to continue. + /// + internal static string MessageOnActivateEntered { + get { + return ResourceManager.GetString("MessageOnActivateEntered", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In OnApply. + /// + internal static string MessageOnApply { + get { + return ResourceManager.GetString("MessageOnApply", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Press Cancel to cancel this OnApply. OK to continue. + /// + internal static string MessageOnApplyEntered { + get { + return ResourceManager.GetString("MessageOnApplyEntered", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OnClosed. + /// + internal static string MessageOnClosed { + get { + return ResourceManager.GetString("MessageOnClosed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Press Cancel to cancel this deactivation. OK to continue. + /// + internal static string MessageOnDeactivateEntered { + get { + return ResourceManager.GetString("MessageOnDeactivateEntered", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] RemarkViewerProps { + get { + object obj = ResourceManager.GetObject("RemarkViewerProps", resourceCulture); + return ((byte[])(obj)); + } + } + } +} Index: remark-viewer-vs/RemarkPackage/Resources.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Resources.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + In OnApply + + + + RemarkViewerProps.props;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Press Cancel to cancel this OnApply. OK to continue + + + Choose &Image + + + Information + + + OnClosed + + + &Clear Image + + + LTORemarkViewerProps.props;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Press Cancel to cancel this activation. OK to continue + + + Press Cancel to cancel this deactivation. OK to continue + + + Resources\CompareFolder_16x.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/ToolOptions/GeneralPageControl.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/GeneralPageControl.Designer.cs @@ -0,0 +1,252 @@ +namespace RemarkPackage.RemarkOptions +{ + partial class GeneralPageControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("Analysis"); + System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("AnalysisAliasing"); + System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("AnalysisFPCommute"); + System.Windows.Forms.ListViewItem listViewItem4 = new System.Windows.Forms.ListViewItem("Missed"); + System.Windows.Forms.ListViewItem listViewItem5 = new System.Windows.Forms.ListViewItem("Passed"); + this.DisplayOutofDateRemarksBox = new System.Windows.Forms.CheckBox(); + this.HotnessBar = new System.Windows.Forms.TrackBar(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.HotnessValueBox = new System.Windows.Forms.TextBox(); + this.panel2 = new System.Windows.Forms.Panel(); + this.RemarkTypeList = new System.Windows.Forms.ListView(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.FullDebugLocBox = new System.Windows.Forms.CheckBox(); + this.EnabledBox = new System.Windows.Forms.CheckBox(); + this.DiffBox = new System.Windows.Forms.CheckBox(); + this.AutoEmitRemarksBox = new System.Windows.Forms.CheckBox(); + ((System.ComponentModel.ISupportInitialize)(this.HotnessBar)).BeginInit(); + this.panel1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.SuspendLayout(); + // + // DisplayOutofDateRemarksBox + // + this.DisplayOutofDateRemarksBox.AutoSize = true; + this.DisplayOutofDateRemarksBox.Location = new System.Drawing.Point(468, 160); + this.DisplayOutofDateRemarksBox.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.DisplayOutofDateRemarksBox.Name = "DisplayOutofDateRemarksBox"; + this.DisplayOutofDateRemarksBox.Size = new System.Drawing.Size(323, 29); + this.DisplayOutofDateRemarksBox.TabIndex = 0; + this.DisplayOutofDateRemarksBox.Text = "Display Out-of-Date Remarks"; + this.DisplayOutofDateRemarksBox.UseVisualStyleBackColor = true; + this.DisplayOutofDateRemarksBox.CheckedChanged += new System.EventHandler(this.DisplayOutofDateRemarksBox_CheckedChanged); + // + // HotnessBar + // + this.HotnessBar.Cursor = System.Windows.Forms.Cursors.Default; + this.HotnessBar.Location = new System.Drawing.Point(80, 42); + this.HotnessBar.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.HotnessBar.Maximum = 100; + this.HotnessBar.Name = "HotnessBar"; + this.HotnessBar.RightToLeftLayout = true; + this.HotnessBar.Size = new System.Drawing.Size(310, 90); + this.HotnessBar.TabIndex = 1; + this.HotnessBar.TickFrequency = 10; + this.HotnessBar.TickStyle = System.Windows.Forms.TickStyle.Both; + this.HotnessBar.Scroll += new System.EventHandler(this.HotnessBar_Scroll); + // + // textBox1 + // + this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox1.Location = new System.Drawing.Point(6, 6); + this.textBox1.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(326, 24); + this.textBox1.TabIndex = 2; + this.textBox1.Text = "Remark Hotness Settings"; + // + // panel1 + // + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel1.Controls.Add(this.HotnessValueBox); + this.panel1.Controls.Add(this.HotnessBar); + this.panel1.Controls.Add(this.textBox1); + this.panel1.Location = new System.Drawing.Point(28, 63); + this.panel1.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(398, 140); + this.panel1.TabIndex = 3; + // + // HotnessValueBox + // + this.HotnessValueBox.Location = new System.Drawing.Point(6, 63); + this.HotnessValueBox.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.HotnessValueBox.Name = "HotnessValueBox"; + this.HotnessValueBox.Size = new System.Drawing.Size(58, 31); + this.HotnessValueBox.TabIndex = 3; + this.HotnessValueBox.Text = "0%"; + // + // panel2 + // + this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel2.Controls.Add(this.RemarkTypeList); + this.panel2.Controls.Add(this.textBox2); + this.panel2.Location = new System.Drawing.Point(28, 217); + this.panel2.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(398, 235); + this.panel2.TabIndex = 4; + // + // RemarkTypeList + // + this.RemarkTypeList.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.RemarkTypeList.CheckBoxes = true; + listViewItem1.Checked = true; + listViewItem1.StateImageIndex = 1; + listViewItem1.UseItemStyleForSubItems = false; + listViewItem2.Checked = true; + listViewItem2.StateImageIndex = 1; + listViewItem3.Checked = true; + listViewItem3.StateImageIndex = 1; + listViewItem4.Checked = true; + listViewItem4.StateImageIndex = 1; + listViewItem5.Checked = true; + listViewItem5.StateImageIndex = 1; + this.RemarkTypeList.Items.AddRange(new System.Windows.Forms.ListViewItem[] { + listViewItem1, + listViewItem2, + listViewItem3, + listViewItem4, + listViewItem5}); + this.RemarkTypeList.LabelWrap = false; + this.RemarkTypeList.Location = new System.Drawing.Point(8, 40); + this.RemarkTypeList.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.RemarkTypeList.Name = "RemarkTypeList"; + this.RemarkTypeList.Size = new System.Drawing.Size(358, 187); + this.RemarkTypeList.TabIndex = 1; + this.RemarkTypeList.UseCompatibleStateImageBehavior = false; + this.RemarkTypeList.View = System.Windows.Forms.View.List; + this.RemarkTypeList.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.RemarkTypeList_ItemChecked); + // + // textBox2 + // + this.textBox2.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox2.Location = new System.Drawing.Point(8, 6); + this.textBox2.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.textBox2.Name = "textBox2"; + this.textBox2.ReadOnly = true; + this.textBox2.Size = new System.Drawing.Size(318, 24); + this.textBox2.TabIndex = 0; + this.textBox2.Text = "Remark Types To View"; + // + // FullDebugLocBox + // + this.FullDebugLocBox.AutoSize = true; + this.FullDebugLocBox.Location = new System.Drawing.Point(468, 115); + this.FullDebugLocBox.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.FullDebugLocBox.Name = "FullDebugLocBox"; + this.FullDebugLocBox.Size = new System.Drawing.Size(387, 29); + this.FullDebugLocBox.TabIndex = 5; + this.FullDebugLocBox.Text = "Display Full Debug Source Location"; + this.FullDebugLocBox.UseVisualStyleBackColor = true; + this.FullDebugLocBox.CheckedChanged += new System.EventHandler(this.FullDebugLocBox_CheckedChanged); + // + // EnabledBox + // + this.EnabledBox.AutoSize = true; + this.EnabledBox.Location = new System.Drawing.Point(468, 71); + this.EnabledBox.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.EnabledBox.Name = "EnabledBox"; + this.EnabledBox.Size = new System.Drawing.Size(139, 29); + this.EnabledBox.TabIndex = 6; + this.EnabledBox.Text = "IsEnabled"; + this.EnabledBox.UseVisualStyleBackColor = true; + this.EnabledBox.CheckedChanged += new System.EventHandler(this.EnabledBox_CheckedChanged); + // + // DiffBox + // + this.DiffBox.AutoSize = true; + this.DiffBox.Location = new System.Drawing.Point(468, 204); + this.DiffBox.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.DiffBox.Name = "DiffBox"; + this.DiffBox.Size = new System.Drawing.Size(323, 29); + this.DiffBox.TabIndex = 7; + this.DiffBox.Text = "Only Show Diffs On Compare"; + this.DiffBox.UseVisualStyleBackColor = true; + this.DiffBox.CheckedChanged += new System.EventHandler(this.DiffBox_CheckedChanged); + // + // AutoEmitRemarksBox + // + this.AutoEmitRemarksBox.AutoSize = true; + this.AutoEmitRemarksBox.Location = new System.Drawing.Point(468, 248); + this.AutoEmitRemarksBox.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.AutoEmitRemarksBox.Name = "AutoEmitRemarksBox"; + this.AutoEmitRemarksBox.Size = new System.Drawing.Size(268, 29); + this.AutoEmitRemarksBox.TabIndex = 8; + this.AutoEmitRemarksBox.Text = "Auto Emit Remark Files"; + this.AutoEmitRemarksBox.UseVisualStyleBackColor = true; + this.AutoEmitRemarksBox.CheckedChanged += new System.EventHandler(this.AutoEmitRemarksBox_CheckedChanged); + // + // GeneralPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.Controls.Add(this.AutoEmitRemarksBox); + this.Controls.Add(this.DiffBox); + this.Controls.Add(this.EnabledBox); + this.Controls.Add(this.FullDebugLocBox); + this.Controls.Add(this.panel2); + this.Controls.Add(this.DisplayOutofDateRemarksBox); + this.Controls.Add(this.panel1); + this.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.Name = "GeneralPageControl"; + this.Size = new System.Drawing.Size(864, 471); + ((System.ComponentModel.ISupportInitialize)(this.HotnessBar)).EndInit(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.CheckBox DisplayOutofDateRemarksBox; + private System.Windows.Forms.TrackBar HotnessBar; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.TextBox HotnessValueBox; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.ListView RemarkTypeList; + private System.Windows.Forms.CheckBox FullDebugLocBox; + private System.Windows.Forms.CheckBox EnabledBox; + private System.Windows.Forms.CheckBox DiffBox; + private System.Windows.Forms.CheckBox AutoEmitRemarksBox; + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/GeneralPageControl.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/GeneralPageControl.cs @@ -0,0 +1,64 @@ +using System; +using System.Windows.Forms; + +namespace RemarkPackage.RemarkOptions { + public partial class GeneralPageControl : UserControl { + public GeneralPageControl() => InitializeComponent(); + + internal void LoadSettings() { + // Set initial hotness bar value. + HotnessBar.Value = OptionsPage.HotnessFilterValue; + HotnessValueBox.Text = HotnessBar.Value + "%"; + + // Out of date remarks val. + DisplayOutofDateRemarksBox.Checked = OptionsPage.DisplayOutofDateRemarks; + + // Full source line val. + FullDebugLocBox.Checked = OptionsPage.ShowFullSourceFileLine; + + // Only show diffs. + DiffBox.Checked = OptionsPage.OnlyShowDiffs; + + // Remark ignore list. + for (int i = 0, e = RemarkTypeList.Items.Count; i < e; ++i) + RemarkTypeList.Items[i].Checked = !OptionsPage.getIgnoreStatus(i); + + // Enabled. + EnabledBox.Checked = OptionsPage.Enabled; + + // Auto emit. + AutoEmitRemarksBox.Checked = OptionsPage.AutoEmitRemarks; + } + + public OptionsPageGeneral OptionsPage { get; set; } + + private void HotnessBar_Scroll(object sender, EventArgs e) { + HotnessValueBox.Text = HotnessBar.Value + "%"; + OptionsPage.HotnessFilterValue = HotnessBar.Value; + } + + private void DisplayOutofDateRemarksBox_CheckedChanged(object sender, EventArgs e) { + OptionsPage.DisplayOutofDateRemarks = DisplayOutofDateRemarksBox.Checked; + } + + private void RemarkTypeList_ItemChecked(object sender, System.Windows.Forms.ItemCheckedEventArgs e) { + OptionsPage.SetIgnoreStatus(e.Item.Index, !e.Item.Checked); + } + + private void FullDebugLocBox_CheckedChanged(object sender, EventArgs e) { + OptionsPage.ShowFullSourceFileLine = FullDebugLocBox.Checked; + } + + private void EnabledBox_CheckedChanged(object sender, EventArgs e) { + OptionsPage.Enabled = EnabledBox.Checked; + } + + private void DiffBox_CheckedChanged(object sender, EventArgs e) { + OptionsPage.OnlyShowDiffs = DiffBox.Checked; + } + + private void AutoEmitRemarksBox_CheckedChanged(object sender, EventArgs e) { + OptionsPage.AutoEmitRemarks = AutoEmitRemarksBox.Checked; + } + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/GeneralPageControl.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/GeneralPageControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/ToolOptions/OptionsPageGeneral.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/OptionsPageGeneral.cs @@ -0,0 +1,119 @@ +using Microsoft.VisualStudio.Shell; +using RemarkPackage.RemarkOptions; +using RemarkInterface; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace RemarkPackage { + [Guid(GuidStrings.GuidPageGeneral)] + public class OptionsPageGeneral : DialogPage { + private GeneralPageControl OptionsControl; + + // Display our general page control instead. + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected override IWin32Window Window { + get { + if (OptionsControl == null) { + OptionsControl = new GeneralPageControl { + Location = new Point(0, 0), + OptionsPage = this + }; + } + return OptionsControl; + } + } + protected override void Dispose(bool Disposing) { + if (Disposing) { + if (OptionsControl != null) { + OptionsControl.Dispose(); + OptionsControl = null; + } + } + base.Dispose(Disposing); + } + + // Private fields controlling the different options. + internal bool DisplayOutofDateRemarks { get; set; } + internal bool ShowFullSourceFileLine { get; set; } + internal bool OnlyShowDiffs { get; set; } + internal bool AutoEmitRemarks { get; set; } + internal bool Enabled { get; set; } + internal int HotnessFilterValue { get; set; } + private BitArray IgnoreStatus = new BitArray((int)RemarkType.Count); + internal bool getIgnoreStatus(int Idx) => IgnoreStatus.Get(Idx); + internal void SetIgnoreStatus(int Idx, bool Status) => IgnoreStatus.Set(Idx, Status); + + protected override void OnActivate(CancelEventArgs e) { + // Get the current settings. + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + RemarkSettings Filter = RM.Settings; + + // Build the values from the current settings. + DisplayOutofDateRemarks = Filter.AllowOutOfDateRemarks; + ShowFullSourceFileLine = Filter.ShowFullSourceFileLine; + OnlyShowDiffs = Filter.OnlyShowDiffs; + AutoEmitRemarks = Filter.AutoEmitRemarks; + HotnessFilterValue = (int)Filter.HotnessFilter; + Enabled = Filter.IsEnabled; + IgnoreStatus = new BitArray(Filter.IgnoreMask); + OptionsControl.LoadSettings(); + + base.OnActivate(e); + } + + protected override void OnApply(PageApplyEventArgs evt) { + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + RemarkSettings RF = RM.Settings; + bool Changed = false; + + // Change setting for out of date remarks. + if (RF.AllowOutOfDateRemarks != DisplayOutofDateRemarks) { + Changed = true; + RF.AllowOutOfDateRemarks = DisplayOutofDateRemarks; + } + + // Showing full source line. + if (RF.ShowFullSourceFileLine != ShowFullSourceFileLine) { + Changed = true; + RF.ShowFullSourceFileLine = ShowFullSourceFileLine; + } + + // Only show diff on compare. + if (RF.OnlyShowDiffs != OnlyShowDiffs) { + Changed = true; + RF.OnlyShowDiffs = OnlyShowDiffs; + } + + // Auto emit remarks. + if (RF.AutoEmitRemarks != AutoEmitRemarks) { + Changed = true; + RF.AutoEmitRemarks = AutoEmitRemarks; + } + + // Change settings for displaying remark types. + for (int i = 0, e = (int)RemarkType.Count; i < e; ++i) + Changed |= RF.ignoreType((RemarkType)i, IgnoreStatus[i]); + + // Change the hotness settings. + if (RF.HotnessFilter != HotnessFilterValue) { + Changed = true; + RF.HotnessFilter = (uint)HotnessFilterValue; + } + // Enabled. + if (RF.IsEnabled != Enabled) { + Changed = true; + RF.IsEnabled = Enabled; + } + // If we changed anything send an event. + if (Changed) { + ORS.dispatchRemarkCacheInvalidateEvent(); + RF.saveSettings(); + } + } + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/OptionsPagePerRemarkStatus.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/OptionsPagePerRemarkStatus.cs @@ -0,0 +1,42 @@ +using Microsoft.VisualStudio.Shell; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace RemarkPackage.RemarkOptions { + [Guid(GuidStrings.GuidOptionsPagePerRemarkStatus)] + public class OptionsPagePerRemarkStatus : DialogPage { + private PerRemarkStatusPageControl OptionsControl; + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected override IWin32Window Window { + get { + if (OptionsControl == null) { + OptionsControl = new PerRemarkStatusPageControl { + Location = new Point(0, 0), + OptionsPage = this + }; + } + return OptionsControl; + } + } + + protected override void Dispose(bool Disposing) { + if (Disposing) { + if (OptionsControl != null) { + OptionsControl.Dispose(); + OptionsControl = null; + } + } + base.Dispose(Disposing); + } + + protected override void OnActivate(CancelEventArgs e) { + OptionsControl.refreshPanel(); + base.OnActivate(e); + } + + protected override void OnApply(PageApplyEventArgs evt) => OptionsControl.OnApply(); + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/OptionsPageRemarkSubStatusPage.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/OptionsPageRemarkSubStatusPage.cs @@ -0,0 +1,43 @@ +using Microsoft.VisualStudio.Shell; +using RemarkPackage.RemarkOptions; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace RemarkPackage { + [Guid(GuidStrings.GuidOptionsPagePerRemarkSubStatus)] + public class OptionsPagePerRemarkSubStatus : DialogPage { + private PerRemarkSubStatusPageControl OptionsControl; + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + protected override IWin32Window Window { + get { + if (OptionsControl == null) { + OptionsControl = new PerRemarkSubStatusPageControl { + Location = new Point(0, 0), + OptionsPage = this + }; + } + return OptionsControl; + } + } + + protected override void Dispose(bool Disposing) { + if (Disposing) { + if (OptionsControl != null) { + OptionsControl.Dispose(); + OptionsControl = null; + } + } + base.Dispose(Disposing); + } + + protected override void OnActivate(CancelEventArgs e) { + OptionsControl.refreshPanel(); + base.OnActivate(e); + } + + protected override void OnApply(PageApplyEventArgs evt) => OptionsControl.OnApply(); + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkStatusPageControl.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkStatusPageControl.Designer.cs @@ -0,0 +1,83 @@ +namespace RemarkPackage.RemarkOptions +{ + partial class PerRemarkStatusPageControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.Title = new System.Windows.Forms.TextBox(); + this.PassPanel = new System.Windows.Forms.TableLayoutPanel(); + this.SuspendLayout(); + // + // Title + // + this.Title.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.Title.Cursor = System.Windows.Forms.Cursors.Arrow; + this.Title.Location = new System.Drawing.Point(178, 6); + this.Title.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.Title.Name = "Title"; + this.Title.ReadOnly = true; + this.Title.Size = new System.Drawing.Size(482, 24); + this.Title.TabIndex = 0; + this.Title.Text = "Select Global View Status Per Optimization Pass."; + // + // PassPanel + // + this.PassPanel.AutoScroll = true; + this.PassPanel.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.OutsetDouble; + this.PassPanel.ColumnCount = 2; + this.PassPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.Location = new System.Drawing.Point(6, 42); + this.PassPanel.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.PassPanel.Name = "PassPanel"; + this.PassPanel.RowCount = 2; + this.PassPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.Size = new System.Drawing.Size(818, 521); + this.PassPanel.TabIndex = 1; + // + // PerRemarkStatusPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.Controls.Add(this.PassPanel); + this.Controls.Add(this.Title); + this.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.Name = "PerRemarkStatusPageControl"; + this.Size = new System.Drawing.Size(850, 569); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox Title; + private System.Windows.Forms.TableLayoutPanel PassPanel; + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkStatusPageControl.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkStatusPageControl.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using RemarkInterface; +using System.Collections; + +namespace RemarkPackage.RemarkOptions { + public partial class PerRemarkStatusPageControl : UserControl { + public PerRemarkStatusPageControl() => InitializeComponent(); + + private class PassRemarkVisibilityItem { + public string Name { get; set; } + public string Pass; + } + // Visibility mask per pass. + Dictionary PassMasks = new Dictionary(); + HashSet DirtyMasks = new HashSet(); + + public void refreshPanel() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + + // Recreate the controls for the panel with the new list of passes. + PassPanel.Controls.Clear(); + RemarkManager RM = ORS.getOptRemarkManager(); + RemarkSettings Settings = RM.Settings; + + PassPanel.ColumnCount = 2; + PassPanel.RowCount = Settings.PassList.Count; + PassPanel.ColumnStyles[0].Width = 30; + PassPanel.ColumnStyles[1].Width = 70; + + // Update the rows to have identical spacing. + int RowPercentage = 0; + if (PassPanel.RowCount > 6) + RowPercentage = (int)((1.0 / 6.0) * 100); + else + RowPercentage = (int)((1.0 / (double)PassPanel.RowCount) * 100); + int RowNum = 0; + PassPanel.RowStyles.Clear(); + foreach (KeyValuePair> PassPair in Settings.PassList) { + string Pass = PassPair.Key; + + // Create the name portion. + TextBox PassName = new TextBox { + Text = Pass, + ReadOnly = true, + BorderStyle = BorderStyle.None, + Dock = DockStyle.Fill + }; + PassPanel.Controls.Add(PassName, 0, RowNum); + PassPanel.RowStyles.Add(new RowStyle(SizeType.Percent, RowPercentage)); + + // Create the drop down control. + CheckedComboBox Selection = new CheckedComboBox { + MaxDropDownItems = (int)RemarkType.Count, + DisplayMember = "Name", + ValueSeparator = ", ", + Width = (int)((double)PassPanel.Width * .7) + }; + + // Get the current visibility mask for this pass. + BitArray Mask = Settings.getPassVisibilityMask(Pass); + BitArray NewMask = new BitArray(Mask); + if (!PassMasks.ContainsKey(Pass)) + PassMasks.Add(Pass, NewMask); + else + PassMasks[Pass] = NewMask; + + // Add the types. + string[] Types = Enum.GetNames(typeof(RemarkType)); + for (int i = 0, e = (int)RemarkType.Count; i < e; ++i) { + var Item = new PassRemarkVisibilityItem { + Name = Types[i], + Pass = Pass + }; + Selection.Items.Add(Item); + Selection.SetItemChecked(i, !Mask[i]); + } + Selection.ItemCheck += Selection_ItemCheck; + PassPanel.Controls.Add(Selection, 1, RowNum++); + } + + PassPanel.Refresh(); + PassPanel.PerformLayout(); + } + public void OnApply() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + RemarkSettings RF = RM.Settings; + bool Changed = false; + foreach (string Pass in DirtyMasks) + Changed |= RF.setPassVisibilityMask(Pass, PassMasks[Pass]); + if (Changed) { + ORS.dispatchRemarkCacheInvalidateEvent(); + RF.saveSettings(); + } + } + + private void Selection_ItemCheck(object sender, ItemCheckEventArgs e) { + CheckedComboBox CCB = (CheckedComboBox)sender; + var Item = (PassRemarkVisibilityItem)CCB.Items[e.Index]; + PassMasks[Item.Pass].Set(e.Index, e.NewValue == CheckState.Unchecked); + DirtyMasks.Add(Item.Pass); + return; + } + + public OptionsPagePerRemarkStatus OptionsPage { get; set; } + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkStatusPageControl.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkStatusPageControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkSubStatusPageControl.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkSubStatusPageControl.Designer.cs @@ -0,0 +1,83 @@ +namespace RemarkPackage.RemarkOptions +{ + partial class PerRemarkSubStatusPageControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.Title = new System.Windows.Forms.TextBox(); + this.PassPanel = new System.Windows.Forms.TableLayoutPanel(); + this.SuspendLayout(); + // + // Title + // + this.Title.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.Title.Cursor = System.Windows.Forms.Cursors.Arrow; + this.Title.Location = new System.Drawing.Point(178, 6); + this.Title.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.Title.Name = "Title"; + this.Title.ReadOnly = true; + this.Title.Size = new System.Drawing.Size(512, 24); + this.Title.TabIndex = 0; + this.Title.Text = "Select SubType View Status Per Optimization Pass."; + // + // PassPanel + // + this.PassPanel.AutoScroll = true; + this.PassPanel.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.OutsetDouble; + this.PassPanel.ColumnCount = 2; + this.PassPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.Location = new System.Drawing.Point(6, 42); + this.PassPanel.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.PassPanel.Name = "PassPanel"; + this.PassPanel.RowCount = 2; + this.PassPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.PassPanel.Size = new System.Drawing.Size(814, 521); + this.PassPanel.TabIndex = 1; + // + // PerRemarkSubStatusPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.Controls.Add(this.PassPanel); + this.Controls.Add(this.Title); + this.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); + this.Name = "PerRemarkSubStatusPageControl"; + this.Size = new System.Drawing.Size(850, 569); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox Title; + private System.Windows.Forms.TableLayoutPanel PassPanel; + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkSubStatusPageControl.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkSubStatusPageControl.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using System.Windows.Forms; +using RemarkInterface; + +namespace RemarkPackage.RemarkOptions { + public partial class PerRemarkSubStatusPageControl : UserControl { + public PerRemarkSubStatusPageControl() => InitializeComponent(); + + private class PassRemarkVisibilityItem { + public string Name { get; set; } + public string Pass; + } + // Visibility mask per pass. + Dictionary PassSubTypeMasks = new Dictionary(); + HashSet DirtyMasks = new HashSet(); + + public void refreshPanel() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + + // Recreate the controls for the panel with the new list of passes. + PassPanel.Controls.Clear(); + RemarkManager RM = ORS.getOptRemarkManager(); + RemarkSettings Settings = RM.Settings; + + PassPanel.ColumnCount = 2; + PassPanel.RowCount = Settings.PassList.Count; + PassPanel.ColumnStyles[0].Width = 30; + PassPanel.ColumnStyles[1].Width = 70; + + // Update the rows to have identical spacing. + int RowPercentage = 0; + if (PassPanel.RowCount > 6) + RowPercentage = (int)((1.0 / 6.0) * 100); + else + RowPercentage = (int)((1.0 / PassPanel.RowCount) * 100); + int RowNum = 0; + PassPanel.RowStyles.Clear(); + foreach (KeyValuePair> PassPair in Settings.PassList) { + string Pass = PassPair.Key; + HashSet SubTypes = PassPair.Value; + + // Create the name portion. + TextBox PassName = new TextBox { + Text = Pass, + ReadOnly = true, + BorderStyle = BorderStyle.None, + Dock = DockStyle.Fill + }; + PassPanel.Controls.Add(PassName, 0, RowNum); + PassPanel.RowStyles.Add(new RowStyle(SizeType.Percent, RowPercentage)); + + // Create the drop down control. + CheckedComboBox Selection = new CheckedComboBox { + MaxDropDownItems = SubTypes.Count, + DisplayMember = "Name", + ValueSeparator = ", ", + Width = (int)((double)PassPanel.Width * .7) + }; + + // Build the visibility for each of the sub types. + string PassKeyPrefix = Pass + "<>"; + int Idx = 0; + foreach (string SubTy in SubTypes) { + // Add the value to the cache. + string Key = PassKeyPrefix + SubTy; + bool IsIgnored = Settings.getPassSubTypeIgnored(Pass, SubTy); + if (PassSubTypeMasks.ContainsKey(Key)) + PassSubTypeMasks[Key] = IsIgnored; + else + PassSubTypeMasks.Add(Key, IsIgnored); + + // Add the UI item. + var Item = new PassRemarkVisibilityItem { + Name = SubTy, + Pass = Pass + }; + Selection.Items.Add(Item); + Selection.SetItemChecked(Idx++, !IsIgnored); + } + + Selection.ItemCheck += Selection_ItemCheck; + PassPanel.Controls.Add(Selection, 1, RowNum++); + } + + PassPanel.Refresh(); + PassPanel.PerformLayout(); + } + public void OnApply() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS.getOptRemarkManager(); + RemarkSettings RF = RM.Settings; + bool Changed = false; + foreach (string Pass in DirtyMasks) + Changed |= RF.setPassSubTypeIgnored(Pass, PassSubTypeMasks[Pass]); + if (Changed) { + ORS.dispatchRemarkCacheInvalidateEvent(); + RF.saveSettings(); + } + } + + private void Selection_ItemCheck(object sender, ItemCheckEventArgs e) { + CheckedComboBox CCB = (CheckedComboBox)sender; + var Item = (PassRemarkVisibilityItem)CCB.Items[e.Index]; + string Key = Item.Pass + "<>" + Item.Name; + bool Ignored = e.NewValue == CheckState.Unchecked; + PassSubTypeMasks[Key] = Ignored; + DirtyMasks.Add(Key); + return; + } + + public OptionsPagePerRemarkSubStatus OptionsPage { get; set; } + } +} Index: remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkSubStatusPageControl.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/ToolOptions/PerRemarkSubStatusPageControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/Util/CheckedComboBox.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Util/CheckedComboBox.cs @@ -0,0 +1,413 @@ +using System; +using System.Text; +using System.Windows.Forms; +using System.Drawing; +using System.Diagnostics; + +namespace RemarkPackage { + public class CheckedComboBox : ComboBox { + /// + /// Internal class to represent the dropdown list of the CheckedComboBox + /// + internal class Dropdown : Form { + // ---------------------------------- internal class CCBoxEventArgs -------------------------------------------- + /// + /// Custom EventArgs encapsulating value as to whether the combo box value(s) should be assignd to or not. + /// + internal class CCBoxEventArgs : EventArgs { + private bool assignValues; + public bool AssignValues { + get { return assignValues; } + set { assignValues = value; } + } + private EventArgs e; + public EventArgs EventArgs { + get { return e; } + set { e = value; } + } + public CCBoxEventArgs(EventArgs e, bool assignValues) : base() { + this.e = e; + this.assignValues = assignValues; + } + } + + // ---------------------------------- internal class CustomCheckedListBox -------------------------------------------- + + /// + /// A custom CheckedListBox being shown within the dropdown form representing the dropdown list of the CheckedComboBox. + /// + internal class CustomCheckedListBox : CheckedListBox { + private int curSelIndex = -1; + + public CustomCheckedListBox() : base() { + this.SelectionMode = SelectionMode.One; + this.HorizontalScrollbar = true; + } + + /// + /// Intercepts the keyboard input, [Enter] confirms a selection and [Esc] cancels it. + /// + /// The Key event arguments + protected override void OnKeyDown(KeyEventArgs e) { + if (e.KeyCode == Keys.Enter) { + // Enact selection. + ((CheckedComboBox.Dropdown)Parent).OnDeactivate(new CCBoxEventArgs(null, true)); + e.Handled = true; + + } else if (e.KeyCode == Keys.Escape) { + // Cancel selection. + ((CheckedComboBox.Dropdown)Parent).OnDeactivate(new CCBoxEventArgs(null, false)); + e.Handled = true; + + } else if (e.KeyCode == Keys.Delete) { + // Delete unckecks all, [Shift + Delete] checks all. + for (int i = 0; i < Items.Count; i++) { + SetItemChecked(i, e.Shift); + } + e.Handled = true; + } + // If no Enter or Esc keys presses, let the base class handle it. + base.OnKeyDown(e); + } + + protected override void OnMouseMove(MouseEventArgs e) { + base.OnMouseMove(e); + int index = IndexFromPoint(e.Location); + Debug.WriteLine("Mouse over item: " + (index >= 0 ? GetItemText(Items[index]) : "None")); + if ((index >= 0) && (index != curSelIndex)) { + curSelIndex = index; + SetSelected(index, true); + } + } + + } // end internal class CustomCheckedListBox + + // -------------------------------------------------------------------------------------------------------- + + // ********************************************* Data ********************************************* + + private CheckedComboBox ccbParent; + + // Keeps track of whether checked item(s) changed, hence the value of the CheckedComboBox as a whole changed. + // This is simply done via maintaining the old string-representation of the value(s) and the new one and comparing them! + private string oldStrValue = ""; + public bool ValueChanged { + get { + string newStrValue = ccbParent.Text; + if ((oldStrValue.Length > 0) && (newStrValue.Length > 0)) { + return (oldStrValue.CompareTo(newStrValue) != 0); + } else { + return (oldStrValue.Length != newStrValue.Length); + } + } + } + + // Array holding the checked states of the items. This will be used to reverse any changes if user cancels selection. + bool[] checkedStateArr; + + // Whether the dropdown is closed. + private bool dropdownClosed = true; + + private CustomCheckedListBox cclb; + public CustomCheckedListBox List { + get { return cclb; } + set { cclb = value; } + } + + // ********************************************* Construction ********************************************* + + public Dropdown(CheckedComboBox ccbParent) { + this.ccbParent = ccbParent; + InitializeComponent(); + this.ShowInTaskbar = false; + // Add a handler to notify our parent of ItemCheck events. + this.cclb.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.cclb_ItemCheck); + } + + // ********************************************* Methods ********************************************* + + // Create a CustomCheckedListBox which fills up the entire form area. + private void InitializeComponent() { + this.cclb = new CustomCheckedListBox(); + this.SuspendLayout(); + // + // cclb + // + this.cclb.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.cclb.Dock = System.Windows.Forms.DockStyle.Fill; + this.cclb.FormattingEnabled = true; + this.cclb.Location = new System.Drawing.Point(0, 0); + this.cclb.Name = "cclb"; + this.cclb.Size = new System.Drawing.Size(47, 15); + this.cclb.TabIndex = 0; + // + // Dropdown + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.Menu; + this.ClientSize = new System.Drawing.Size(47, 16); + this.ControlBox = false; + this.Controls.Add(this.cclb); + this.ForeColor = System.Drawing.SystemColors.ControlText; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.MinimizeBox = false; + this.Name = "ccbParent"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.ResumeLayout(false); + } + + public string GetCheckedItemsStringValue() { + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < cclb.CheckedItems.Count; i++) { + sb.Append(cclb.GetItemText(cclb.CheckedItems[i])).Append(ccbParent.ValueSeparator); + } + if (sb.Length > 0) { + sb.Remove(sb.Length - ccbParent.ValueSeparator.Length, ccbParent.ValueSeparator.Length); + } + return sb.ToString(); + } + + /// + /// Closes the dropdown portion and enacts any changes according to the specified boolean parameter. + /// NOTE: even though the caller might ask for changes to be enacted, this doesn't necessarily mean + /// that any changes have occurred as such. Caller should check the ValueChanged property of the + /// CheckedComboBox (after the dropdown has closed) to determine any actual value changes. + /// + /// + public void CloseDropdown(bool enactChanges) { + if (dropdownClosed) { + return; + } + Debug.WriteLine("CloseDropdown"); + // Perform the actual selection and display of checked items. + if (enactChanges) { + ccbParent.SelectedIndex = -1; + // Set the text portion equal to the string comprising all checked items (if any, otherwise empty!). + ccbParent.Text = GetCheckedItemsStringValue(); + + } else { + // Caller cancelled selection - need to restore the checked items to their original state. + for (int i = 0; i < cclb.Items.Count; i++) { + cclb.SetItemChecked(i, checkedStateArr[i]); + } + } + // From now on the dropdown is considered closed. We set the flag here to prevent OnDeactivate() calling + // this method once again after hiding this window. + dropdownClosed = true; + // Set the focus to our parent CheckedComboBox and hide the dropdown check list. + ccbParent.Focus(); + this.Hide(); + // Notify CheckedComboBox that its dropdown is closed. (NOTE: it does not matter which parameters we pass to + // OnDropDownClosed() as long as the argument is CCBoxEventArgs so that the method knows the notification has + // come from our code and not from the framework). + ccbParent.OnDropDownClosed(new CCBoxEventArgs(null, false)); + } + + protected override void OnActivated(EventArgs e) { + Debug.WriteLine("OnActivated"); + base.OnActivated(e); + dropdownClosed = false; + // Assign the old string value to compare with the new value for any changes. + oldStrValue = ccbParent.Text; + // Make a copy of the checked state of each item, in cace caller cancels selection. + checkedStateArr = new bool[cclb.Items.Count]; + for (int i = 0; i < cclb.Items.Count; i++) { + checkedStateArr[i] = cclb.GetItemChecked(i); + } + } + + protected override void OnDeactivate(EventArgs e) { + Debug.WriteLine("OnDeactivate"); + base.OnDeactivate(e); + CCBoxEventArgs ce = e as CCBoxEventArgs; + if (ce != null) { + CloseDropdown(ce.AssignValues); + + } else { + // If not custom event arguments passed, means that this method was called from the + // framework. We assume that the checked values should be registered regardless. + CloseDropdown(true); + } + } + + private void cclb_ItemCheck(object sender, ItemCheckEventArgs e) { + if (ccbParent.ItemCheck != null) { + ccbParent.ItemCheck(ccbParent, e); + } + } + + } // end internal class Dropdown + + // ******************************** Data ******************************** + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + // A form-derived object representing the drop-down list of the checked combo box. + private Dropdown dropdown; + + // The valueSeparator character(s) between the ticked elements as they appear in the + // text portion of the CheckedComboBox. + private string valueSeparator; + public string ValueSeparator { + get { return valueSeparator; } + set { valueSeparator = value; } + } + + public bool CheckOnClick { + get { return dropdown.List.CheckOnClick; } + set { dropdown.List.CheckOnClick = value; } + } + + public new string DisplayMember { + get { return dropdown.List.DisplayMember; } + set { dropdown.List.DisplayMember = value; } + } + + public new CheckedListBox.ObjectCollection Items { + get { return dropdown.List.Items; } + } + + public CheckedListBox.CheckedItemCollection CheckedItems { + get { return dropdown.List.CheckedItems; } + } + + public CheckedListBox.CheckedIndexCollection CheckedIndices { + get { return dropdown.List.CheckedIndices; } + } + + public bool ValueChanged { + get { return dropdown.ValueChanged; } + } + + // Event handler for when an item check state changes. + public event ItemCheckEventHandler ItemCheck; + + // ******************************** Construction ******************************** + + public CheckedComboBox() : base() { + // We want to do the drawing of the dropdown. + this.DrawMode = DrawMode.OwnerDrawVariable; + // Default value separator. + this.valueSeparator = ", "; + // This prevents the actual ComboBox dropdown to show, although it's not strickly-speaking necessary. + // But including this remove a slight flickering just before our dropdown appears (which is caused by + // the empty-dropdown list of the ComboBox which is displayed for fractions of a second). + this.DropDownHeight = 1; + // This is the default setting - text portion is editable and user must click the arrow button + // to see the list portion. Although we don't want to allow the user to edit the text portion + // the DropDownList style is not being used because for some reason it wouldn't allow the text + // portion to be programmatically set. Hence we set it as editable but disable keyboard input (see below). + this.DropDownStyle = ComboBoxStyle.DropDown; + this.dropdown = new Dropdown(this); + // CheckOnClick style for the dropdown (NOTE: must be set after dropdown is created). + this.CheckOnClick = true; + } + + // ******************************** Operations ******************************** + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + protected override void OnDropDown(EventArgs e) { + base.OnDropDown(e); + DoDropDown(); + } + protected override void OnClick(EventArgs e) { + base.OnClick(e); + this.Parent.Focus(); + this.dropdown.Show(this); + } + + private void DoDropDown() { + if (!dropdown.Visible) { + Rectangle rect = RectangleToScreen(this.ClientRectangle); + dropdown.Location = new Point(rect.X, rect.Y + this.Size.Height); + int count = dropdown.List.Items.Count; + if (count > this.MaxDropDownItems) { + count = this.MaxDropDownItems; + } else if (count == 0) { + count = 1; + } + dropdown.Size = new Size(this.Size.Width, (dropdown.List.ItemHeight) * count + 2); + dropdown.Show(this); + } + } + + protected override void OnDropDownClosed(EventArgs e) { + // Call the handlers for this event only if the call comes from our code - NOT the framework's! + // NOTE: that is because the events were being fired in a wrong order, due to the actual dropdown list + // of the ComboBox which lies underneath our dropdown and gets involved every time. + if (e is Dropdown.CCBoxEventArgs) { + base.OnDropDownClosed(e); + } + } + + protected override void OnKeyDown(KeyEventArgs e) { + if (e.KeyCode == Keys.Down) { + // Signal that the dropdown is "down". This is required so that the behaviour of the dropdown is the same + // when it is a result of user pressing the Down_Arrow (which we handle and the framework wouldn't know that + // the list portion is down unless we tell it so). + // NOTE: all that so the DropDownClosed event fires correctly! + OnDropDown(null); + } + // Make sure that certain keys or combinations are not blocked. + e.Handled = !e.Alt && !(e.KeyCode == Keys.Tab) && + !((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.Right) || (e.KeyCode == Keys.Home) || (e.KeyCode == Keys.End)); + + base.OnKeyDown(e); + } + + protected override void OnKeyPress(KeyPressEventArgs e) { + e.Handled = true; + base.OnKeyPress(e); + } + + public bool GetItemChecked(int index) { + if (index < 0 || index > Items.Count) { + throw new ArgumentOutOfRangeException("index", "value out of range"); + } else { + return dropdown.List.GetItemChecked(index); + } + } + + public void SetItemChecked(int index, bool isChecked) { + if (index < 0 || index > Items.Count) { + throw new ArgumentOutOfRangeException("index", "value out of range"); + } else { + dropdown.List.SetItemChecked(index, isChecked); + // Need to update the Text. + this.Text = dropdown.GetCheckedItemsStringValue(); + } + } + + public CheckState GetItemCheckState(int index) { + if (index < 0 || index > Items.Count) { + throw new ArgumentOutOfRangeException("index", "value out of range"); + } else { + return dropdown.List.GetItemCheckState(index); + } + } + + public void SetItemCheckState(int index, CheckState state) { + if (index < 0 || index > Items.Count) { + throw new ArgumentOutOfRangeException("index", "value out of range"); + } else { + dropdown.List.SetItemCheckState(index, state); + // Need to update the Text. + this.Text = dropdown.GetCheckedItemsStringValue(); + } + } + + } // end public class CheckedComboBox + +} Index: remark-viewer-vs/RemarkPackage/Util/NativeMethods.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/Util/NativeMethods.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.OLE.Interop; + +namespace RemarkPackage { + internal static class NativeMethods { + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + [DllImport("user32.dll", EntryPoint = "IsDialogMessageA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + public static extern bool IsDialogMessageA(IntPtr hDlg, ref MSG msg); + + public const int + WM_KEYFIRST = 0x0100, + WM_KEYLAST = 0x0108, + WM_MOUSEFIRST = 0x0200, + WM_MOUSELAST = 0x020A; + } +} Index: remark-viewer-vs/RemarkPackage/VS2015/source.extension.vsixmanifest =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/VS2015/source.extension.vsixmanifest @@ -0,0 +1,23 @@ + + + + + OptRemarkViewer + View optimization remarks generated by the llvm compiler framework. + license.txt + + + + + + + + + + + + + + + + Index: remark-viewer-vs/RemarkPackage/VS2017/source.extension.vsixmanifest =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/VS2017/source.extension.vsixmanifest @@ -0,0 +1,23 @@ + + + + + OptRemarkViewer + View optimization remarks generated by the llvm compiler framework. + license.txt + + + + + + + + + + + + + + + + Index: remark-viewer-vs/RemarkPackage/VSPackage.Designer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/VSPackage.Designer.cs @@ -0,0 +1,127 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RemarkPackage { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class VSPackage { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal VSPackage() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RemarkPackage.VSPackage", typeof(VSPackage).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Optimization Remark Viewer. + /// + internal static string _100 { + get { + return ResourceManager.GetString("100", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to General Options. + /// + internal static string _101 { + get { + return ResourceManager.GetString("101", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Global View Status Per Pass. + /// + internal static string _102 { + get { + return ResourceManager.GetString("102", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SubType View Status Per Pass. + /// + internal static string _103 { + get { + return ResourceManager.GetString("103", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opt Remark Viewer. + /// + internal static string _110 { + get { + return ResourceManager.GetString("110", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Optimization Remark Viewer. + /// + internal static string _111 { + get { + return ResourceManager.GetString("111", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon _400 { + get { + object obj = ResourceManager.GetObject("400", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} Index: remark-viewer-vs/RemarkPackage/VSPackage.resx =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/VSPackage.resx @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + GenericPackage.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Optimization Remark Viewer + Options category + + + General Options + General page + + + Global View Status Per Pass + Custom page + + + Opt Remark Viewer + + + Optimization Remark Viewer + + + SubType View Status Per Pass + Custom page + + \ No newline at end of file Index: remark-viewer-vs/RemarkPackage/app.config =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/app.config @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: remark-viewer-vs/RemarkPackage/license.txt =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/license.txt @@ -0,0 +1,63 @@ +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- + + Index: remark-viewer-vs/RemarkPackage/packages.config =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkPackage/packages.config @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: remark-viewer-vs/RemarkTagger/Properties/AssemblyInfo.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("62652e5b-7dfe-44d1-920e-37e449b87855")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] Index: remark-viewer-vs/RemarkTagger/Properties/GlobalSuppressions.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Properties/GlobalSuppressions.cs @@ -0,0 +1,3 @@ +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased", Scope = "namespace", Target = "Microsoft.Samples.VisualStudio.IDE.Package")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "Microsoft.Samples.VisualStudio.IDE.Package.VSPackage.resources")] Index: remark-viewer-vs/RemarkTagger/RemarkTagger.csproj =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/RemarkTagger.csproj @@ -0,0 +1,104 @@ + + + + + Debug + AnyCPU + {E6C96829-D87B-489D-9C05-38110DA50296} + Library + Properties + OptRemarkViewer + RemarkTagger + v4.6 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {6a858a68-448f-4b27-93fc-3ebbc3d3fc07} + RemarkInterface + + + + true + + + true + bin\Debug - VS2015\ + DEBUG;TRACE + full + AnyCPU + prompt + AllRules.ruleset + + + bin\Release - VS2015\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + \ No newline at end of file Index: remark-viewer-vs/RemarkTagger/Support/IntraTextAdornmentTagTransformer.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Support/IntraTextAdornmentTagTransformer.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace RemarkPackage { + internal abstract class IntraTextAdornmentTagTransformer + : IntraTextAdornmentTagger, IDisposable + where TDataTag : ITag + where TAdornment : UIElement { + protected readonly ITagAggregator DataTagger; + protected readonly PositionAffinity? AdornmentAffinity; + + protected IntraTextAdornmentTagTransformer(IWpfTextView View, ITagAggregator DataTagger, PositionAffinity AdornmentAffinity = PositionAffinity.Successor) + : base(View) { + this.AdornmentAffinity = AdornmentAffinity; + this.DataTagger = DataTagger; + this.DataTagger.TagsChanged += HandleDataTagsChanged; + } + + protected override IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection Spans) { + if (Spans.Count == 0) + yield break; + + ITextSnapshot Snapshot = Spans[0].Snapshot; + foreach (IMappingTagSpan DataTagSpan in DataTagger.GetTags(Spans)) { + NormalizedSnapshotSpanCollection DataTagSpans = DataTagSpan.Span.GetSpans(Snapshot); + + // Ignore data tags that are split by projection. + // This is theoretically possible but unlikely in current scenarios. + if (DataTagSpans.Count != 1) + continue; + + SnapshotSpan Span = DataTagSpans[0]; + PositionAffinity? Affinity = Span.Length > 0 ? null : AdornmentAffinity; + yield return Tuple.Create(Span, Affinity, DataTagSpan.Tag); + } + } + + private void HandleDataTagsChanged(object sender, TagsChangedEventArgs args) { + var ChangedSpans = args.Span.GetSpans(View.TextBuffer.CurrentSnapshot); + InvalidateSpans(ChangedSpans); + } + + public virtual void Dispose() => DataTagger.Dispose(); + } +} Index: remark-viewer-vs/RemarkTagger/Support/IntraTextAdornmentTagger.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Support/IntraTextAdornmentTagger.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace RemarkPackage { + internal abstract class IntraTextAdornmentTagger + : ITagger + where TAdornment : UIElement { + protected readonly IWpfTextView View; + protected Dictionary AdornmentCache = new Dictionary(); + protected ITextSnapshot Snapshot { get; private set; } + private readonly List InvalidatedSpans = new List(); + + protected IntraTextAdornmentTagger(IWpfTextView PView) { + View = PView; + Snapshot = PView.TextBuffer.CurrentSnapshot; + View.LayoutChanged += HandleLayoutChanged; + View.TextBuffer.Changed += HandleBufferChanged; + } + + protected abstract TAdornment CreateAdornment(TData data, SnapshotSpan span); + + protected abstract IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection spans); + + private void HandleBufferChanged(object sender, TextContentChangedEventArgs args) { + var EditedSpans = args.Changes.Select(Change => new SnapshotSpan(args.After, Change.NewSpan)).ToList(); + InvalidateSpans(EditedSpans); + } + + protected void InvalidateSpans(IList Spans) { + if (Spans.Count == 0) + return; + bool WasEmpty = false; + lock (InvalidatedSpans) { + WasEmpty = InvalidatedSpans.Count == 0; + InvalidatedSpans.AddRange(Spans); + } + if (WasEmpty) + View.VisualElement.Dispatcher.BeginInvoke(new Action(AsyncUpdate)); + } + + private void AsyncUpdate() { + // Store the snapshot that we're now current with and send an event + // for the text that has changed. + if (Snapshot != View.TextBuffer.CurrentSnapshot) { + Snapshot = View.TextBuffer.CurrentSnapshot; + var TranslatedAdornmentCache = new Dictionary(); + foreach (var keyValuePair in AdornmentCache) + TranslatedAdornmentCache.Add(keyValuePair.Key.TranslateTo(Snapshot, SpanTrackingMode.EdgeExclusive), keyValuePair.Value); + AdornmentCache = TranslatedAdornmentCache; + } + + List SpansCopy; + lock (InvalidatedSpans) { + SpansCopy = InvalidatedSpans.ToList(); + InvalidatedSpans.Clear(); + } + + List TranslatedSpans = SpansCopy.Select(s => s.TranslateTo(this.Snapshot, SpanTrackingMode.EdgeInclusive)).ToList(); + if (TranslatedSpans.Count == 0) + return; + + var Start = TranslatedSpans.Select(Span => Span.Start).Min(); + var End = TranslatedSpans.Select(Span => Span.End).Max(); + RaiseTagsChanged(new SnapshotSpan(Start, End)); + } + + protected void RaiseTagsChanged(SnapshotSpan span) => TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(span)); + + private void HandleLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { + SnapshotSpan VisibleSpan = View.TextViewLines.FormattedSpan; + + // Setting out the adornments that are no longer visible. + List ToRemove = new List( + from KVP in AdornmentCache + where !KVP.Key.TranslateTo(VisibleSpan.Snapshot, SpanTrackingMode.EdgeExclusive).IntersectsWith(VisibleSpan) + select KVP.Key); + foreach (var Span in ToRemove) + AdornmentCache.Remove(Span); + } + + + // Produces tags on the snapshot that the tag consumer asked for. + public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection Spans) { + if (Spans == null || Spans.Count == 0) + yield break; + + // Translate the request to the snapshot that this tagger is current with. + ITextSnapshot RequestedSnapshot = Spans[0].Snapshot; + var TranslatedSpans = new NormalizedSnapshotSpanCollection(Spans.Select(span => span.TranslateTo(Snapshot, SpanTrackingMode.EdgeExclusive))); + + // Grab the adornments. + foreach (var TagSpan in GetAdornmentTagsOnSnapshot(TranslatedSpans)) { + // Translate each adornment to the snapshot that the tagger was asked about. + SnapshotSpan Span = TagSpan.Span.TranslateTo(RequestedSnapshot, SpanTrackingMode.EdgeExclusive); + IntraTextAdornmentTag Tag = new IntraTextAdornmentTag(TagSpan.Tag.Adornment, TagSpan.Tag.RemovalCallback, TagSpan.Tag.Affinity); + yield return new TagSpan(Span, Tag); + } + } + + // Produces tags on the snapshot that this tagger is current with. + private IEnumerable> GetAdornmentTagsOnSnapshot(NormalizedSnapshotSpanCollection Spans) { + if (Spans.Count == 0) + yield break; + + ITextSnapshot Snapshot = Spans[0].Snapshot; + System.Diagnostics.Debug.Assert(Snapshot == this.Snapshot); + + // Since WPF UI objects have state (like mouse hover or animation) and are relatively expensive to create and lay out, + // this code tries to reuse controls as much as possible. + // The controls are stored in this.adornmentCache between the calls. + + // Mark which adornments fall inside the requested spans with Keep=false + // so that they can be removed from the cache if they no longer correspond to data tags. + HashSet ToRemove = new HashSet(); + foreach (var Ar in AdornmentCache) + if (Spans.IntersectsWith(new NormalizedSnapshotSpanCollection(Ar.Key))) + ToRemove.Add(Ar.Key); + + foreach (var SpanDataPair in GetAdornmentData(Spans).Distinct(new Comparer())) { + // Look up the corresponding adornment or create one if it's new. + SnapshotSpan SnapshotSpan = SpanDataPair.Item1; + PositionAffinity? Affinity = SpanDataPair.Item2; + TData AdornmentData = SpanDataPair.Item3; + if (!AdornmentCache.TryGetValue(SnapshotSpan, out TAdornment Adornment)) { + Adornment = CreateAdornment(AdornmentData, SnapshotSpan); + if (Adornment == null) + continue; + + // Get the adornment to measure itself. Its DesiredSize property is used to determine + // how much space to leave between text for this adornment. + // Note: If the size of the adornment changes, the line will be reformatted to accommodate it. + // Note: Some adornments may change size when added to the view's visual tree due to inherited + // dependency properties that affect layout. Such options can include SnapsToDevicePixels, + // UseLayoutRounding, TextRenderingMode, TextHintingMode, and TextFormattingMode. Making sure + // that these properties on the adornment match the view's values before calling Measure here + // can help avoid the size change and the resulting unnecessary re-format. + Adornment.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + AdornmentCache.Add(SnapshotSpan, Adornment); + } + + yield return new TagSpan(SnapshotSpan, new IntraTextAdornmentTag(Adornment, null, Affinity)); + } + + foreach (var SnapshotSpan in ToRemove) + AdornmentCache.Remove(SnapshotSpan); + } + + public event EventHandler TagsChanged; + + private class Comparer : IEqualityComparer> { + public bool Equals(Tuple X, Tuple Y) { + if (X == null) + return Y == null; + else if (Y == null) + return false; + return X.Item1.Equals(Y.Item1); + } + + public int GetHashCode(Tuple Obj) => Obj.Item1.GetHashCode(); + } + } +} Index: remark-viewer-vs/RemarkTagger/Support/TextDocumentUtils.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Support/TextDocumentUtils.cs @@ -0,0 +1,10 @@ +using Microsoft.VisualStudio.Text; + +namespace RemarkPackage { + public static class TextDocumentUtils { + public static ITextDocument GetTextDocument(this ITextBuffer TextBuffer) { + return TextBuffer.Properties.TryGetProperty( + typeof(ITextDocument), out ITextDocument TextDoc) ? TextDoc : null; + } + } +} Index: remark-viewer-vs/RemarkTagger/Tagging/RemarkAdornment.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Tagging/RemarkAdornment.cs @@ -0,0 +1,273 @@ +using Microsoft.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.Shell; +using RemarkInterface; +using System.Collections.Generic; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using System.Linq; +using System.Globalization; + +namespace RemarkPackage { + static class ColorUtil { + static public Brush makeBrush(System.Drawing.Color C) => makeBrush(Color.FromArgb(C.A, C.R, C.G, C.B)); + static public Brush makeBrush(Color C) { + var B = new SolidColorBrush(C); + B.Freeze(); + return B; + } + } + public class DebugTextBlock : TextBlock { + public DebugTextBlock(NameReference Ref) : base() { + string ToolTipText = ""; + + // Add debug location info to the tooltip. + bool HasDebugLoc = Ref.DebugLoc != null; + if (HasDebugLoc) { + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkSettings RS = ORS.getOptRemarkManager().Settings; + ToolTipText += Ref.DebugLoc.toMSVCString(RS.ShowFullSourceFileLine); + + MouseEnter += DebugLink_MouseEnter; + MouseLeave += DebugLink_MouseLeave; + MouseDown += DebugLink_DebugReference_MouseDown; + } + + // Set the text based upon what type of reference we are. + if (Ref is FunctionReference FRef) + ToolTipText = ToolTipText.Insert(0, FRef.DemangledName + (HasDebugLoc ? "\n" : "")); + Text = Ref.ToString(); + TextBlock TTContent = new TextBlock() { + Text = ToolTipText, + TextAlignment = System.Windows.TextAlignment.Center + }; + ToolTip = new ToolTip() { Content = TTContent }; + Foreground = ColorUtil.makeBrush(Color.FromArgb(255, 0, 0, 255)); + this.Ref = Ref; + } + internal NameReference Ref; + + private void DebugLink_MouseLeave(object sender, MouseEventArgs e) { + Foreground = ColorUtil.makeBrush(Color.FromArgb(255, 0, 0, 255)); + } + + private void DebugLink_MouseEnter(object sender, MouseEventArgs e) { + Foreground = ColorUtil.makeBrush(Color.FromArgb(175, 70, 70, 255)); + } + + private void DebugLink_DebugReference_MouseDown(object sender, MouseButtonEventArgs e) { + ProjectUtils.navigateTo(Ref.DebugLoc); + } + } + + internal sealed class RemarkAdornment : TextBlock { + static Color[] AvailableColorList = new Color[] { + Color.FromRgb(0,0,255), + Color.FromRgb(238, 118, 0), + Color.FromRgb(148,0,211), + Color.FromRgb(40,79,79), + Color.FromRgb(0,0,139), + Color.FromRgb(210,105,30), + Color.FromRgb(0,255,255), + Color.FromRgb(255,255,0), + Color.FromRgb(119,136,153), + Color.FromRgb(75,0,130), + Color.FromRgb(144,238,144) + }; + internal RemarkAdornment(RemarkAdornmentTag RT) { + Foreground = (Brush)FindResource(VsBrushes.SideBarTextKey); + Background = (Brush)FindResource(VsBrushes.SideBarBackgroundKey); + + // Create an instance for each config. + for(int i = 0, e = RT.DataList.Count, back = e - 1; i < e; ++i) { + var Data = RT.DataList[i]; + Inlines.Add(new RemarkAdornmentInstance(Data, AvailableColorList[Data.ConfigID])); + if(i != back) + Inlines.Add(new TextBlock() { Text = " " }); + } + } + } + internal sealed class RemarkAdornmentInstance : Border { + Popup RemarkList = null; + internal RemarkVisibilityData RTag = null; + internal RemarkAdornmentInstance(RemarkVisibilityData RT, Color BaseColor) { + TextBlock MainContentBlock = new TextBlock(); + Child = MainContentBlock; + BorderThickness = new System.Windows.Thickness(0.2); + BorderBrush = (Brush)FindResource(VsBrushes.SideBarTextKey); + + // Get the max hotness from remarks in our list as well as the types of remarks. + int TypesOfRemarks = 0; + uint MaxListHotness = 0; + foreach(Remark ListR in RT.RemarkList) { + if (ListR.Hotness > MaxListHotness) + MaxListHotness = ListR.Hotness; + TypesOfRemarks |= 1 << (int)ListR.Type; + } + + // Set up the remark types. + if ((TypesOfRemarks & 1 << (int)RemarkType.Passed) != 0) + MainContentBlock.Inlines.Add(new TextBlock() { Text = " ", Background = ColorUtil.makeBrush(Remark.PassedColor)}); + if ((TypesOfRemarks & 1 << (int)RemarkType.Missed) != 0) + MainContentBlock.Inlines.Add(new TextBlock() { Text = " ", Background = ColorUtil.makeBrush(Remark.MissedColor) }); + if ((TypesOfRemarks & 1 << (int)RemarkType.Analysis) != 0 + || (TypesOfRemarks & 1 << (int)RemarkType.AnalysisAliasing) != 0 + || (TypesOfRemarks & 1 << (int)RemarkType.AnalysisFPCommute) != 0) + MainContentBlock.Inlines.Add(new TextBlock() { Text = " ", Background = ColorUtil.makeBrush(Remark.AnalysisColor) }); + + TextBlock RemarkText = new TextBlock(); + MainContentBlock.Inlines.Add(RemarkText); + + // Set an opacity based upon the hotness of a remark. + uint MaxHotness = RT.Config.MaxHotness; + byte Alpha = 75; + if (MaxHotness != 0) { + double HotnessPctg = 100 * ((double)MaxListHotness / (double)MaxHotness); + RemarkText.Text = ((int)HotnessPctg).ToString(CultureInfo.CurrentCulture) + "% | "; + Alpha += (byte)HotnessPctg; + } + + // Setup internals + // Visual. + RemarkText.Text += ' ' + RT.Config.ToString() + ' '; + RemarkText.Foreground = BorderBrush; + RemarkText.Background = ColorUtil.makeBrush(Color.FromArgb(Alpha, BaseColor.R, BaseColor.G, BaseColor.B)); + // Remark list popup. + MouseEnter += RemarkList_Show; + // Remark list destroy. + MouseLeave += RemarkList_LostMouseFocus; + RTag = RT; + } + + private void RemarkList_Show(object sender, System.Windows.Input.MouseEventArgs evt) { + if (RemarkList != null) + return; + + // Get our brush colors. + Brush VsBackground = ColorUtil.makeBrush(VSColorTheme.GetThemedColor(EnvironmentColors.ToolTipBrushKey)); + Brush WhiteBrush = (Brush)FindResource(VsBrushes.SideBarTextKey); + + // Populate a grid for the remarks. + UniformGrid UG = new UniformGrid { + Columns = 1, + Background = VsBackground + }; + + /// Border for the grid. + Border UGBorder = new Border(); + UGBorder.MouseLeave += RemarkList_LostMouseFocus; + UGBorder.BorderBrush = VsBackground; + UGBorder.BorderThickness = new System.Windows.Thickness(1); + UGBorder.Child = UG; + + // Event to destroy the remark list when VS loses focus. + System.Windows.Application.Current.Deactivated += RemarkList_LostFocus; + + // Create the main popup window. + Popup P = new Popup { + StaysOpen = true, + PlacementTarget = this, + Placement = PlacementMode.MousePoint, + Child = UGBorder, + IsOpen = true + }; + RemarkList = P; + + // Add a header that will act as a return to the remark location. + Border HeaderBorder = new Border { + BorderBrush = VsBackground, + BorderThickness = new System.Windows.Thickness(1) + }; + + Remark FirstRemark = RTag.RemarkList[0]; + NameReference RemarkListRef + = new NameReference(RTag.Config.ToString() + " Remark List:", FirstRemark.DebugLoc); + DebugTextBlock RemarkListBlock = new DebugTextBlock(RemarkListRef); + RemarkListBlock.TextAlignment = System.Windows.TextAlignment.Center; + HeaderBorder.Child = RemarkListBlock; + UG.Children.Add(HeaderBorder); + + // Construct the valid remarks. + foreach (Remark R in RTag.RemarkList) { + Brush RBrush = ColorUtil.makeBrush(R.Color); + + // Create a border for this cell. + Border RBorder = new Border { + BorderBrush = VsBackground, + BorderThickness = new System.Windows.Thickness(1) + }; + + // Setup main textbox ui. + TextBlock TB = new TextBlock { + Background = RBrush, + Foreground = WhiteBrush + }; + RBorder.Child = TB; + UG.Children.Add(RBorder); + + // Add in the source functions + TextBlock FunctionListBox = new TextBlock(); + FunctionListBox.Inlines.Add(new DebugTextBlock(R.SourceFunctions.ElementAt(0))); + foreach(FunctionReference FR in R.SourceFunctions.Skip(1)) { + FunctionListBox.Inlines.Add(new TextBlock() { Text = ", ", Foreground = WhiteBrush }); + FunctionListBox.Inlines.Add(new DebugTextBlock(FR)); + } + TB.Inlines.Add(FunctionListBox); + + // Pass details. + TB.Inlines.Add(new TextBlock() { + Text = " : " + R.Pass + " - " + R.Name + " : ", + Foreground = WhiteBrush + }); + + // Argumens. + foreach (object Arg in R.Args) { + // Name reference. + if (typeof(NameReference).IsAssignableFrom(Arg.GetType())) + TB.Inlines.Add(new DebugTextBlock((NameReference)Arg)); + // String. + else if (Arg.GetType() == typeof(string)) + TB.Inlines.Add(new TextBlock() { Foreground = WhiteBrush, Text = (string)Arg }); + // List of name references. + else { + TB.Inlines.Add(new TextBlock() { Text = "{", Foreground = WhiteBrush }); + HashSet ArgList = (HashSet)Arg; + TB.Inlines.Add(new DebugTextBlock((NameReference)ArgList.ElementAt(0))); + foreach(var NRArg in ArgList.Skip(1)) { + TB.Inlines.Add(new TextBlock() { Text = ", ", Foreground = WhiteBrush }); + TB.Inlines.Add(new DebugTextBlock(NRArg as NameReference)); + } + TB.Inlines.Add(new TextBlock() { Text = "}", Foreground = WhiteBrush }); + } + } + TB.Inlines.Add(new TextBlock() { Text = ".", Foreground = WhiteBrush }); + } + } + + /// Utilities + private void destroyRemarkList() { + System.Windows.Application.Current.Deactivated -= RemarkList_LostFocus; + RemarkList.IsOpen = false; + RemarkList = null; + } + private static bool isParent(System.Windows.DependencyObject C, System.Windows.FrameworkElement P) { + if (C == P) + return true; + System.Windows.DependencyObject CPar = VisualTreeHelper.GetParent(C); + if (CPar == null && C.GetType().IsSubclassOf(typeof(System.Windows.FrameworkElement))) + CPar = (C as System.Windows.FrameworkElement).Parent; + return CPar != null && (CPar == P || isParent(CPar, P)); + } + + private void RemarkList_LostFocus(object sender, System.EventArgs e) => destroyRemarkList(); + private void RemarkList_LostMouseFocus(object sender, MouseEventArgs e) { + if (RemarkList == null) + return; + var Ele = Mouse.DirectlyOver as System.Windows.UIElement; + if (Ele == null || isParent(Ele, this) || isParent(Ele, RemarkList)) + return; + destroyRemarkList(); + } + } +} Index: remark-viewer-vs/RemarkTagger/Tagging/RemarkAdornmentTagger.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Tagging/RemarkAdornmentTagger.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using RemarkInterface; +using System.Linq; + +namespace RemarkPackage { + internal sealed class RemarkAdornmentTagger + : IntraTextAdornmentTagger { + internal static ITagger GetTagger(IWpfTextView View, Lazy> RemarkTagger) { + return View.Properties.GetOrCreateSingletonProperty(() => new RemarkAdornmentTagger(View, RemarkTagger.Value)); + } + private ITagAggregator RemarkTagger; + + private RemarkAdornmentTagger(IWpfTextView View, ITagAggregator RemarkTagger) + : base(View) { + this.RemarkTagger = RemarkTagger; + + // Register our remark event handler to catch cache invalidations. + IRemarkService ORS = ServiceLocator.getGlobalService(); + ORS.registerEventHandler(RemarkEventHandler); + + // Check the text document to listen to dirty events. + ITextDocument TD = View.TextBuffer.GetTextDocument(); + if (TD == null) + return; + RemarkSettings RS = ORS.getOptRemarkManager().Settings; + + // Handler for dirty state when we don't allow out of synch remarks. + TD.DirtyStateChanged += (sender, e) => { + if (!RS.AllowOutOfDateRemarks) + ReloadCache(); + }; + TD.FileActionOccurred += (sender, e) => { + if (!RS.AllowOutOfDateRemarks && e.FileActionType == FileActionTypes.ContentLoadedFromDisk) + ReloadCache(); + }; + } + private void Dispose() { + IRemarkService ORS = ServiceLocator.getGlobalService(); + ORS.unRegisterEventHandler(RemarkEventHandler); + RemarkTagger.Dispose(); + View.Properties.RemoveProperty(typeof(RemarkAdornmentTagger)); + } + private void RemarkEventHandler(object sender, RemarkEvent Evt) { + if (Evt.Code != EvtCode.CacheInvalidated) + return; + // Check to see if we have a list of files that were actually invalidate. + // If we do then we can avoid invalidating a file that hasn't changed. + if (Evt.Data is IEnumerable Invalidated) { + string FileName = View.TextBuffer.GetTextDocument()?.FilePath; + if (FileName == null || !Invalidated.Any(RF => RF.FullName == FileName)) + return; + } + + // Invoke an update to the UI. + View.VisualElement.Dispatcher.BeginInvoke(new Action(ReloadCache)); + } + + // To produce adornments that don't obscure the text, the adornment tags + // should have zero length spans. Overriding this method allows control + // over the tag spans. + protected override IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection Spans) { + if (Spans.Count == 0) + yield break; + + ITextSnapshot Snapshot = Spans[0].Snapshot; + foreach (IMappingTagSpan DataTagSpan in RemarkTagger.GetTags(Spans)) { + NormalizedSnapshotSpanCollection RemarkTagSpans = DataTagSpan.Span.GetSpans(Snapshot); + + // Ignore data tags that are split by projection. + // This is theoretically possible but unlikely in current scenarios. + if (RemarkTagSpans.Count != 1) + continue; + SnapshotSpan AdornmentSpan = new SnapshotSpan(RemarkTagSpans[0].Start, 0); + yield return Tuple.Create(AdornmentSpan, (PositionAffinity?)PositionAffinity.Successor, DataTagSpan.Tag); + } + } + + private void ReloadCache() { + AdornmentCache.Clear(); + RaiseTagsChanged(new SnapshotSpan(Snapshot, 0, Snapshot.Length)); + } + + protected override RemarkAdornment CreateAdornment(RemarkAdornmentTag dataTag, SnapshotSpan span) { + return new RemarkAdornment(dataTag); + } + } +} Index: remark-viewer-vs/RemarkTagger/Tagging/RemarkAdornmentTaggerProvider.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Tagging/RemarkAdornmentTaggerProvider.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace RemarkPackage { + [Export(typeof(IViewTaggerProvider))] + [ContentType("C/C++")] + [ContentType("projection")] + [TagType(typeof(IntraTextAdornmentTag))] + internal sealed class RemarkAdornmentToggleTaggerProvider : IViewTaggerProvider { +#pragma warning disable 649 // "field never assigned to" -- field is set by MEF. + [Import] + internal IBufferTagAggregatorFactoryService BufferTagAggregatorFactoryService; +#pragma warning restore 649 + + public ITagger CreateTagger(ITextView TextView, ITextBuffer Buffer) where T : ITag { + if (Buffer == null || Buffer != TextView?.TextBuffer) + return null; + return RemarkAdornmentTagger.GetTagger((IWpfTextView)TextView, + new Lazy>( + () => BufferTagAggregatorFactoryService.CreateTagAggregator(TextView.TextBuffer))) + as ITagger; + } + } +} Index: remark-viewer-vs/RemarkTagger/Tagging/RemarkTag.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Tagging/RemarkTag.cs @@ -0,0 +1,10 @@ +using Microsoft.VisualStudio.Text.Tagging; +using System.Collections.Generic; +using RemarkInterface; + +namespace RemarkPackage { + internal class RemarkAdornmentTag : ITag { + internal RemarkAdornmentTag(List DL) => DataList = DL; + internal readonly List DataList; + } +} Index: remark-viewer-vs/RemarkTagger/Tagging/RemarkTagger.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Tagging/RemarkTagger.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; +using RemarkInterface; + +namespace RemarkPackage { + internal class RemarkTagger : ITagger { + ITextDocument TaggerDoc = null; + public RemarkTagger(ITextBuffer Buffer, ITextDocument Doc) { + Buffer.Changed += (sender, args) => HandleBufferChanged(args); + TaggerDoc = Doc; + } + + #region ITagger implementation + public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { + // Get our global remark service. + IRemarkService ORS = ServiceLocator.getGlobalService(); + RemarkManager RM = ORS?.getOptRemarkManager(); + + // Not enabled. + if (RM == null || !RM.Settings.IsEnabled) + yield return null; + + // Check to see if it was modified. + if (!RM.Settings.AllowOutOfDateRemarks && TaggerDoc.IsDirty) + yield return null; + + // Get the remarks for each line in the span. + ITextSnapshot snapshot = spans[0].Snapshot; + foreach (var VisibleRemarks in RM.getVisibleRemarks(TaggerDoc.FilePath, GetIntersectingLines(spans))) { + uint LineNumber = VisibleRemarks[0].RemarkList[0].DebugLoc.Line - 1; + ITextSnapshotLine TextLine = snapshot.GetLineFromLineNumber((int)LineNumber); + yield return new TagSpan(TextLine.Extent, new RemarkAdornmentTag(VisibleRemarks)); + } + } + public event EventHandler TagsChanged; + + #endregion + + IEnumerable GetIntersectingLines(NormalizedSnapshotSpanCollection Spans) { + if (Spans.Count == 0) + yield break; + int LastVisitedLineNumber = -1; + ITextSnapshot Snapshot = Spans[0].Snapshot; + foreach (var Span in Spans) { + int FirstLine = Snapshot.GetLineNumberFromPosition(Span.Start); + int LastLine = Snapshot.GetLineNumberFromPosition(Span.End); + for (int i = Math.Max(LastVisitedLineNumber, FirstLine); i <= LastLine; i++) + yield return i + 1; + LastVisitedLineNumber = LastLine; + } + } + + protected virtual void HandleBufferChanged(TextContentChangedEventArgs Args) { + if (Args.Changes.Count == 0 || TagsChanged == null) + return; + + // Combine all changes into a single span so that + // the ITagger<>.TagsChanged event can be raised just once for a compound edit + // with many parts. + ITextSnapshot Snapshot = Args.After; + int Start = Args.Changes[0].NewPosition; + int End = Args.Changes[Args.Changes.Count - 1].NewEnd; + SnapshotSpan TotalAffectedSpan = new SnapshotSpan( + Snapshot.GetLineFromPosition(Start).Start, + Snapshot.GetLineFromPosition(End).End); + TagsChanged(this, new SnapshotSpanEventArgs(TotalAffectedSpan)); + } + } +} Index: remark-viewer-vs/RemarkTagger/Tagging/RemarkTaggerProvider.cs =================================================================== --- /dev/null +++ remark-viewer-vs/RemarkTagger/Tagging/RemarkTaggerProvider.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace RemarkPackage { + [Export(typeof(ITaggerProvider))] + [ContentType("C/C++")] + [TagType(typeof(RemarkAdornmentTag))] + internal sealed class RemarkTaggerProvider : ITaggerProvider { + public ITagger CreateTagger(ITextBuffer Buffer) where T : ITag { + if (Buffer == null) + return null; + var TextDoc = Buffer.GetTextDocument(); + return Buffer.Properties.GetOrCreateSingletonProperty(() => new RemarkTagger(Buffer, TextDoc)) as ITagger; + } + } +} Index: remark-viewer-vs/demangler/demangle.cpp =================================================================== --- /dev/null +++ remark-viewer-vs/demangler/demangle.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +extern "C" { +char* __cxa_demangle(const char* mangled_name, + char* buf, + size_t* n, + int* status); + +typedef void* (*malloc_func_t)(size_t); +typedef void (*free_func_t)(void*); +char* __unDName(char* buffer, + const char* mangled, + int buflen, + malloc_func_t memget, + free_func_t memfree, + unsigned short int flags); +} + +static bool starts_with(const char* s, const char* prefix) { + return strncmp(s, prefix, strlen(prefix)) == 0; +} + +static char *allocateAndCopyStr(const char *Src) { + char* Ret = (char*)::CoTaskMemAlloc(strlen(Src) + 1); + return strcpy(Ret, Src); +} + +extern "C" { + __declspec(dllexport) char *__cdecl get_demangled(const char* s) { + char *Res; + const char* cxa_in = s; + if (starts_with(s, "__Z") || starts_with(s, "____Z")) + cxa_in += 1; + if (char* itanium = __cxa_demangle(cxa_in, NULL, NULL, NULL)) { + Res = allocateAndCopyStr(itanium); + free(itanium); + } + else if (char* ms = __unDName(NULL, s, 0, &malloc, &free, 0)) { + Res = allocateAndCopyStr(ms); + free(ms); + } + else + Res = allocateAndCopyStr(s); + return Res; + } +} Index: remark-viewer-vs/demangler/demangler.vcxproj =================================================================== --- /dev/null +++ remark-viewer-vs/demangler/demangler.vcxproj @@ -0,0 +1,89 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + {D539632C-BFBC-4191-ABA0-A98F8F172A01} + demumble_demangler + 10.0.16299.0 + Demangler + + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions) + + + echo F|xcopy /Y /Q "$(TargetDir)Demangler.dll" "$(SolutionDir)\RemarkInterface\Resources\Demangler.dll" + Copy the demangler dll. + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions) + + + true + true + true + + + echo F|xcopy /Y /Q "$(TargetDir)Demangler.dll" "$(SolutionDir)\RemarkInterface\Resources\Demangler.dll" + + + Copy the demangler dll. + + + + + + \ No newline at end of file Index: remark-viewer-vs/demangler/demangler.vcxproj.filters =================================================================== --- /dev/null +++ remark-viewer-vs/demangler/demangler.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + {1af83b7b-ec83-4ed7-8d85-27fbefd0f3e3} + + + + + libcxxabi + + + + + + libcxxabi + + + \ No newline at end of file Index: remark-viewer-vs/demangler/libcxxabi/LICENSE.txt =================================================================== --- /dev/null +++ remark-viewer-vs/demangler/libcxxabi/LICENSE.txt @@ -0,0 +1,37 @@ +http://libcxxabi.llvm.org/ +All of the code in libc++abi is dual licensed under the MIT license and the +UIUC License (a BSD-like license). + +http://llvm.org/docs/DeveloperPolicy.html#license + + +The University of Illinois/NCSA Open Source License (NCSA) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. +Neither the names of , nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + + + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. Index: remark-viewer-vs/demangler/libcxxabi/cxa_demangle.cpp =================================================================== --- /dev/null +++ remark-viewer-vs/demangler/libcxxabi/cxa_demangle.cpp @@ -0,0 +1,4992 @@ +//===-------------------------- cxa_demangle.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LIBCPP_EXTERN_TEMPLATE(...) +#define _LIBCPP_NO_EXCEPTIONS + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +// snprintf is implemented in VS 2015 +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif + +namespace __cxxabiv1 +{ + +namespace +{ + +enum +{ + unknown_error = -4, + invalid_args = -3, + invalid_mangled_name, + memory_alloc_failure, + success +}; + +template + const char* parse_type(const char* first, const char* last, C& db); +template + const char* parse_encoding(const char* first, const char* last, C& db); +template + const char* parse_name(const char* first, const char* last, C& db, + bool* ends_with_template_args = 0); +template + const char* parse_expression(const char* first, const char* last, C& db); +template + const char* parse_template_args(const char* first, const char* last, C& db); +template + const char* parse_operator_name(const char* first, const char* last, C& db); +template + const char* parse_unqualified_name(const char* first, const char* last, C& db); +template + const char* parse_decltype(const char* first, const char* last, C& db); + +template +void +print_stack(const C& db) +{ + fprintf(stderr, "---------\n"); + fprintf(stderr, "names:\n"); + for (auto& s : db.names) + fprintf(stderr, "{%s#%s}\n", s.first.c_str(), s.second.c_str()); + int i = -1; + fprintf(stderr, "subs:\n"); + for (auto& v : db.subs) + { + if (i >= 0) + fprintf(stderr, "S%i_ = {", i); + else + fprintf(stderr, "S_ = {"); + for (auto& s : v) + fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); + fprintf(stderr, "}\n"); + ++i; + } + fprintf(stderr, "template_param:\n"); + for (auto& t : db.template_param) + { + fprintf(stderr, "--\n"); + i = -1; + for (auto& v : t) + { + if (i >= 0) + fprintf(stderr, "T%i_ = {", i); + else + fprintf(stderr, "T_ = {"); + for (auto& s : v) + fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); + fprintf(stderr, "}\n"); + ++i; + } + } + fprintf(stderr, "---------\n\n"); +} + +template +void +print_state(const char* msg, const char* first, const char* last, const C& db) +{ + fprintf(stderr, "%s: ", msg); + for (; first != last; ++first) + fprintf(stderr, "%c", *first); + fprintf(stderr, "\n"); + print_stack(db); +} + +// ::= [n] + +const char* +parse_number(const char* first, const char* last) +{ + if (first != last) + { + const char* t = first; + if (*t == 'n') + ++t; + if (t != last) + { + if (*t == '0') + { + first = t+1; + } + else if ('1' <= *t && *t <= '9') + { + first = t+1; + while (first != last && std::isdigit(*first)) + ++first; + } + } + } + return first; +} + +template +struct float_data; + +template <> +struct float_data +{ + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static constexpr const char* spec = "%af"; +}; + +constexpr const char* float_data::spec; + +template <> +struct float_data +{ + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static constexpr const char* spec = "%a"; +}; + +constexpr const char* float_data::spec; + +template <> +struct float_data +{ +#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ + defined(__wasm__) + static const size_t mangled_size = 32; +#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) + static const size_t mangled_size = 16; +#else + static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms +#endif + static const size_t max_demangled_size = 40; + static constexpr const char* spec = "%LaL"; +}; + +constexpr const char* float_data::spec; + +template +const char* +parse_floating_number(const char* first, const char* last, C& db) +{ + const size_t N = float_data::mangled_size; + if (static_cast(last - first) > N) + { + last = first + N; + union + { + Float value; + char buf[sizeof(Float)]; + }; + const char* t = first; + char* e = buf; + for (; t != last; ++t, ++e) + { + if (!isxdigit(*t)) + return first; + unsigned d1 = isdigit(*t) ? static_cast(*t - '0') : + static_cast(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast(*t - '0') : + static_cast(*t - 'a' + 10); + *e = static_cast((d1 << 4) + d0); + } + if (*t == 'E') + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[float_data::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), float_data::spec, value); + if (static_cast(n) >= sizeof(num)) + return first; + db.names.push_back(typename C::String(num, static_cast(n))); + first = t+1; + } + } + return first; +} + +// ::= + +template +const char* +parse_source_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + char c = *first; + if (isdigit(c) && first+1 != last) + { + const char* t = first+1; + size_t n = static_cast(c - '0'); + for (c = *t; isdigit(c); c = *t) + { + n = n * 10 + static_cast(c - '0'); + if (++t == last) + return first; + } + if (static_cast(last - t) >= n) + { + typename C::String r(t, n); + if (r.substr(0, 10) == "_GLOBAL__N") + db.names.push_back("(anonymous namespace)"); + else + db.names.push_back(std::move(r)); + first = t + n; + } + } + } + return first; +} + +// ::= S _ +// ::= S_ +// ::= Sa # ::std::allocator +// ::= Sb # ::std::basic_string +// ::= Ss # ::std::basic_string < char, +// ::std::char_traits, +// ::std::allocator > +// ::= Si # ::std::basic_istream > +// ::= So # ::std::basic_ostream > +// ::= Sd # ::std::basic_iostream > + +template +const char* +parse_substitution(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'S') + { + switch (first[1]) + { + case 'a': + db.names.push_back("std::allocator"); + first += 2; + break; + case 'b': + db.names.push_back("std::basic_string"); + first += 2; + break; + case 's': + db.names.push_back("std::string"); + first += 2; + break; + case 'i': + db.names.push_back("std::istream"); + first += 2; + break; + case 'o': + db.names.push_back("std::ostream"); + first += 2; + break; + case 'd': + db.names.push_back("std::iostream"); + first += 2; + break; + case '_': + if (!db.subs.empty()) + { + for (const auto& n : db.subs.front()) + db.names.push_back(n); + first += 2; + } + break; + default: + if (std::isdigit(first[1]) || std::isupper(first[1])) + { + size_t sub = 0; + const char* t = first+1; + if (std::isdigit(*t)) + sub = static_cast(*t - '0'); + else + sub = static_cast(*t - 'A') + 10; + for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t) + { + sub *= 36; + if (std::isdigit(*t)) + sub += static_cast(*t - '0'); + else + sub += static_cast(*t - 'A') + 10; + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < db.subs.size()) + { + for (const auto& n : db.subs[sub]) + db.names.push_back(n); + first = t+1; + } + } + break; + } + } + } + return first; +} + +// ::= v # void +// ::= w # wchar_t +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= n # __int128 +// ::= o # unsigned __int128 +// ::= f # float +// ::= d # double +// ::= e # long double, __float80 +// ::= g # __float128 +// ::= z # ellipsis +// ::= Dd # IEEE 754r decimal floating point (64 bits) +// ::= De # IEEE 754r decimal floating point (128 bits) +// ::= Df # IEEE 754r decimal floating point (32 bits) +// ::= Dh # IEEE 754r half-precision floating point (16 bits) +// ::= Di # char32_t +// ::= Ds # char16_t +// ::= Da # auto (in dependent new-expressions) +// ::= Dc # decltype(auto) +// ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) +// ::= u # vendor extended type + +template +const char* +parse_builtin_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'v': + db.names.push_back("void"); + ++first; + break; + case 'w': + db.names.push_back("wchar_t"); + ++first; + break; + case 'b': + db.names.push_back("bool"); + ++first; + break; + case 'c': + db.names.push_back("char"); + ++first; + break; + case 'a': + db.names.push_back("signed char"); + ++first; + break; + case 'h': + db.names.push_back("unsigned char"); + ++first; + break; + case 's': + db.names.push_back("short"); + ++first; + break; + case 't': + db.names.push_back("unsigned short"); + ++first; + break; + case 'i': + db.names.push_back("int"); + ++first; + break; + case 'j': + db.names.push_back("unsigned int"); + ++first; + break; + case 'l': + db.names.push_back("long"); + ++first; + break; + case 'm': + db.names.push_back("unsigned long"); + ++first; + break; + case 'x': + db.names.push_back("long long"); + ++first; + break; + case 'y': + db.names.push_back("unsigned long long"); + ++first; + break; + case 'n': + db.names.push_back("__int128"); + ++first; + break; + case 'o': + db.names.push_back("unsigned __int128"); + ++first; + break; + case 'f': + db.names.push_back("float"); + ++first; + break; + case 'd': + db.names.push_back("double"); + ++first; + break; + case 'e': + db.names.push_back("long double"); + ++first; + break; + case 'g': + db.names.push_back("__float128"); + ++first; + break; + case 'z': + db.names.push_back("..."); + ++first; + break; + case 'u': + { + const char*t = parse_source_name(first+1, last, db); + if (t != first+1) + first = t; + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'd': + db.names.push_back("decimal64"); + first += 2; + break; + case 'e': + db.names.push_back("decimal128"); + first += 2; + break; + case 'f': + db.names.push_back("decimal32"); + first += 2; + break; + case 'h': + db.names.push_back("decimal16"); + first += 2; + break; + case 'i': + db.names.push_back("char32_t"); + first += 2; + break; + case 's': + db.names.push_back("char16_t"); + first += 2; + break; + case 'a': + db.names.push_back("auto"); + first += 2; + break; + case 'c': + db.names.push_back("decltype(auto)"); + first += 2; + break; + case 'n': + db.names.push_back("std::nullptr_t"); + first += 2; + break; + } + } + break; + } + } + return first; +} + +// ::= [r] [V] [K] + +const char* +parse_cv_qualifiers(const char* first, const char* last, unsigned& cv) +{ + cv = 0; + if (first != last) + { + if (*first == 'r') + { + cv |= 4; + ++first; + } + if (*first == 'V') + { + cv |= 2; + ++first; + } + if (*first == 'K') + { + cv |= 1; + ++first; + } + } + return first; +} + +// ::= T_ # first template parameter +// ::= T _ + +template +const char* +parse_template_param(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'T') + { + if (first[1] == '_') + { + if (db.template_param.empty()) + return first; + if (!db.template_param.back().empty()) + { + for (auto& t : db.template_param.back().front()) + db.names.push_back(t); + first += 2; + } + else + { + db.names.push_back("T_"); + first += 2; + db.fix_forward_references = true; + } + } + else if (isdigit(first[1])) + { + const char* t = first+1; + size_t sub = static_cast(*t - '0'); + for (++t; t != last && isdigit(*t); ++t) + { + sub *= 10; + sub += static_cast(*t - '0'); + } + if (t == last || *t != '_' || db.template_param.empty()) + return first; + ++sub; + if (sub < db.template_param.back().size()) + { + for (auto& temp : db.template_param.back()[sub]) + db.names.push_back(temp); + first = t+1; + } + else + { + db.names.push_back(typename C::String(first, t+1)); + first = t+1; + db.fix_forward_references = true; + } + } + } + } + return first; +} + +// cc # const_cast (expression) + +template +const char* +parse_const_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// dc # dynamic_cast (expression) + +template +const char* +parse_dynamic_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// rc # reinterpret_cast (expression) + +template +const char* +parse_reinterpret_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sc # static_cast (expression) + +template +const char* +parse_static_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sp # pack expansion + +template +const char* +parse_pack_expansion(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'p') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + first = t; + } + return first; +} + +// st # sizeof (a type) + +template +const char* +parse_sizeof_type_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sz # sizeof (a expression) + +template +const char* +parse_sizeof_expr_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sZ # size of a parameter pack + +template +const char* +parse_sizeof_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') + { + size_t k0 = db.names.size(); + const char* t = parse_template_param(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + typename C::String tmp("sizeof...("); + size_t k = k0; + if (k != k1) + { + tmp += db.names[k].move_full(); + for (++k; k != k1; ++k) + tmp += ", " + db.names[k].move_full(); + } + tmp += ")"; + for (; k1 != k0; --k1) + db.names.pop_back(); + db.names.push_back(std::move(tmp)); + first = t; + } + } + return first; +} + +// ::= fp _ # L == 0, first parameter +// ::= fp _ # L == 0, second and later parameters +// ::= fL p _ # L > 0, first parameter +// ::= fL p _ # L > 0, second and later parameters + +template +const char* +parse_function_param(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && *first == 'f') + { + if (first[1] == 'p') + { + unsigned cv; + const char* t = parse_cv_qualifiers(first+2, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + else if (first[1] == 'L') + { + unsigned cv; + const char* t0 = parse_number(first+2, last); + if (t0 != last && *t0 == 'p') + { + ++t0; + const char* t = parse_cv_qualifiers(t0, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + } + } + return first; +} + +// sZ # size of a function parameter pack + +template +const char* +parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f') + { + const char* t = parse_function_param(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// te # typeid (expression) +// ti # typeid (type) + +template +const char* +parse_typeid_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) + { + const char* t; + if (first[1] == 'e') + t = parse_expression(first+2, last, db); + else + t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "typeid(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// tw # throw expression + +template +const char* +parse_throw_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && first[1] == 'w') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "throw " + db.names.back().move_full(); + first = t; + } + } + return first; +} + +// ds # expr.*expr + +template +const char* +parse_dot_star_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 's') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += ".*" + expr; + first = t1; + } + } + } + return first; +} + +// ::= [ ] + +template +const char* +parse_simple_id(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_source_name(first, last, db); + if (t != first) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + first = t1; + } + else + first = t; + } + return first; +} + +// ::= +// ::= +// ::= + +template +const char* +parse_unresolved_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = first; + switch (*first) + { + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first && k1 == k0 + 1) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + else + { + for (; k1 != k0; --k1) + db.names.pop_back(); + } + break; + } + case 'D': + t = parse_decltype(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + break; + case 'S': + t = parse_substitution(first, last, db); + if (t != first) + first = t; + else + { + if (last - first > 2 && first[1] == 't') + { + t = parse_unqualified_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "std::"); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + } + } + return first; +} + +// ::= # e.g., ~T or ~decltype(f()) +// ::= # e.g., ~A<2*N> + +template +const char* +parse_destructor_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_unresolved_type(first, last, db); + if (t == first) + t = parse_simple_id(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "~"); + first = t; + } + } + return first; +} + +// ::= # unresolved name +// extension ::= # unresolved operator-function-id +// extension ::= # unresolved operator template-id +// ::= on # unresolved operator-function-id +// ::= on # unresolved operator template-id +// ::= dn # destructor or pseudo-destructor; +// # e.g. ~X or ~X + +template +const char* +parse_base_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n') + { + if (first[0] == 'o') + { + const char* t = parse_operator_name(first+2, last, db); + if (t != first+2) + { + first = parse_template_args(t, last, db); + if (first != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + { + const char* t = parse_destructor_name(first+2, last, db); + if (t != first+2) + first = t; + } + } + else + { + const char* t = parse_simple_id(first, last, db); + if (t == first) + { + t = parse_operator_name(first, last, db); + if (t != first) + { + first = parse_template_args(t, last, db); + if (first != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + first = t; + } + } + return first; +} + +// ::= + +template +const char* +parse_unresolved_qualifier_level(const char* first, const char* last, C& db) +{ + return parse_simple_id(first, last, db); +} + +// +// extension ::= srN [] * E +// ::= [gs] # x or (with "gs") ::x +// ::= [gs] sr + E +// # A::x, N::y, A::z; "gs" means leading "::" +// ::= sr # T::x / decltype(p)::x +// extension ::= sr +// # T::N::x /decltype(p)::N::x +// (ignored) ::= srN + E + +template +const char* +parse_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first > 2) + { + const char* t = first; + bool global = false; + if (t[0] == 'g' && t[1] == 's') + { + global = true; + t += 2; + } + const char* t2 = parse_base_unresolved_name(t, last, db); + if (t2 != t) + { + if (global) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + first = t2; + } + else if (last - t > 2 && t[0] == 's' && t[1] == 'r') + { + if (t[2] == 'N') + { + t += 3; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + if (t == last) + { + db.names.pop_back(); + return first; + } + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t += 2; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 != t) + { + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + } + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + if (global) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + } + } + } + return first; +} + +// dt # expr.name + +template +const char* +parse_dot_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_unresolved_name(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "." + name; + first = t1; + } + } + } + return first; +} + +// cl + E # call + +template +const char* +parse_call_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'c' && first[1] == 'l') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (t == last) + return first; + if (db.names.empty()) + return first; + db.names.back().first += db.names.back().second; + db.names.back().second = typename C::String(); + db.names.back().first.append("("); + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + if (!first_expr) + { + db.names.back().first.append(", "); + first_expr = false; + } + db.names.back().first.append(tmp); + } + t = t1; + } + ++t; + if (db.names.empty()) + return first; + db.names.back().first.append(")"); + first = t; + } + } + return first; +} + +// [gs] nw * _ E # new (expr-list) type +// [gs] nw * _ # new (expr-list) type (init) +// [gs] na * _ E # new[] (expr-list) type +// [gs] na * _ # new[] (expr-list) type (init) +// ::= pi * E # parenthesized initialization + +template +const char* +parse_new_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a')) + { + bool is_array = t[1] == 'a'; + t += 2; + if (t == last) + return first; + bool has_expr_list = false; + bool first_expr = true; + while (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + has_expr_list = true; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + ++t; + const char* t1 = parse_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + bool has_init = false; + if (last - t >= 3 && t[0] == 'p' && t[1] == 'i') + { + t += 2; + has_init = true; + first_expr = true; + while (*t != 'E') + { + t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + if (*t != 'E') + return first; + typename C::String init_list; + if (has_init) + { + if (db.names.empty()) + return first; + init_list = db.names.back().move_full(); + db.names.pop_back(); + } + if (db.names.empty()) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + typename C::String expr_list; + if (has_expr_list) + { + if (db.names.empty()) + return first; + expr_list = db.names.back().move_full(); + db.names.pop_back(); + } + typename C::String r; + if (parsed_gs) + r = "::"; + if (is_array) + r += "[] "; + else + r += " "; + if (has_expr_list) + r += "(" + expr_list + ") "; + r += type; + if (has_init) + r += " (" + init_list + ")"; + db.names.push_back(std::move(r)); + first = t+1; + } + } + return first; +} + +// cv # conversion with one argument +// cv _ * E # conversion with a different number of arguments + +template +const char* +parse_conversion_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'v') + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2 && t != last) + { + if (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t) + return first; + t = t1; + } + else + { + ++t; + if (t == last) + return first; + if (*t == 'E') + db.names.emplace_back(); + else + { + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + ++t; + } + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; + first = t; + } + } + return first; +} + +// pt # expr->name + +template +const char* +parse_arrow_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'p' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "->"; + db.names.back().first += tmp; + first = t1; + } + } + } + return first; +} + +// ::= R # & ref-qualifier +// ::= O # && ref-qualifier + +// ::= F [Y] [] E + +template +const char* +parse_function_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'F') + { + const char* t = first+1; + if (t != last) + { + if (*t == 'Y') + { + /* extern "C" */ + if (++t == last) + return first; + } + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + t = t1; + typename C::String sig("("); + int ref_qual = 0; + while (true) + { + if (t == last) + { + db.names.pop_back(); + return first; + } + if (*t == 'E') + { + ++t; + break; + } + if (*t == 'v') + { + ++t; + continue; + } + if (*t == 'R' && t+1 != last && t[1] == 'E') + { + ref_qual = 1; + ++t; + continue; + } + if (*t == 'O' && t+1 != last && t[1] == 'E') + { + ref_qual = 2; + ++t; + continue; + } + size_t k0 = db.names.size(); + t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 == t || t1 == last) + return first; + for (size_t k = k0; k < k1; ++k) + { + if (sig.size() > 1) + sig += ", "; + sig += db.names[k].move_full(); + } + for (size_t k = k0; k < k1; ++k) + db.names.pop_back(); + t = t1; + } + sig += ")"; + switch (ref_qual) + { + case 1: + sig += " &"; + break; + case 2: + sig += " &&"; + break; + } + if (db.names.empty()) + return first; + db.names.back().first += " "; + db.names.back().second.insert(0, sig); + first = t; + } + } + } + return first; +} + +// ::= M + +template +const char* +parse_pointer_to_member_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'M') + { + const char* t = parse_type(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto func = std::move(db.names.back()); + db.names.pop_back(); + auto class_type = std::move(db.names.back()); + if (!func.second.empty() && func.second.front() == '(') + { + db.names.back().first = std::move(func.first) + "(" + class_type.move_full() + "::*"; + db.names.back().second = ")" + std::move(func.second); + } + else + { + db.names.back().first = std::move(func.first) + " " + class_type.move_full() + "::*"; + db.names.back().second = std::move(func.second); + } + first = t2; + } + } + } + return first; +} + +// ::= A _ +// ::= A [] _ + +template +const char* +parse_array_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'A' && first+1 != last) + { + if (first[1] == '_') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " []"); + first = t; + } + } + else if ('1' <= first[1] && first[1] <= '9') + { + const char* t = parse_number(first+1, last); + if (t != last && *t == '_') + { + const char* t2 = parse_type(t+1, last, db); + if (t2 != t+1) + { + if (db.names.empty()) + return first; + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]"); + first = t2; + } + } + } + else + { + const char* t = parse_expression(first+1, last, db); + if (t != first+1 && t != last && *t == '_') + { + const char* t2 = parse_type(++t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto type = std::move(db.names.back()); + db.names.pop_back(); + auto expr = std::move(db.names.back()); + db.names.back().first = std::move(type.first); + if (type.second.substr(0, 2) == " [") + type.second.erase(0, 1); + db.names.back().second = " [" + expr.move_full() + "]" + std::move(type.second); + first = t2; + } + } + } + } + return first; +} + +// ::= Dt E # decltype of an id-expression or class member access (C++0x) +// ::= DT E # decltype of an expression (C++0x) + +template +const char* +parse_decltype(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'D') + { + switch (first[1]) + { + case 't': + case 'T': + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + { + if (db.names.empty()) + return first; + db.names.back() = "decltype(" + db.names.back().move_full() + ")"; + first = t+1; + } + } + break; + } + } + return first; +} + +// extension: +// ::= Dv _ +// +// ::= Dv [] _ +// ::= +// ::= p # AltiVec vector pixel + +template +const char* +parse_vector_type(const char* first, const char* last, C& db) +{ + if (last - first > 3 && first[0] == 'D' && first[1] == 'v') + { + if ('1' <= first[2] && first[2] <= '9') + { + const char* t = parse_number(first+2, last); + if (t == last || *t != '_') + return first; + const char* num = first + 2; + size_t sz = static_cast(t - num); + if (++t != last) + { + if (*t != 'p') + { + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + if (db.names.empty()) + return first; + db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; + first = t1; + } + } + else + { + ++t; + db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]"); + first = t; + } + } + } + else + { + typename C::String num; + const char* t1 = first+2; + if (*t1 != '_') + { + const char* t = parse_expression(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + num = db.names.back().move_full(); + db.names.pop_back(); + t1 = t; + } + } + if (t1 != last && *t1 == '_' && ++t1 != last) + { + const char* t = parse_type(t1, last, db); + if (t != t1) + { + if (db.names.empty()) + return first; + db.names.back().first += " vector[" + num + "]"; + first = t; + } + } + } + } + return first; +} + +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= +// ::= P # pointer-to +// ::= R # reference-to +// ::= O # rvalue reference-to (C++0x) +// ::= C # complex pair (C 2000) +// ::= G # imaginary (C 2000) +// ::= Dp # pack expansion (C++0x) +// ::= U # vendor extended type qualifier +// extension := U # objc-type +// extension := # starts with Dv + +// ::= objcproto # k0 = 9 + + k1 +// := # PU<11+>objcproto 11objc_object 11objc_object -> id + +template +const char* +parse_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'r': + case 'V': + case 'K': + { + unsigned cv = 0; + const char* t = parse_cv_qualifiers(first, last, cv); + if (t != first) + { + bool is_function = *t == 'F'; + size_t k0 = db.names.size(); + const char* t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 != t) + { + if (is_function) + db.subs.pop_back(); + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (is_function) + { + size_t p = db.names[k].second.size(); + if (db.names[k].second[p-2] == '&') + p -= 3; + else if (db.names[k].second.back() == '&') + p -= 2; + if (cv & 1) + { + db.names[k].second.insert(p, " const"); + p += 6; + } + if (cv & 2) + { + db.names[k].second.insert(p, " volatile"); + p += 9; + } + if (cv & 4) + db.names[k].second.insert(p, " restrict"); + } + else + { + if (cv & 1) + db.names[k].first.append(" const"); + if (cv & 2) + db.names[k].first.append(" volatile"); + if (cv & 4) + db.names[k].first.append(" restrict"); + } + db.subs.back().push_back(db.names[k]); + } + first = t1; + } + } + } + break; + default: + { + const char* t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + switch (*first) + { + case 'A': + t = parse_array_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'C': + t = parse_type(first+1, last, db); + if (t != first+1) + { + if (db.names.empty()) + return first; + db.names.back().first.append(" complex"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'F': + t = parse_function_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'G': + t = parse_type(first+1, last, db); + if (t != first+1) + { + if (db.names.empty()) + return first; + db.names.back().first.append(" imaginary"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'M': + t = parse_pointer_to_member_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'O': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (!db.names[k].second.empty() && + db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'P': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (!db.names[k].second.empty() && + db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + if (first[1] != 'U' || db.names[k].first.substr(0, 12) != "objc_object<") + { + db.names[k].first.append("*"); + } + else + { + db.names[k].first.replace(0, 11, "id"); + } + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'R': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (!db.names[k].second.empty() && + db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + if (db.try_to_parse_template_args && k1 == k0+1) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t = t1; + } + } + first = t; + } + break; + } + case 'U': + if (first+1 != last) + { + t = parse_source_name(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + if (db.names.size() < 2) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + if (db.names.back().first.substr(0, 9) != "objcproto") + { + db.names.back() = type + " " + db.names.back().move_full(); + } + else + { + auto proto = db.names.back().move_full(); + db.names.pop_back(); + t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db); + if (t != proto.data() + 9) + { + db.names.back() = type + "<" + db.names.back().move_full() + ">"; + } + else + { + db.names.push_back(type + " " + proto); + } + } + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t2; + } + } + } + break; + case 'S': + if (first+1 != last && first[1] == 't') + { + t = parse_name(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + else + { + t = parse_substitution(first, last, db); + if (t != first) + { + first = t; + // Parsed a substitution. If the substitution is a + // it might be followed by . + t = parse_template_args(first, last, db); + if (t != first) + { + if (db.names.size() < 2) + return first; + auto template_args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += template_args; + // Need to create substitution for + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'p': + { + size_t k0 = db.names.size(); + t = parse_type(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + first = t; + return first; + } + break; + } + case 't': + case 'T': + t = parse_decltype(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + case 'v': + t = parse_vector_type(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + } + } + // drop through + default: + // must check for builtin-types before class-enum-types to avoid + // ambiguities with operator-names + t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + t = parse_name(first, last, db); + if (t != first) + { + if (db.names.empty()) + return first; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + break; + } + } + break; + } + } + } + return first; +} + +// +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= li # operator "" +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= v # vendor extended operator + +template +const char* +parse_operator_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + switch (first[0]) + { + case 'a': + switch (first[1]) + { + case 'a': + db.names.push_back("operator&&"); + first += 2; + break; + case 'd': + case 'n': + db.names.push_back("operator&"); + first += 2; + break; + case 'N': + db.names.push_back("operator&="); + first += 2; + break; + case 'S': + db.names.push_back("operator="); + first += 2; + break; + } + break; + case 'c': + switch (first[1]) + { + case 'l': + db.names.push_back("operator()"); + first += 2; + break; + case 'm': + db.names.push_back("operator,"); + first += 2; + break; + case 'o': + db.names.push_back("operator~"); + first += 2; + break; + case 'v': + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator "); + db.parsed_ctor_dtor_cv = true; + first = t; + } + } + break; + } + break; + case 'd': + switch (first[1]) + { + case 'a': + db.names.push_back("operator delete[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator*"); + first += 2; + break; + case 'l': + db.names.push_back("operator delete"); + first += 2; + break; + case 'v': + db.names.push_back("operator/"); + first += 2; + break; + case 'V': + db.names.push_back("operator/="); + first += 2; + break; + } + break; + case 'e': + switch (first[1]) + { + case 'o': + db.names.push_back("operator^"); + first += 2; + break; + case 'O': + db.names.push_back("operator^="); + first += 2; + break; + case 'q': + db.names.push_back("operator=="); + first += 2; + break; + } + break; + case 'g': + switch (first[1]) + { + case 'e': + db.names.push_back("operator>="); + first += 2; + break; + case 't': + db.names.push_back("operator>"); + first += 2; + break; + } + break; + case 'i': + if (first[1] == 'x') + { + db.names.push_back("operator[]"); + first += 2; + } + break; + case 'l': + switch (first[1]) + { + case 'e': + db.names.push_back("operator<="); + first += 2; + break; + case 'i': + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator\"\" "); + first = t; + } + } + break; + case 's': + db.names.push_back("operator<<"); + first += 2; + break; + case 'S': + db.names.push_back("operator<<="); + first += 2; + break; + case 't': + db.names.push_back("operator<"); + first += 2; + break; + } + break; + case 'm': + switch (first[1]) + { + case 'i': + db.names.push_back("operator-"); + first += 2; + break; + case 'I': + db.names.push_back("operator-="); + first += 2; + break; + case 'l': + db.names.push_back("operator*"); + first += 2; + break; + case 'L': + db.names.push_back("operator*="); + first += 2; + break; + case 'm': + db.names.push_back("operator--"); + first += 2; + break; + } + break; + case 'n': + switch (first[1]) + { + case 'a': + db.names.push_back("operator new[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator!="); + first += 2; + break; + case 'g': + db.names.push_back("operator-"); + first += 2; + break; + case 't': + db.names.push_back("operator!"); + first += 2; + break; + case 'w': + db.names.push_back("operator new"); + first += 2; + break; + } + break; + case 'o': + switch (first[1]) + { + case 'o': + db.names.push_back("operator||"); + first += 2; + break; + case 'r': + db.names.push_back("operator|"); + first += 2; + break; + case 'R': + db.names.push_back("operator|="); + first += 2; + break; + } + break; + case 'p': + switch (first[1]) + { + case 'm': + db.names.push_back("operator->*"); + first += 2; + break; + case 'l': + db.names.push_back("operator+"); + first += 2; + break; + case 'L': + db.names.push_back("operator+="); + first += 2; + break; + case 'p': + db.names.push_back("operator++"); + first += 2; + break; + case 's': + db.names.push_back("operator+"); + first += 2; + break; + case 't': + db.names.push_back("operator->"); + first += 2; + break; + } + break; + case 'q': + if (first[1] == 'u') + { + db.names.push_back("operator?"); + first += 2; + } + break; + case 'r': + switch (first[1]) + { + case 'm': + db.names.push_back("operator%"); + first += 2; + break; + case 'M': + db.names.push_back("operator%="); + first += 2; + break; + case 's': + db.names.push_back("operator>>"); + first += 2; + break; + case 'S': + db.names.push_back("operator>>="); + first += 2; + break; + } + break; + case 'v': + if (std::isdigit(first[1])) + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator "); + first = t; + } + } + break; + } + } + return first; +} + +template +const char* +parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& db) +{ + const char* t = parse_number(first, last); + if (t != first && t != last && *t == 'E') + { + if (lit.size() > 3) + db.names.push_back("(" + lit + ")"); + else + db.names.emplace_back(); + if (*first == 'n') + { + db.names.back().first += '-'; + ++first; + } + db.names.back().first.append(first, t); + if (lit.size() <= 3) + db.names.back().first += lit; + first = t+1; + } + return first; +} + +// ::= L E # integer literal +// ::= L E # floating literal +// ::= L E # string literal +// ::= L E # nullptr literal (i.e., "LDnE") +// ::= L _ E # complex floating point literal (C 2000) +// ::= L E # external name + +template +const char* +parse_expr_primary(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && *first == 'L') + { + switch (first[1]) + { + case 'w': + { + const char* t = parse_integer_literal(first+2, last, "wchar_t", db); + if (t != first+2) + first = t; + } + break; + case 'b': + if (first[3] == 'E') + { + switch (first[2]) + { + case '0': + db.names.push_back("false"); + first += 4; + break; + case '1': + db.names.push_back("true"); + first += 4; + break; + } + } + break; + case 'c': + { + const char* t = parse_integer_literal(first+2, last, "char", db); + if (t != first+2) + first = t; + } + break; + case 'a': + { + const char* t = parse_integer_literal(first+2, last, "signed char", db); + if (t != first+2) + first = t; + } + break; + case 'h': + { + const char* t = parse_integer_literal(first+2, last, "unsigned char", db); + if (t != first+2) + first = t; + } + break; + case 's': + { + const char* t = parse_integer_literal(first+2, last, "short", db); + if (t != first+2) + first = t; + } + break; + case 't': + { + const char* t = parse_integer_literal(first+2, last, "unsigned short", db); + if (t != first+2) + first = t; + } + break; + case 'i': + { + const char* t = parse_integer_literal(first+2, last, "", db); + if (t != first+2) + first = t; + } + break; + case 'j': + { + const char* t = parse_integer_literal(first+2, last, "u", db); + if (t != first+2) + first = t; + } + break; + case 'l': + { + const char* t = parse_integer_literal(first+2, last, "l", db); + if (t != first+2) + first = t; + } + break; + case 'm': + { + const char* t = parse_integer_literal(first+2, last, "ul", db); + if (t != first+2) + first = t; + } + break; + case 'x': + { + const char* t = parse_integer_literal(first+2, last, "ll", db); + if (t != first+2) + first = t; + } + break; + case 'y': + { + const char* t = parse_integer_literal(first+2, last, "ull", db); + if (t != first+2) + first = t; + } + break; + case 'n': + { + const char* t = parse_integer_literal(first+2, last, "__int128", db); + if (t != first+2) + first = t; + } + break; + case 'o': + { + const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db); + if (t != first+2) + first = t; + } + break; + case 'f': + { + const char* t = parse_floating_number(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'd': + { + const char* t = parse_floating_number(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'e': + { + const char* t = parse_floating_number(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case '_': + if (first[2] == 'Z') + { + const char* t = parse_encoding(first+3, last, db); + if (t != first+3 && t != last && *t == 'E') + first = t+1; + } + break; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + break; + default: + { + // might be named type + const char* t = parse_type(first+1, last, db); + if (t != first+1 && t != last) + { + if (*t != 'E') + { + const char* n = t; + for (; n != last && isdigit(*n); ++n) + ; + if (n != t && n != last && *n == 'E') + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); + first = n+1; + break; + } + } + else + { + first = t+1; + break; + } + } + } + } + } + return first; +} + +template +String +base_name(String& s) +{ + if (s.empty()) + return s; + if (s == "std::string") + { + s = "std::basic_string, std::allocator >"; + return "basic_string"; + } + if (s == "std::istream") + { + s = "std::basic_istream >"; + return "basic_istream"; + } + if (s == "std::ostream") + { + s = "std::basic_ostream >"; + return "basic_ostream"; + } + if (s == "std::iostream") + { + s = "std::basic_iostream >"; + return "basic_iostream"; + } + const char* const pf = s.data(); + const char* pe = pf + s.size(); + if (pe[-1] == '>') + { + unsigned c = 1; + while (true) + { + if (--pe == pf) + return String(); + if (pe[-1] == '<') + { + if (--c == 0) + { + --pe; + break; + } + } + else if (pe[-1] == '>') + ++c; + } + } + const char* p0 = pe - 1; + for (; p0 != pf; --p0) + { + if (*p0 == ':') + { + ++p0; + break; + } + } + return String(p0, pe); +} + +// ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C5 # ? +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D5 # ? + +template +const char* +parse_ctor_dtor_name(const char* first, const char* last, C& db) +{ + if (last-first >= 2 && !db.names.empty()) + { + switch (first[0]) + { + case 'C': + switch (first[1]) + { + case '1': + case '2': + case '3': + case '5': + if (db.names.empty()) + return first; + db.names.push_back(base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + case 'D': + switch (first[1]) + { + case '0': + case '1': + case '2': + case '5': + if (db.names.empty()) + return first; + db.names.push_back("~" + base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + } + } + return first; +} + +// ::= Ut [ ] _ +// ::= +// +// ::= Ul E [ ] _ +// +// ::= + # Parameter types or "v" if the lambda has no parameters + +template +const char* +parse_unnamed_type_name(const char* first, const char* last, C& db) +{ + if (last - first > 2 && first[0] == 'U') + { + char type = first[1]; + switch (type) + { + case 't': + { + db.names.push_back(typename C::String("'unnamed")); + const char* t0 = first+2; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.append(t0, t1); + t0 = t1; + } + db.names.back().first.push_back('\''); + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + case 'l': + { + db.names.push_back(typename C::String("'lambda'(")); + const char* t0 = first+2; + if (first[2] == 'v') + { + db.names.back().first += ')'; + ++t0; + } + else + { + const char* t1 = parse_type(t0, last, db); + if (t1 == t0) + { + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append(tmp); + t0 = t1; + while (true) + { + t1 = parse_type(t0, last, db); + if (t1 == t0) + break; + if (db.names.size() < 2) + return first; + tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + } + t0 = t1; + } + db.names.back().first.append(")"); + } + if (t0 == last || *t0 != 'E') + { + db.names.pop_back(); + return first; + } + ++t0; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.insert(db.names.back().first.begin()+7, t0, t1); + t0 = t1; + } + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + } + } + return first; +} + +// ::= +// ::= +// ::= +// ::= + +template +const char* +parse_unqualified_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'C': + case 'D': + t = parse_ctor_dtor_name(first, last, db); + if (t != first) + first = t; + break; + case 'U': + t = parse_unnamed_type_name(first, last, db); + if (t != first) + first = t; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + t = parse_source_name(first, last, db); + if (t != first) + first = t; + break; + default: + t = parse_operator_name(first, last, db); + if (t != first) + first = t; + break; + }; + } + return first; +} + +// ::= +// ::= St # ::std:: +// extension ::= StL + +template +const char* +parse_unscoped_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t0 = first; + bool St = false; + if (first[0] == 'S' && first[1] == 't') + { + t0 += 2; + St = true; + if (t0 != last && *t0 == 'L') + ++t0; + } + const char* t1 = parse_unqualified_name(t0, last, db); + if (t1 != t0) + { + if (St) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "std::"); + } + first = t1; + } + } + return first; +} + +// at # alignof (a type) + +template +const char* +parse_alignof_type(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// az # alignof (a expression) + +template +const char* +parse_alignof_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +template +const char* +parse_noexcept_expression(const char* first, const char* last, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + if (db.names.empty()) + return first; + db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template +const char* +parse_prefix_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + if (db.names.empty()) + return first; + db.names.back().first = op + "(" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template +const char* +parse_binary_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + if (db.names.size() < 2) + return first; + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + auto& nm = db.names.back().first; + nm.clear(); + if (op == ">") + nm += '('; + nm += "(" + op1 + ") " + op + " (" + op2 + ")"; + if (op == ">") + nm += ')'; + first = t2; + } + else + db.names.pop_back(); + } + return first; +} + +// ::= +// ::= +// ::= +// ::= cl + E # call +// ::= cv # conversion with one argument +// ::= cv _ * E # conversion with a different number of arguments +// ::= [gs] nw * _ E # new (expr-list) type +// ::= [gs] nw * _ # new (expr-list) type (init) +// ::= [gs] na * _ E # new[] (expr-list) type +// ::= [gs] na * _ # new[] (expr-list) type (init) +// ::= [gs] dl # delete expression +// ::= [gs] da # delete[] expression +// ::= pp_ # prefix ++ +// ::= mm_ # prefix -- +// ::= ti # typeid (type) +// ::= te # typeid (expression) +// ::= dc # dynamic_cast (expression) +// ::= sc # static_cast (expression) +// ::= cc # const_cast (expression) +// ::= rc # reinterpret_cast (expression) +// ::= st # sizeof (a type) +// ::= sz # sizeof (an expression) +// ::= at # alignof (a type) +// ::= az # alignof (an expression) +// ::= nx # noexcept (expression) +// ::= +// ::= +// ::= dt # expr.name +// ::= pt # expr->name +// ::= ds # expr.*expr +// ::= sZ # size of a parameter pack +// ::= sZ # size of a function parameter pack +// ::= sp # pack expansion +// ::= tw # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= + +template +const char* +parse_expression(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t = first; + bool parsed_gs = false; + if (last - first >= 4 && t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + switch (*t) + { + case 'L': + first = parse_expr_primary(first, last, db); + break; + case 'T': + first = parse_template_param(first, last, db); + break; + case 'f': + first = parse_function_param(first, last, db); + break; + case 'a': + switch (t[1]) + { + case 'a': + t = parse_binary_expression(first+2, last, "&&", db); + if (t != first+2) + first = t; + break; + case 'd': + t = parse_prefix_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'n': + t = parse_binary_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'N': + t = parse_binary_expression(first+2, last, "&=", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "=", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_alignof_type(first, last, db); + break; + case 'z': + first = parse_alignof_expr(first, last, db); + break; + } + break; + case 'c': + switch (t[1]) + { + case 'c': + first = parse_const_cast_expr(first, last, db); + break; + case 'l': + first = parse_call_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, ",", db); + if (t != first+2) + first = t; + break; + case 'o': + t = parse_prefix_expression(first+2, last, "~", db); + if (t != first+2) + first = t; + break; + case 'v': + first = parse_conversion_expr(first, last, db); + break; + } + break; + case 'd': + switch (t[1]) + { + case 'a': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + if (db.names.empty()) + return first; + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete[] " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'c': + first = parse_dynamic_cast_expr(first, last, db); + break; + case 'e': + t = parse_prefix_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'l': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + if (db.names.empty()) + return first; + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'n': + return parse_unresolved_name(first, last, db); + case 's': + first = parse_dot_star_expr(first, last, db); + break; + case 't': + first = parse_dot_expr(first, last, db); + break; + case 'v': + t = parse_binary_expression(first+2, last, "/", db); + if (t != first+2) + first = t; + break; + case 'V': + t = parse_binary_expression(first+2, last, "/=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'e': + switch (t[1]) + { + case 'o': + t = parse_binary_expression(first+2, last, "^", db); + if (t != first+2) + first = t; + break; + case 'O': + t = parse_binary_expression(first+2, last, "^=", db); + if (t != first+2) + first = t; + break; + case 'q': + t = parse_binary_expression(first+2, last, "==", db); + if (t != first+2) + first = t; + break; + } + break; + case 'g': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, ">=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, ">", db); + if (t != first+2) + first = t; + break; + } + break; + case 'i': + if (t[1] == 'x') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + if (db.names.size() < 2) + return first; + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ")[" + op2 + "]"; + first = t2; + } + else + db.names.pop_back(); + } + } + break; + case 'l': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, "<=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, "<<", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "<<=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, "<", db); + if (t != first+2) + first = t; + break; + } + break; + case 'm': + switch (t[1]) + { + case 'i': + t = parse_binary_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 'I': + t = parse_binary_expression(first+2, last, "-=", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "*=", db); + if (t != first+2) + first = t; + break; + case 'm': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "--", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")--"; + first = t1; + } + } + break; + } + break; + case 'n': + switch (t[1]) + { + case 'a': + case 'w': + first = parse_new_expr(first, last, db); + break; + case 'e': + t = parse_binary_expression(first+2, last, "!=", db); + if (t != first+2) + first = t; + break; + case 'g': + t = parse_prefix_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_prefix_expression(first+2, last, "!", db); + if (t != first+2) + first = t; + break; + case 'x': + t = parse_noexcept_expression(first+2, last, db); + if (t != first+2) + first = t; + break; + } + break; + case 'o': + switch (t[1]) + { + case 'n': + return parse_unresolved_name(first, last, db); + case 'o': + t = parse_binary_expression(first+2, last, "||", db); + if (t != first+2) + first = t; + break; + case 'r': + t = parse_binary_expression(first+2, last, "|", db); + if (t != first+2) + first = t; + break; + case 'R': + t = parse_binary_expression(first+2, last, "|=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'p': + switch (t[1]) + { + case 'm': + t = parse_binary_expression(first+2, last, "->*", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "+=", db); + if (t != first+2) + first = t; + break; + case 'p': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "++", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + if (db.names.empty()) + return first; + db.names.back() = "(" + db.names.back().move_full() + ")++"; + first = t1; + } + } + break; + case 's': + t = parse_prefix_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_arrow_expr(first, last, db); + break; + } + break; + case 'q': + if (t[1] == 'u') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + const char* t3 = parse_expression(t2, last, db); + if (t3 != t2) + { + if (db.names.size() < 3) + return first; + auto op3 = db.names.back().move_full(); + db.names.pop_back(); + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ") ? (" + op2 + ") : (" + op3 + ")"; + first = t3; + } + else + { + db.names.pop_back(); + db.names.pop_back(); + } + } + else + db.names.pop_back(); + } + } + break; + case 'r': + switch (t[1]) + { + case 'c': + first = parse_reinterpret_cast_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, "%", db); + if (t != first+2) + first = t; + break; + case 'M': + t = parse_binary_expression(first+2, last, "%=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, ">>", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, ">>=", db); + if (t != first+2) + first = t; + break; + } + break; + case 's': + switch (t[1]) + { + case 'c': + first = parse_static_cast_expr(first, last, db); + break; + case 'p': + first = parse_pack_expansion(first, last, db); + break; + case 'r': + return parse_unresolved_name(first, last, db); + case 't': + first = parse_sizeof_type_expr(first, last, db); + break; + case 'z': + first = parse_sizeof_expr_expr(first, last, db); + break; + case 'Z': + if (last - t >= 3) + { + switch (t[2]) + { + case 'T': + first = parse_sizeof_param_pack_expr(first, last, db); + break; + case 'f': + first = parse_sizeof_function_param_pack_expr(first, last, db); + break; + } + } + break; + } + break; + case 't': + switch (t[1]) + { + case 'e': + case 'i': + first = parse_typeid_expr(first, last, db); + break; + case 'r': + db.names.push_back("throw"); + first += 2; + break; + case 'w': + first = parse_throw_expr(first, last, db); + break; + } + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parse_unresolved_name(first, last, db); + } + } + return first; +} + +// ::= # type or template +// ::= X E # expression +// ::= # simple expressions +// ::= J * E # argument pack +// ::= LZ E # extension + +template +const char* +parse_template_arg(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'X': + t = parse_expression(first+1, last, db); + if (t != first+1) + { + if (t != last && *t == 'E') + first = t+1; + } + break; + case 'J': + t = first+1; + if (t == last) + return first; + while (*t != 'E') + { + const char* t1 = parse_template_arg(t, last, db); + if (t1 == t) + return first; + t = t1; + } + first = t+1; + break; + case 'L': + // or LZ E + if (first+1 != last && first[1] == 'Z') + { + t = parse_encoding(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + first = t+1; + } + else + first = parse_expr_primary(first, last, db); + break; + default: + // + first = parse_type(first, last, db); + break; + } + } + return first; +} + +// ::= I * E +// extension, the abi says + + +template +const char* +parse_template_args(const char* first, const char* last, C& db) +{ + if (last - first >= 2 && *first == 'I') + { + if (db.tag_templates) + db.template_param.back().clear(); + const char* t = first+1; + typename C::String args("<"); + while (*t != 'E') + { + if (db.tag_templates) + db.template_param.emplace_back(db.names.get_allocator()); + size_t k0 = db.names.size(); + const char* t1 = parse_template_arg(t, last, db); + size_t k1 = db.names.size(); + if (db.tag_templates) + db.template_param.pop_back(); + if (t1 == t || t1 == last) + return first; + if (db.tag_templates) + { + db.template_param.back().emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.template_param.back().back().push_back(db.names[k]); + } + for (size_t k = k0; k < k1; ++k) + { + if (args.size() > 1) + args += ", "; + args += db.names[k].move_full(); + } + for (; k1 != k0; --k1) + db.names.pop_back(); + t = t1; + } + first = t + 1; + if (args.back() != '>') + args += ">"; + else + args += " >"; + db.names.push_back(std::move(args)); + + } + return first; +} + +// ::= N [] [] E +// ::= N [] [] E +// +// ::= +// ::= +// ::= +// ::= +// ::= # empty +// ::= +// ::= +// extension ::= L +// +// ::=