没有技术说明文档的开源都是耍流氓:微软Roslyn编译即服务在CIIP中具体应用(上)

前段时间我发布了 github开源:企业级应用快速开发框架CIIP WEB+WIN+移动端,很多园友们都表示支持并与我探讨相关技术问题,上篇中我也承诺会写相关的技术文章,本篇就来介绍一下建模模块中使用的动态编译技术。


那些年我们用过的动态编译技术

说是动态编译技术有些片面,因为在框架中,具体应用到的是建模模块,在CIIP中,使用的是xpo的orm,基于XAF开发,而在XAF中建模过程就是以codefirst(多数人喜爱的)方式写BusinessObject(BO、Entity)的过程。所以建模的过程多数工作就变成了组装出BusinessObject的过程了。

在CIIP中,尝试过下面列出的2,3,4的方式:

1.CodeDom,这个比较老了,TA看起来是Roslyn的语法树的子级,但看起来挺麻烦的,所以我没有用这个。

2.Reflection.Emit,使用.net提供的运行时以emit il的方式生成方法(Method),定义程序集、定义模块、定义类、定义接口等都比较容易,这个方式我从头到尾的应用过,在CIIP中,需要从头到尾的生成类,类有很多复杂情况,Reflection.Emit是处理不了的,比如说泛型的循环引用。

public class 单据基类<单据明细>
{

}

public class 单据明细<单据基类>
{

}

public class 订单:单据基类<订单明细>
{

}

public class 订单明细:单据明细<订单>
{

}

比如上面的代码,当然还有构造函数,生成就是有问题的。我查询了 装配脑袋 的早期博文,在解析程序集、类,时再去调用CreateType,对于他举出的例子中是可用的,但在上述情景下,是不能用的,这个比他的例子还要复杂些。

3.Mono.Cecil,这个是Mono下面的开源项目,动态生成程序集、做dll级别的重构、生成动态程序集,做得很不错。这个也曾经大规模使用于项目中,最后放弃了,原因有点记不清了.

4.Roslyn,这个是微软自己的开源项目,项目地址:https://github.com/dotnet/roslyn ,Roslyn可以编译代码,解析语法树.CIIP中最需要的一个功能是模仿Visual Studio中的智能感知功能,Roslyn可是帮了大忙.

比如给出一段代码,并给出当前光标位置,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 == "	" || 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 == "
")
            {
                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()}")]
";
            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(){{}} }}
";
            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

 

原文地址:https://www.cnblogs.com/foreachlife/p/ciiproslyn.html