Emacs'de C # için bir tamamlama (intellisense) tesisi üzerinde çalışıyorum.
Buradaki fikir şudur, eğer bir kullanıcı bir parça yazarsa ve daha sonra belirli bir tuş vuruşu kombinasyonu yoluyla tamamlama isterse, tamamlama tesisi olası tamamlamaları belirlemek için .NET yansımasını kullanır.
Bunu yapmak, tamamlanan şeyin türünün bilinmesini gerektirir. Bir dizeyse, bilinen bir dizi olası yöntem ve özellik vardır; bir Int32 ise, ayrı bir kümesi vardır ve bu böyle devam eder.
Emacs'de bulunan bir kod lexer / ayrıştırıcı paketi olan semantik kullanarak, değişken bildirimlerini ve türlerini bulabilirim. Buna göre, türdeki yöntemleri ve özellikleri almak için yansımayı kullanmak ve ardından seçenekler listesini kullanıcıya sunmak kolaydır. (Tamam, emacs içinde yapmak pek kolay değil , ancak emacs içinde bir powershell işlemi çalıştırma yeteneğini kullanarak çok daha kolay hale geliyor. Yansıtma yapmak, powershell'e yüklemek için özel bir .NET derlemesi yazıyorum ve sonra içinde çalışan elisp emacs, powershell'e komutlar gönderebilir ve yanıtları comint aracılığıyla okuyabilir. Sonuç olarak emacs yansıma sonuçlarını hızlı bir şekilde alabilir.)
Kod, var
tamamlanan şeyin bildiriminde kullanıldığında sorun ortaya çıkar . Bu, türün açıkça belirtilmediği ve tamamlamanın çalışmayacağı anlamına gelir.
Değişken var
anahtar kelimeyle bildirildiğinde kullanılan gerçek türü nasıl güvenilir bir şekilde belirleyebilirim ? Açık olmak gerekirse, çalışma zamanında belirlememe gerek yok. Bunu "Tasarım zamanında" belirlemek istiyorum.
Şimdiye kadar şu fikirlere sahibim:
- derleyin ve çağırın:
- bildirim ifadesini ayıklayın, örneğin `var foo =" bir dizge değeri ";`
- `foo.GetType () 'ifadesini birleştirmek;
- Sonuçta ortaya çıkan C # parçasını dinamik olarak yeni bir derlemeye derleyin
- montajı yeni bir AppDomain'e yükleyin, çerçeveyi çalıştırın ve dönüş türünü alın.
- düzeneği boşaltın ve atın
Tüm bunları nasıl yapacağımı biliyorum. Ancak editördeki her tamamlama isteği için kulağa çok ağır geliyor.
Sanırım her seferinde yeni bir AppDomain'e ihtiyacım yok. Birden çok geçici montaj için tek bir AppDomain'i yeniden kullanabilir ve birden çok tamamlama talebi karşısında onu kurma ve yırtma maliyetini amorti edebilirim. Bu daha çok temel fikrin ince ayarı.
- IL derleyin ve inceleyin
Bildirimi bir modülde derleyin ve ardından derleyici tarafından çıkarılan gerçek türü belirlemek için IL'yi inceleyin. Bu nasıl mümkün olabilir? Bilgi okuryazarlığını incelemek için ne kullanmalıyım?
Daha iyi bir fikir var mı? Yorumlar? öneriler?
DÜZENLEME - bunu daha fazla düşünmek, derleme ve çağırma kabul edilemez çünkü çağrının yan etkileri olabilir. Bu yüzden ilk seçenek göz ardı edilmelidir.
Ayrıca, .NET 4.0'ın varlığını varsayamayacağımı düşünüyorum.
GÜNCELLEME - Yukarıda bahsedilmeyen, ancak Eric Lippert tarafından nazikçe işaret edilen doğru cevap, tam bir aslına uygunluk türü çıkarım sistemi uygulamaktır. Tasarım zamanında bir değişkenin türünü güvenilir bir şekilde belirlemenin tek yolu budur. Ama yapmak da kolay değil. Böyle bir şeyi inşa etmeye çalıştığım yanılsamasına maruz kalmadığım için, seçenek 2'nin kısayolunu kullandım - ilgili bildirim kodunu çıkar ve derledikten sonra elde edilen IL'yi inceledim.
Bu aslında tamamlama senaryolarının adil bir alt kümesi için işe yarar.
Örneğin, aşağıdaki kod parçalarında? kullanıcının tamamlanmasını istediği konumdur. Bu çalışıyor:
var x = "hello there";
x.?
Tamamlanma, x'in bir Dize olduğunu anlar ve uygun seçenekleri sağlar. Bunu, aşağıdaki kaynak kodunu oluşturup sonra derleyerek yapar:
namespace N1 {
static class dmriiann5he { // randomly-generated class name
static void M1 () {
var x = "hello there";
}
}
}
... ve sonra IL'yi basit bir yansımayla incelemek.
Bu aynı zamanda çalışır:
var x = new XmlDocument();
x.?
Motor, üretilen kaynak koduna uygun kullanım maddelerini ekler, böylece düzgün bir şekilde derlenir ve ardından IL incelemesi aynı olur.
Bu da işe yarar:
var x = "hello";
var y = x.ToCharArray();
var z = y.?
Bu sadece IL incelemesinin ilk yerine üçüncü yerel değişkenin türünü bulması gerektiği anlamına gelir.
Ve bu:
var foo = "Tra la la";
var fred = new System.Collections.Generic.List<String>
{
foo,
foo.Length.ToString()
};
var z = fred.Count;
var x = z.?
... önceki örnekten sadece bir seviye daha derin olan.
Ama, ne yapar değil iş başlatma bir örnek üyesi veya yerel yöntem argüman üzerinde herhangi bir noktada bağlıdır herhangi bir yerel değişkeni tamamlanmasıdır. Sevmek:
var foo = this.InstanceMethod();
foo.?
LINQ sözdizimi de.
Tamamlanması için kesinlikle "sınırlı bir tasarım" (hack için kibar bir kelime) yoluyla onları ele almayı düşünmeden önce, bu şeylerin ne kadar değerli olduğunu düşünmem gerekecek.
Yöntem bağımsız değişkenlerine veya örnek yöntemlere bağımlılıklarla sorunu ele almaya yönelik bir yaklaşım, oluşturulan, derlenen ve daha sonra IL analiz edilen kod parçasındaki bu şeylere referansları aynı türden "sentetik" yerel değişkenlerle değiştirmek olacaktır.
Başka bir Güncelleme - örnek üyelerine bağlı değişkenlerin tamamlanması artık çalışıyor.
Yaptığım şey, türü sorgulamaktı (anlambilim yoluyla) ve ardından mevcut tüm üyeler için sentetik stand-in üyeleri oluşturmaktı. Bunun gibi bir C # arabelleği için:
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 çıktısından yerel var nnn türünü öğrenebilmem için derlenen oluşturulan kod şu şekilde görünür:
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);
}
}
}
Tüm örnek ve statik tür üyeleri iskelet kodunda mevcuttur. Başarıyla derler. Bu noktada, yerel değişkenin türünü Yansıtma yoluyla belirlemek kolaydır.
Bunu mümkün kılan şey şudur:
- emacs'de powershell çalıştırma yeteneği
- C # derleyicisi gerçekten hızlıdır. Benim makinemde bir bellek içi derlemeyi derlemek yaklaşık 0,5 saniye sürüyor. Tuş vuruşları arası analiz için yeterince hızlı değil, ancak isteğe bağlı tamamlama listeleri oluşturmayı destekleyecek kadar hızlı.
Henüz LINQ'ya bakmadım.
Bu çok daha büyük bir problem olacak çünkü emacs için C # için anlamsal sözcük / ayrıştırıcı var, LINQ "yapmıyor".