БетаЛаборатория - внеочередной IT-блог

Записки обычного программиста

 

Подстветка синтаксиса, часть третья

Задумался о том, что в текущей реализации подсветки кода я могу столкнуться с чрезмерным увеличением количества AJAX-запросов. Учитывая что на одной странице выводится до 20 записей, в каждой из которой может оказаться по несколько блоков кода, это чревато десятками запросов.
 
В сети я нашел несколько скриптов для подсветки синтаксиса на JavaScript, однако, после нескольких экспериментов, я убедился что они не дают никаких реальных преимуществ. Самым качественным решением, из тех которые я нашел, является библиотека Ивана Салагаева (http://softwaremaniacs.org/soft/highlight/). Но, во-первых, упакованная библиотека со всеми языками весит 78 Килобайт, а во-вторых, имеет несколько странную реализацию - подсветка осуществляется ТОЛЬКО автоматически, основываясь на анализе кода, то есть нельзя вручную указать какой блок подсвечивать. Скорость его работы я не анализировал, поскольку для меня достаточным для отказа фактором стал именно вес библиотеки.
 
Поэтому я несколько оптимизировал свою систему. В новой версии все блоки на странице обрабатываются одним AJAX запросом. Для этого сначала генерируется форма, в которую агрегируются все блоки с кодом. Форма создается по правилам Django Formset, для удобства ее обработки на сервере.
Полученный от сервера ответ разбит на блоки, которые очень легко разбираются и размещаются где положено.
 
$(document).ready(function() {
	var form = $("<form>"); // Создаем форму.
	var i = 0;
	$(".highlight").each(function() { //Перебираем все блоки требующие подсветки
		var hObject = $(this);
		hObject.attr("id", "highlight-"+i); // Присваиваем блоку уникальный id, чтобы потом найти его.
		// Создаем поле language для формы. Содержит название языка.
		language = $("<input>").attr({
			"name" : "form-"+i+"-language",
			"value" :  hObject.attr("lang")? hObject.attr("lang") : "text", // Если аттрибудет lang не задан, код будет возвращен без подсветки.
			"type" : "text"
		});
		// Создаем поле id для формы. Используется для идентификации блока с результатом.
		id = $("<input>").attr({
			"name" : "form-"+i+"-id",
			"value" :  i,
			"type" : "text"
		});
		// Создаем поле code для формы. Содержит код для подсветки.
		code = $("<textarea>").attr({
			"name" : "form-"+i+"-code",
		}).text(hObject.html());
		// Добавляем поля к форме.
		form.append(language);
		form.append(code);
		form.append(id);
		i++;
	});
	// Если есть хотя бы один блок
	if (i) {
		// Создаем поле form-TOTAL_FORMS. Нужен для Django Formset для обработки формы. 
		total = $("<input>").attr({
			"name" : "form-TOTAL_FORMS",
			"type" : "text",
			"value" : i
		});
		// Создаем поле form-INITIAL_FORMS. Нужен для Django Formset для обработки формы. В нашем случае всегда 0.
		initial  = $("<input>").attr({
			"name" : "form-INITIAL_FORMS",
			"type" : "text",
			"value" : "0"
		});
		// Добавляем поля к форме.
		form.append(total); 
		form.append(initial);
		// Отправляем запрос 
		$.post("/tools/highlight/", form.serialize(), function(response_data) {
			// Перебираем блоки ответа.
			$(response_data).find(".highlighted").each(function(){
				// Заменяем исходный блок, на подсвеченный, ореинтируясь на id.
				$("#highlight-"+$(this).attr("id")).replaceWith($(this).html());
			});
		});
	}
});
pyhoster, 05/12/2009 14:40

Обратите внимание так же на темы:

Комментарии

Иван Сагалаев(Гость)

> Но, во-первых, упакованная библиотека со всеми языками весит 78 Килобайт

Вам почти наверняка не нужны все. Обычно реальный набор без экзотики укладывается в 30.

> подсветка осуществляется ТОЛЬКО автоматически

Неправда. Язык всегда можно указывать явно, про это в доке написано.

pyhoster

Спасибо за комментарий, Иван! Я совершенно согласен с обоими Вашими замечаниями. Но, все-таки и 30 дополнительных килобайт, это все-таки много для одной задачи. Причем я заостряю внимание на том, что именно дополнительные, в том смысле, что помимо вашей библиотеки используются другие большие модули, как jQuery в моем случае. Для stand alone использования ваше решение как раз оптимально.

Насчет автоматической подсветки, тут скорее я не верно выразился. Я имел ввиду не механизм определения языка, а именно механизм определения блоков для подсветки. То есть ваша библиотека подсвечивает все блоки "<pre><code></code></pre>" на странице обнаруженные при загрузке. Но, ведь не всегда все нужные блоки есть на странице изначально - некоторые могут образоваться позднее, либо просто сгенерированные, либо полученные через AJAX-запросы. Я вполне допускаю, что не полностью разобрался в исходном коде и документации, но я не обнаружил возможности подсветить определенный блок вручную. Если такая возможность есть и вы мне укажете на нее, буду очень благодарен вам.

Иван Сагалаев(Гость)

> Но, все-таки и 30 дополнительных килобайт, это все-таки много для одной задачи.

Честно говоря, я не замечал особенных проблем с этим на практике, где бы highlight.js ни использовался. Причём, подходя к вопросу с другой стороны, сильно уменьшить этот размер всё равно вряд ли возможно: большую часть места занимают перечисления ключевых слов языков.

> Но, ведь не всегда все нужные блоки есть на странице изначально - некоторые могут образоваться позднее, либо просто сгенерированные, либо полученные через AJAX-запросы.

При загрузке хайлайтер не делает никакой магии, кроме вызова своих же функций. Точно так же их может использовать и ваш код. Например, чтобы сделать подсветку в произвольном блоке, можно использовать hljs.highlightBlock. Если хочется ещё более низкоуровневого сервиса -- есть hljs.highlight, который принимает текст, а возвращает расцвеченный текст и цифры релевантности.

Иван Сагалаев(Гость)

Кстати, если у вас всё равно есть jQuery, вы можете и изначальную подсветку на него перевалить:

$('pre code').each(function(block){hljs.highlightBlock(block);};

(или как-то так)

pyhoster

> сильно уменьшить этот размер всё равно вряд ли возможно: большую часть места занимают перечисления ключевых слов языков.

С этим я и не спорю. Просто получается что при каждой загрузке страницы клиенту отдаются и инициализируются все упакованные языки, даже если на странице нужен только один, или вообще ни одного. Хотя, скорее всего, это действительно не так уж и страшно...

> Например, чтобы сделать подсветку в произвольном блоке, можно использовать hljs.highlightBlock

Спасибо. Я пробовал работать с этим методом, однако это не дало результатов. Сейчас копнул глубже и понял, что не инициализировал библиотеку методом hljs.initHighlighting().

  • Как пользователь
  • Без регистрации
  • OpenID
Вы должны зарегистрироваться или авторизоваться, чтобы оставлять комментарии.

Введите код указаный на картинке:  

OpenID is not supported at this moment =(

Авторизация

Логин:

Пароль:


Регистрация | Забыли пароль?


Последние записи


Promo

Follow pyhoster on Twitter Subscribe

Реклама

A Django project.