IT

코드 완성은 어떻게 작동합니까?

lottoking 2020. 9. 25. 08:20

코드 완성은 어떻게 작동합니까?


많은 편집기와 IDE에는 코드 완성 기능이 있습니다. 그들 중 일부는 매우 "지능적"이고 다른 일부는 있습니다. 나는 더 지능적인 유형에 관심이 있습니다. 예를 들어, a) 현재 범위에서 사용 가능 b) 반환 값이 유효한 경우에만 함수를 제공하는 IDE를 보았습니다. (예를 들어 "5 + foo [tab]"뒤에는 올바른 유형의 정수 또는 변수 이름에 추가 할 수있는 것을 반환하는 함수 만 제공됩니다.) 또한 더 자주 사용하고 가장 긴 옵션을 앞에 배치하는 것을 보았습니다. 목록의.

코드를 구문 분석해야한다는 것을 알고 있습니다. 그러나 일반적으로 현재 코드를 편집하는 동안 유효하지 않은 구문 오류가 있습니다. 불완전하고 오류가있는 경우 어떻게 구문 분석을 수행합니까?

시간 제약도 있습니다. 목록을 만드는 데 몇 초가 걸리면 완성은 쓸모가 없습니다. 완료 알고리즘이 수천 개의 클래스를 처리합니다.

이를위한 좋은 알고리즘과 데이터 구조는 무엇입니까?


제 UnrealScript 언어 서비스 제품의 IntelliSense 엔진은 복잡하지만 여기서 가능한 한 최선의 개요를 제공하겠습니다. VS2008 SP1의 C # 언어 서비스는 제 성능 목표입니다. 아직은 없지만 Ctrl 키 + 스페이스를 기다리거나 사용자가 .(점)을 입력 입력 할 때까지 기다릴 필요없이 one-문자를 입력 입력 한 후 안전하게 제안을 제공 할 수있을만큼 빠르고 정확합니다 . [언어 서비스에 종사하는] 사람들이이 주제에 대해 더 많은 정보를 얻고, 제품을 사용하면 더 나은 사용자 경험을 얻게됩니다. 불행하게도 작업하면서 세부 사항에 세심한주의를 기울이지 않은 제품이 많이 발생하고 코딩보다 IDE와 더 많이 싸우고 있습니다.

내 언어 서비스에서 다음과 같이 배치됩니다.

  1. 커서에서 목적지를 가져옵니다. 이는 멤버 액세스 식 의 시작 에서 커서가있는 식별자의 끝까지 이동합니다. 멤버 액세스 식은 일반적으로 형식 aa.bb.cc이지만에서와 같이 메서드 호출을 포함 할 수도 있습니다 aa.bb(3+2).cc.
  2. 가져 오기 상황에 맞는 커서 주변을. 이것은 컴파일러와 항상 규칙을 사용하지 않기 때문에 매우 까다 롭습니다 (긴 이야기). 여기서는 그렇게 가정합니다. 일반적으로 이것은 커서가있는 메서드 / 클래스에 대한 캐시 된 정보를 가져 오는 것을 의미합니다.
  3. object-가 구현 컨텍스트하는 말 IDeclarationProvider당신이 호출 할 수 GetDeclarations()얻을 IEnumerable<IDeclaration>범위에서 볼 수있는 모든 항목을. 필자의 경우이 목록에는 지역 / 매개 변수 (메서드에있는 경우), 멤버 (필드 및 메소드, 인스턴스 메소드가 아닌 경우에만 정적 정적, 기본 유형의 전용 멤버 없음), 전역 (언어 I의 유형 및 상수)이 포함됩니다 . 작업 중) 및 키워드. 이 목록에는 이름이있는 항목이 있습니다 aa. # 1의 발현을 평가하는 첫 번째 단계로, 우리는 이름으로 열거에서 항목을 선택 aa우리에게주는 IDeclaration다음 단계.
  4. 다음으로 연산자를 IDeclaration표현 aa적용하여 IEnumerable<IDeclaration>"멤버"(어떤 의미에서)를 포함하는 다른 연산자를 얻습니다 aa. .연산자가 연산자와 다르기 때문에 연산자 연산자를 선택 적용 할 개체를 ->호출 declaration.GetMembers(".")하고 예상 합니다 IDeclaration.
  5. 이 내가 명중 할 때까지 계속 cc됩니다. 선언 목록 ... 여기서 이름을 가진 object-를 포함 하거나 포함 하지 않을 수 있습니다 cc. 아시다시피으로 시작하는 항목이 여러 개이면 해당 항목도 cc표시되어야합니다. 최종 열거를 가져와 문서화 된 알고리즘

    통해 전달 하여 user-에게 가능한 추론 유용한 정보를 제공 함으로써이 문제를 해결 합니다.

IntelliSense 백엔드에 대한 몇 가지 추가 사항 사항은 다음과 다양합니다.

  • .NET 구현시 LINQ의 지연 평가를 사용합니다 GetMembers. 내 캐시의 각 개체는 구성원에게 평가하는 펑터를 제공 할 수 있으므로 트리로 복잡한 작업을 수행하는 것은 거의 사소한 일입니다.
  • 대신 유지하는 각 개체의 List<IDeclaration>회원들의, 나는 계속 List<Name>여기서 Name구성원을 설명하는 특별한 형식의 소유의 해시를 포함하는 것입니다. 이름을 개체에 매핑하는 엄청난 캐시가 있습니다. 이렇게하면 파일을 다시 구문 분석 할 때 파일에 선언 된 모든 항목을 캐시에서 제거하고 업데이트 된 멤버로 다시 채울 수 있습니다. 펑터가 작동 방식으로 모든 것이 즉시 새 항목으로 평가됩니다.

IntelliSense "프런트 엔드"

사용자가 입력 할 때 파일 올바른 것보다 더 자주 구문 적으로 부정확 합니다. 따라서 사용자가 입력 할 때 캐시 섹션을 선택 제거하고 싶지 않습니다. 가능한 한 빨리 증분 업데이트를 처리하기 위해 많은 특수 사례 규칙이 있습니다. 증분 캐시는 열린 파일에 존재하고있는 것으로 유지되고 사용자가 입력하지 않아 백엔드 캐시가 파일의 각 방법과 같은 존재하고 잘못된 행 / 열 정보를 보유하고 있음을 사용자가 인식 못합니다.

  • 하나의 구속 요소는 내 파서가 빠르다는 것 입니다. 20000 라인 소스 파일의 전체 캐시 업데이트를 처리 할 수 ​​있습니다. 이 구문 분석기가 열린 파일에 대해 이야기로 (구문 적으로) 패스를 완료 할 때마다 파일의 현재 상태가 전역 캐시로 이동됩니다.
  • 파일의 구문이 읽지 메일 링하는 파일 ANTLR 필터에 대해 분석합니다. 대부분의 정보는 수집됨을 사용하여 다음을 찾는 재분석합니다.
    • 변수 / 필드 선언.
    • 클래스 / 클래스 정의에 대한 서명입니다.
    • 메서드 정의에 대한 서명입니다.
  • 로컬 캐시에서 클래스 / 메소드 / 메소드 정의는 서명에서 시작하여 중괄호 중첩 수준이 짝수로 끝납니다. 다른 방법 가능성에 도달하면 방법이 종료 될 수도 있습니다.
  • 로컬 캐시에서 변수 / 필드는 바로 앞의 닫히지 않은 요소에 연결 됩니다. 이것이 중요한 이유에 대한 예는 아래의 간단한 코드 스 니펫을 참조하십시오.
  • 또한 사용자가 입력 할 때 추가 / 제거 된 문자 범위를 표시하는 리맵 테이블을 유지합니다. 다음 용도로 사용됩니다.
    • 메서드가 전체 구문 분석간에 파일에서 그렇기 때문에 커서의 올바른 맥락을 파악할 수 있습니다.
    • 선언 / 정의 / 참조로 이동하면 열린 파일에서 항목을 선택합니다.

이전 섹션의 코드 스 니펫 :

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

이 레이아웃으로 구현 한 IntelliSense 기능 목록을 추가 할 것이라고 생각했습니다. 각각의 사진은 여기에 있습니다.

  • 자동 완성
  • 도구 팁
  • 방법 팁
  • 클래스보기
  • 코드 정의 창
  • Call Browser (VS 2010은 마침내 이것을 C #에 추가합니다)
  • 의미 상 올바른 모든 참조 찾기

특정 구현에서 어떤 알고리즘이 사용되는지 정확히 말할 수는 없지만 몇 가지 교육적인 추측을 할 수 있습니다. 트라이은 이 문제에 대한 매우 유용한 데이터 구조입니다 : IDE에서 각 노드에서 몇 가지 추가 메타 데이터, 프로젝트의 모든 심볼의 메모리에 큰 트라이을 유지할 수 있습니다.

문자를 입력하면 트라이의 경로를 따라 이동합니다. 특정 트라이 노드의 모든 자손은 가능한 완료입니다. 그런 다음 IDE는 현재 컨텍스트에서 의미가있는 것으로 필터링하기 만하면되지만 탭 완성 팝업 창에 표시 할 수있는만큼만 계산하면됩니다.

고급 탭 완성에는 더 복잡한 시도가 필요합니다. 예를 들어 Visual Assist X 에는 CamelCase 기호의 대문자 만 입력하면되는 기능이 있습니다. 예를 들어 SFN을 입력하면 SomeFunctionName탭 완성 창에 기호 가 표시됩니다.

트라이 (또는 기타 데이터 구조)를 계산하려면 프로젝트의 모든 기호 목록을 가져 오기 위해 모든 코드를 구문 분석해야합니다. Visual Studio는이를 .ncb프로젝트와 함께 저장 되는 파일 인 IntelliSense 데이터베이스에 저장하므로 프로젝트를 닫고 다시 열 때마다 모든 것을 다시 구문 분석 할 필요가 없습니다. 대규모 프로젝트 (예 : 소스 컨트롤에서 방금 동기화 한 프로젝트)를 처음 열면 VS는 모든 것을 구문 분석하고 데이터베이스를 생성하는 데 시간이 걸립니다.

점진적 변경을 어떻게 처리하는지 모르겠습니다. 말했듯이 코드를 작성할 때 90 %의 유효하지 않은 구문이며, 유휴 상태 일 때마다 모든 것을 다시 구문 분석하면 CPU에 막대한 세금이 부과되지만 특히에 포함 된 헤더 파일을 수정하는 경우 많은 수의 소스 파일.

나는 그것이 (a) 프로젝트를 실제로 빌드 할 때만 (또는 아마도 그것을 닫거나 열 때) 재분석하거나, (b) 방금 한 곳에서만 코드를 구문 분석하는 일종의 로컬 구문 분석을 수행한다고 생각합니다. 관련 기호의 이름을 얻기 위해 제한된 방식으로 편집되었습니다. C ++에는 매우 복잡한 문법이 있기 때문에 무거운 템플릿 메타 프로그래밍 등을 사용하는 경우 어두운 구석에서 이상하게 작동 할 수 있습니다.


다음 링크가 더 도움이 될 것입니다 ..

구문 강조 : 구문 강조를 위한 빠른 색상의 TextBox

참고 URL : https://stackoverflow.com/questions/1220099/how-does-code-completion-work