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

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

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

Author24 — интернет-сервис помощи студентам
Доброе время суток!
Задался целью сделать подсветку кода на 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
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.11.2016, 20:16
Ответы с готовыми решениями:

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

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

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

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

21
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
05.11.2016, 20:42 2
Сначала можно split-ом разбить строку с разделителем на кавычки, значения внутри кавычек(нечетные индексы получившегося массива) сохранить в отдельный массив, вместо них в текст добавить ~1~, ~2~ И т.д., провести все операции, а потом в самом конце все ~1~, ~2~ и т.д. заменить на сохраненные в отдельном массиве строки заключив в спаны с классом string.
1
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
05.11.2016, 21:02  [ТС] 3
Идея хороша, читал про такой способ. Меня смущает, что если в коде (в котором делаем подсветку) уже есть символы ~1~, ~2~... вся подсветка полетит. А ещё это решит вопрос со строками, но не проблему =this
0
392 / 294 / 121
Регистрация: 26.08.2016
Сообщений: 902
06.11.2016, 00:47 4
Цитата Сообщение от quotes Посмотреть сообщение
а вот перед this нет ничего, ни какого-то символа ни даже начала строки, поэтому он его и пропускает.
А куда равно-то делось?
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 09:25  [ТС] 5
= на предыдущем этапе заключилось в 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 6
quotes, А в каком месте вышего выражения определено, то перед ним должны быть какие-то символы? Просто поставьте в это место |^, что означает "или начало строки"

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

Цитата Сообщение от quotes Посмотреть сообщение
3)Эта идея очень понравилась. Но грабли оказались те же
тут аналогичная идея, после каждой замены добавлять в исходную строку - str - пробел после найденного куска, тогда поиск каждый раз будет начинаться с пробела.
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
16844 / 6723 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
06.11.2016, 17:56 13
зачем вы каждый символ в регулярках экранируете? Это же ухудшает читабельность.
Цитата Сообщение от quotes Посмотреть сообщение
не начало строки, ни пробел, ни знак препинания), поэтому считает что это не начало слова.
использовать заглядывания в духе (?=[a-z]) не пробовали? Ecmascript вроде поддерживает условные выражения в регулярках.
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
06.11.2016, 18:33  [ТС] 14
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 15
Цитата Сообщение от quotes Посмотреть сообщение
получается в результате из 'var self=this;' я получу 'var self = this;' с подсветкой.
Так вы и заменяйте их вместе с пробелом. На самом деле абсолютно любой символ подойдет. Сначала этот символ вставляете перед каждым словом, потом ищете слова с этим символом вначале, и соответственно в вашей функции вы можете управлять заменой, взяв от pN подстроку со второй позиции.

Добавлено через 3 минуты
Точнее не любой символ подойдет, а любой из тех, что вы не собираетесь подсвечивать.
1
Ренегат
Эксперт HTML/CSS
1740 / 1085 / 386
Регистрация: 06.08.2014
Сообщений: 5,203
Записей в блоге: 1
06.11.2016, 23:06 16
Цитата Сообщение от KOPOJI Посмотреть сообщение
не пробовали
Я вообще не понимаю кипиша, есть же highlight.js, на кой вообще свой велик изобретать?
0
0 / 0 / 0
Регистрация: 05.11.2016
Сообщений: 10
07.11.2016, 10:38  [ТС] 17
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
16844 / 6723 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
07.11.2016, 10:59 18

Не по теме:

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  [ТС] 19
KOPOJI, ключевым словом может быть не только class но и другие например span где-то захочется подсветить. Но фиг с ним. Как после этого подсветить строки и, например, комментарии? Ведь теги '<span SPAN_CLASS="...">' тоже теперь участвуют в замене, и в них есть строки "...".
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
16844 / 6723 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
07.11.2016, 11:19 20
Вот поэтому я и говорил про раздельный поиск и замену. Вам надо будет либо построчно искать и заменять, либо сохранять массив замен. Еще один вариант есть в фиксировании текущей строки замены - т.е., например, если строки с 10 по 20 - многострочный комментарий, то вы должны проверять, не находитесь ли вы сейчас внутри этого комментария? Ведь в этом случае замена не нужна. В общем, головоломка еще та у вас будет при парсинге путем регулярок
1
07.11.2016, 11:19
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.11.2016, 11:19
Помогаю со студенческими работами здесь

Подсветка синтаксиса на Си
Натолкните на мысль: как организовывается подсветка синтаксиса? Допустим мне надо подсветить...

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

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

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


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru