Многие редакторы и IDE имеют автозавершение кода. Некоторые из них очень «умны», другие нет. Меня интересует более умный тип. Например, я видел IDE, которые предлагают функцию только в том случае, если она а) доступна в текущей области б) ее возвращаемое значение является допустимым. (Например, после "5 + foo [tab]" он предлагает только функции, которые возвращают то, что может быть добавлено к целым числам или именам переменных правильного типа.) Я также видел, что они помещают впереди наиболее часто используемый или самый длинный вариант. списка.
Я понимаю, что вам нужно разобрать код. Но обычно при редактировании недействительного текущего кода в нем есть синтаксические ошибки. Как вы разбираете что-то, если оно неполное и содержит ошибки?
Также есть временные ограничения. Завершение бесполезно, если создание списка занимает секунды. Иногда алгоритм завершения имеет дело с тысячами классов.
Какие для этого подходят хорошие алгоритмы и структуры данных?
источник
Ответы:
Механизм IntelliSense в моем сервисном продукте языка UnrealScript сложен, но я дам здесь как можно лучший обзор. Служба языка C # в VS2008 SP1 - моя цель производительности (не зря). Этого еще нет, но он достаточно быстрый / точный, чтобы я мог безопасно предлагать предложения после ввода одного символа, не дожидаясь Ctrl + пробел или пользователя, вводящего
.
(точки). Чем больше информации люди [работающие над языковыми услугами] получат по этому поводу, тем лучше будет опыт конечных пользователей, если я когда-нибудь буду использовать их продукты. Есть ряд продуктов, с которыми у меня был неудачный опыт работы, которые не уделяли такого пристального внимания деталям, и в результате я боролся с IDE больше, чем кодировал.В моей языковой службе это выглядит следующим образом:
aa.bb.cc
, но также может содержать вызовы методов, как вaa.bb(3+2).cc
.IDeclarationProvider
, где вы можете вызвать,GetDeclarations()
чтобы получитьIEnumerable<IDeclaration>
из всех элементов, видимых в области. В моем случае этот список содержит локальные переменные / параметры (если в методе), члены (поля и методы, только статические, если только в методе экземпляра, и нет закрытых членов базовых типов), глобальные переменные (типы и константы для языка I работаю над) и ключевыми словами. В этом списке будет пункт с названиемaa
. В качестве первого шага в оценке выражения в №1 мы выбираем элемент из контекстного перечисления с именемaa
, которое дает намIDeclaration
следующий шаг.IDeclaration
представлению,aa
чтобы получить другой,IEnumerable<IDeclaration>
содержащий «члены» (в некотором смысле) изaa
. Поскольку.
оператор отличается от->
оператора, я вызываюdeclaration.GetMembers(".")
и ожидаю, чтоIDeclaration
объект правильно применит указанный оператор.cc
, где список объявлений может содержать или не содержать объект с именемcc
. Как я уверен, вы знаете, если несколько элементов начинаются сcc
, они также должны появиться. Я решил эту проблему, взяв окончательное перечисление и пропустив его через свой документированный алгоритм, чтобы предоставить пользователю максимально полезную информацию.Вот несколько дополнительных примечаний для серверной части IntelliSense:
GetMembers
. Каждый объект в моем кэше может предоставить функтор, оценивающий его члены, поэтому выполнение сложных действий с деревом почти тривиально.List<IDeclaration>
из своих членов, я сохраняюList<Name>
, гдеName
- структура, содержащая хеш специально отформатированной строки, описывающей член. Есть огромный кеш, который сопоставляет имена объектам. Таким образом, когда я повторно анализирую файл, я могу удалить все элементы, объявленные в файле, из кеша и повторно заполнить его обновленными членами. Из-за способа настройки функторов все выражения немедленно вычисляют новые элементы."Интерфейс" IntelliSense
По мере того как пользователь набирает текст, файл синтаксически неверен чаще, чем правильный. Таким образом, я не хочу случайно удалять разделы кеша, когда пользователь вводит текст. У меня есть большое количество правил для особых случаев, чтобы обрабатывать инкрементные обновления как можно быстрее. Инкрементный кеш хранится только локально для открытого файла и помогает убедиться, что пользователь не понимает, что его ввод заставляет внутренний кеш хранить неверную информацию о строках / столбцах для таких вещей, как каждый метод в файле.
Фрагмент кода для предыдущего раздела:
Я решил добавить список функций IntelliSense, которые я реализовал с помощью этого макета. Фотографии каждого находятся здесь.
источник
Я не могу точно сказать, какие алгоритмы используются в какой-либо конкретной реализации, но могу сделать некоторые обоснованные предположения. Trie очень полезная структура данных для этой проблемы: IDE может поддерживать большой в памяти синтаксического дерева всех символов в вашем проекте, с некоторыми дополнительными метаданными на каждом узле.
Когда вы вводите символ, он идет по тропинке в дереве. Все потомки конкретного trie-узла являются возможными дополнениями. Затем IDE просто нужно отфильтровать их по тем, которые имеют смысл в текущем контексте, но ей нужно только вычислить столько, сколько может быть отображено во всплывающем окне завершения табуляции.
Более продвинутое завершение табуляции требует более сложного дерева. Например, Visual Assist X имеет функцию, при которой вам нужно вводить только заглавные буквы символов CamelCase - например, если вы вводите SFN, он показывает вам символ
SomeFunctionName
в своем окне завершения табуляции.Вычисление дерева (или других структур данных) требует синтаксического анализа всего вашего кода, чтобы получить список всех символов в вашем проекте. Visual Studio хранит это в своей базе данных IntelliSense,
.ncb
файле, который хранится вместе с вашим проектом, поэтому ему не нужно повторно анализировать все каждый раз, когда вы закрываете и снова открываете свой проект. В первый раз, когда вы откроете большой проект (скажем, тот, который вы только что синхронизировали с системой управления версиями), VS потребуется время, чтобы проанализировать все и сгенерировать базу данных.Я не знаю, как он обрабатывает постепенные изменения. Как вы сказали, когда вы пишете код, это неверный синтаксис в 90% случаев, и повторный анализ всего, когда вы простаиваете, приведет к огромному налогу на ваш процессор с очень небольшой выгодой, особенно если вы изменяете файл заголовка, включенный большое количество исходных файлов.
Я подозреваю, что он либо (а) только повторно анализирует, когда вы действительно создаете свой проект (или, возможно, когда вы его закрываете / открываете), либо (б) он выполняет своего рода локальный синтаксический анализ, где он анализирует только код там, где вы только что отредактировано ограниченным образом, чтобы получить имена соответствующих символов. Поскольку C ++ имеет такую чрезвычайно сложную грамматику, он может вести себя странно в темных углах, если вы используете тяжелое метапрограммирование шаблонов и тому подобное.
источник
Следующая ссылка поможет вам в дальнейшем ..
Подсветка синтаксиса: быстрое цветное текстовое поле для выделения синтаксиса
источник