Поиск и индексация кода: символы, AST, ранжирование и быстрые навигации
В предыдущих статьях вы настроили Python-воркфлоу в Cursor и разобрали, как контекст попадает в запрос к модели. Теперь разберём «под капотом» одну из самых практичных частей IDE: как Cursor находит нужный код, почему результаты иногда «мимо», и как вы можете управлять качеством поиска.
Ключевая идея: Cursor (как и VS Code) опирается не на один «поиск», а на несколько подсистем, каждая из которых отвечает на свой тип вопросов.
!Как IDE комбинирует разные виды поиска и превращает результаты в контекст для ИИ
Зачем вообще нужна индексация
Индексация — это подготовка структур данных, которые позволяют отвечать на запросы быстрее, чем «прочитать все файлы каждый раз».
В IDE обычно индексируют:
Текст: чтобы быстро найти строки в файлах.
Символы: чтобы знать, где определены функции, классы, методы, переменные.
Структуру кода: чтобы понимать границы сущностей и связи между ними.
Семантику: чтобы искать «по смыслу», а не по совпадению слов.Практическая связь с Cursor из прошлой темы про контекст: чем точнее и быстрее IDE находит кандидатов, тем лучше она собирает контекст для чата, Composer и агента.
Текстовый поиск: быстрый, точный, но «буквальный»
Самый базовый слой — поиск по тексту. В экосистеме VS Code для этого часто используется ripgrep.
Плюсы:
- очень быстрый поиск по точному совпадению;
- регулярные выражения;
- хорошо работает для якорей вроде
fromisoformat(, сообщения ошибки, имени поля JSON.
Минусы:
- не понимает структуру кода;
- не различает «определение» и «использование»;
- переименования и алиасы ухудшают точность.
Если ваш запрос в Cursor звучит как «найди все места, где используется X», под капотом часто стартует именно этот слой.
Полезные источники:
Документация VS Code по поиску
ripgrep (GitHub)Символьный индекс: функции, классы, определения и ссылки
Когда вам нужна навигация вроде «перейти к определению» или «найти все ссылки», текстового поиска недостаточно. IDE должна понимать, что Foo в одном месте — это именно тот Foo, который объявлен в другом.
Для этого VS Code и Cursor опираются на language server — процесс (или сервис), который умеет анализировать код и выдавать IDE структурные ответы.
Language Server Protocol (LSP) — стандарт, как IDE общается с language server: «дай определения», «дай ссылки», «дай диагностику».
Для Python такую роль обычно выполняют серверы наподобие Pylance (на базе Pyright) или другие LSP-реализации.Типичные операции, которые строятся на символьном индексе:
Go to Definition: перейти туда, где символ объявлен.
Find References: найти все места использования.
Rename Symbol: безопасно переименовать во всех местах (лучше, чем заменить строкой).
Outline: показать структуру файла (классы/функции).Почему это важно для Cursor: агент и Composer часто пытаются понять «где менять», и символьная навигация даёт более надёжные привязки, чем grep.
Полезные источники:
Language Server Protocol
Pyright (GitHub)AST: как IDE понимает структуру Python-кода
Чтобы строить символьный индекс, нужен анализ кода. Один из центральных способов — построить AST.
AST (Abstract Syntax Tree, «абстрактное синтаксическое дерево») — это дерево, где узлы соответствуют конструкциям языка: функция, вызов функции, присваивание, условие.
Для Python важно понимать ограничение:
AST строится по синтаксису и не всегда даёт однозначный ответ «на что ссылается имя» без дополнительного анализа (типизация, импорт-граф, выполнение).Но даже без «полного понимания исполнения» AST полезен для:
поиска всех def/class и их границ;
понимания «где тело функции», чтобы правильно нарезать код на чанки;
структурных преобразований (в идеале), когда важна корректность, а не просто совпадение текста.В Python есть стандартный модуль ast, который показывает идею на минимальном примере.
Этот пример не объясняет «как устроен Cursor», но помогает понять, какую структуру видит анализатор и почему IDE может выделять функции, импорты и вызовы.
Источник:
Документация Python по модулю astПарсеры в редакторах: почему часто слышно про Tree-sitter
Для быстрого структурного анализа в редакторах часто используют готовые инкрементальные парсеры: они умеют обновлять дерево при небольших правках, а не пересобирать всё.
Один из популярных проектов — Tree-sitter.
Зачем это IDE:
- быстро поддерживать подсветку синтаксиса;
- быстро выделять «границы сущности» (например, текущая функция);
- помогать нарезать код на чанки для контекста.
Источник:
Tree-sitter (GitHub)Семантический поиск: «по смыслу», а не по словам
Когда вы пишете в Cursor что-то вроде «найди место, где валидируются входные данные» или «где формируется payload для этого запроса», точных ключевых слов может не быть.
Тогда применяется семантический поиск:
код и текст режутся на чанки (небольшие фрагменты);
для каждого чанка строится эмбеддинг — числовой вектор, который отражает смысл фрагмента;
запрос тоже превращается в вектор;
далее ищутся ближайшие чанки.Векторный поиск часто реализуют через специализированные библиотеки.
Источник:
FAISS (GitHub)Важно помнить ограничение: «похожесть по смыслу» иногда вытаскивает похожую, но не ту логику. Поэтому для надёжности комбинируйте семантику с якорями: имя функции, модуль, сообщение ошибки.
Ранжирование: почему найденные кандидаты приходят в «неправильном» порядке
Когда IDE получила кандидатов из разных источников (grep, символы, семантика), она должна отсортировать их и выбрать несколько фрагментов в контекст.
Ранжирование — это набор правил, которые решают, что важнее.
Типичные факторы ранжирования:
близость к открытому файлу или текущему выделению;
совпадение точных токенов (имена функций, классов);
совпадение пути (например, src/app/ важнее, чем tests/, если вы чините runtime-ошибку);
«свежесть»: файлы, которые недавно менялись;
уверенность language server (например, точные ссылки vs сомнительные).Почему это важно для Cursor-правок:
модель не видит «весь репозиторий», она видит выбранные фрагменты;
если ранжирование принесло не те фрагменты, ответ будет выглядеть правдоподобно, но окажется неприменимым.Практический приём из прошлой статьи про контекст: просите Cursor перечислить какие файлы и почему он выбрал, прежде чем применять большой патч.
Быстрые навигации, которые усиливают ИИ-режимы
Cursor унаследовал от VS Code привычные навигации. Они важны именно потому, что помогают вам быстро давать точный контекст.
Навигации на базе символов
Перейти к определению: быстро открыть нужное место и дать модели правильный файл.
Найти все ссылки: понять реальную область изменений перед Composer.
Переименовать символ: предпочитать этому «глобальную замену текста».Навигации на базе текста
Поиск по проекту: хорошо для якорей и диагностик.
Поиск по открытому файлу: быстро уточнить локальный контекст перед inline-правкой.Как использовать это вместе с чатом и агентом
Рабочий паттерн:
Текстовым поиском найдите якорь (имя, ошибка, строка).
Символьной навигацией проверьте определение и реальные ссылки.
Откройте 1–3 релевантных файла.
Только после этого просите Cursor менять код, ограничив область.Пример запроса, который учитывает индексы:
В этом запросе вы фактически говорите IDE: «используй текстовый поиск для кандидатов и символьный для уточнения».
Типовые сбои и как их диагностировать
Поиск нашёл много похожего
Причины:
повторяющиеся утилиты и дублирование логики;
несколько версий одной функции в разных пакетах.Что делать:
уточнять путь: src/app/...;
просить перечислить кандидатов и выбрать один с аргументацией;
добавлять якорь: имя конкретной функции, имя исключения, строку лога.«Перейти к определению» ведёт не туда или не работает
Причины:
неверно выбран интерпретатор/окружение;
отсутствуют зависимости в .venv;
language server не смог построить корректный импорт-граф.Что делать:
проверить, что Cursor использует нужный интерпретатор (из первой статьи);
установить зависимости;
переоткрыть проект, дождаться индексации.Семантический поиск предлагает «почти то же самое»
Причины:
векторная близость не гарантирует эквивалентность;
чанк не содержит критический контекст (например, импорт или контракт).Что делать:
просить включить «шапку файла» (импорты, константы);
добавлять точные токены в запрос;
просить минимальный дифф и подтверждение предположений вопросами.Что забрать в практику
В Cursor нет «одного поиска»: текстовый, символьный и семантический слои решают разные задачи.
Для правок по проекту лучший результат даёт связка: якорь (grep) → уточнение сущности (символы) → контролируемая правка (дифф и тесты).
Если Cursor «уверенно ошибается», почти всегда проблема в том, что в контекст попали не те фрагменты из-за отбора и ранжирования.В следующей логичной теме курса можно разбирать агентные сценарии: как агент планирует шаги, чем отличается от Composer, и где именно возникают лишние правки из-за неверно выбранных кандидатов.