怎么代码完成的工作?
-
10-07-2019 - |
题
大量的编辑和IDEs有代码完成。他们中的一些是非常"智能"其他人是不是真的。我感兴趣的更加智能化的类型。例如我看到的IDEs,仅提供一函数,如果它是一个)提供在当前范围b)其返回的数值是有效的。(例如在"5+foo[标签]"它只提供职能返回的东西,可以添加到整或变量名称的正确的类型。) 我也看见他们把更多的时候使用或者最长的选项之前的列表。
我知道你需要分析代码。但是,通常在编辑前码是无效的,那里的语法错误。你怎么分析的东西时,这是不完整的,并包含的错误?
还有一个时间限制。完成是无用的,如果它需要几秒钟拿出一个清单。有时候完成的算法交易与成千上万的课程。
什么是良好的算法和数据结构是为了这个?
解决方案
智能感知的发动机在我UnrealScript语言服务产品是复杂的,但我会给尽可能最好的概述,在这里,我可以。C#语言服务在VS2008SP1是我的业绩目标(有很好的理由).它不存在,但它的快速/不够准确,我可以安全提出建议后一个单字输入,而没有等待着按ctrl+空间或用户输入一个 .
(dot)。更多信息的人[工作语言服务]获得关于这个主题,更好的最终用户的经验,我得到我应该使用他们的产品。有一定数量的产品,我已经受的不幸经验的工作,没有支付这种密切关注细节,并且因此我是战斗的IDE比我更是编码。
在我的语言服务,它规定如下:
- 获得表达的光标。这个走开 成员访问表达 到结束的标识符标是结束。成员访问表达的一般形式
aa.bb.cc
, 但还可以包含的方法的呼吁作为在aa.bb(3+2).cc
. - 得到的 上下文 周围的光标。这是非常棘手的,因为它并不总是按照相同的规则的编译器(很长的故事),但是对这里的假设它不会。通常这意味着获得缓存的信息有关的方法/类的标内。
- 说的上下文对象实现了
IDeclarationProvider
, ,你可以打电话GetDeclarations()
得到一个IEnumerable<IDeclaration>
的所有项目可见的范围。在我的情况下,这个清单包含当地人/参数(如果在法),成员(领域和方法,静态只,除非在一方法的实例,并没有私人成员的基类型),全局(类型和常用的语言的,我的工作),和关键词。在该列表中将一个项目的名称aa
.作为第一步在评价所表达#1中,我们选择的项目从上下文中列举的名字aa
, 给我们一个IDeclaration
为下一个步骤。 - 接下来,我可适用的操作
IDeclaration
代表aa
拿到另一个IEnumerable<IDeclaration>
含有的"成员"(在某种意义上说)的aa
.由于.
操作人员是从不同的->
操作者,我呼declaration.GetMembers(".")
并期望IDeclaration
对象的正确适用所列出的操作员。 - 这种情况持续下去,直到我击中了
cc
, ,《宣言》列表 可以或不可以 包含一个目的名称cc
.因为我敢肯定你知道,如果多个项目开始cc
, 他们应该出现。我解决这个通过采取最后枚举,并使它通过 我的记录的算法 向用户提供的最有用的信息可能的。
这里有一些附加说明为智能感知后:
- 我广泛使用的皇宫的懒惰的评价机制的执行
GetMembers
.每个对象在我的缓是能够提供一个函数,评估它的成员,因此进行复杂的行动树的附近是微不足道的。 - 而不是每个对象保留一个
List<IDeclaration>
其成员,我保留一个List<Name>
, ,哪里Name
是一个结构含有的散列的一种特殊格式的串描述成员。有一个巨大的高速缓存这地图名称的对象。这样,当我重新分析文件,我可以移除所有的项目宣布在文件从高速缓存和重新填充它与更新的成员。由于本函的配置情况,所有表达立即评估的新项目。
IntelliSense"前端"
作为用户的类型,文件在语法上 错误的 往往多于它是正确的。因此,我不想要随便删除的部分缓存在的用户的类型。我有大量的特殊情况的规则以处理增量更新尽可能快地。增量缓仅仅是保持地方到一个开放的文件和有助于确保用户没有认识到,他们打造成的后台高速缓存持有不正确的行/分列的信息喜欢的事情中的每个方法的文件。
- 一个兑换因子是我的分析程序 快速.它可以处理一个完整的高速缓存更新的20000线的源文件中的150毫秒的同时,操作的自包含在较低优先的背景线。只要这种分析器完成了通过在一个开放的文件的成功(在语法上),目前状态的文件被搬入全球高速缓存。
- 如果文件不是语法正确,我用一个 这些代码滤波分析器(对不起有关的链接的最为信息是通过电子邮件名单或从收集到的读取的来源) 来重新分析的文件,寻找:
- 变量/场声明。
- 的签名类/结构定义。
- 该签名方法的定义。
- 在本地缓,类/struct/方法的定义开始在签署和结束时的括号嵌套的水平又回到甚至。方法也可以结束,如果另一个方法声明是达到(无套方法)。
- 在本地高速缓冲存、变量/领域是与前 未关闭 元素。看到简要代码下面的一个例子为什么这是重要的。
- 此外,作为用户类型中,我把重置表的标记加入或删除字的范围。这是用于:
- 确保我可以确定正确的上下文中标,由于一个方法能够/不动在文件之间的全面分析.
- 确保走到《宣言》/Definition/参考查找项目在正确的公开文件。
代码,用于以前的部分:
class A
{
int x; // linked to A
void foo() // linked to A
{
int local; // linked to foo()
// foo() ends here because bar() is starting
void bar() // linked to A
{
int local2; // linked to bar()
}
int y; // linked again to A
我想添加一个清单的智能感知的特点,我们实现这个布局。 照片中的每一个都设在这里。
- 自完成
- 工具的技巧
- 方法的提示
- 类图
- 代码的定义窗口
- 呼叫浏览器(VS2010年最后添加这C#)
- 语义上正确的找到的所有参考文献
其他提示
我不能确切地说出任何特定实现使用的算法,但我可以做一些有根据的猜测。对于此问题, trie 是一个非常有用的数据结构:IDE可以在内存中维护一个大的trie项目中的所有符号,每个节点都有一些额外的元数据。
当你输入一个角色时,它沿着特里的路径走下去。特定trie节点的所有后代都是可能的完成。然后,IDE只需要在当前上下文中对那些有意义的那些进行过滤,但它只需要计算在tab-completion弹出窗口中可以显示的数量。
更高级的制表符完成需要更复杂的特里。例如, Visual Assist X 具有一项功能,您只需键入CamelCase符号的大写字母即可 - 例如,如果键入SFN,它会在其选项卡完成窗口中显示符号SomeFunctionName
。
计算trie(或其他数据结构)确实需要解析所有代码以获取项目中所有符号的列表。 Visual Studio将其存储在IntelliSense数据库中,这是一个与项目一起存储的.ncb
文件,因此每次关闭并重新打开项目时都不需要重新解析所有内容。第一次打开一个大型项目(比如一个刚刚同步的源代码控制)时,VS会花时间解析所有内容并生成数据库。
我不知道它如何处理增量变化。正如你所说,当你编写代码时,它在90%的时间内都是无效的语法,并且每当你闲置时重新解析所有内容都会对你的CPU造成巨大的负担,但收效甚微,特别是当你修改包含的头文件时大量的源文件。
我怀疑它是(a)只在实际构建项目时(或者可能在你关闭/打开它时)进行重新分析,或者(b)它进行某种本地解析,它只解析你周围的代码我只是以某种有限的方式编辑,只是为了得到相关符号的名称。由于C ++具有如此非常复杂的语法,如果你使用大量的模板元编程等,它可能在黑暗的角落表现得很奇怪。
以下链接将帮助您进一步..
语法突出显示:快速着色的TextBox,用于语法突出显示