Как отмечалось ранее, для работы JS вместо Spket IDE я теперь использую Eclipse JSDT, который входит в состав Eclipse Web Tools Project. Причины для такого перехода вполне естественные: проекты, с которыми я работаю, становятся всё сложнее и больше, нужно больше удобства и контроля над ситуацией.
В JSDT меня больше всего привлекло следующее:
- Рефакторинг: переименование объектов, выделение блока в отдельную переменную, объединение определения переменной и присваивания и т.д. Некоторые вещи вроде выделения в метод пока толком не работают, но, надеюсь, в ближайшее время это будет исправлено.
- Валидация кода. Помимо обычной проверки синтаксиса, можно настроить более сложные проверки вроде поиска неиспользованных переменных, недостижимый код, переопределение переменной из внешней области видимости и т.д.
- Выделение фрагментов camelCase-переменных с помощью Shift+Alt+← и Shift+Alt+→
- Поддержка JSDoc.
- Удобный Outline/Quick outline; дополнительные окна, в которых показывается документация и код определения текущего объекта.
- Автоматическая отбивка кода при его перемещении из/в блок.
- Подключение внешних библиотек.
- Встроенный дебаггер.
Лучше всего будет, если читатель поставит Eclipse for JavaScript Web Developers и изучит все настройки и менюшки — в том числе контекстные — JSDT (лучше включить перспективу JavaScript), потому что возможностей действительно очень много и их сложно описать в одной статье.
Однако при всей «крутости» этой среды разработки, в ней есть ряд проблем, с которыми пришлось столкнуться прежде, чем окончательно перейти на JSDT.
Начинаем работу
Для того, чтобы полноценно использовать все возможности JSDT, обязательно нужно создать проект с JavaScript-природой. Делается очень легко: вызываем File > New > Project… и в появившемся окошке выбираем JavaScript Project. В появившемся диалоговом окне вводим название проекта и жмём Finish. По умолчанию создаётся веб-проект с поддержкой DOM, папкой с JS-исходниками является сам проект. Когда вы лучше освоитесь с JSDT, то сможете более тонко настраивать проект: указывать подключаемые библиотеки, исключать ненужные файлы и папки из индекса. Пока оставим как есть.
Module pattern
Главным преимуществом для меня в Spket IDE была поддержка современных паттернов, в том числе и модуля:
var module = (function() { return { method: function() {} }; })();
В Spket такая конструкция без проблем отображается в outline и по ней работает code complete, но в JSDT ни то, ни другое не работает:
Небольшой JSDoc исправит ситуацию:
/** * @type module */ var module = (function() { return { /** * @memberOf module */ method: function() {} }; })();
Теперь работает как надо:
Но есть ещё одна проблема: приватные переменные и методы модуля не отображаются в Outline, а очень хотелось бы. Это можно исправить, описав самовызывающуюся функцию как конструктор несуществующего класса:
/** * @memberOf __module * @type module */ var module = (/** @constructor */ function() { function myPrivateMethod() { } return { /** * @memberOf module */ method: function() {} }; })();
Как видно из примера, я описал несуществующий класс __module
, двойное подчёркивание я использую в качестве своеобразного соглашения об именовании объектов. Проблема в том, что этот несуществующий класс попадёт в code complete всего проекта, и использование двойного подчёркивания — простой и понятный способ отфильтровать ненужные данные при вызове code complete. Однако этот недостаток очень легко можно превратить в достоинство: таким образом можно описывать структуры объектов, доступ к описанию которых затруднён из-за отсутствия строгой типизации в JS:
Поддержка популярных библиотек
Базовый набор библиотек для JSDT довольно невелик: это стандартные объекты JavaScript (Array
, String
и прочее) и стандартный DOM (HTMLElement
, document
и так далее). То есть если мы напишем, например, document.
и вызовем code complete, то увидем список свойств и методов объекта document
, экземпляра класса Document
. Но современная веб-разработка немыслима без использования популярных библиотек вроде jQuery.
Имея в своём распоряжении JSDoc, можно создать описание практически любого популярно фреймворка и использовать его для подсказок в коде. Так как таких описаний в интернете найдено не было, я воспользовался своим любимым принципом «если хочешь, чтобы что-то было сделано хорошо, сделай это сам» и запустил проект, в котором создаю описания популярных библиотек и фреймворков, с которыми работаю:
jsdt-docs — JSDoc для популярных библиотек
В этом проекте сейчас есть следующие библиотеки:
- Modernizr 2
- Browser Addons — разные методы и свойства, которые почему-то отсутствуют в стандартном описании DOM в JSDT.
- console — небольшое описание объекта
console
, который присутствует в современных браузерах. - CSS2Properties — список CSS-свойств для свойства
style
DOM-элементов. Несмотря на то, что он называется CSS2, в нём присутствуют и CSS3-свойства: такое название выбрано потому, что в стандартном описанииElement.prototype.style
является объектом классаCSS2Properties
. - JS SIgnals
- Node.JS
- Socket.IO
- Underscore.js.
- Zepto.js
Добавить библиотеку довольно просто:
- Идём в настройки: Preferences > JavaScript > Include Path > User Libraries.
- Создаём новую библиотеку: нажимаем кнопку New…
- Вводим название библиотеки в появившемся окне и жмём ОК.
- Выделив только что добавленную библиотеку, нажимаем на кнопку Add .js file… и выбираем один или несколько файлов, относящихся к данной библиотеке.
После того, как библиотека была создана, нужно добавить её в проект:
- Идём в настройки проекта: Project > Properties > JavaScript > Include Path > Libraries.
- Жмём кнопку Add JavaScript Library, выбираем User Library, а затем и библиотеки, которые хотим добавить.
Теперь у вас в проекте будет работать code complete для указанных библиотек:
Поддержка jQuery
Добавление поддержки своего любимого фреймворка оказалась не такой уж и простой задачей. Во-первых, он довольно большой и содержит внушительных объемов документацию. Во-вторых — многие методы в нём «перегружены»: например, val()
возвращает текстовое значение поля, а val(str)
— записывает его и возвращает уже jQuery-объект.
К счастью, у документации к jQuery есть публичный API, который выдаёт описание всех методов в XML. Я написал парсер на node.js, который опрашивает API и преобразует XML в JSDoc, понятный для JSDT (и, надеюсь, другим IDE). Так что в случае выхода новой версии jQuery можно быстро перегенерировать всё документацию. Парсер я писал для себя и по-простому, он не поддерживает передачу параметров через командную строку, всё настраивается в main.js:
- Можно сгенерировать документацию для определённой версии jQuery (
TARGET_VERSION
). - Можно сгенерировать описание в виде нескольких файлов. Это связано с особенностью JSDT. Как уже отмечалось ранее, в jQuery много «перегруженных» методов, и если их описать в одном JS-файле, то JSDT будет использовать только одно (самое последнее) описание. Однако если сохранить описание методов с одинаковым названием в разных файлах, то JSDT вполне неплохо покажет по ним code complete и документацию. Так что у вас есть выбор: при создании библиотеки jQuery добавить все файлы вида
jquery-jsdoc-N.js
(Eclipse JSDT) или же толькоjquery-jsdoс.js
если ваша IDE способна прочитать описание с перегруженными методами. - Можно переписать некоторые определения для более удобной работы с code complete (
class_map
,prefix_map
). Например, так переписываются описания дляDeferred
иPromise
объектов, чтобы можно было использовать их описание в JSDoc:
В описании есть ряд классов, которые можно использовать в JSDoc: __jQueryDeferred
, __jQueryPromise
и __jQueryEvent
.
Пишем плагины к jQuery
Собственно, как писать плагины рассказывать нет смысла, это подробно описано в документации. Покажу лишь как сделать так, чтобы ваш плагин появился в code complete. А сделать это очень просто, достаточно методу плагина указать, что он является членом класса jQuery:
jQuery.extend(jQuery.fn, { /** @memberOf jQuery */ myPlugin: function() { } }); $('div').m // тут можно вызвать code complete
Поддержка Node.JS
Отдельно стоит упомянуть Node.JS, так как к модулям нужно обращаться не напрямую, а через функцию require()
:
var http = require('http'); http.createServer(function() { });
То есть подсказки для модуля должны зависеть от того, какой аргумент передали в функцию require()
. В целом, в JSDT эта ситуация решаема: нужно написать отдельный плагин в виде подключаемой библиотеки, который будет находить вызовы функции require()
, смотреть на аргумент и возвращать виртуальный объект с необходимыми методами, и может быть я когда-нибудь напишу такую библиотеку. А пока будем довольствоваться малым: указывать тип модуля через JSDoc
.
Все модули в моей документации описаны в виде классов Node{Name}Module
, например: http
→ NodeHttpModule
, util
→ NodeUtilModule
. Поэтому для переменной, в которую записывается модуль, указываем через JSDoc нужный тип:
/** @type NodeHttpModule */ var http = require('http'); http. // вызываем code complete
Советы
В качестве заключения дам несколько советов, которые помогут вам в работе с JSDT:
- Не используйте фигурные скобки при указании типа переменной с помощью тэга
@type
— в некоторых случаях JSDT не сможет подцепить тип переменной. Лучше писать так:@type Array
(вместо@type {Array}
). - При описании модуля тэг
@memberOf moduleName
достаточно указать только у одного свойства/метода в литерале, все остальные подцепятся автоматически. - Если у вас в проекте есть непосредственно код библиотек (например, jQuery), то его лучше исключать из индекса, чтобы он не мешал работе code complete. Делается это так: заходите в настройки проекта Project > Properties > JavaScript > Include Path > Source. Разворачиваете папку с исходниками, двойной клик по фильтру Excluded и в диалоговом окне в секцию Exclusion patterns добавляете файлы, которые надо исключить.