Форум программистов, компьютерный форум, киберфорум
JavaScript: RegExp
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.61/18: Рейтинг темы: голосов - 18, средняя оценка - 4.61
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10

Подсветка синтаксиса

05.11.2016, 20:16. Показов 3563. Ответов 21
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброе время суток!
Задался целью сделать подсветку кода на javascript. В процессе столкнулся со сложностью регулярных выражений.
Делаю один большой реплейс:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var reg = /(([\'"])(.*?)\2)|([\+\-\*\=\!])|(([a-zA-Z][a-zA-Z0-9]*?[\s]*?)(\())|((^|[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\])((\d+?)|(RegExp|var|let|function|document|window|location|console|global|const|class)|(if|for|while|do|case|switch|try|catche|instanceof|return|new|of)|(self|this)|(null|NULL|true|false))(?=[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\]|$))/g;
 
function fn(str, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, offset, s){
    if (p1) return '<span class="string">'+p1+'</span>';
    if (p4) return '<span class="oper">'+p4+'</span>';
    if (p5) return '<span class="method">'+p6+'</span>'+p7;
    if (p8) {
        if (p11) return p9+'<span class="num">'+p11+'</span>';
        if (p12) return p9+'<span class="keyword-1">'+p12+'</span>'
        if (p13) return p9+'<span class="keyword-2">'+p13+'</span>';
        if (p14) return p9+'<span class="keyword-3">'+p14+'</span>';
        if (p15) return p9+'<span class="keyword-4">'+p15+'</span>';
    }
"code".replace(reg, fn);
и вроде работает, но есть исключение.
В строке "var self=this;" заключает все слова в спаны кроме this.
И оно понятно... Сначала он видит ^var\s и заключает его в keyword-1, затем видит \sself= и заключает self в keyword-3, затем видит = его заключает в oper, а дальше он видит this; И тут ему, что бы понять что это слово, нужно увидеть какой-то символ до this и после него. После - без проблем ";" не буквенный символ, а вот перед this нет ничего, ни какого-то символа ни даже начала строки, поэтому он его и пропускает.

Помогите, пожалуйста, разобраться в этой ситуации. Возможно как-то иначе стоит подойти к процессу подсветки синтаксиса, или как-то переосмыслить регулярку?

Сразу я пробовал делать цепочку реплейсов, вроде:
JavaScript
1
2
3
4
5
6
7
//сначала нахожу все строки в любых кавычках и заключаю их в span.string для задания цвета
var code = "code".replace(/(([\'"])(.*?)\2)/g, '<span class="string">$1</span>')
//затем ищу все ключевые слова и также заключаю их в span.keyword 
//в первых скобках перечисляю знаки препинания, начало строки, всякие пробелы (вроде как \b но русские буквы не считаю границей), аналогично в третьей скобки
//следом за "пробельным символом" идёт скобка со списком ключевиков которые и заключаются в span
code = code.replace(/(^|[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\])(var|class|function|for|if)([\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\]|$)/g, '$1<span class="keyword">$2</span>$3')
// и так далее
но тут я обламался. После первого реплейса в строке появляется конструкция "<span class="string">...</span>", которая участвует в последующих заменах (в частности при втором реплейсе в ней слово class заменится на <span class="keyword"class</span>).
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
05.11.2016, 20:16
Ответы с готовыми решениями:

Подсветка синтаксиса в SHJS
Мне непонятен синтаксис кода начиная с 5 строки; sh_languages - толи это массив, толи что не понятно. Почему скобки квадратные а не...

<textarea> и подсветка синтаксиса.
Есть ли готовые скрипты, чтобы сделать динамическую подсветку синтаксиса PHP или HTML в &lt;textarea&gt;? Нужно для 'текстового редактора...

подсветка в notepad++
пишу код в notepad++ зарезервированые слова обычно выделяются цветом var,function. когда пишу к примеру this.x -слово this никак не...

21
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
05.11.2016, 20:42
Сначала можно split-ом разбить строку с разделителем на кавычки, значения внутри кавычек(нечетные индексы получившегося массива) сохранить в отдельный массив, вместо них в текст добавить ~1~, ~2~ И т.д., провести все операции, а потом в самом конце все ~1~, ~2~ и т.д. заменить на сохраненные в отдельном массиве строки заключив в спаны с классом string.
1
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
05.11.2016, 21:02  [ТС]
Идея хороша, читал про такой способ. Меня смущает, что если в коде (в котором делаем подсветку) уже есть символы ~1~, ~2~... вся подсветка полетит. А ещё это решит вопрос со строками, но не проблему =this
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 00:47
Цитата Сообщение от quotes Посмотреть сообщение
а вот перед this нет ничего, ни какого-то символа ни даже начала строки, поэтому он его и пропускает.
А куда равно-то делось?
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 09:25  [ТС]
= на предыдущем этапе заключилось в span.oper. Реплейс последовательно идёт по строке, видит "=", оно соответствует части регулярки ([\+\-\*\=\!]), значит заключает его в span. Дальше продолжает работу с оставшейся частью строки и видит сразу this без каких либо символов перед ним
В итоге 'var self=this;' => '<span class="keyword-1">var</span> <span class="keyword-3">self</span><span class="oper">=</span>this;'
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 10:11
quotes, А в каком месте вышего выражения определено, то перед ним должны быть какие-то символы? Просто поставьте в это место |^, что означает "или начало строки"

Добавлено через 3 минуты
PS но вообще я проверил на онлайн-калькуляторе - ваше регулярное выражение находит "this;" даже если перед ним ничего не стоит, так что может дело в чем-то другом.
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 10:27  [ТС]
JavaScript
1
((^|[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\])((\d+?)|(RegExp|var|let|function|document|window|location|console|global|const|class)|(if|for|while|do|case|switch|try|catche|instanceof|return|new|of)|(self|this)|(null|NULL|true|false))(?=[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\]|$))
вот эта часть отвечает за цифры и ключевые слова. Вот эта скобка перечисляет символы, которые могут быть перед словом (^|[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\]) тут и знаки препинания и пробелы и начало строки.
Но в строке 'var self=this;' this - не начало строки, а начало рассматриваемого отрезка. После того как регулярка разобралась с "=", она начинает рассматривать отрезок "this;". И первый символ который она видит это "t" (не начало строки, ни пробел, ни знак препинания), поэтому считает что это не начало слова.
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 10:35
quotes, Странное поведение функции, тогда либо найти все через match, потом заменять, либо несколькими регулярками, сначала найти идентификаторы, затем все остальное, что не идет впритык друг к другу.
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 11:10  [ТС]
Не совсем понимаю как это... Можно простенькие примеры этого?
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 11:31
quotes,

1) Я думаю, что все-таки ваш код должен работать, не может быть что нет такой возможности. Может надо поставить в регулярное выражение \b, типа граница слова или что-то такое.
2) Делайте все то же самое, просто в два захода. В первый возьмите из регулярного выражения только часть, касающуюся идентификаторов, ну и окружите проверкой, что перед и после нет числовых или буквенных символов, типа вот так:

JavaScript
1
[^A-Za-z0-9_](RegExp|var|let|function|document|window|location|console|global|const|class)|(if|for|while|do|case|switch|try|catche|instanceof|return|new|of)|(self|this)|(null|NULL|true|false))[^A-Za-z0-9_]
замените сначала их, а потом уже все остальное.

3) Использовать метод exec для regExp, Он вернет все найденные вхождения, а вы уже будете думать что с ними делать

JavaScript
1
2
3
4
5
6
7
8
var code = 'code';
var reg = /...ваше регулярное выражение.../g
var match;
 
while (match = reg.exec(code)) {
  expression = match[0]; pos = match.index;
  //И тут уже имея найденные вхождения и их позиции решите что с ними делать
}
1
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 15:18  [ТС]
1) Да, если использовать \b то всё нормально. Вместо (^|[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\]) пишу \b и всё здорово. Но \b не понимает букв кроме латинских, т.е. русские буквы он считает равносильными пробелу. Поэтому использовать его не могу.

2) на примере 'var self=this;'. Сначала:
JavaScript
1
[^A-Za-z0-9_](RegExp|var|let|function|document|window|location|console|global|const|class)|(if|for|while|do|case|switch|try|catche|instanceof|return|new|of)|(self|this)|(null|NULL|true|false))[^A-Za-z0-9_]
получаю строку '<span class="keyword">var</span> <span class="keyword">self</span>=<span class="keyword">this</span>;'
Затем ищу, например, операторы (+-=/*):
JavaScript
1
/([\!\/\*\-\+\=])/g
И получаю '<span class<span class="ed-oper">=</span>"keyword">var</span> <span class<span class="ed-oper">=</span>"keyword">self</span><span class="ed-oper">=</span><span class<span class="ed-oper">=</span>"keyword">this</span>;'. Потому что вставленные на предыдущем этапе спаны тоже участвуют в поиске.

3)Эта идея очень понравилась. Но грабли оказались те же
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var str = "var self=this;",
        newStr = str, //строка со спанами, что бы не менять текущую строку, а то получиться ситуация как в пункте 2)
    lengthDef = 0, //разница в длинне оригинальной строки и строки со спанами
    reg = /([\!\/\*\-\+\=])|((^|\s|\;|\=)(var|self|this|class)($|\s|\;|\=))/g,
    matches;
while(matches = reg.exec(str)){
    if (matches[1]) {
        newStr = newStr.substring(0, matches.index+lengthDef) + '<span class="ed-oper">' + matches[1] + '</span>' + newStr.substring(matches.index + lengthDef + matches[1].length);
            lengthDef += '<span class="ed-oper"></span>'.length;
    }
    if (matches[2] && matches[4]) {
        newStr = newStr.substring(0, matches.index+lengthDef) + '<span class="ed-keyword-1">' + matches[4] + '</span>' + newStr.substring(matches.index + lengthDef + matches[4].length);
            lengthDef += '<span class="ed-keyword-1"></span>'.length;
    }
}
console.log(newStr)
// тут выводит '<span class="ed-keyword-1">var</span> self<span class="ed-oper">=</span>this;'
self и this пропустило. Сначала взял "var " потому что начинается с ^ и заканчивается \s. потом продолжил с 'self=this;' и так как первый символ буква (а не начало строки или пробел или знак препинания) self он словом не считает

Может я что-то не то делаю внутри цикла? Может как-то иначе можно?
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 15:48
Цитата Сообщение от quotes Посмотреть сообщение
1) Да, если использовать \b то всё нормально. Вместо (^|[\s\!\@\#\$\%\^\&\*\)\(\-\+\=\[\{\]\}\'"\/\.\,\>\<\?\:\;\\]) пишу \b и всё здорово. Но \b не понимает букв кроме латинских, т.е. русские буквы он считает равносильными пробелу. Поэтому использовать его не могу.
Сперва добавьте в начало каждого слова пробел. Здесь проблем возникнуть не должно, начало слова это всегда любой небуквенный знак перед любой буквой, русской или латинской. И потом уже запускайте свой код.

Цитата Сообщение от quotes Посмотреть сообщение
3)Эта идея очень понравилась. Но грабли оказались те же
тут аналогичная идея, после каждой замены добавлять в исходную строку - str - пробел после найденного куска, тогда поиск каждый раз будет начинаться с пробела.
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6724 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
06.11.2016, 17:56
зачем вы каждый символ в регулярках экранируете? Это же ухудшает читабельность.
Цитата Сообщение от quotes Посмотреть сообщение
не начало строки, ни пробел, ни знак препинания), поэтому считает что это не начало слова.
использовать заглядывания в духе (?=[a-z]) не пробовали? Ecmascript вроде поддерживает условные выражения в регулярках.
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 18:33  [ТС]
renat_dmitriev, получается в результате из 'var self=this;' я получу 'var self = this;' с подсветкой. Такой расклад не годится. Лишние пробелы не допустимы. Или я что-то не так понял?..
KOPOJI, да. Для пробела после слова можно использовать (?=....), а для пробела перед словом (?<=....), и всё будет работать идеально (на php проверял). Но в JS не работает конструкция (?<=....).

Добавлено через 15 минут
Может есть способ применять регулярки по-очереди, но исключать при этом из замены теги <span class="oper">?
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 18:47
Цитата Сообщение от quotes Посмотреть сообщение
получается в результате из 'var self=this;' я получу 'var self = this;' с подсветкой.
Так вы и заменяйте их вместе с пробелом. На самом деле абсолютно любой символ подойдет. Сначала этот символ вставляете перед каждым словом, потом ищете слова с этим символом вначале, и соответственно в вашей функции вы можете управлять заменой, взяв от pN подстроку со второй позиции.

Добавлено через 3 минуты
Точнее не любой символ подойдет, а любой из тех, что вы не собираетесь подсвечивать.
1
Ренегат
Эксперт HTML/CSS
 Аватар для BANO
1740 / 1085 / 386
Регистрация: 06.08.2014
Сообщений: 5,203
Записей в блоге: 1
06.11.2016, 23:06
Цитата Сообщение от KOPOJI Посмотреть сообщение
не пробовали
Я вообще не понимаю кипиша, есть же highlight.js, на кой вообще свой велик изобретать?
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
07.11.2016, 10:38  [ТС]
BANO, для меня подсветка - не сама цель, а промежуточный этап. И этот этап мне нужно полностью контролировать, поэтому сторонние библиотеки мне не подходят (нужно хорошо осознавать, что куда). Я делаю редактор вроде brakets для web, и с подсветкой синтаксиса могут быть связаны какой-то api или что-то ещё.

renat_dmitriev, застрял на строках. Для "var self=this;" делаю превращение в "<var> <self>=<this>;", а потом границу слова отслеживаю по треугольным скобкам. Но если есть строки то получается бред при первой замене:
JavaScript
1
2
3
4
5
6
7
var str = 'var self="строка с function и прочим";';
str = str.replace(/((['"])(.*?)\2)|((^|[\s+-=*!@#$%^&*();.,:><[\]\\\/])([^\s+-=*!@#$%^&*();.,:><[\]\\\/]+?)(?=[\s+-=*!@#$%^&*();.,:><[\]\\/]|$))/g, function(str, p1, p2, p3, p4, p5, p6){
    if (p1) return p1;
    if (p6) return p5+'<'+p6+'>';
    return str;
});
console.log(str) //<var> <self>=<"строка> <с> <function> <и> <прочим">;
И хотя в регулярке я попытался отдельно отслеживать строки ((['"])(.*?)\2), почему то видит только слова
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6724 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
07.11.2016, 10:59

Не по теме:

BANO, ну захотелось видимо человеку изобрести свой велосипед - почему бы и нет :)
Я тоже когда-то на яве парсил сишные объявления переменных)


Цитата Сообщение от quotes Посмотреть сообщение
JavaScript
1
/([\!\/\*\-\+\=])|((^|\s|\;|\=)(var|self|this|class)($|\s|\;|\=))/g
JavaScript
1
/([-!\/*+=])|((^|[\s;=])(var|self|this|class)($|[\s;=]))/g
Так разве не получше будет выглядеть?
quotes, может лучше все-таки разбить на части, а не пытаться найти все разом?
Ключевые слова вообще можно заменить все скопом, просто добавить проверку, что это не часть переменной и т.п.
Навскидку, можно написать нечто подобное
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var rKeywords = /\b(RegExp|var|let|function|document|window|location|console|global|const|class)|(if|for|while|do|case|switch|try|catche|instanceof|return|new|of)|(self|this)|(null|NULL|true|false)\b/g,
    rSymbols = /\b[-:!\/*+=]+\b/g,
    str = newStr = 'var self=this;',
    lengthDef = 0,
    matches;
newStr = str.replace(
  rSymbols, function(m) {
    return '<span SPAN_CLASS="ed-oper">' + m + '</span>';
  }
).replace(
  rKeywords, function(m, m1, m2, m3, m4) {
    if(m1) return '<span SPAN_CLASS="ed-keyword-1">' + m1 + '</span>';
    if(m2) return '<span SPAN_CLASS="ed-keyword-2">' + m2 + '</span>';
    if(m3) return '<span SPAN_CLASS="ed-keyword-3">' + m3 + '</span>';
    if(m4) return '<span SPAN_CLASS="ed-keyword-4">' + m4 + '</span>';
    return m;
  }
).replace(/SPAN_CLASS/g, 'class')
console.log(newStr)
https://jsfiddle.net/kjy0xt3h/
чтобы избежать проблем с ключевым словом class, которое используется и в верстке, имхо, проще сделать дополнительную простую замену.. Но вы можете помучаться и так, если есть желание..
1
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
07.11.2016, 11:12  [ТС]
KOPOJI, ключевым словом может быть не только class но и другие например span где-то захочется подсветить. Но фиг с ним. Как после этого подсветить строки и, например, комментарии? Ведь теги '<span SPAN_CLASS="...">' тоже теперь участвуют в замене, и в них есть строки "...".
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6724 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
07.11.2016, 11:19
Вот поэтому я и говорил про раздельный поиск и замену. Вам надо будет либо построчно искать и заменять, либо сохранять массив замен. Еще один вариант есть в фиксировании текущей строки замены - т.е., например, если строки с 10 по 20 - многострочный комментарий, то вы должны проверять, не находитесь ли вы сейчас внутри этого комментария? Ведь в этом случае замена не нужна. В общем, головоломка еще та у вас будет при парсинге путем регулярок
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
07.11.2016, 11:19
Помогаю со студенческими работами здесь

Подсветка кода на js
Добрый день! Подскажите как на js подсветить следующий код: ] Т.е. нужно подсветить квадратные скобки и все что внутри фоном. А...

Подсветка синтаксиса на Си
Натолкните на мысль: как организовывается подсветка синтаксиса? Допустим мне надо подсветить исходник на Си. Открываю файл и всё такое,...

Подсветка синтаксиса
Делаю редактор с подсветкой индекса, возник вопрос, как сделать поиск файла(и его выделение) только в той строке в которой пишится текст, а...

Подсветка синтаксиса
Здравствуйте! Пишу текстовый редактор с подсветкой синтаксиса. Имею следующий код: public Regex keyWords = new...

Подсветка синтаксиса
Здравствуйте. Можете пожалуйста помочь с подсветкой синтаксиса на C#. После того, как у меня возникили проблемы - я начал гуглить. Нагуглил...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Первый деплой
lagorue 16.01.2026
Не спеша развернул своё 1ое приложение в kubernetes. А дальше мне интересно создать 1фронтэнд приложения и 2 бэкэнд приложения развернуть 2 деплоя в кубере получится 2 сервиса и что-бы они. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит: токи, напряжения и их 1 и 2 производные при t = 0;. . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru