Tuesday, December 28, 2010

у меня javascript.

В последнее время я много занимался поддержкой javascript. В основном, дело касалось code completion. Расскажу немного о трудностях, которые возникают, когда в языке отсутствуют типовые аннотации. Как ни странно это звучит, с решарпером javascript становится языком с довольно внушительной системой типов. Еще более странно будет звучать то, что все эти типы не используются для резолва, но обо всем по порядку.
Первое, что надо делать при поддержке любого языка - это построить кэш деклараций. В javascript нелокальными являются только свойства объектов. Существует бесчисленное (в прямом смысле этого слова) множество способов определить свойство в объекте. Начать можно с функции конструктора, для разминки поддержать микрософтовский RegisterNamespace("Jetbrains.ReSharper"), после чего можно сделать jQuery.Extend(). Для любителей пособирать декларации самостоятельно в этом месте все расширяемо и можно писать свои провайдеры.
Декларации просто так собирать не интересно, для каждого свойства хорошо бы знать его объемлющий класс (для которого это свойство может быть статическим или свойством объекта). Тут же собираем информацию о наследовании классов. Инвертируем полученную информацию об объемлющих классах и имеем основной источник информации об объеках и их свойствах.
Второй источник - это выражение, которым было проинициализировано свойство. Опять же, здесь есть место для тех, кто знает про типы выражений какие-то свои тайны.
А теперь главное: как представляется тип. Он почти всегда является наборов из нескольких типов, свойства которых объединяются. Самих же свойств довольно много:
1. Собственно свойства объекта. Пример: x.
2. Сигнатура (в случае если объект можно вызывать). Пример: x(1,2,3).
3. Тип возвращаемого значентя. Пример тот же.
4. Тип создаваемого объекта. Это значит что функция была конструктором. Пример: new x().
5. Базовый тип. Пример: x.prototype.
Вычисление типов может залезать довольно далеко в код и кэши, поэтому в вычислялке есть механизм кэширования и защита от зацикливаний. На посчитанных типах работает только комплишен и информация о параметрах. Для резолва тип квалификатора не используется, что мне кажется правильным. В ближайшем будущем я сделаю киллер фичу: create property from usage, которая станет третьим клиентом моей типизации javascript, а провыйдеры свойств я обяжу уметь создавать свойства которые они распознают. И теперь представьте себе, что расширение для jQuery можно будет сделать нажав alt+enter!

Tuesday, December 7, 2010

Плюс десять процентов к карме

Код комплишен остался где-то позади. Нет, про него никто не забыл, просто теперь он может жить своей новой и прекрасной жизнью самостоятельно, а я могу лишь радоваться тому, что за пару недель количество правил выросло на 10% до 110. Значит есть жизнь после рефакторинга и есть смысл двигаться дальше. 
А дальше идет еще более чудесный JavaScript, в котором из комментариев мы пытаемся извлекать информацию о типах, которой так не хватает человеку, избалованному строгими языками, пытаемся воплотить тысячу и одну идею облегчения жизни пользователям jQuery, полируем код комплишен и активно работаем с чудо-листочком, пытаясь ужать безграничную фантазию в 3 месяца сроку... Не знаю, почему я пишу слово 'мы'.
И вот в этой суете я задумался над одной вещью. Допустим, мы удалим поддержку js. Да, прямо сегодня мы стираем всю поддержку js и начинаем писать все заново. Мы укладываемся в 3 месяца? Ах, черт, боюсь что нет. Хотя это я считаю на одного человека. Если их будет двое? Уже более реалистично. А было ли это так же реалистично полгода назад?

Thursday, November 25, 2010

У меня утро!

Попробуйте сформулировать, что делает ваша фича или подсистема. Сделайте это письменно, на хорошем русском языке и Вы, возможно, увидите некоторые проблемы в архитектуре. Возможно, Вы увидите и некоторые достоинства Вашей реализации! Но то, что Вы увидите что-то, о чем раньше не думали - это факт.

Tuesday, November 9, 2010

Не спрашивайте меня ничего, я пишу!

Я сказал сегодня, что не могу вспомнить, чем именно примечателен некий персонаж из пьесы, которую мы читали на английском на прошлой неделе. Отчасти это была правда, отчасти я надеялся на сочувствие, говоря, что у меня в голову загружен code completion и потребуется некоторое время чтобы освободить немного ресурсов для других задач. Но сочувствия я не нашел. Оказалось, что по мнению Н., мой мозг не обладает должной эластичностью и это качество мне необходимо развивать. Черт возьми, я этого совсем не ожидал! Действительно ли, погружаясь с головой в решение некоторой проблемы, какой бы сложной она не казалась, мы убиваем способность мыслить шире? Возможно ли, без потери эффективности, научиться мгновенно переключаться между разными задачами? 
Я не знаю ответ, но code completion дописал. Осталось сделать так, чтобы он заработал. Или взглянем шире... нужен ли нам code completion?      

Sunday, November 7, 2010

двигаю гору code completion

На этой неделе, уходя домой, я каждый день прикреплял к стенке бумажку с количеством некомпилирующихся файлов. В последний раз написал 19. А в течение недели это число временами превышало 100.
Теперь я знаю, как делается code completion, а точнее как он будет делаться, когда все взлетит. Вот некоторые предварительные итоги.
Теперь в решарпере 4 вида code completion: basic, automatic, import и smart.  И здесь у нас первое достижение - ничто не мешает сделать новый пятый вид, если кто-то придумает что он будет делать. Кстати, почему бы не сделать дважды умный или дважды импортирующий code completion? В коде такая гибкость выражается не только открытым множеством значений видов code completion, но и декларативным стилем проверок этих значений.
Автоматический code completion является обособленным видом, так как, в отличие от его родственников, он связан с некоторой сессией, время жизни которой определяется довольно сложно и зависит от печати кода, показаных lookup окошек и асинхронного построителя синтаксического дерева. Опять же, сейчас стало понятно, что управление этой сессией надо выносить в отдельную компоненту.
Теперь немного о дизайне. Программном.
Все просто: есть два набора компонент. Первый набор отвечает за создание контекста. Обычно на каждый язык приходится по одному такому контексту. Сложность контекста определяется серьезностью намерений реализовать хороший и быстрый code completion. Сейчас в c# контекст сложный, а в некоторых языках тривиальный. Но это мы исправим.
Имея набор контекстов, мы берем второй набор компонент - правила добавления и изменения списка completion item-ов. Каждое правило состоит из следующих стадий: подготовка, добавление новых элементов, редактирование списка элементов с возможностью удаления существующих и добавлением новых, декорация или изменение атрибутов элементов, определение стиля показа lookup окошка и опций автоматического применения. Все стадии выполняются последовательно для всех правил преобразования.
Некоторый примеры стадий:
1. С подготовкой все понятно.
2. Добавление ключевых слов или live template item-ов.
3. Собственно, замена ключевых слов на омонимичный live template item-ы.
4. Замена методов со скобочками на методы без скобочек в месте, где ожидается делегат.
5. Тоже нечего сказать.
Итого, мы имеем расширяемый по технологиям, изменяемый в пределах одной технологии и, к тому же, быстро работающий code completion. Правильно?

Friday, October 29, 2010

code completion

Что больше влияет на качество программного продукта? Продуманность и мощь ядра, или отполированная до блеска функциональность фич для конечных пользователей? Очевидно, что многие фичи Решарпера были бы невозможными без мощной поддержки со стороны ядра, но также очевидно, что все достоинства космической архитектуры могут быть сильно испорчены неаккуратной реализацией фич. Хорошим примером листовой фичи, требующей особой аккуратности, является код комплишен.
Трудно подсчитать количество времени, которое потребовалось для того чтобы реализовать комплишен в C#. Думаю, что это многие месяцы работы.
Сейчас в Решарпере поддерживается более десятка различных языков, и во всех них хочется иметь такой же отшлифованный код комплишен как и в C#. Да, реализовывать его в разных языках будут разные люди, и задача у них будет сложнее, так как многие технологии, которые мы поддерживаем сейчас напрямую не используются нами, а значит продумывать поведение нужно будет буквально в воздухе!
Возможно ли создать такую инфраструктуру, в которой качественный код комплишен получался бы автоматически? В общем случае, думаю, нет. Тонкая настройка нужна. Но хочется сделать так, чтобы реализация для конкретного языка сводилась бы исключительно только к такой настройке.
Собственно, созданием общей инфраструктуры код комплишена я и буду заниматься в ближайшее время. Посмотрим, что получится.

Sunday, October 24, 2010

Conflicts page

Интересно, кто-нибудь читает что написано на страничке конфликтов при выполнении рефакторингов? Лично я их не читаю, и каждый раз расстраиваюсь, что опять надо нажимать 'next' и ждать. Да, я расстраиваюсь еще больше от осознания того, что к моменту показа странички с конфликтами рефакторинг уже отработал, но изменения не были применены к реальным файлам!
Объясняется все очень просто. Самый удобный способ найти возможные проблемы при выполнении рефакторинга - это попытаться его выполнить и в процессе выполнения запоминать, что и где не получилось поменять. В случае, если есть ошибки или предупреждения, необходимо откатить все изменения и показать отчет. Конечно, результаты самых тяжелых вычислений необходимо закэшировать, но в общем случае рефакторинг выполняется дважды.
Когда-то мы стремились к тому, чтобы выдавать конфликты во всех случаях, когда рефакторинг может привести к появлению некомпилирующегося кода. Используя схему с двумя проходами, мы без проблем можем справиться с любой неординарной ситуацией и выдать сообщение, но как это сообщение поможет выполнить рефакторинг пользователю?