Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Roslyn による Visual Studio のアドイン

833 views

Published on

2013-07-27 新宿, 東京
こみゅぷらす Tech Aid 2013
http://comuplus.net/CLT2013/

Published in: Technology, News & Politics
  • Be the first to comment

Roslyn による Visual Studio のアドイン

  1. 1. COMU+ こみゅぷらす Sep 27, 2013 小島 富治雄 Roslyn による Visual Studio のアドイン
  2. 2. COMU+ こみゅぷらす 自己紹介 • 小島 富治雄 • @Fujiwo • 福井コンピュータアーキテクト株式会社 • Microsoft MVP C# (2005-2014)
  3. 3. COMU+ こみゅぷらす 本日の資料 • http://blog.shos.info/archives/2013/07/addinbyroslyn.html にて公開します • スライドは http://www.slideshare.net/Fujiwo/roslyn-visual-studio
  4. 4. COMU+ こみゅぷらす 宣伝 • 来週土曜 「Hokuriku.NET Vol.12 in 福井」 http://atnd.org/events/40509 ※ 個人の体験です。効果には個人差があります。
  5. 5. COMU+ こみゅぷらす Roslyn について
  6. 6. COMU+ こみゅぷらす Roslyn • C# や Visual Basic のコンパイラー の内部の API 等を公開 • "Compiler as a Service" と表現 http://www.zdnet.com/blog/microsoft/microsofts-roslyn-compiler-as-a-service-support-unlikely-to-be-in-visual-studio-2012/10896 より
  7. 7. COMU+ こみゅぷらす Microsoft “Roslyn” CTP • http://msdn.microsoft.com/en- us/vstudio/roslyn.aspx
  8. 8. COMU+ こみゅぷらす Roslyn • 参考: Channel 9 のビデオと資料 – Future directions for C# and Visual Basic - BUILD2011 (September 15, 2011) – Going Deeper with Project Roslyn: Exposing the C# and VB compiler’s code analysis - Lang.NEXT 2012 (April 3, 2012) – Roslyn Anders Hejlsberg Q&A: TypeScript, C#, Roslyn, and More - Build 2013 (June 27, 2013)
  9. 9. COMU+ こみゅぷらす Roslyn の機能 • C# や Visual Basic のソースコード解析機能 • Visual Studio の拡張機能 • C#スクリプト機能 • 等 http://www.zdnet.com/blog/microsoft/microsofts-roslyn-compiler-as-a-service-support-unlikely-to-be-in-visual-studio-2012/10896 より
  10. 10. COMU+ こみゅぷらす 本日のデモ • 今日は、「ソースコード解析機能」を用いて、 簡単な Visual Studio のアドインを • C# のソースコードを解析して、識別子等を数え て、それを WPF で作成したウィンドウに表示
  11. 11. COMU+ こみゅぷらす 「Visual Studio アドイン」プロジェクトの新規作成
  12. 12. COMU+ こみゅぷらす NuGet から Roslyn をインストール
  13. 13. COMU+ こみゅぷらす Roslyn 追加後のプロジェクトの参照設定
  14. 14. COMU+ こみゅぷらす ViewModel の作成 • 今回 WPF で出すウィンドウ用のViewModel • ソースコード内の識別子や using、クラス等の 数を保持 ※ 実装の参考: 「INotifyPropertyChanged の実装に便利なクラスとコードスニペット」
  15. 15. COMU+ こみゅぷらす ViewModel using System; using System.ComponentModel; using System.Linq.Expressions; using System.Runtime.CompilerServices; namespace AddinByRoslyn { public static class PropertyChangedEventHandlerExtensions { public static void Raise(this PropertyChangedEventHandler onPropertyChanged, object sender, [CallerMemberName] string propertyName = "") { if (onPropertyChanged != null) onPropertyChanged(sender, new PropertyChangedEventArgs(propertyName)); } public static void Raise<PropertyType>(this PropertyChangedEventHandler onPropertyChanged, object sender, Expression<Func<PropertyType>> propertyExpression) { onPropertyChanged.Raise(sender, propertyExpression.GetMemberName()); } static string GetMemberName<MemberType>(this Expression<Func<MemberType>> expression) { return ((MemberExpression)expression.Body).Member.Name; } }
  16. 16. COMU+ こみゅぷらす ViewModelpublic class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; int identifierCount = 0; public int IdentifierCount { get { return identifierCount; } set { if (value != identifierCount) { identifierCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } int usingCount = 0; public int UsingCount { get { return usingCount; } set { if (value != usingCount) { usingCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } int classCount = 0; public int ClassCount { get { return classCount; } set { if (value != classCount) { classCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } int fieldCount = 0; public int FieldCount { get { return fieldCount; } set { if (value != fieldCount) { fieldCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } }
  17. 17. COMU+ こみゅぷらす ViewModel int propertyCount = 0; public int PropertyCount { get { return propertyCount; } set { if (value != propertyCount) { propertyCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } int methodCount = 0; public int MethodCount { get { return methodCount; } set { if (value != methodCount) { methodCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } int variableCount = 0; public int VariableCount { get { return variableCount; } set { if (value != variableCount) { variableCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } int ifCount = 0; public int IfCount { get { return ifCount; } set { if (value != ifCount) { ifCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } }
  18. 18. COMU+ こみゅぷらす ViewModel int lambdaCount = 0; public int LambdaCount { get { return lambdaCount; } set { if (value != lambdaCount) { lambdaCount = value; PropertyChanged.Raise(this); RaiseUpdateData(); } } } public object Data { get { return new [] { new { 名称 = "識別子の数" , 結果 = IdentifierCount }, new { 名称 = "using の数" , 結果 = UsingCount }, new { 名称 = "クラスの数" , 結果 = ClassCount }, new { 名称 = "フィールドの数", 結果 = FieldCount }, new { 名称 = "プロパティの数", 結果 = PropertyCount }, new { 名称 = "メソッドの数" , 結果 = MethodCount }, new { 名称 = "変数の数" , 結果 = VariableCount }, new { 名称 = "if の数" , 結果 = IfCount }, new { 名称 = "ラムダ式の数" , 結果 = string result = string.Empty; public string Result { get { return result; } set { if (value != result) { result = value; PropertyChanged.Raise(this); } } } public void Clear() { IdentifierCount = UsingCount = ClassCount = FieldCount = PropertyCount = MethodCount = VariableCount = IfCount = LambdaCount = 0; Result = string.Empty; } void RaiseUpdateData() { PropertyChanged.Raise(this, () => Data); } } }
  19. 19. COMU+ こみゅぷらす Roslyn を用いたコード解析部 • Roslyn.Compilers.CSharp.SyntaxWalker クラスを継承 • SyntaxWalker クラス – Visitor パターン – Visit メソッドを呼ぶと、全ノードを辿 り、ノードの種類毎の virtual メソッド を呼ぶ • 例えば、識別子のノードの場合には、 VisitIdentifierName という virtual メソッ ドが呼ばれる • それぞれの virtual メソッドをオーバーライ ドすれば、そこに処理を書ける • 今日は、呼ばれた回数を数えるだけ
  20. 20. COMU+ こみゅぷらす Roslyn を用いたコード解析部 using Roslyn.Compilers.CSharp; using System.IO; using System.Text; namespace AddinByRoslyn { class SyntaxCounter : SyntaxWalker { readonly ViewModel viewModel; StringBuilder stringBuilder; // viewModel.Result 用 public SyntaxCounter(ViewModel viewModel) { this.viewModel = viewModel; } // ソース ファイルの中を解析 public void AnalyzeSourceFile(string sourceFileName) { using (var reader = new StreamReader(sourceFileName, false)) { var sourceCode = reader.ReadToEnd(); Analyze(sourceCode); } }
  21. 21. COMU+ こみゅぷらす Roslyn を用いたコード解析部 // ソース コードの中を解析 void Analyze(string sourceCode) { // Roslyn.Compilers.CSharp.SyntaxTree クラスによるシンタックス ツリーの取得 var tree = SyntaxTree.ParseText(sourceCode); Analyze(tree.GetRoot()); // シンタックス ツリーのルート要素を解析 } // ルート要素の中を解析 void Analyze(CompilationUnitSyntax root) { viewModel.Clear(); stringBuilder = new StringBuilder(); Visit(root); // Visit メソッドにより、全ノードを辿る (Visitor パターン) viewModel.Result = stringBuilder.ToString(); } // 全ノードについて public override void DefaultVisit(SyntaxNode node) { base.DefaultVisit(node); // ノードの情報を stringBuilder に追加 stringBuilder.AppendLine(string.Format("NodeType: {0}, Node: {1}", node.GetType().Name, node.GetText())); }
  22. 22. COMU+ こみゅぷらす Roslyn を用いたコード解析部// 識別子のノードの場合 public override void VisitIdentifierName(IdentifierNameSyntax node) { base.VisitIdentifierName(node); viewModel.IdentifierCount++; // 識別子を数える } // using のノードの場合 public override void VisitUsingDirective(UsingDirectiveSyntax node) { base.VisitUsingDirective(node); viewModel.UsingCount++; // using を数える } // クラスのノードの場合 public override void VisitClassDeclaration(ClassDeclarationSyntax node) { base.VisitClassDeclaration(node); viewModel.ClassCount++; // クラスを数える } // フィールドのノードの場合 public override void VisitFieldDeclaration(FieldDeclarationSyntax node) { base.VisitFieldDeclaration(node); viewModel.FieldCount++; // フィールドを数える }
  23. 23. COMU+ こみゅぷらす Roslyn を用いたコード解析部 // プロパティのノードの場合 public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) { base.VisitPropertyDeclaration(node); viewModel.PropertyCount++; // プロパティを数える } // メソッドのノードの場合 public override void VisitMethodDeclaration(MethodDeclarationSyntax node) { base.VisitMethodDeclaration(node); viewModel.MethodCount++; // メソッドを数える } // 変数のノードの場合 public override void VisitVariableDeclaration(VariableDeclarationSyntax node) { base.VisitVariableDeclaration(node); viewModel.VariableCount++; // 変数を数える } // if のノードの場合 public override void VisitIfStatement(IfStatementSyntax node) { base.VisitIfStatement(node);
  24. 24. COMU+ こみゅぷらす Roslyn を用いたコード解析部 // シンプルなラムダ式のノードの場合 public override void VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node) { base.VisitSimpleLambdaExpression(node); viewModel.LambdaCount++; // ラムダ式を数える } // 括弧付きのラムダ式のノードの場合 public override void VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node) { base.VisitParenthesizedLambdaExpression(node); viewModel.LambdaCount++; // ラムダ式を数える } } }
  25. 25. COMU+ こみゅぷらす View.xaml • XAML の Grid 内 – ViewModel の Result を表示するための TextBox – ViewModel の Data を表示するための DataGrid • プロジェクトに System.Xaml への参照設定を追 加
  26. 26. COMU+ こみゅぷらす View.xaml <Window x:Class="AddinByRoslyn.View" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="500" d:DesignWidth="500"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBox TextWrapping="Wrap" Text="{Binding Path=Result}" FontFamily="Meiryo" FontSize="14" IsReadOnlyCaretVisible="True" VerticalScrollBarVisibility="Auto" /> <DataGrid Grid.Row="1" ItemsSource="{Binding Path=Data}" FontFamily="Meiryo" FontSize="14" /> </Grid> </Window>
  27. 27. COMU+ こみゅぷらす View.xaml.cs • ViewModel のインスタンスを保持 – DataContext • コンストラクターで C# のソース ファイル名を 受け取る – SyntaxCounter クラスで解析
  28. 28. COMU+ こみゅぷらす View.xaml.cs using System.Windows; namespace AddinByRoslyn { public partial class View : Window { ViewModel viewModel = new ViewModel(); public View(string sourceFileName) { InitializeComponent(); DataContext = viewModel; // DataContext に ViewModel のインスタンスを設定 if (!string.IsNullOrWhiteSpace(sourceFileName)) // SyntaxCounter クラスを用いたソース ファイルの解析 new SyntaxCounter(viewModel).AnalyzeSourceFile(sourceFileName); } } }
  29. 29. COMU+ こみゅぷらす • ViewModel のインスタンスを保持 – DataContext • コンストラクターで C# のソース ファイル名を 受け取る – SyntaxCounter クラスで解析
  30. 30. COMU+ こみゅぷらす Visual Studio アドイン部 • Visual Studio アドイン部のコード をカスタマイズ –プロジェクトの新規作成時に自動生成 された Connect.cs 内 –Exec メソッドの中で View を作成し、 ソースコード ファイル名を渡す
  31. 31. COMU+ こみゅぷらす Visual Studio アドイン部 // カスタマイズする Exec メソッド public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled) { if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault && commandName == "AddinByRoslyn.Connect.AddinByRoslyn") { var view = new View(GetSourceFileName()); // View のインスタンスにソースファイル名を渡す view.Show(); handled = true; } else { handled = false; } } // ソース ファイル名の取得 (追加する private メソッド) string GetSourceFileName() { var items = _applicationObject.SelectedItems; if (items.Count == 0) return null; var item = items.Item(1); return item.ProjectItem.get_FileNames(1); }
  32. 32. COMU+ こみゅぷらす 実行結果
  33. 33. COMU+ こみゅぷらす 実行結果
  34. 34. COMU+ こみゅぷらす まとめ • 今日は、Roslyn を使って何種類かの文法要素を 数えただけ • 同様の方法で、より複雑な解析を行ったり、一 部を変更できる • Roslyn の機能は C# や Visual Basic のソース コードの解析だけではない
  35. 35. COMU+ こみゅぷらす

×