Zachary Turner | 1122be8 | 2016-09-07 18:28:55 +0000 | [diff] [blame] | 1 | //===-- ClangTidyPropertyGrid.cs - UI for configuring clang-tidy -*- C# -*-===//
|
| 2 | //
|
| 3 | // The LLVM Compiler Infrastructure
|
| 4 | //
|
| 5 | // This file is distributed under the University of Illinois Open Source
|
| 6 | // License. See LICENSE.TXT for details.
|
| 7 | //
|
| 8 | //===----------------------------------------------------------------------===//
|
| 9 | //
|
| 10 | // This class contains a UserControl consisting of a .NET PropertyGrid control
|
| 11 | // allowing configuration of checks and check options for ClangTidy.
|
| 12 | //
|
| 13 | //===----------------------------------------------------------------------===//
|
| 14 | using System;
|
| 15 | using System.Collections.Generic;
|
| 16 | using System.ComponentModel;
|
| 17 | using System.Drawing;
|
| 18 | using System.Data;
|
| 19 | using System.Linq;
|
| 20 | using System.Text;
|
| 21 | using System.Threading.Tasks;
|
| 22 | using System.Windows.Forms;
|
| 23 | using System.IO;
|
| 24 | using Microsoft.VisualStudio.Shell;
|
| 25 |
|
| 26 | namespace LLVM.ClangTidy
|
| 27 | {
|
| 28 | /// <summary>
|
| 29 | /// A UserControl displaying a PropertyGrid allowing configuration of clang-tidy
|
| 30 | /// checks and check options, as well as serialization and deserialization of
|
| 31 | /// clang-tidy configuration files. When a configuration file is loaded, the
|
| 32 | /// entire chain of configuration files is analyzed based on the file path,
|
| 33 | /// and quick access is provided to edit or view any of the files in the
|
| 34 | /// configuration chain, allowing easy visualization of where values come from
|
| 35 | /// (similar in spirit to the -explain-config option of clang-tidy).
|
| 36 | /// </summary>
|
| 37 | public partial class ClangTidyPropertyGrid : UserControl
|
| 38 | {
|
| 39 | /// <summary>
|
| 40 | /// The sequence of .clang-tidy configuration files, starting from the root
|
| 41 | /// of the filesystem, down to the selected file.
|
| 42 | /// </summary>
|
| 43 | List<KeyValuePair<string, ClangTidyProperties>> PropertyChain_ = null;
|
| 44 |
|
Zachary Turner | 1122be8 | 2016-09-07 18:28:55 +0000 | [diff] [blame] | 45 | public ClangTidyPropertyGrid()
|
| 46 | {
|
| 47 | InitializeComponent();
|
| 48 | InitializeSettings();
|
| 49 | }
|
| 50 |
|
| 51 | private enum ShouldCancel
|
| 52 | {
|
| 53 | Yes,
|
| 54 | No,
|
| 55 | }
|
| 56 |
|
| 57 | public void SaveSettingsToStorage()
|
| 58 | {
|
| 59 | PersistUnsavedChanges(false);
|
| 60 | }
|
| 61 |
|
| 62 | private ShouldCancel PersistUnsavedChanges(bool PromptFirst)
|
| 63 | {
|
| 64 | var UnsavedResults = PropertyChain_.Where(x => x.Key != null && x.Value.GetHasUnsavedChanges());
|
| 65 | if (UnsavedResults.Count() == 0)
|
| 66 | return ShouldCancel.No;
|
| 67 |
|
| 68 | bool ShouldSave = false;
|
| 69 | if (PromptFirst)
|
| 70 | {
|
| 71 | var Response = MessageBox.Show(
|
| 72 | "You have unsaved changes! Do you want to save before loading a new file?",
|
| 73 | "clang-tidy",
|
| 74 | MessageBoxButtons.YesNoCancel);
|
| 75 |
|
| 76 | ShouldSave = (Response == DialogResult.Yes);
|
| 77 | if (Response == DialogResult.Cancel)
|
| 78 | return ShouldCancel.Yes;
|
| 79 | }
|
| 80 | else
|
| 81 | ShouldSave = true;
|
| 82 |
|
| 83 | if (ShouldSave)
|
| 84 | {
|
| 85 | foreach (var Result in UnsavedResults)
|
| 86 | {
|
| 87 | ClangTidyConfigParser.SerializeClangTidyFile(Result.Value, Result.Key);
|
| 88 | Result.Value.SetHasUnsavedChanges(false);
|
| 89 | }
|
| 90 | }
|
| 91 | return ShouldCancel.No;
|
| 92 | }
|
| 93 |
|
| 94 | public void InitializeSettings()
|
| 95 | {
|
| 96 | PropertyChain_ = new List<KeyValuePair<string, ClangTidyProperties>>();
|
| 97 | PropertyChain_.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));
|
| 98 | reloadPropertyChain();
|
| 99 | }
|
| 100 |
|
| 101 | private void button1_Click(object sender, EventArgs e)
|
| 102 | {
|
| 103 | ShouldCancel Cancel = PersistUnsavedChanges(true);
|
| 104 | if (Cancel == ShouldCancel.Yes)
|
| 105 | return;
|
| 106 |
|
| 107 | using (OpenFileDialog D = new OpenFileDialog())
|
| 108 | {
|
| 109 | D.Filter = "Clang Tidy files|.clang-tidy";
|
| 110 | D.CheckPathExists = true;
|
| 111 | D.CheckFileExists = true;
|
| 112 |
|
| 113 | if (D.ShowDialog() == DialogResult.OK)
|
| 114 | {
|
| 115 | PropertyChain_.Clear();
|
| 116 | PropertyChain_ = ClangTidyConfigParser.ParseConfigurationChain(D.FileName);
|
| 117 | textBox1.Text = D.FileName;
|
| 118 | reloadPropertyChain();
|
| 119 | }
|
| 120 | }
|
| 121 | }
|
| 122 |
|
| 123 | private static readonly string DefaultText = "(Default)";
|
| 124 | private static readonly string BrowseText = "Browse for a file to edit its properties";
|
| 125 |
|
| 126 | /// <summary>
|
| 127 | /// After a new configuration file is chosen, analyzes the directory hierarchy
|
| 128 | /// and finds all .clang-tidy files in the path, parses them and updates the
|
| 129 | /// PropertyGrid and quick-access LinkLabel control to reflect the new property
|
| 130 | /// chain.
|
| 131 | /// </summary>
|
| 132 | private void reloadPropertyChain()
|
| 133 | {
|
| 134 | StringBuilder LinkBuilder = new StringBuilder();
|
| 135 | LinkBuilder.Append(DefaultText);
|
| 136 | LinkBuilder.Append(" > ");
|
| 137 | int PrefixLength = LinkBuilder.Length;
|
| 138 |
|
| 139 | if (PropertyChain_.Count == 1)
|
| 140 | LinkBuilder.Append(BrowseText);
|
| 141 | else
|
| 142 | LinkBuilder.Append(PropertyChain_[PropertyChain_.Count - 1].Key);
|
| 143 |
|
| 144 | linkLabelPath.Text = LinkBuilder.ToString();
|
| 145 |
|
| 146 | // Given a path like D:\Foo\Bar\Baz, construct a LinkLabel where individual
|
| 147 | // components of the path are clickable iff they contain a .clang-tidy file.
|
| 148 | // Clicking one of the links then updates the PropertyGrid to display the
|
| 149 | // selected .clang-tidy file.
|
| 150 | ClangTidyProperties LastProps = ClangTidyProperties.RootProperties;
|
| 151 | linkLabelPath.Links.Clear();
|
| 152 | linkLabelPath.Links.Add(0, DefaultText.Length, LastProps);
|
| 153 | foreach (var Prop in PropertyChain_.Skip(1))
|
| 154 | {
|
| 155 | LastProps = Prop.Value;
|
| 156 | string ClangTidyFolder = Path.GetFileName(Prop.Key);
|
| 157 | int ClangTidyFolderOffset = Prop.Key.Length - ClangTidyFolder.Length;
|
| 158 | linkLabelPath.Links.Add(PrefixLength + ClangTidyFolderOffset, ClangTidyFolder.Length, LastProps);
|
| 159 | }
|
| 160 | propertyGrid1.SelectedObject = LastProps;
|
| 161 | }
|
| 162 |
|
| 163 | private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
|
| 164 | {
|
| 165 | ClangTidyProperties Props = (ClangTidyProperties)propertyGrid1.SelectedObject;
|
| 166 | Props.SetHasUnsavedChanges(true);
|
| 167 |
|
| 168 | // When a CategoryVerb is selected, perform the corresponding action.
|
| 169 | PropertyDescriptor Property = e.ChangedItem.PropertyDescriptor;
|
| 170 | if (!(e.ChangedItem.Value is CategoryVerb))
|
| 171 | return;
|
| 172 |
|
| 173 | CategoryVerb Action = (CategoryVerb)e.ChangedItem.Value;
|
| 174 | if (Action == CategoryVerb.None)
|
| 175 | return;
|
| 176 |
|
| 177 | var Category = Property.Attributes.OfType<CategoryAttribute>().FirstOrDefault();
|
| 178 | if (Category == null)
|
| 179 | return;
|
| 180 | var SameCategoryProps = Props.GetProperties(new Attribute[] { Category });
|
| 181 | foreach (PropertyDescriptor P in SameCategoryProps)
|
| 182 | {
|
| 183 | if (P == Property)
|
| 184 | continue;
|
| 185 | switch (Action)
|
| 186 | {
|
| 187 | case CategoryVerb.Disable:
|
| 188 | P.SetValue(propertyGrid1.SelectedObject, false);
|
| 189 | break;
|
| 190 | case CategoryVerb.Enable:
|
| 191 | P.SetValue(propertyGrid1.SelectedObject, true);
|
| 192 | break;
|
| 193 | case CategoryVerb.Inherit:
|
| 194 | P.ResetValue(propertyGrid1.SelectedObject);
|
| 195 | break;
|
| 196 | }
|
| 197 | }
|
| 198 | Property.ResetValue(propertyGrid1.SelectedObject);
|
| 199 | propertyGrid1.Invalidate();
|
| 200 | }
|
| 201 |
|
| 202 | private void linkLabelPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
| 203 | {
|
| 204 | ClangTidyProperties Props = (ClangTidyProperties)e.Link.LinkData;
|
| 205 | propertyGrid1.SelectedObject = Props;
|
| 206 | }
|
| 207 | }
|
| 208 | }
|