Nie potrafię dokładnie powiedzieć, jakie algorytmy są używane w konkretnej implementacji, ale mogę zgadywać. Trie jest bardzo przydatnym struktura danych dla tego problemu: IDE może utrzymać dużą Trie w pamięci wszystkich symboli w projekcie, z pewnym dodatkowym metadanych w każdym węźle.
Kiedy wpisujesz znak, idzie on ścieżką w trie. Wszystkie elementy potomne określonego węzła trie są możliwymi uzupełnieniami. Następnie IDE musi tylko odfiltrować te, które mają sens w bieżącym kontekście, ale musi obliczyć tylko tyle, ile można wyświetlić w wyskakującym okienku uzupełniania tabulatorów.
Bardziej zaawansowane wypełnianie kart wymaga bardziej skomplikowanej próby. Na przykład Visual Assist X ma funkcję, dzięki której wystarczy wpisać wielkie litery symboli CamelCase - np. Jeśli wpiszesz SFN, pokaże ci symbol SomeFunctionName
w oknie uzupełniania tabulatora.
Obliczanie trie (lub innych struktur danych) wymaga przeanalizowania całego kodu w celu uzyskania listy wszystkich symboli w projekcie. Program Visual Studio przechowuje to w swojej bazie danych IntelliSense, .ncb
pliku przechowywanym wraz z projektem, dzięki czemu nie musi ponownie analizować wszystkiego za każdym razem, gdy zamykasz i ponownie otwierasz projekt. Gdy pierwszy raz otworzysz duży projekt (powiedzmy, który właśnie zsynchronizowałeś z formantem kontroli źródła), VS zajmie trochę czasu, aby przeanalizować wszystko i wygenerować bazę danych.
Nie wiem, jak radzi sobie ze zmianami przyrostowymi. Jak powiedziałeś, kiedy piszesz kod, jego składnia jest nieprawidłowa w 90% przypadków, a ponowne parsowanie wszystkiego za każdym razem, gdy będziesz bezczynny, obciąży Twój procesor bardzo niewielkim podatkiem, szczególnie jeśli modyfikujesz plik nagłówkowy dołączony duża liczba plików źródłowych.
Podejrzewam, że albo (a) dokonuje ponownej analizy tylko wtedy, gdy faktycznie budujesz swój projekt (lub prawdopodobnie kiedy go zamykasz / otwierasz), albo (b) wykonuje jakąś lokalną analizę, w której analizuje kod tylko w miejscu, w którym zredagowane w pewien ograniczony sposób, aby uzyskać nazwy odpowiednich symboli. Ponieważ C ++ ma tak wyjątkowo skomplikowaną gramatykę, może zachowywać się dziwnie w ciemnych zakamarkach, jeśli używasz ciężkiego metaprogramowania szablonów i tym podobnych.