Zwykły blame odpowiada na pytanie „kto zmienił linijkę 40 w pliku X”. Lens odpowiada na pytanie, które naprawdę zadajesz: co się działo z tą konkretną funkcją, także wtedy, gdy plik po drodze zmienił nazwę, a kod przewędrował do innego modułu. Dotyczy to też Blueprintów Unreala, nie tylko kodu. Poniżej dokładnie: co śledzi, w jakich językach, jak dopasowuje i gdzie ma granice. Bez obietnic na wyrost.
Pytasz o funkcję po nazwie. Dostajesz jej cały życiorys, sklejony przez wszystkie zmiany nazw i przeprowadzki między plikami. Zmiany czysto kosmetyczne (formatowanie, komentarze, nazwy zmiennych) nie zaśmiecają wyniku.
tq lens blame --symbol calculate_damage CREATED ch-6f9 rev:1 krzysztof „pierwsza wersja” MODIFIED ch-a12 rev:3 marta „buff obrażeń” MOVED+RENAMED ch-c73 rev:5 krzysztof (z compute_damage w Weapon.rs) [~86%]
Liczba w nawiasie, na przykład [~86%], to pewność dopasowania. Gdy nazwa i kod pasują dokładnie, jest to 100%. Gdy funkcję przemianowano i przepisano naraz, Lens pokazuje, jak bardzo jest pewny, że to wciąż ten sam byt.
Lens rozpoznaje i śledzi symbole kodu, czyli nazwane elementy programu. Konkretnie osiem rodzajów:
Wolne funkcje i metody wewnątrz klas. Metoda ma nazwę kwalifikowaną, na przykład Enemy::TakeDamage, żeby dwie różne metody o tej samej nazwie się nie myliły.
Klasy, struktury, enumy, interfejsy oraz trait'y (w Rust). Śledzone tak samo jak funkcje, przez zmiany nazw i przenosiny.
Jednostki grupujące kod (moduł, namespace, pakiet), na tyle na ile dany język je wyraża.
Blueprinty Unreala (.uasset): EventGraph, funkcje, makra i ConstructionScript. „Kto zmienił funkcję Interact w BP_Door?" działa też bez linijki kodu.
Dla większości języków Lens używa pełnego parsera składni (tree-sitter), więc rozumie strukturę kodu, a nie zgaduje z tekstu. Kilka języków bez gotowego parsera obsługuje prostszym mechanizmem zapasowym.
| Sposób | Języki | Dokładność |
|---|---|---|
| Pełny parser (tree-sitter) | Rust, C, C++, C#, Python, JavaScript, TypeScript, Go, Java, Lua | wysoka |
| Blueprinty UE (natywny) | pliki .uasset / .umap — grafy Blueprintu: EventGraph, funkcje, makra, ConstructionScript | grafy |
| Ekstraktor zapasowy (heurystyka) | Verse (UEFN), GDScript, ActionScript, shadery HLSL / GLSL / USF / USH | przybliżona |
Ekstraktor zapasowy czyta kod linia po linii i rozpoznaje deklaracje po wcięciach i słowach kluczowych. Działa, ale jest wrażliwy na nietypowe formatowanie, bloki w jednej linii czy mieszanie tabów ze spacjami. Rozszerzenia plików można też domapować ręcznie w pliku .tqlens.toml (na przykład potraktować .inc jak C++).
Sedno Lens: rozpoznaje, że symbol to wciąż ten sam symbol, nawet po sporych zmianach. Wykrywa:
| Sytuacja | Co robi Lens |
|---|---|
| Zmiana nazwy pliku lub przeniesienie funkcji do innego pliku | Śledzi dalej, niezależnie od ścieżki. |
| Zmiana nazwy samej funkcji | Rozpoznaje po strukturze kodu, z oceną pewności. |
| Zmiana nazwy i przepisanie ciała naraz | Dopasowuje rozmyto, pokazuje pewność (na przykład [~72%]). |
| Zamiana nazw dwóch funkcji miejscami | Rozstrzyga po strukturze, nie po nazwie. |
| Wydzielenie fragmentu do nowej funkcji albo wklejenie jej z powrotem | Wykrywa jako podział albo scalenie. |
| Skopiowanie funkcji | Oznacza jako kopię wciąż żywego oryginału. |
Lens sprowadza kod do znormalizowanej postaci: nazwy zmiennych zamienia na anonimowe znaczniki, pomija formatowanie i komentarze. Dzięki temu zmiana nazw w środku funkcji nie gubi jej tożsamości. Odporne jest nawet na lawinę zmian nazw wywoływanych funkcji.
Gdy dwie funkcje pasują niemal tak samo dobrze, Lens nie wybiera po cichu jednej. Oznacza wynik jako niejednoznaczny i pokazuje kandydatów. Rozstrzygasz sam komendą tq lens link albo w Studio jednym kliknięciem, a Twoja decyzja wygrywa z heurystyką.
Gdy funkcja znika w jednym commicie i pojawia się w innym w ciągu 7 dni (okno konfigurowalne), Lens skleja to w jedno przeniesienie, zamiast pokazać osobno „usunięto” i „utworzono”.
Metoda obrażeń w projekcie Unreal: zaczyna w jednym pliku, po drodze trafia do osobnego modułu i zmienia nazwę. Zwykły blame zgubiłby ślad przy każdym z tych kroków. Lens skleja to w jeden życiorys:
tq lens trace --symbol ApplyDamage CREATED rev:1 Source/Combat/Enemy.cpp TakeDamage MODIFIED rev:4 Source/Combat/Enemy.cpp (balans obrażeń) MOVED rev:7 Source/Combat/DamageSystem.cpp (z Enemy.cpp) [~100%] RENAMED rev:9 Source/Combat/DamageSystem.cpp TakeDamage → ApplyDamage [~91%]
Przeniesienie między modułami rozpoznaje po dokładnym odcisku (100%), a rename razem z drobną edycją ciała dostaje ocenę pewności (91%). Gdy później wydzielisz kawałek tej metody do osobnej funkcji, Lens pokaże to jako podział:
tq lens blame --symbol UpdateCooldowns
CREATED rev:12 Source/Combat/DamageSystem.cpp (wydzielone z ApplyDamage) [~78%]UCLASS, UFUNCTION, UPROPERTY, GENERATED_BODY). Historia symbolu żyje tam, gdzie logika, czyli w Twoich metodach, niezależnie od tego, do którego pliku czy modułu je przeniesiesz.| Komenda | Co robi |
|---|---|
tq lens blame --symbol <nazwa> | Historia symbolu. Przyjmuje pełną nazwę albo samą końcówkę (TakeDamage znajdzie Enemy::TakeDamage). |
tq lens trace --symbol <nazwa> | Pełny życiorys przez wszystkie nazwy, jakie symbol nosił po drodze. |
tq lens symbols <plik> | Lista symboli w pliku w bieżącej wersji. |
tq lens link <nowszy> <starszy> | Ręcznie zszywasz dwie historie, gdy heurystyka się zawahała. unlink to cofa. |
tq lens reindex / index | Przebudowa indeksu albo dociągnięcie zaległych rewizji. |
Każda komenda przyjmuje --json, więc łatwo wpiąć Lens w skrypty i narzędzia.
Lens: nazwa (CodeLens). Klik otwiera historię symbolu. To samo działa z menu kontekstowego na symbolu pod kursorem, a wynik ląduje w panelu. Pod spodem to tq lens blame --json, więc edytor i terminal pokazują dokładnie to samo. Na dziś to jedyna gotowa wtyczka; pełne Visual Studio i JetBrains Rider są w planach (patrz „Plany rozwoju” niżej).Wyniki parsowania są zapamiętywane pod odciskiem treści pliku. Ponieważ magazyn adresuje pliki po zawartości, plik niezmieniony albo identyczny z innym nie jest parsowany drugi raz.
| Indeksowanie 64 plików | Czas |
|---|---|
| pierwszy raz (zimny cache) | 135 ms |
| ponownie (ciepły cache) | 19 ms |
Lens rozumie tekst. Pliki binarne, jak Blueprint czy .uasset, to inna bajka i tu trzeba mówić uczciwie.
Lens ma działający host wtyczek (WASM) razem z zestawem narzędzi (SDK) do napisania własnego ekstraktora symboli dla dowolnego formatu, także binarnego. Taki ekstraktor liczy własny odcisk, a Lens śledzi te symbole tak samo jak tekstowe: przez zmiany nazw i przenosiny.
Blueprintów ani plików .uasset Lens jeszcze nie czyta. Obsługa Blueprintów jest w planach rozwoju i powstanie właśnie na tym mechanizmie wtyczek. Na razie Lens śledzi symbole w kodzie tekstowym.
Uczciwie oddzielamy to, co działa dziś, od tego, co dopiero planujemy. Na liście najbliższych rzeczy:
Śledzenie symboli wprost w grafach Blueprint i plikach .uasset, zbudowane na istniejącym mechanizmie wtyczek. Historia węzła czy funkcji Blueprinta tak samo jak dla kodu tekstowego.
Obok istniejącego rozszerzenia do VS Code planujemy wtyczki do pełnego Visual Studio oraz JetBrains Rider, żeby historia symbolu była pod ręką także tam, gdzie realnie piszecie kod gry.
Żeby nie było niespodzianek, oto rzeczy, których Lens nie robi albo robi z ograniczeniami:
Chcesz zobaczyć Lens w akcji na własnym repo? Napisz do nas. Więcej o codziennej pracy jest w dokumentacji.