12
12
//
13
13
//===----------------------------------------------------------------------===//
14
14
15
- using Microsoft . VisualStudio . Editor ;
15
+ using EnvDTE ;
16
16
using Microsoft . VisualStudio . Shell ;
17
17
using Microsoft . VisualStudio . Shell . Interop ;
18
18
using Microsoft . VisualStudio . Text ;
19
19
using Microsoft . VisualStudio . Text . Editor ;
20
- using Microsoft . VisualStudio . TextManager . Interop ;
21
20
using System ;
22
21
using System . Collections ;
23
22
using System . ComponentModel ;
24
23
using System . ComponentModel . Design ;
25
24
using System . IO ;
26
25
using System . Runtime . InteropServices ;
27
26
using System . Xml . Linq ;
27
+ using System . Linq ;
28
28
29
29
namespace LLVM . ClangFormat
30
30
{
@@ -36,6 +36,17 @@ public class OptionPageGrid : DialogPage
36
36
private string fallbackStyle = "LLVM" ;
37
37
private bool sortIncludes = false ;
38
38
private string style = "file" ;
39
+ private bool formatOnSave = false ;
40
+ private string formatOnSaveFileExtensions =
41
+ ".c;.cpp;.cxx;.cc;.tli;.tlh;.h;.hh;.hpp;.hxx;.hh;.inl" +
42
+ ".java;.js;.ts;.m;.mm;.proto;.protodevel;.td" ;
43
+
44
+ public OptionPageGrid Clone ( )
45
+ {
46
+ // Use MemberwiseClone to copy value types.
47
+ var clone = ( OptionPageGrid ) MemberwiseClone ( ) ;
48
+ return clone ;
49
+ }
39
50
40
51
public class StyleConverter : TypeConverter
41
52
{
@@ -74,7 +85,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, System.Global
74
85
}
75
86
}
76
87
77
- [ Category ( "LLVM/Clang " ) ]
88
+ [ Category ( "Format Options " ) ]
78
89
[ DisplayName ( "Style" ) ]
79
90
[ Description ( "Coding style, currently supports:\n " +
80
91
" - Predefined styles ('LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit').\n " +
@@ -121,7 +132,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, System.Global
121
132
}
122
133
}
123
134
124
- [ Category ( "LLVM/Clang " ) ]
135
+ [ Category ( "Format Options " ) ]
125
136
[ DisplayName ( "Assume Filename" ) ]
126
137
[ Description ( "When reading from stdin, clang-format assumes this " +
127
138
"filename to look for a style config file (with 'file' style) " +
@@ -142,7 +153,7 @@ public FallbackStyleConverter()
142
153
}
143
154
}
144
155
145
- [ Category ( "LLVM/Clang " ) ]
156
+ [ Category ( "Format Options " ) ]
146
157
[ DisplayName ( "Fallback Style" ) ]
147
158
[ Description ( "The name of the predefined style used as a fallback in case clang-format " +
148
159
"is invoked with 'file' style, but can not find the configuration file.\n " +
@@ -154,7 +165,7 @@ public string FallbackStyle
154
165
set { fallbackStyle = value ; }
155
166
}
156
167
157
- [ Category ( "LLVM/Clang " ) ]
168
+ [ Category ( "Format Options " ) ]
158
169
[ DisplayName ( "Sort includes" ) ]
159
170
[ Description ( "Sort touched include lines.\n \n " +
160
171
"See also: http://clang.llvm.org/docs/ClangFormat.html." ) ]
@@ -163,20 +174,48 @@ public bool SortIncludes
163
174
get { return sortIncludes ; }
164
175
set { sortIncludes = value ; }
165
176
}
177
+
178
+ [ Category ( "Format On Save" ) ]
179
+ [ DisplayName ( "Enable" ) ]
180
+ [ Description ( "Enable running clang-format when modified files are saved. " +
181
+ "Will only format if Style is found (ignores Fallback Style)."
182
+ ) ]
183
+ public bool FormatOnSave
184
+ {
185
+ get { return formatOnSave ; }
186
+ set { formatOnSave = value ; }
187
+ }
188
+
189
+ [ Category ( "Format On Save" ) ]
190
+ [ DisplayName ( "File extensions" ) ]
191
+ [ Description ( "When formatting on save, clang-format will be applied only to " +
192
+ "files with these extensions." ) ]
193
+ public string FormatOnSaveFileExtensions
194
+ {
195
+ get { return formatOnSaveFileExtensions ; }
196
+ set { formatOnSaveFileExtensions = value ; }
197
+ }
166
198
}
167
199
168
200
[ PackageRegistration ( UseManagedResourcesOnly = true ) ]
169
201
[ InstalledProductRegistration ( "#110" , "#112" , "1.0" , IconResourceID = 400 ) ]
170
202
[ ProvideMenuResource ( "Menus.ctmenu" , 1 ) ]
203
+ [ ProvideAutoLoad ( UIContextGuids80 . SolutionExists ) ] // Load package on solution load
171
204
[ Guid ( GuidList . guidClangFormatPkgString ) ]
172
205
[ ProvideOptionPage ( typeof ( OptionPageGrid ) , "LLVM/Clang" , "ClangFormat" , 0 , 0 , true ) ]
173
206
public sealed class ClangFormatPackage : Package
174
207
{
175
208
#region Package Members
209
+
210
+ RunningDocTableEventsDispatcher _runningDocTableEventsDispatcher ;
211
+
176
212
protected override void Initialize ( )
177
213
{
178
214
base . Initialize ( ) ;
179
215
216
+ _runningDocTableEventsDispatcher = new RunningDocTableEventsDispatcher ( this ) ;
217
+ _runningDocTableEventsDispatcher . BeforeSave += OnBeforeSave ;
218
+
180
219
var commandService = GetService ( typeof ( IMenuCommandService ) ) as OleMenuCommandService ;
181
220
if ( commandService != null )
182
221
{
@@ -195,6 +234,11 @@ protected override void Initialize()
195
234
}
196
235
#endregion
197
236
237
+ OptionPageGrid GetUserOptions ( )
238
+ {
239
+ return ( OptionPageGrid ) GetDialogPage ( typeof ( OptionPageGrid ) ) ;
240
+ }
241
+
198
242
private void MenuItemCallback ( object sender , EventArgs args )
199
243
{
200
244
var mc = sender as System . ComponentModel . Design . MenuCommand ;
@@ -204,21 +248,45 @@ private void MenuItemCallback(object sender, EventArgs args)
204
248
switch ( mc . CommandID . ID )
205
249
{
206
250
case ( int ) PkgCmdIDList . cmdidClangFormatSelection :
207
- FormatSelection ( ) ;
251
+ FormatSelection ( GetUserOptions ( ) ) ;
208
252
break ;
209
253
210
254
case ( int ) PkgCmdIDList . cmdidClangFormatDocument :
211
- FormatDocument ( ) ;
255
+ FormatDocument ( GetUserOptions ( ) ) ;
212
256
break ;
213
257
}
214
258
}
215
259
260
+ private static bool FileHasExtension ( string filePath , string fileExtensions )
261
+ {
262
+ var extensions = fileExtensions . ToLower ( ) . Split ( new char [ ] { ';' } , StringSplitOptions . RemoveEmptyEntries ) ;
263
+ return extensions . Contains ( Path . GetExtension ( filePath ) . ToLower ( ) ) ;
264
+ }
265
+
266
+ private void OnBeforeSave ( object sender , Document document )
267
+ {
268
+ var options = GetUserOptions ( ) ;
269
+
270
+ if ( ! options . FormatOnSave )
271
+ return ;
272
+
273
+ if ( ! FileHasExtension ( document . FullName , options . FormatOnSaveFileExtensions ) )
274
+ return ;
275
+
276
+ if ( ! Vsix . IsDocumentDirty ( document ) )
277
+ return ;
278
+
279
+ var optionsWithNoFallbackStyle = GetUserOptions ( ) . Clone ( ) ;
280
+ optionsWithNoFallbackStyle . FallbackStyle = "none" ;
281
+ FormatDocument ( document , optionsWithNoFallbackStyle ) ;
282
+ }
283
+
216
284
/// <summary>
217
285
/// Runs clang-format on the current selection
218
286
/// </summary>
219
- private void FormatSelection ( )
287
+ private void FormatSelection ( OptionPageGrid options )
220
288
{
221
- IWpfTextView view = GetCurrentView ( ) ;
289
+ IWpfTextView view = Vsix . GetCurrentView ( ) ;
222
290
if ( view == null )
223
291
// We're not in a text view.
224
292
return ;
@@ -231,34 +299,43 @@ private void FormatSelection()
231
299
// of the file.
232
300
if ( start >= text . Length && text . Length > 0 )
233
301
start = text . Length - 1 ;
234
- string path = GetDocumentParent ( view ) ;
235
- string filePath = GetDocumentPath ( view ) ;
302
+ string path = Vsix . GetDocumentParent ( view ) ;
303
+ string filePath = Vsix . GetDocumentPath ( view ) ;
236
304
237
- RunClangFormatAndApplyReplacements ( text , start , length , path , filePath , view ) ;
305
+ RunClangFormatAndApplyReplacements ( text , start , length , path , filePath , options , view ) ;
238
306
}
239
307
240
308
/// <summary>
241
309
/// Runs clang-format on the current document
242
310
/// </summary>
243
- private void FormatDocument ( )
311
+ private void FormatDocument ( OptionPageGrid options )
312
+ {
313
+ FormatView ( Vsix . GetCurrentView ( ) , options ) ;
314
+ }
315
+
316
+ private void FormatDocument ( Document document , OptionPageGrid options )
317
+ {
318
+ FormatView ( Vsix . GetDocumentView ( document ) , options ) ;
319
+ }
320
+
321
+ private void FormatView ( IWpfTextView view , OptionPageGrid options )
244
322
{
245
- IWpfTextView view = GetCurrentView ( ) ;
246
323
if ( view == null )
247
324
// We're not in a text view.
248
325
return ;
249
326
250
- string filePath = GetDocumentPath ( view ) ;
327
+ string filePath = Vsix . GetDocumentPath ( view ) ;
251
328
var path = Path . GetDirectoryName ( filePath ) ;
252
329
string text = view . TextBuffer . CurrentSnapshot . GetText ( ) ;
253
330
254
- RunClangFormatAndApplyReplacements ( text , 0 , text . Length , path , filePath , view ) ;
331
+ RunClangFormatAndApplyReplacements ( text , 0 , text . Length , path , filePath , options , view ) ;
255
332
}
256
333
257
- private void RunClangFormatAndApplyReplacements ( string text , int offset , int length , string path , string filePath , IWpfTextView view )
334
+ private void RunClangFormatAndApplyReplacements ( string text , int offset , int length , string path , string filePath , OptionPageGrid options , IWpfTextView view )
258
335
{
259
336
try
260
337
{
261
- string replacements = RunClangFormat ( text , offset , length , path , filePath ) ;
338
+ string replacements = RunClangFormat ( text , offset , length , path , filePath , options ) ;
262
339
ApplyClangFormatReplacements ( replacements , view ) ;
263
340
}
264
341
catch ( Exception e )
@@ -283,7 +360,7 @@ private void RunClangFormatAndApplyReplacements(string text, int offset, int len
283
360
///
284
361
/// Formats the text range starting at offset of the given length.
285
362
/// </summary>
286
- private string RunClangFormat ( string text , int offset , int length , string path , string filePath )
363
+ private static string RunClangFormat ( string text , int offset , int length , string path , string filePath , OptionPageGrid options )
287
364
{
288
365
string vsixPath = Path . GetDirectoryName (
289
366
typeof ( ClangFormatPackage ) . Assembly . Location ) ;
@@ -293,16 +370,16 @@ private string RunClangFormat(string text, int offset, int length, string path,
293
370
process . StartInfo . FileName = vsixPath + "\\ clang-format.exe" ;
294
371
// Poor man's escaping - this will not work when quotes are already escaped
295
372
// in the input (but we don't need more).
296
- string style = GetStyle ( ) . Replace ( "\" " , "\\ \" " ) ;
297
- string fallbackStyle = GetFallbackStyle ( ) . Replace ( "\" " , "\\ \" " ) ;
373
+ string style = options . Style . Replace ( "\" " , "\\ \" " ) ;
374
+ string fallbackStyle = options . FallbackStyle . Replace ( "\" " , "\\ \" " ) ;
298
375
process . StartInfo . Arguments = " -offset " + offset +
299
376
" -length " + length +
300
377
" -output-replacements-xml " +
301
378
" -style \" " + style + "\" " +
302
379
" -fallback-style \" " + fallbackStyle + "\" " ;
303
- if ( GetSortIncludes ( ) )
380
+ if ( options . SortIncludes )
304
381
process . StartInfo . Arguments += " -sort-includes " ;
305
- string assumeFilename = GetAssumeFilename ( ) ;
382
+ string assumeFilename = options . AssumeFilename ;
306
383
if ( string . IsNullOrEmpty ( assumeFilename ) )
307
384
assumeFilename = filePath ;
308
385
if ( ! string . IsNullOrEmpty ( assumeFilename ) )
@@ -352,7 +429,7 @@ private string RunClangFormat(string text, int offset, int length, string path,
352
429
/// <summary>
353
430
/// Applies the clang-format replacements (xml) to the current view
354
431
/// </summary>
355
- private void ApplyClangFormatReplacements ( string replacements , IWpfTextView view )
432
+ private static void ApplyClangFormatReplacements ( string replacements , IWpfTextView view )
356
433
{
357
434
// clang-format returns no replacements if input text is empty
358
435
if ( replacements . Length == 0 )
@@ -369,70 +446,5 @@ private void ApplyClangFormatReplacements(string replacements, IWpfTextView view
369
446
}
370
447
edit . Apply ( ) ;
371
448
}
372
-
373
- /// <summary>
374
- /// Returns the currently active view if it is a IWpfTextView.
375
- /// </summary>
376
- private IWpfTextView GetCurrentView ( )
377
- {
378
- // The SVsTextManager is a service through which we can get the active view.
379
- var textManager = ( IVsTextManager ) Package . GetGlobalService ( typeof ( SVsTextManager ) ) ;
380
- IVsTextView textView ;
381
- textManager . GetActiveView ( 1 , null , out textView ) ;
382
-
383
- // Now we have the active view as IVsTextView, but the text interfaces we need
384
- // are in the IWpfTextView.
385
- var userData = ( IVsUserData ) textView ;
386
- if ( userData == null )
387
- return null ;
388
- Guid guidWpfViewHost = DefGuidList . guidIWpfTextViewHost ;
389
- object host ;
390
- userData . GetData ( ref guidWpfViewHost , out host ) ;
391
- return ( ( IWpfTextViewHost ) host ) . TextView ;
392
- }
393
-
394
- private string GetStyle ( )
395
- {
396
- var page = ( OptionPageGrid ) GetDialogPage ( typeof ( OptionPageGrid ) ) ;
397
- return page . Style ;
398
- }
399
-
400
- private string GetAssumeFilename ( )
401
- {
402
- var page = ( OptionPageGrid ) GetDialogPage ( typeof ( OptionPageGrid ) ) ;
403
- return page . AssumeFilename ;
404
- }
405
-
406
- private string GetFallbackStyle ( )
407
- {
408
- var page = ( OptionPageGrid ) GetDialogPage ( typeof ( OptionPageGrid ) ) ;
409
- return page . FallbackStyle ;
410
- }
411
-
412
- private bool GetSortIncludes ( )
413
- {
414
- var page = ( OptionPageGrid ) GetDialogPage ( typeof ( OptionPageGrid ) ) ;
415
- return page . SortIncludes ;
416
- }
417
-
418
- private string GetDocumentParent ( IWpfTextView view )
419
- {
420
- ITextDocument document ;
421
- if ( view . TextBuffer . Properties . TryGetProperty ( typeof ( ITextDocument ) , out document ) )
422
- {
423
- return Directory . GetParent ( document . FilePath ) . ToString ( ) ;
424
- }
425
- return null ;
426
- }
427
-
428
- private string GetDocumentPath ( IWpfTextView view )
429
- {
430
- ITextDocument document ;
431
- if ( view . TextBuffer . Properties . TryGetProperty ( typeof ( ITextDocument ) , out document ) )
432
- {
433
- return document . FilePath ;
434
- }
435
- return null ;
436
- }
437
449
}
438
450
}
0 commit comments