前段時間我發布了 github開源:企業級應用快速開發框架CIIP WEB+WIN+移動端,很多園友們都表示支持並與我探討相關技術問題,上篇中我也承諾會寫相關的技術文章,本篇就來介紹一下建模模塊中使用的動態編譯技術。
說是動態編譯技術有些片面,因為在框架中,具體應用到的是建模模塊,在CIIP中,使用的是xpo的orm,基於XAF開發,而在XAF中建模過程就是以codefirst(多數人喜愛的)方式寫BusinessObject(BO、Entity)的過程。所以建模的過程多數工作就變成了組裝出BusinessObject的過程了。
在CIIP中,嘗試過下面列出的2,3,4的方式:
public class 單據基類<單據明細> { } public class 單據明細<單據基類> { } public class 訂單:單據基類<訂單明細> { } public class 訂單明細:單據明細<訂單> { }
比如上面的代碼,當然還有構造函數,生成就是有問題的。我查詢了 裝配腦袋 的早期博文,在解析程序集、類,時再去調用CreateType,對於他舉出的例子中是可用的,但在上述情景下,是不能用的,這個比他的例子還要復雜些。
比如給出一段代碼,並給出當前光標位置,Roslyn的相關API,就會結合整個project/solution計算出可以給出哪些提示條目,如:var x = new 光標在這裡時,將會列出可以new出哪些類的名稱,比如,引用了.net的程序集,自己項目中已經編寫的類型等,當然,會過濾掉abstract的類型.
效果如下:
如果想了解CIIP中的建模模塊的具體操作方法,請看這裡.
那麼,如何在自己的項目中集成上面的Roslyn呢?
第一步,來這裡下載Roslyn,當然用netget在vs中獲取也是可以的,不過有時很慢. https://github.com/tylike/CIIP/tree/master/packages 下接下載比較省心.
第二步,在nuget中搜索Avalonedit.是的,上圖中的編輯器是Avalonedit,也是開源的,mono在linux下的sharpDevelop就是用的這個控件.需要注意的是,AvalonEdit是WPF控件,在Winform中可以用ElementHost這件控件連接WPF和普通winform的世界.
下面來看看代碼,在CIIP中,下面這個路徑是代碼編輯器相關的內容:
https://github.com/tylike/CIIP/tree/master/CIIP.Module.Win/Editors/CodeEditor
首先,來看看這個SmartVisualStudio.cs,名字起的有點大哈, ^_^
代碼不算多,才300多行,這個控件主要是先包裝一下,做個自定義控件出來,包含了使用ElementHost.
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 CIIP.Module.BusinessObjects.SYS; using System.Windows.Input; using System.Diagnostics; using ICSharpCode.AvalonEdit.CodeCompletion; using System.Windows.Forms.Integration; using ICSharpCode.AvalonEdit.Folding; using Microsoft.CodeAnalysis; using CIIP.Module.BusinessObjects.SYS.Logic; using DevExpress.ExpressApp; using System.IO; using System.Xml; using ICSharpCode.AvalonEdit.Highlighting; using System.Text.RegularExpressions; using DevExpress.XtraEditors; namespace CIIP.Module.Win.Editors { public partial class SmartVisualStudio : UserControl { protected SmartVisualStudio() { InitializeComponent(); } readonly SmartIDEWorkspace _workspace; public ICSharpCode.AvalonEdit.TextEditor Editor { get; set; } //IList<BusinessObject> businessObjects; //MethodDefine method; //private BusinessObjectPartialLogic logic; private CsharpCode _codeObject; readonly IDocumentProvider _document; public SmartVisualStudio(IObjectSpace os, CsharpCode value) : this() { this._codeObject = value; CreateEditor(); if (value != null) { if (value.Workspace != null) { this._workspace = (SmartIDEWorkspace) value.Workspace; } else { this._workspace = SmartIDEWorkspace.GetIDE(os); } this._document = value.Provider; } #region 預設置智能感知項目,如果是一個方法,就需要先看一下方法中可以用的智能感知條目列表 if (_document is IPartCodeProvider && _document!=null) { var code = value?.Code + ""; IList<ICompletionData> list = new List<ICompletionData>(); _workspace.GetIntellisenseItems(_document, 0, true, code, null, list); } if (value == null) { Editor.IsEnabled = false; } #endregion #region 設置環境 if (value != null) { tabSolution.Visibility = value.ShowSolutionFiles ? DevExpress.XtraBars.Docking.DockVisibility.Visible : DevExpress.XtraBars.Docking.DockVisibility.Hidden; if (value.ShowSolutionFiles) { solutionTreeView.Nodes.Clear(); solutionTreeView.MouseClick += SolutionTreeView_MouseClick; var solution = solutionTreeView.Nodes.Add("Solution"); foreach (var item in _workspace.Workspace.CurrentSolution.Projects) { var projectNode = solution.Nodes.Add(item.Name, item.Name); projectNode.Tag = item; var references = projectNode.Nodes.Add("引用", "引用"); foreach (var refence in item.MetadataReferences) { references.Nodes.Add(refence.Display); } foreach (var doc in item.Documents.OrderBy(x=>x.Name)) { var docNode = projectNode.Nodes.Add(doc.Name, doc.Name); docNode.Tag = doc; } } } if (value.Diagnostics != null && value.Diagnostics.Count > 0) { this.SetDiagnosticMessage(value.Diagnostics); } } #endregion } private void SolutionTreeView_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { if (e.Button == MouseButtons.Right) { var fbd = new FolderBrowserDialog(); if (fbd.ShowDialog() == DialogResult.OK) { var path = fbd.SelectedPath; foreach (var item in _workspace.Workspace.CurrentSolution.Projects.First().Documents) { //var file = File.(path + "\\" + item.Name); File.WriteAllText(path + "\\" + item.Name, item.GetTextAsync().Result.ToString()); } XtraMessageBox.Show("導出代碼完成!"); } } } private void CreateEditor() { Editor = new ICSharpCode.AvalonEdit.TextEditor(); Editor.TextArea.TextEntering += TextArea_TextEntering; Editor.TextArea.TextEntered += TextArea_TextEntered; //Editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#"); Editor.ShowLineNumbers = true; using (StreamReader s = new StreamReader(AdmiralEnvironment.ApplicationPath + @"\\VSCSharp.xshd")) { using (XmlTextReader reader = new XmlTextReader(s)) { Editor.SyntaxHighlighting = ICSharpCode.AvalonEdit.Highlighting.Xshd.HighlightingLoader.Load( reader, HighlightingManager.Instance); } } Editor.FontFamily = new System.Windows.Media.FontFamily("Consolas"); Editor.FontSize = 12; //Editor.SyntaxHighlighting.MainRuleSet.Rules Editor.TextArea.IndentationStrategy = new ICSharpCode.AvalonEdit.Indentation.CSharp.CSharpIndentationStrategy(Editor.Options); var foldingManager = FoldingManager.Install(Editor.TextArea); var foldingStrategy = new BraceFoldingStrategy(); this.elementHost1.Child = Editor; } private void Listview_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { if (e.Button == MouseButtons.Right && ErrorListView.SelectedItems.Count > 0) { var selected = ErrorListView.SelectedItems[0]; var item = selected.Tag as Diagnostic; GoogleTranslator.TranslateGoogleString(selected.Text); //client.Translate("", selected.Text, "en", "zh-CHS", "text/plan", "general", ""); } } private void Listview_DoubleClick(object sender, EventArgs e) { if (ErrorListView.SelectedItems.Count > 0) { var selected = ErrorListView.SelectedItems[0]; var item = selected.Tag as Diagnostic; if (_document is IPartCodeProvider) { var part = _document as IPartCodeProvider; var code = _workspace.GetText(_document); var begin = code.IndexOf(part.DefaultLocation); var l = item.Location.SourceSpan; if (l.Start > begin) { Editor.Select(l.Start - begin - part.DefaultLocation.Length - 1, l.Length > 0 ? l.Length : 1); } } else { var line = item.Location.GetLineSpan(); var path =line.Path; var doc = _workspace.Workspace.CurrentSolution.Projects.First().Documents.First(x => x.Name == path); OpenFile(doc); Editor.Select(item.Location.SourceSpan.Start, item.Location.SourceSpan.Length); Editor.ScrollToLine(line.StartLinePosition.Line); } } } public bool Validated() { var diags = _workspace.GetDiagnostics(Editor.Text, this._document); SetDiagnosticMessage(diags); return ErrorListView.Items.Count == 0; } private void SetDiagnosticMessage(IEnumerable<Diagnostic> diags) { ErrorListView.Items.Clear(); if (diags != null) { var zhcn = System.Globalization.CultureInfo.CreateSpecificCulture("zh-CN"); foreach (var item in diags.Where(x => x.DefaultSeverity != DiagnosticSeverity.Hidden)) { var line = item.Location.GetLineSpan().ToString(); var lvi = new ListViewItem(new string[] { item.Severity.ToString(), item.GetMessage(zhcn), line }, 0); lvi.Tag = item; ErrorListView.Items.Add(lvi); } } } private string SetStatusBarText() { return "行:" + Editor.TextArea.Caret.Position.Line + " 列:" + Editor.TextArea.Caret.Position.Column; } //默認為沒有輸入 private int startTokenPosition = -1; private int inputedLen = 0; private void TextArea_TextEntered(object sender, TextCompositionEventArgs e) { Debug.WriteLine("TextArea_TextEntered:" + e.Text); if (completionWindow == null && (e.Text == "." || e.Text == " " || e.Text == "\t" || e.Text == "(" || e.Text == "[")) { // Open code completion after the user has pressed dot: completionWindow = new CompletionWindow(Editor.TextArea); completionWindow.Width = 300; var data = completionWindow.CompletionList.CompletionData; _workspace.GetIntellisenseItems(this._document, Editor.CaretOffset, e.Text != ".", Editor.Text, null, data); completionWindow.Show(); completionWindow.Closed += delegate { completionWindow = null; }; } if (e.Text == "\n") { this.Validated(); this.OnValueChanged?.Invoke(this, null); } } private void TextArea_TextEntering(object sender, TextCompositionEventArgs e) { Debug.WriteLine("TextArea_TextEntering:" + e.Text); if (e.Text.Length > 0 && completionWindow != null) { if (!char.IsLetterOrDigit(e.Text[0])) { completionWindow.CompletionList.RequestInsertion(e); } } } public event EventHandler OnValueChanged; CompletionWindow completionWindow; public CsharpCode Code { get { if (this._codeObject != null) this._codeObject.Code = Editor.Text; return _codeObject; } set { _codeObject = value; if (value == null) { Editor.Text = ""; } else { Editor.Text = value.Code; } } } private void solutionTreeView_DoubleClick(object sender, EventArgs e) { if (solutionTreeView.SelectedNode != null) { var s = solutionTreeView.SelectedNode.Tag as Document; if (s != null) { OpenFile(s); } } } private void OpenFile(Document s) { Editor.Text = _workspace.Workspace.CurrentSolution.Projects.Single(x => x.Id == s.Project.Id).Documents.Single(x => x.Name == s.Name).GetTextAsync().Result.ToString(); } } } View Code
多數邏輯都是調用WorkSpace來工作的.
再來看看WorkSpace的代碼:
他對應著VS中的solution所在容器.簡單的來講,下面的代碼就是組裝出一個Solution,Project,及每個文件,文件是根據CIIP的業務模型來生成的.業務模型與.net中的類型信息很相似,內容也稍多了些,此處先不詳細說明了,有興趣的同學可以讀下源碼.
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using CIIP.Module.BusinessObjects.SYS; using CIIP.Module.BusinessObjects.SYS.BOBuilder; using CIIP.Module.BusinessObjects.SYS.Logic; using DevExpress.Data.Filtering; using DevExpress.ExpressApp; using CIIP; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Formatting; using System.Windows.Media.Imaging; using ICSharpCode.AvalonEdit.CodeCompletion; using System.Windows.Media; using CIIP.Module.BusinessObjects.Flow; using DevExpress.Xpo; namespace CIIP.Module.Win.Editors { /// <summary> /// 用於編輯器的後台服務,包含了Solution等信息 /// </summary> public class SmartIDEWorkspace { public static SmartIDEWorkspace GetIDE(IObjectSpace os) { return new SmartIDEWorkspace(os); //if (Instance == null) //{ // Instance = new SmartIDEWorkspace(os); //} //return Instance; } public static SmartIDEWorkspace Instance { get; private set; } public AdhocWorkspace Workspace { get; } Project ModuleProject { get { return Workspace.CurrentSolution.Projects.First(); } } private IObjectSpace objectSpace; public SmartIDEWorkspace(IObjectSpace objectSpace) { this.Workspace = new AdhocWorkspace();// MSBuildWorkspace.Create(); this.objectSpace = objectSpace; CreateSolution(); InitializeKeywordItems(); } string keywords = "var dynamic abstract as base break case catch checked continue default delegate do else event explicit extern false finally fixed for foreach goto if implicit in interface internal is lock namespace new null object operator out override params private protected public readonly ref return sealed sizeof stackalloc switch this throw true try typeof unchecked unsafe using virtual while bool byte char class const decimal double enum float int long sbyte short static string struct uint ulong ushort void"; private readonly List<CompletionData> keywordItmes = new List<CompletionData>(); //public string Code { get; } List<ImageSource> images = new List<ImageSource>(); private void InitializeKeywordItems() { var path = AdmiralEnvironment.ApplicationPath + "\\AutoCompleteIcons\\"; for (int i = 0; i <= 150; i++) { images.Add(new BitmapImage(new Uri(path + i + ".bmp"))); } var ks = keywords.Split(' '); foreach (var s in ks) { keywordItmes.Add(new CompletionData(s, images[0], "", TokenType.iKeyWords)); } } public EmitResult Compile() { var outputPath = AdmiralEnvironment.UserDefineBusinessTempFile.FullName; foreach (var doc in this.ModuleProject.Documents) { Debug.WriteLine(doc.Name); } var pdb = outputPath + ".pdb"; return ModuleProject.GetCompilationAsync().Result.Emit(outputPath); } public IEnumerable<Diagnostic> GetDocumentDiagnostic(Guid documentGuid) { return Documents[documentGuid].GetSemanticModelAsync().Result.GetDiagnostics(); } protected void CreateSolution() { CreateModuleProject(); } List<MetadataReference> CollectReferencedAssemblies() { List<MetadataReference> refs = new List<MetadataReference>(); var asms = AppDomain.CurrentDomain.GetAssemblies(); foreach (var item in typeof(ERPModule).Assembly.GetReferencedAssemblies()) { var asm = asms.SingleOrDefault(x => x.FullName == item.FullName); if (asm == null) { asm = Assembly.Load(item); } if (asm == null) { throw new Exception("Not found referenced assembly:" + item.FullName); } refs.Add(MetadataReference.CreateFromFile(asm.Location)); } refs.Add(MetadataReference.CreateFromFile(typeof(CIIP.Module.ERPModule).Assembly.Location)); refs.Add(MetadataReference.CreateFromFile(typeof(XPCollection).Assembly.Location)); return refs; } private void CreateModuleProject() { var refs = CollectReferencedAssemblies(); var moduleProjectName = "RuntimeModule"; var moduleProjectID = ProjectId.CreateNewId(); var versionStamp = VersionStamp.Create(); var projInfo = ProjectInfo.Create(moduleProjectID, versionStamp, moduleProjectName, moduleProjectName, LanguageNames.CSharp, compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); projInfo = projInfo.WithMetadataReferences(refs); Workspace.AddProject(projInfo); //取得所有用戶定義的業務類型,並生成文檔. var businessObjects = this.objectSpace.GetObjects<BusinessObject>(new BinaryOperator("IsRuntimeDefine", true)); CreateAssembyInfoDocument(); CreateRuntimeModule(); Documents.Clear(); foreach (var bo in businessObjects) { CreateDocument(bo); } var partialLogics = this.objectSpace.GetObjects<BusinessObjectPartialLogic>(null,true); foreach (var logic in partialLogics) { CreateDocument(logic); } var flowactions = this.objectSpace.GetObjects<FlowAction>(null, true); foreach (var flowacion in flowactions) { CreateDocument(flowacion); } var layouts = this.objectSpace.GetObjects<BusinessObjectLayout>(); foreach (var item in layouts) { CreateDocument(item); } var controllers = this.objectSpace.GetObjects<RuntimeController>(null,true); foreach (var item in controllers) { CreateDocument(item); } } string AggregatedAttribute = typeof(AggregatedAttribute).FullName; public static string GetCommonUsing() { return BusinessObject.CommonUsing(); } private Dictionary<Guid, Document> Documents = new Dictionary<Guid, Document>(); private Dictionary<Guid, SemanticModel> SemanticModels = new Dictionary<Guid, SemanticModel>(); public void CreateAssembyInfoDocument() { #region GetVersion var ver = BusinessBuilder.GetVersion(AdmiralEnvironment.UserDefineBusinessFile); if (ver != null) { ver = new Version(ver.Major + 1, ver.Minor, ver.Build, ver.Revision); } else { ver = new Version(1, 0, 0, 0); } #endregion var str = $"[assembly: {typeof (AssemblyVersionAttribute).FullName}(\"{ver.ToString()}\")]\n"; Workspace.AddDocument(ModuleProject.Id, "AssemblyInfo.cs", SourceText.From(str,Encoding.UTF8)); } public void CreateRuntimeModule() { var str = $"public class RuntimeModule:{typeof(RuntimeModuleBase).FullName} {{ public RuntimeModule():base(){{}} }}\n"; Workspace.AddDocument(ModuleProject.Id, "RunttimeModule.cs", SourceText.From(str,Encoding.UTF8)); } #region document provider services #region create document private void CreateDocument(IDocumentProvider documentProvider) { var doc = Workspace.AddDocument(ModuleProject.Id, documentProvider.GetFileName() + ".cs", SourceText.From(documentProvider.GetCode(),Encoding.UTF8)); var updated = Workspace.TryApplyChanges(doc.Project.Solution); Debug.WriteLine("Updated:" + updated); Documents.Add(documentProvider.GetDocumentGuid(), doc); SemanticModels.Add(documentProvider.GetDocumentGuid(), doc.GetSemanticModelAsync().Result); } #endregion #region get text public string GetText(IDocumentProvider doc) { return Documents[doc.GetDocumentGuid()].GetTextAsync().Result.ToString(); } #endregion #region update text private void UpdateText(string text, IDocumentProvider doc) { SourceText sourceText = SourceText.From(text,Encoding.UTF8); var document = Documents[doc.GetDocumentGuid()]; document = document.WithText(sourceText); var rs = Workspace.TryApplyChanges(document.Project.Solution); Debug.WriteLine("enter updated:" + rs); //重要:當更改了項目後,文檔實例被變化了,必須重新保存 document = this.ModuleProject.Documents.First(x => x.Id == document.Id); Documents[doc.GetDocumentGuid()] = document; SemanticModels[doc.GetDocumentGuid()] = document.GetSemanticModelAsync().Result; } #endregion #region get intellisense items /// <summary> /// 取得光標位置的可用智能感知項目 /// </summary> /// <param name="bodytext">代碼</param> /// <param name="logic">對應項目</param> /// <param name="index">光標所在位置</param> /// <returns>可能智能感知項目</returns> public IEnumerable<ISymbol> GetRecommendedSymbolsAtPositionAsync(string bodytext, IDocumentProvider logic, int index) { if (logic is IPartCodeProvider) { var doc = Documents[logic.GetDocumentGuid()]; bodytext = (logic as IPartCodeProvider).ReplaceNewCode(doc.GetTextAsync().Result.ToString(), bodytext); //var doc = Documents[method.BusinessObject.Oid]; //var newText = method.ReplaceNewCode(doc.GetTextAsync().Result.ToString(), bodytext); //UpdateText(newText, method.BusinessObject); //var semanticModel = SemanticModels[method.BusinessObject.Oid]; //return Recommender.GetRecommendedSymbolsAtPositionAsync(semanticModel, pos, Workspace).Result; } UpdateText(bodytext, logic); if (logic is IPartCodeProvider) { var define = (logic as IPartCodeProvider).DefaultLocation; index = bodytext.IndexOf(define) + define.Length + index + 1; } var semanticModel = SemanticModels[logic.GetDocumentGuid()]; return Recommender.GetRecommendedSymbolsAtPositionAsync(semanticModel, index, Workspace).Result; } public void GetIntellisenseItems(IDocumentProvider document, int cartIndex, bool needKeyword, string code, string inputed, IList<ICompletionData> result) { IEnumerable<ISymbol> symbols = GetRecommendedSymbolsAtPositionAsync(code, document, cartIndex); ProcessSymbols(needKeyword, symbols, inputed, result); } private void ProcessSymbols(bool needKeyword, IEnumerable<ISymbol> symbols, string inputed, IList<ICompletionData> list) { TokenType idx = TokenType.iValueType; if (!string.IsNullOrEmpty(inputed)) { symbols = symbols.Where(x => x.Name.Contains(inputed)).ToArray(); } if (symbols == null) { } foreach (var symbol in symbols) { if (symbol is INamespaceSymbol) { idx = TokenType.iNamespace; } else if (symbol is ITypeSymbol) { idx = TokenType.iClass; } else if (symbol is IMethodSymbol) { var m = symbol as IMethodSymbol; if (m.IsExtensionMethod) { idx = TokenType.iMethodShortCut; } else if (symbol.DeclaredAccessibility == Accessibility.Public) { idx = TokenType.iMethod; } else if (symbol.DeclaredAccessibility == Accessibility.Protected) { idx = TokenType.iMethodProtected; } else if (symbol.DeclaredAccessibility == Accessibility.Private) { idx = TokenType.iMethodPrivate; } else if (symbol.DeclaredAccessibility == Accessibility.Internal) { idx = TokenType.iMethodFriend; } else { idx = TokenType.iMethodShortCut; } } else if (symbol is IPropertySymbol) { if (symbol.DeclaredAccessibility == Accessibility.Public) { idx = TokenType.iProperties; } else if (symbol.DeclaredAccessibility == Accessibility.Protected) { idx = TokenType.iPropertiesProtected; } else if (symbol.DeclaredAccessibility == Accessibility.Private) { idx = TokenType.iPropertiesPrivate; } else if (symbol.DeclaredAccessibility == Accessibility.Internal) { idx = TokenType.iPropertiesFriend; } else { idx = TokenType.iPropertiesShortCut; } } else if (symbol is IFieldSymbol) { if (symbol.DeclaredAccessibility == Accessibility.Public) { idx = TokenType.iField; } else if (symbol.DeclaredAccessibility == Accessibility.Protected) { idx = TokenType.iFieldProtected; } else if (symbol.DeclaredAccessibility == Accessibility.Private) { idx = TokenType.iFieldPrivate; } else if (symbol.DeclaredAccessibility == Accessibility.Internal) { idx = TokenType.iFieldFriend; } else { idx = TokenType.iFieldShortCut; } } else if (symbol is IEventSymbol) { if (symbol.DeclaredAccessibility == Accessibility.Public) { idx = TokenType.iEvent; } else if (symbol.DeclaredAccessibility == Accessibility.Protected) { idx = TokenType.iEventProtected; } else if (symbol.DeclaredAccessibility == Accessibility.Private) { idx = TokenType.iEventPrivate; } else if (symbol.DeclaredAccessibility == Accessibility.Internal) { idx = TokenType.iEventFriend; } else { idx = TokenType.iEventFriend; } } else if (symbol is ILocalSymbol) { idx = TokenType.iValueType; } else if (symbol is IParameterSymbol) { idx = TokenType.iField; } else if (symbol is IPreprocessingSymbol) { idx = TokenType.iProperties; } //if (idx != (int) AutoListIcons.iNamespace) if (symbol is IMethodSymbol) { var m = symbol as IMethodSymbol; list.Add( new CompletionData( symbol.Name + "(" + string.Join(",", m.Parameters.Select(x => x.Type.Name)) + ")", images[(int)idx], m.ToString(), idx)); } else { list.Add(new CompletionData(symbol.Name, images[(int)idx], "", idx)); } } if (needKeyword) { foreach (var item in keywordItmes) { list.Add(item); } } } #endregion #region get diagnostics public IEnumerable<Diagnostic> GetDiagnostics(string text, IDocumentProvider method) { if (method is IPartCodeProvider) { var doc = Documents[method.GetDocumentGuid()]; text = (method as IPartCodeProvider).ReplaceNewCode(doc.GetTextAsync().Result.ToString(), text); } UpdateText(text, method); var rst = SemanticModels[method.GetDocumentGuid()].GetDiagnostics(); return rst; } #endregion #endregion public string GetAllCode() { var sb = new StringBuilder(); foreach (var document in ModuleProject.Documents) { var root = document.GetSyntaxRootAsync().Result; var formattedResult = Formatter.Format(root, this.Workspace); var code = formattedResult.GetText().ToString(); sb.AppendLine(code); } return sb.ToString(); } } } View Code值得說明的是,取得當前光標所在位置的可用智能感知項目是通過這句關鍵代碼實現的.
return Recommender.GetRecommendedSymbolsAtPositionAsync(semanticModel, index, Workspace).Result;
另外,可以從圖中看出,圖中顯示的代碼只是片斷,並沒有類的定義,using,命名空間的內容,這裡只是使用了一個小技巧,即:將當前編輯器中的代碼嵌入到了寫好的類型文件中,在敲下代碼時,不停的去替換代碼,最終得到正確的編碼文件,然後再去取得智能感知的內容.
除了建模中用到了代碼編輯器,在單據轉換流程中,也有相關應用,下篇我將描述實現思路,先上一個圖看看效果吧!:D