blob: 20c8a8fff5eab3c78c028082a8d0b8862e11272a [file] [log] [blame]
Zachary Turner1122be82016-09-07 18:28:55 +00001//===-- 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//===----------------------------------------------------------------------===//
14using System;
15using System.Collections.Generic;
16using System.ComponentModel;
17using System.Drawing;
18using System.Data;
19using System.Linq;
20using System.Text;
21using System.Threading.Tasks;
22using System.Windows.Forms;
23using System.IO;
24using Microsoft.VisualStudio.Shell;
25
26namespace 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 Turner1122be82016-09-07 18:28:55 +000045 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}