使用NRefactory 5实现C#代码自动补全功能

8
我刚刚了解到NRefactory 5,我猜想它是我目前问题最适合的解决方案。目前,我正在开发一个小型C#脚本应用程序,我希望提供代码自动完成功能。最近我一直在使用微软的“Roslyn”项目来实现这个功能。但由于此项目的最新更新需要.NET Framework 4.5,而我希望该应用程序也能在Win XP下运行,因此我不能再使用该技术。所以我必须转向另一种技术。
我的问题不是编译问题。使用.NET CodeDomProvider也可以完成这项工作,但需要更多的工作量。问题在于代码自动完成。据我所知,NRefactory 5提供了提供代码自动完成所需的所有内容(解析器、类型系统等),但我只是无法弄清如何使用它。我查看了SharpDevelop源代码,但他们在那里并没有使用NRefactory 5来进行代码自动完成,他们只将其用作反编译器。由于在网络上找不到关于如何将其用于代码自动完成的示例,因此我认为我可能会在这里找到一些帮助。
情况如下。我有一个包含脚本代码的单个文件。实际上它甚至不是一个文件,而是从编辑器控件中获取的一个字符串(顺便说一下:我正在使用AvalonEdit。非常棒的编辑器!),以及需要引用的一些程序集。因此,没有解决方案文件、项目文件等,只有一个源代码字符串和这些程序集。
我查看了NRefactory 5附带的演示版和Code Project上的文章,得到了类似于这样的东西:
var unresolvedTypeSystem = syntaxTree.ToTypeSystem();

IProjectContent pc = new CSharpProjectContent();

// Add parsed files to the type system
pc = pc.AddOrUpdateFiles(unresolvedTypeSystem);

// Add referenced assemblies:
pc = pc.AddAssemblyReferences(new CecilLoader().LoadAssemblyFile(
    System.Reflection.Assembly.GetAssembly(typeof(Object)).Location));

我的问题是我不知道该怎么做。我甚至不确定这是否是实现我的目标的正确方法。如何使用 CSharpCompletionEngine?还需要什么?等等。你可以看到,目前有很多事情是非常不清楚的,我希望你能为此带来一些光明。

非常感谢您提前的帮助!


MonoDevelop 使用 CSharpCompletionEngine 类。也许这会展示它的工作原理。 - Simon Lindgren
3个回答

7

你真的让我开心!非常感谢你的出色工作。我花了一点时间才让它正常运行。由于我的测试项目部分链接了另一个AvalonEdit版本,所以我一直遇到问题! - Gonzo345

3
请查看方法ICSharpCode.NRefactory.CSharp.CodeCompletion.CreateEngine。您需要创建一个 CSharpCompletionEngine 的实例,并传入正确的文档和解析器。我已经成功地让它在 CTRL+Space 补全场景中运行。但是,我在处理其他命名空间中的类型引用时遇到了问题。看起来 CSharpTypeResolveContext 没有考虑使用命名空间语句——如果我使用 CSharpAstResolver 解析引用,则可以解析正确,但我无法在代码补全场景中正确地使用此解析器... 更新#1: 我刚刚设法通过从未解决的故障中获取解析器而使其正常工作。
这是片段:
var mb = new DefaultCompletionContextProvider(doc, unresolvedFile);
var resolver3 = unresolvedFile.GetResolver(cmp, loc); // get the resolver from unresolvedFile
var engine = new CSharpCompletionEngine(doc, mb, new CodeCompletionBugTests.TestFactory(resolver3), pctx, resolver3.CurrentTypeResolveContext );

更新 #2:

这是完整的方法。它引用了单元测试项目中的类,因此您需要将它们引用/复制到您的项目中:

public static IEnumerable<ICompletionData> DoCodeComplete(string editorText, int offset) // not the best way to put in the whole string every time
{

  var doc = new ReadOnlyDocument(editorText);
  var location = doc.GetLocation(offset);

  string parsedText = editorText; // TODO: Why there are different values in test cases?


  var syntaxTree = new CSharpParser().Parse(parsedText, "program.cs");
  syntaxTree.Freeze();
  var unresolvedFile = syntaxTree.ToTypeSystem();

  var mb = new DefaultCompletionContextProvider(doc, unresolvedFile);

  IProjectContent pctx = new CSharpProjectContent();
  var refs = new List<IUnresolvedAssembly> { mscorlib.Value, systemCore.Value, systemAssembly.Value}; 
  pctx = pctx.AddAssemblyReferences(refs);
  pctx = pctx.AddOrUpdateFiles(unresolvedFile);

  var cmp = pctx.CreateCompilation();

  var resolver3 = unresolvedFile.GetResolver(cmp, location);
  var engine = new CSharpCompletionEngine(doc, mb, new CodeCompletionBugTests.TestFactory(resolver3), pctx, resolver3.CurrentTypeResolveContext );


  engine.EolMarker = Environment.NewLine;
  engine.FormattingPolicy = FormattingOptionsFactory.CreateMono();

  var data = engine.GetCompletionData(offset, controlSpace: false);
  return data;

}

希望能够帮到您, Matra


2
NRefactory 5正在SharpDevelop 5中使用。SharpDevelop 5的源代码目前可在github上的newNR分支中获取。您可以查看CSharpCompletionBinding类,该类具有使用NRefactory的CSharpCompletionEngine信息显示完成列表窗口的代码。

嗨,马特。不幸的是,SharpDevelop 5也需要.NET Framework 4.5,所以我无法在我的机器上探索解决方案。我希望在周末能够拿到一台win 8机器来深入研究这个问题。你能否缩小涉及完成内容的项目或dll范围?除了CSharpBinding之外,似乎还涉及以下项目:Mono.Cecil、SharpDevelop、ICSharpCode.SharpDevelop和ICSharpCode.Core。这正确吗?(#D版本5.0.0.1472)看起来所需的类分布在几个项目和命名空间中。谢谢! - Tobias Breuer
1
你应该可以直接使用AvalonEdit和NRefactory的NuGet包。AvalonEdit有自己的完成列表窗口,所以只需要捕获输入的文本并将其提供给完成引擎即可。我会尝试编写一个示例。 - Matt Ward
1
@MattWard,你那个示例准备好了吗?谢谢! - georgiosd
@georgiosd - Lukas Buehler 创建了一个示例 - https://github.com/lukebuehler/NRefactory-Completion-Sample - Matt Ward

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接