emacs에서 C #에 대한 완성 (지능형) 기능을 개발 중입니다.
아이디어는 사용자가 조각을 입력 한 다음 특정 키 입력 조합을 통해 완료를 요청하면 완료 기능이 .NET 리플렉션을 사용하여 가능한 완료를 결정한다는 것입니다.
이렇게하려면 완료되는 사물의 유형을 알아야합니다. 문자열 인 경우 알려진 가능한 메서드 및 속성 집합이 있습니다. Int32 인 경우 별도의 집합이 있습니다.
emacs에서 사용할 수있는 코드 렉서 / 파서 패키지 인 semantic을 사용하여 변수 선언과 해당 유형을 찾을 수 있습니다. 따라서 리플렉션을 사용하여 유형에 대한 메서드와 속성을 가져온 다음 사용자에게 옵션 목록을 제공하는 것은 간단합니다. (좋아, 꽤 간단 할 내 이맥스하지만, 사용 이맥스 내부의 파워 쉘 프로세스를 실행하는 능력을 , 훨씬 쉬워집니다. 나는 내에서 실행 elisp 후, 반사를 할 어셈블리 정의 .NET 쓰기 PowerShell을에로드하고, emacs는 comint를 통해 powershell에 명령을 보내고 응답을 읽을 수 있습니다. 따라서 emacs는 반영 결과를 빠르게 얻을 수 있습니다.)
코드 var
가 완료되는 것을 선언 할 때 문제가 발생합니다 . 즉, 유형이 명시 적으로 지정되지 않았으며 완성이 작동하지 않습니다.
변수를 var
키워드 로 선언 할 때 사용되는 실제 유형을 어떻게 안정적으로 결정할 수 있습니까? 명확히하기 위해 런타임에 결정할 필요가 없습니다. "디자인 타임"에서 결정하고 싶습니다.
지금까지 다음 아이디어가 있습니다.
- 컴파일 및 호출 :
- 선언문 추출, 예 :`var foo = "a string value";`
- `foo.GetType ();`문을 연결합니다.
- 결과 C # 조각을 새 어셈블리로 동적으로 컴파일합니다.
- 어셈블리를 새 AppDomain에로드하고 조각을 실행하고 반환 유형을 가져옵니다.
- 어셈블리를 언로드 및 폐기
나는이 모든 것을하는 방법을 알고있다. 그러나 편집기의 각 완료 요청에 대해 매우 무겁게 들립니다.
매번 새로운 AppDomain이 필요하지 않다고 생각합니다. 여러 임시 어셈블리에 단일 AppDomain을 재사용하고 여러 완료 요청에서 설정 및 해체 비용을 분할 할 수 있습니다. 그것은 기본 아이디어의 수정입니다.
- IL 컴파일 및 검사
선언을 모듈로 컴파일 한 다음 IL을 검사하여 컴파일러에서 유추 한 실제 유형을 확인하면됩니다. 이것이 어떻게 가능할까요? IL을 검사하기 위해 무엇을 사용합니까?
더 나은 아이디어가 있습니까? 코멘트? 제안?
편집 -이것에 대해 더 생각하면 호출에 부작용이있을 수 있으므로 컴파일 및 호출이 허용되지 않습니다. 따라서 첫 번째 옵션은 배제되어야합니다.
또한 .NET 4.0의 존재를 짐작할 수 없다고 생각합니다.
업데이트 -위에서 언급하지 않았지만 Eric Lippert가 부드럽게 지적한 정답은 완전한 충실도 유형 추론 시스템을 구현하는 것입니다. 디자인 타임에 var의 유형을 안정적으로 결정하는 유일한 방법입니다. 그러나 그것은 또한 쉽지 않습니다. 그런 것을 만들고 싶다는 착각을 겪지 않았기 때문에 옵션 2의 지름길을 사용했습니다. 관련 선언 코드를 추출하고 컴파일 한 다음 결과 IL을 검사합니다.
이것은 완료 시나리오의 공정한 하위 집합에 대해 실제로 작동합니다.
예를 들어 다음 코드 조각에서? 사용자가 완료를 요청하는 위치입니다. 이것은 작동합니다 :
var x = "hello there";
x.?
완성은 x가 문자열임을 인식하고 적절한 옵션을 제공합니다. 다음 소스 코드를 생성하고 컴파일하여이를 수행합니다.
namespace N1 {
static class dmriiann5he { // randomly-generated class name
static void M1 () {
var x = "hello there";
}
}
}
... 간단한 반사로 IL을 검사합니다.
이것은 또한 작동합니다 :
var x = new XmlDocument();
x.?
엔진은 생성 된 소스 코드에 적절한 using 절을 추가하여 제대로 컴파일되고 IL 검사는 동일합니다.
이것도 작동합니다.
var x = "hello";
var y = x.ToCharArray();
var z = y.?
IL 검사가 첫 번째 대신 세 번째 지역 변수의 유형을 찾아야 함을 의미합니다.
이:
var foo = "Tra la la";
var fred = new System.Collections.Generic.List<String>
{
foo,
foo.Length.ToString()
};
var z = fred.Count;
var x = z.?
... 이전 예제보다 한 단계 더 깊습니다.
그러나 작동 하지 않는 것은 초기화가 인스턴스 멤버 또는 로컬 메서드 인수의 어느 지점에서든 종속되는 로컬 변수에 대한 완료입니다. 처럼:
var foo = this.InstanceMethod();
foo.?
LINQ 구문도 아닙니다.
완성을 위해 확실히 "제한된 디자인"(정중 한 단어)을 통해 문제를 해결하는 것을 고려하기 전에 이러한 것들이 얼마나 가치가 있는지 생각해야합니다.
메서드 인수 또는 인스턴스 메서드에 대한 종속성 문제를 해결하는 방법은 생성되고 컴파일 된 다음 IL이 분석되는 코드 조각에서 이러한 항목에 대한 참조를 동일한 유형의 "합성"로컬 변수로 대체하는 것입니다.
또 다른 업데이트 -인스턴스 멤버에 의존하는 변수에 대한 완성이 이제 작동합니다.
내가 한 일은 (시맨틱을 통해) 유형을 조사한 다음 모든 기존 멤버에 대한 합성 대기 멤버를 생성하는 것입니다. 다음과 같은 C # 버퍼의 경우 :
public class CsharpCompletion
{
private static int PrivateStaticField1 = 17;
string InstanceMethod1(int index)
{
...lots of code here...
return result;
}
public void Run(int count)
{
var foo = "this is a string";
var fred = new System.Collections.Generic.List<String>
{
foo,
foo.Length.ToString()
};
var z = fred.Count;
var mmm = count + z + CsharpCompletion.PrivateStaticField1;
var nnn = this.InstanceMethod1(mmm);
var fff = nnn.?
...more code here...
... 컴파일 된 생성 된 코드는 출력 IL에서 로컬 var nnn의 유형을 배울 수 있습니다.
namespace Nsbwhi0rdami {
class CsharpCompletion {
private static int PrivateStaticField1 = default(int);
string InstanceMethod1(int index) { return default(string); }
void M0zpstti30f4 (int count) {
var foo = "this is a string";
var fred = new System.Collections.Generic.List<String> { foo, foo.Length.ToString() };
var z = fred.Count;
var mmm = count + z + CsharpCompletion.PrivateStaticField1;
var nnn = this.InstanceMethod1(mmm);
}
}
}
모든 인스턴스 및 정적 유형 멤버는 스켈레톤 코드에서 사용할 수 있습니다. 성공적으로 컴파일됩니다. 이 시점에서 지역 변수의 유형을 결정하는 것은 Reflection을 통해 간단합니다.
이를 가능하게하는 것은 다음과 같습니다.
- emacs에서 powershell을 실행하는 기능
- C # 컴파일러는 정말 빠릅니다. 내 컴퓨터에서 메모리 내 어셈블리를 컴파일하는 데 약 0.5 초가 걸립니다. 키 입력 간 분석에는 빠르지 않지만 주문형 완성 목록 생성을 지원할만큼 빠릅니다.
아직 LINQ를 살펴 보지 않았습니다.
의미 론적 렉서 / 파서 emacs가 C #에 대해 가지고 있지만 LINQ를 "실행"하지 않기 때문에 훨씬 더 큰 문제가 될 것입니다.