Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Roslyn CTP API的代码差异_C#_.net_Roslyn - Fatal编程技术网

C# 使用Roslyn CTP API的代码差异

C# 使用Roslyn CTP API的代码差异,c#,.net,roslyn,C#,.net,Roslyn,我试图用Roslyn API进行一些基本的代码差异,但遇到了一些意想不到的问题。本质上,我有两段代码是相同的,只是增加了一行。这应该只是返回更改文本的行,但出于某种原因,它告诉我一切都已更改。我也尝试过只编辑一行而不是添加一行,但我得到了相同的结果。我希望能够将此应用于源文件的两个版本,以确定两者之间的差异。以下是我当前使用的代码: SyntaxTree tree = SyntaxTree.ParseCompilationUnit( @"using Sys

我试图用Roslyn API进行一些基本的代码差异,但遇到了一些意想不到的问题。本质上,我有两段代码是相同的,只是增加了一行。这应该只是返回更改文本的行,但出于某种原因,它告诉我一切都已更改。我也尝试过只编辑一行而不是添加一行,但我得到了相同的结果。我希望能够将此应用于源文件的两个版本,以确定两者之间的差异。以下是我当前使用的代码:

        SyntaxTree tree = SyntaxTree.ParseCompilationUnit(
            @"using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;

            namespace HelloWorld
            {
                class Program
                {
                    static void Main(string[] args)
                    {
                        Console.WriteLine(""Hello, World!"");
                    }
                }
            }");

        var root = (CompilationUnitSyntax)tree.Root;

        var compilation = Compilation.Create("HelloWorld")
                                     .AddReferences(
                                        new AssemblyFileReference(
                                            typeof(object).Assembly.Location))
                                     .AddSyntaxTrees(tree);

        var model = compilation.GetSemanticModel(tree);
        var nameInfo = model.GetSemanticInfo(root.Usings[0].Name);
        var systemSymbol = (NamespaceSymbol)nameInfo.Symbol;

        SyntaxTree tree2 = SyntaxTree.ParseCompilationUnit(
            @"using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;

            namespace HelloWorld
            {
                class Program
                {
                    static void Main(string[] args)
                    {
                        Console.WriteLine(""Hello, World!"");
                        Console.WriteLine(""jjfjjf"");
                    }
                }
            }");

        var root2 = (CompilationUnitSyntax)tree2.Root;

        var compilation2 = Compilation.Create("HelloWorld")
                                     .AddReferences(
                                        new AssemblyFileReference(
                                            typeof(object).Assembly.Location))
                                     .AddSyntaxTrees(tree2);

        var model2 = compilation2.GetSemanticModel(tree2);
        var nameInfo2 = model2.GetSemanticInfo(root2.Usings[0].Name);
        var systemSymbol2 = (NamespaceSymbol)nameInfo2.Symbol;

        foreach (TextSpan t in tree2.GetChangedSpans(tree))
        {
            Console.WriteLine(tree2.Text.GetText(t));
        }
这是我得到的结果:

System
                using System
Collections
Generic
                using System
Linq
                using System
Text

                namespace HelloWorld
                {
                    class Program
                    {
                        static
Main
args
                        {
                            Console
WriteLine
"Hello, World!"
                            Console.WriteLine("jjfjjf");
                        }
                    }
                }
Press any key to continue . . .

有趣的是,它似乎将每一行显示为每一行的标记,但添加的行除外,在添加的行中,它显示该行而不将其拆分。有人知道如何隔离实际的更改吗?

我想,
GetChangedSpans
是用来比较一棵树和从原始树的更改创建的树之间的更改,而不是两棵任意树之间的更改。

@bruceboughton是正确的,
getchangedspan
用于发现增量解析器所做的更改。使用如下代码,我可以获得更好的输出:

        var code = 
        @"using System; 
        using System.Collections.Generic; 
        using System.Linq; 
        using System.Text; 

        namespace HelloWorld 
        { 
            class Program 
            { 
                static void Main(string[] args) 
                { 
                    Console.WriteLine(""Hello, World!""); 
                } 
            } 
        }";
        var text = new StringText(code);
        SyntaxTree tree = SyntaxTree.ParseCompilationUnit(text);

        var index = code.IndexOf("}");
        var added = @"    Console.WriteLine(""jjfjjf""); 
                      ";

        var code2 = code.Substring(0, index) + 
                    added +
                    code.Substring(index);

        var text2 = new StringText(code2);

        var tree2 = tree.WithChange(text2, new [] { new TextChangeRange(new TextSpan(index, 0), added.Length) } );

        foreach (var span in tree2.GetChangedSpans(tree))
        {
            Console.WriteLine(text2.GetText(span));
        }
但是,一般来说,GetChangedSpans是一种合理、快速但保守的差异。为了更好地控制差异,获得更准确的结果,您可能需要实现自己的树差异化算法,可以根据需要进行调整


在上面的代码中,如果您使用的是VS,则编辑器内置了更改报告和文本差异,这将允许您轻松构建
TextChangeRange
对象,但是,如果您想将更改传递给增量解析器,那么您可能至少还需要一个文本差异算法。

布鲁斯·布顿的猜测是正确的。GetChangedSpans方法不是一种通用语法差异化机制,用于在没有共享历史的两个语法树之间获取差异。相反,它的目的是获取通过编辑公共树而生成的两棵树,并确定树的哪些部分由于编辑而不同

如果您使用第一个解析树并将新语句作为编辑插入其中,那么您将看到更小的更改集

如果我简单地描述一下Roslyn lexer和解析器是如何在高层次上工作的,可能会有所帮助

基本思想是lexer生成的“语法标记”和解析器生成的“语法树”是不可变的。他们从不改变。因为它们永远不会改变,所以我们可以在新的解析树中重用以前解析树的一部分。(具有此属性的数据结构通常称为“持久”数据结构。)

因为我们可以重用现有的部分,例如,我们可以对程序中出现的给定标记的每个实例使用相同的值,例如
。每个
令牌的长度和内容完全相同;区分两个不同的
标记的唯一方法是它们的琐事(它们周围的间距和注释)和它们的位置,以及它们的父节点——包含标记的较大语法节点

当你解析一个文本块时,我们会以一种持久不变的形式生成语法标记和语法树,我们称之为“绿色”形式。然后,我们将绿色节点包裹在“红色”层中。绿色层对位置、父对象等一无所知。红色的图层可以。(这些奇怪的名称是因为当我们第一次在白板上绘制此数据结构时,我们使用了这些颜色。)当您创建对给定语法树的编辑时,我们会查看以前的语法树,识别更改的节点,然后仅在更改的脊上构建新节点。这棵绿树的所有其他树枝都保持不变

当对两棵树进行扩散时,我们所做的基本上是取绿色节点的集差。如果其中一棵树是通过编辑另一棵树生成的,那么几乎所有的绿色节点都将是相同的,因为只重建了脊椎。树扩散算法将识别更改的节点并计算出受影响的跨度

如果这两棵树没有共同的历史记录,那么它们唯一共同的绿色节点就是单独的令牌,正如我前面所说的,它们在任何地方都可以重复使用。每个更高级别的绿色语法节点都将是不同的绿色节点,因此树差异引擎将其视为不同的节点,即使其文本相同

此方法的目的是允许编辑器代码快速保守地猜测文本缓冲区的哪些部分在编辑、撤消或类似操作之后需要重新存储。假设这些树有历史关系。目的不是提供一个通用的文本差异机制;已经有很多很好的工具可以实现这一点

例如,假设您已将第一个程序粘贴到编辑器中,然后高亮显示整个内容,然后将第二个程序粘贴到编辑器中。我们可以合理地预期,编辑器不会浪费时间试图找出粘贴代码的哪些部分恰好与先前粘贴的代码相同。这可能非常昂贵,答案可能是“不多”。相反,编辑器做出了保守的假设,即整个粘贴区域是全新的、完全不同的代码。它不会花费任何时间试图在旧代码和新代码之间建立对应关系;它重新包装,因此回忆起整个事情

另一方面,如果您刚刚粘贴了一条不同的语句,那么编辑引擎只需将编辑插入正确的位置。解析树将尽可能使用现有的绿色节点重新生成,差异引擎将确定需要重新着色的跨度:具有不同绿色编号的跨度