forked from OSchip/llvm-project
209 lines
8.2 KiB
C#
209 lines
8.2 KiB
C#
//===-- ClangTidyPropertyGrid.cs - UI for configuring clang-tidy -*- C# -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class contains a UserControl consisting of a .NET PropertyGrid control
|
|
// allowing configuration of checks and check options for ClangTidy.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using Microsoft.VisualStudio.Shell;
|
|
|
|
namespace LLVM.ClangTidy
|
|
{
|
|
/// <summary>
|
|
/// A UserControl displaying a PropertyGrid allowing configuration of clang-tidy
|
|
/// checks and check options, as well as serialization and deserialization of
|
|
/// clang-tidy configuration files. When a configuration file is loaded, the
|
|
/// entire chain of configuration files is analyzed based on the file path,
|
|
/// and quick access is provided to edit or view any of the files in the
|
|
/// configuration chain, allowing easy visualization of where values come from
|
|
/// (similar in spirit to the -explain-config option of clang-tidy).
|
|
/// </summary>
|
|
public partial class ClangTidyPropertyGrid : UserControl
|
|
{
|
|
/// <summary>
|
|
/// The sequence of .clang-tidy configuration files, starting from the root
|
|
/// of the filesystem, down to the selected file.
|
|
/// </summary>
|
|
List<KeyValuePair<string, ClangTidyProperties>> PropertyChain_ = null;
|
|
|
|
public ClangTidyPropertyGrid()
|
|
{
|
|
InitializeComponent();
|
|
InitializeSettings();
|
|
}
|
|
|
|
private enum ShouldCancel
|
|
{
|
|
Yes,
|
|
No,
|
|
}
|
|
|
|
public void SaveSettingsToStorage()
|
|
{
|
|
PersistUnsavedChanges(false);
|
|
}
|
|
|
|
private ShouldCancel PersistUnsavedChanges(bool PromptFirst)
|
|
{
|
|
var UnsavedResults = PropertyChain_.Where(x => x.Key != null && x.Value.GetHasUnsavedChanges());
|
|
if (UnsavedResults.Count() == 0)
|
|
return ShouldCancel.No;
|
|
|
|
bool ShouldSave = false;
|
|
if (PromptFirst)
|
|
{
|
|
var Response = MessageBox.Show(
|
|
"You have unsaved changes! Do you want to save before loading a new file?",
|
|
"clang-tidy",
|
|
MessageBoxButtons.YesNoCancel);
|
|
|
|
ShouldSave = (Response == DialogResult.Yes);
|
|
if (Response == DialogResult.Cancel)
|
|
return ShouldCancel.Yes;
|
|
}
|
|
else
|
|
ShouldSave = true;
|
|
|
|
if (ShouldSave)
|
|
{
|
|
foreach (var Result in UnsavedResults)
|
|
{
|
|
ClangTidyConfigParser.SerializeClangTidyFile(Result.Value, Result.Key);
|
|
Result.Value.SetHasUnsavedChanges(false);
|
|
}
|
|
}
|
|
return ShouldCancel.No;
|
|
}
|
|
|
|
public void InitializeSettings()
|
|
{
|
|
PropertyChain_ = new List<KeyValuePair<string, ClangTidyProperties>>();
|
|
PropertyChain_.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));
|
|
reloadPropertyChain();
|
|
}
|
|
|
|
private void button1_Click(object sender, EventArgs e)
|
|
{
|
|
ShouldCancel Cancel = PersistUnsavedChanges(true);
|
|
if (Cancel == ShouldCancel.Yes)
|
|
return;
|
|
|
|
using (OpenFileDialog D = new OpenFileDialog())
|
|
{
|
|
D.Filter = "Clang Tidy files|.clang-tidy";
|
|
D.CheckPathExists = true;
|
|
D.CheckFileExists = true;
|
|
|
|
if (D.ShowDialog() == DialogResult.OK)
|
|
{
|
|
PropertyChain_.Clear();
|
|
PropertyChain_ = ClangTidyConfigParser.ParseConfigurationChain(D.FileName);
|
|
textBox1.Text = D.FileName;
|
|
reloadPropertyChain();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static readonly string DefaultText = "(Default)";
|
|
private static readonly string BrowseText = "Browse for a file to edit its properties";
|
|
|
|
/// <summary>
|
|
/// After a new configuration file is chosen, analyzes the directory hierarchy
|
|
/// and finds all .clang-tidy files in the path, parses them and updates the
|
|
/// PropertyGrid and quick-access LinkLabel control to reflect the new property
|
|
/// chain.
|
|
/// </summary>
|
|
private void reloadPropertyChain()
|
|
{
|
|
StringBuilder LinkBuilder = new StringBuilder();
|
|
LinkBuilder.Append(DefaultText);
|
|
LinkBuilder.Append(" > ");
|
|
int PrefixLength = LinkBuilder.Length;
|
|
|
|
if (PropertyChain_.Count == 1)
|
|
LinkBuilder.Append(BrowseText);
|
|
else
|
|
LinkBuilder.Append(PropertyChain_[PropertyChain_.Count - 1].Key);
|
|
|
|
linkLabelPath.Text = LinkBuilder.ToString();
|
|
|
|
// Given a path like D:\Foo\Bar\Baz, construct a LinkLabel where individual
|
|
// components of the path are clickable iff they contain a .clang-tidy file.
|
|
// Clicking one of the links then updates the PropertyGrid to display the
|
|
// selected .clang-tidy file.
|
|
ClangTidyProperties LastProps = ClangTidyProperties.RootProperties;
|
|
linkLabelPath.Links.Clear();
|
|
linkLabelPath.Links.Add(0, DefaultText.Length, LastProps);
|
|
foreach (var Prop in PropertyChain_.Skip(1))
|
|
{
|
|
LastProps = Prop.Value;
|
|
string ClangTidyFolder = Path.GetFileName(Prop.Key);
|
|
int ClangTidyFolderOffset = Prop.Key.Length - ClangTidyFolder.Length;
|
|
linkLabelPath.Links.Add(PrefixLength + ClangTidyFolderOffset, ClangTidyFolder.Length, LastProps);
|
|
}
|
|
propertyGrid1.SelectedObject = LastProps;
|
|
}
|
|
|
|
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
|
|
{
|
|
ClangTidyProperties Props = (ClangTidyProperties)propertyGrid1.SelectedObject;
|
|
Props.SetHasUnsavedChanges(true);
|
|
|
|
// When a CategoryVerb is selected, perform the corresponding action.
|
|
PropertyDescriptor Property = e.ChangedItem.PropertyDescriptor;
|
|
if (!(e.ChangedItem.Value is CategoryVerb))
|
|
return;
|
|
|
|
CategoryVerb Action = (CategoryVerb)e.ChangedItem.Value;
|
|
if (Action == CategoryVerb.None)
|
|
return;
|
|
|
|
var Category = Property.Attributes.OfType<CategoryAttribute>().FirstOrDefault();
|
|
if (Category == null)
|
|
return;
|
|
var SameCategoryProps = Props.GetProperties(new Attribute[] { Category });
|
|
foreach (PropertyDescriptor P in SameCategoryProps)
|
|
{
|
|
if (P == Property)
|
|
continue;
|
|
switch (Action)
|
|
{
|
|
case CategoryVerb.Disable:
|
|
P.SetValue(propertyGrid1.SelectedObject, false);
|
|
break;
|
|
case CategoryVerb.Enable:
|
|
P.SetValue(propertyGrid1.SelectedObject, true);
|
|
break;
|
|
case CategoryVerb.Inherit:
|
|
P.ResetValue(propertyGrid1.SelectedObject);
|
|
break;
|
|
}
|
|
}
|
|
Property.ResetValue(propertyGrid1.SelectedObject);
|
|
propertyGrid1.Invalidate();
|
|
}
|
|
|
|
private void linkLabelPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
|
{
|
|
ClangTidyProperties Props = (ClangTidyProperties)e.Link.LinkData;
|
|
propertyGrid1.SelectedObject = Props;
|
|
}
|
|
}
|
|
}
|