diff options
Diffstat (limited to 'lua')
153 files changed, 5374 insertions, 3773 deletions
diff --git a/lua/lexers/actionscript.lua b/lua/lexers/actionscript.lua index 24aef11..4f0b2d1 100644 --- a/lua/lexers/actionscript.lua +++ b/lua/lexers/actionscript.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Actionscript LPeg lexer. local lexer = require('lexer') @@ -53,7 +53,8 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=!<>+-/*%&|^~.,;?()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) lex:add_fold_point(lexer.STRING, '<![CDATA[', ']]>') +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/ada.lua b/lua/lexers/ada.lua index 0a33868..5cac26f 100644 --- a/lua/lexers/ada.lua +++ b/lua/lexers/ada.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Ada LPeg lexer. local lexer = require('lexer') @@ -11,28 +11,23 @@ local lex = lexer.new('ada') lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'abort', 'abs', 'accept', 'all', 'and', 'begin', 'body', 'case', 'declare', 'delay', 'do', 'else', - 'elsif', 'end', 'entry', 'exception', 'exit', 'for', 'generic', 'goto', 'if', 'in', 'is', 'loop', - 'mod', 'new', 'not', 'null', 'or', 'others', 'out', 'protected', 'raise', 'record', 'rem', - 'renames', 'requeue', 'reverse', 'select', 'separate', 'subtype', 'task', 'terminate', 'then', - 'type', 'until', 'when', 'while', 'xor', - -- Preprocessor. - 'package', 'pragma', 'use', 'with', - -- Function. - 'function', 'procedure', 'return', - -- Storage class. - 'abstract', 'access', 'aliased', 'array', 'at', 'constant', 'delta', 'digits', 'interface', - 'limited', 'of', 'private', 'range', 'tagged', 'synchronized', - -- Boolean. +lex:add_rule('keyword', token(lexer.KEYWORD, word_match({ + 'abort', 'abs', 'abstract', 'accept', 'access', 'aliased', 'all', 'and', 'array', 'at', 'begin', + 'body', 'case', 'constant', 'declare', 'delay', 'delta', 'digits', 'do', 'else', 'elsif', 'end', + 'entry', 'exception', 'exit', 'for', 'function', 'generic', 'goto', 'if', 'in', 'interface', 'is', + 'limited', 'loop', 'mod', 'new', 'not', 'null', 'of', 'or', 'others', 'out', 'overriding', + 'package', 'parallel', 'pragma', 'private', 'procedure', 'protected', 'raise', 'range', 'record', + 'rem', 'renames', 'requeue', 'return', 'reverse', 'select', 'separate', 'some', 'subtype', + 'synchronized', 'tagged', 'task', 'terminate', 'then', 'type', 'until', 'use', 'when', 'while', + 'with', 'xor', -- 'true', 'false' -})) +}, true))) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ +lex:add_rule('type', token(lexer.TYPE, word_match({ 'boolean', 'character', 'count', 'duration', 'float', 'integer', 'long_float', 'long_integer', 'priority', 'short_float', 'short_integer', 'string' -})) +}, true))) -- Identifiers. lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) @@ -44,11 +39,11 @@ lex:add_rule('string', token(lexer.STRING, lexer.range('"', true, false))) lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('--'))) -- Numbers. -local integer = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local float = integer^1 * ('.' * integer^0)^-1 * S('eE') * S('+-')^-1 * integer -lex:add_rule('number', token(lexer.NUMBER, S('+-')^-1 * (float + integer))) +lex:add_rule('number', token(lexer.NUMBER, lexer.number_('_'))) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S(':;=<>&+-*/.()'))) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/ansi_c.lua b/lua/lexers/ansi_c.lua index 68aba5d..61ab047 100644 --- a/lua/lexers/ansi_c.lua +++ b/lua/lexers/ansi_c.lua @@ -1,90 +1,58 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- C LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('ansi_c') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'auto', 'break', 'case', 'const', 'continue', 'default', 'do', 'else', 'enum', 'extern', 'for', - 'goto', 'if', 'inline', 'register', 'restrict', 'return', 'sizeof', 'static', 'switch', 'typedef', - 'volatile', 'while', - -- C99. - 'false', 'true', - -- C11. - '_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', '_Static_assert', '_Thread_local', - -- Compiler. - 'asm', '__asm', '__asm__', '__restrict__', '__inline', '__inline__', '__attribute__', '__declspec' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ - 'bool', 'char', 'double', 'float', 'int', 'long', 'short', 'signed', 'struct', 'union', - 'unsigned', 'void', '_Bool', '_Complex', '_Imaginary', - -- Stdlib types. - 'ptrdiff_t', 'size_t', 'max_align_t', 'wchar_t', 'intptr_t', 'uintptr_t', 'intmax_t', 'uintmax_t' -} + P('u')^-1 * 'int' * (P('_least') + '_fast')^-1 * lexer.digit^1 * '_t')) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) + +-- Functions. +local builtin_func = -(B('.') + B('->')) * + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = (B('.') + B('->')) * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '(')) -- Constants. -lex:add_rule('constants', token(lexer.CONSTANT, word_match{ - 'NULL', - -- Preprocessor. - '__DATE__', '__FILE__', '__LINE__', '__TIME__', '__func__', - -- errno.h. - 'E2BIG', 'EACCES', 'EADDRINUSE', 'EADDRNOTAVAIL', 'EAFNOSUPPORT', 'EAGAIN', 'EALREADY', 'EBADF', - 'EBADMSG', 'EBUSY', 'ECANCELED', 'ECHILD', 'ECONNABORTED', 'ECONNREFUSED', 'ECONNRESET', - 'EDEADLK', 'EDESTADDRREQ', 'EDOM', 'EDQUOT', 'EEXIST', 'EFAULT', 'EFBIG', 'EHOSTUNREACH', 'EIDRM', - 'EILSEQ', 'EINPROGRESS', 'EINTR', 'EINVAL', 'EIO', 'EISCONN', 'EISDIR', 'ELOOP', 'EMFILE', - 'EMLINK', 'EMSGSIZE', 'EMULTIHOP', 'ENAMETOOLONG', 'ENETDOWN', 'ENETRESET', 'ENETUNREACH', - 'ENFILE', 'ENOBUFS', 'ENODATA', 'ENODEV', 'ENOENT', 'ENOEXEC', 'ENOLCK', 'ENOLINK', 'ENOMEM', - 'ENOMSG', 'ENOPROTOOPT', 'ENOSPC', 'ENOSR', 'ENOSTR', 'ENOSYS', 'ENOTCONN', 'ENOTDIR', - 'ENOTEMPTY', 'ENOTRECOVERABLE', 'ENOTSOCK', 'ENOTSUP', 'ENOTTY', 'ENXIO', 'EOPNOTSUPP', - 'EOVERFLOW', 'EOWNERDEAD', 'EPERM', 'EPIPE', 'EPROTO', 'EPROTONOSUPPORT', 'EPROTOTYPE', 'ERANGE', - 'EROFS', 'ESPIPE', 'ESRCH', 'ESTALE', 'ETIME', 'ETIMEDOUT', 'ETXTBSY', 'EWOULDBLOCK', 'EXDEV', - -- stdint.h. - 'PTRDIFF_MIN', 'PTRDIFF_MAX', 'SIZE_MAX', 'SIG_ATOMIC_MIN', 'SIG_ATOMIC_MAX', 'WINT_MIN', - 'WINT_MAX', 'WCHAR_MIN', 'WCHAR_MAX' -} + P('U')^-1 * 'INT' * ((P('_LEAST') + '_FAST')^-1 * lexer.digit^1 + 'PTR' + 'MAX') * - (P('_MIN') + '_MAX'))) +lex:add_rule('constants', lex:tag(lexer.CONSTANT_BUILTIN, + -(B('.') + B('->')) * lex:word_match(lexer.CONSTANT_BUILTIN))) -- Labels. -lex:add_rule('label', token(lexer.LABEL, lexer.starts_line(lexer.word * ':'))) +lex:add_rule('label', lex:tag(lexer.LABEL, lexer.starts_line(lexer.word * ':'))) -- Strings. -local sq_str = P('L')^-1 * lexer.range("'", true) -local dq_str = P('L')^-1 * lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +local sq_str = lexer.range("'", true) +local dq_str = lexer.range('"', true) +lex:add_rule('string', lex:tag(lexer.STRING, P('L')^-1 * (sq_str + dq_str))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments. local line_comment = lexer.to_eol('//', true) local block_comment = lexer.range('/*', '*/') + lexer.range('#if' * S(' \t')^0 * '0' * lexer.space, '#endif') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) -- Numbers. -local integer = lexer.integer * word_match('u l ll ul ull lu llu', true)^-1 +local integer = lexer.integer * lexer.word_match('u l ll ul ull lu llu', true)^-1 local float = lexer.float * P('f')^-1 -lex:add_rule('number', token(lexer.NUMBER, float + integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, float + integer)) -- Preprocessor. -local include = token(lexer.PREPROCESSOR, '#' * S('\t ')^0 * 'include') * - (ws * token(lexer.STRING, lexer.range('<', '>', true)))^-1 -local preproc = token(lexer.PREPROCESSOR, '#' * S('\t ')^0 * - word_match('define elif else endif if ifdef ifndef line pragma undef')) +local include = lex:tag(lexer.PREPROCESSOR, '#' * S('\t ')^0 * 'include') * + (lex:get_rule('whitespace') * lex:tag(lexer.STRING, lexer.range('<', '>', true)))^-1 +local preproc = lex:tag(lexer.PREPROCESSOR, '#' * S('\t ')^0 * lex:word_match(lexer.PREPROCESSOR)) lex:add_rule('preprocessor', include + preproc) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>~!=^&|?~:;,.()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>~!=^&|?~:;,.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.PREPROCESSOR, '#if', '#endif') @@ -92,6 +60,140 @@ lex:add_fold_point(lexer.PREPROCESSOR, '#ifdef', '#endif') lex:add_fold_point(lexer.PREPROCESSOR, '#ifndef', '#endif') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'auto', 'break', 'case', 'const', 'continue', 'default', 'do', 'else', 'enum', 'extern', 'for', + 'goto', 'if', 'inline', 'register', 'restrict', 'return', 'sizeof', 'static', 'switch', 'typedef', + 'volatile', 'while', -- + 'false', 'true', -- C99 + 'alignas', 'alignof', '_Atomic', '_Generic', 'noreturn', '_Static_assert', 'thread_local', -- C11 + -- Compiler. + 'asm', '__asm', '__asm__', '__restrict__', '__inline', '__inline__', '__attribute__', '__declspec' +}) + +lex:set_word_list(lexer.TYPE, { + 'bool', 'char', 'double', 'float', 'int', 'long', 'short', 'signed', 'struct', 'union', + 'unsigned', 'void', -- + 'complex', 'imaginary', '_Complex', '_Imaginary', -- complex.h C99 + 'lconv', -- locale.h + 'div_t', -- math.h + 'va_list', -- stdarg.h + 'bool', '_Bool', -- stdbool.h C99 + -- stddef.h. + 'size_t', 'ptrdiff_t', -- + 'max_align_t', -- C11 + -- stdint.h. + 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'int_fast8_t', 'int_fast16_t', 'int_fast32_t', + 'int_fast64_t', 'int_least8_t', 'int_least16_t', 'int_least32_t', 'int_least64_t', 'intmax_t', + 'intptr_t', 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', 'uint_fast8_t', 'uint_fast16_t', + 'uint_fast32_t', 'uint_fast64_t', 'uint_least8_t', 'uint_least16_t', 'uint_least32_t', + 'uint_least64_t', 'uintmax_t', 'uintptr_t', -- + 'FILE', 'fpos_t', -- stdio.h + 'div_t', 'ldiv_t', -- stdlib.h + -- time.h. + 'tm', 'time_t', 'clock_t', -- + 'timespec' -- C11 +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'assert', -- assert.h + -- complex.h. + 'CMPLX', 'creal', 'cimag', 'cabs', 'carg', 'conj', 'cproj', + -- C99 + 'cexp', 'cpow', 'csin', 'ccos', 'ctan', 'casin', 'cacos', 'catan', 'csinh', 'ccosh', 'ctanh', + 'casinh', 'cacosh', 'catanh', + -- ctype.h. + 'isalnum', 'isalpha', 'islower', 'isupper', 'isdigit', 'isxdigit', 'iscntrl', 'isgraph', + 'isspace', 'isprint', 'ispunct', 'tolower', 'toupper', -- + 'isblank', -- C99 + -- inttypes.h. + 'INT8_C', 'INT16_C', 'INT32_C', 'INT64_C', 'INTMAX_C', 'UINT8_C', 'UINT16_C', 'UINT32_C', + 'UINT64_C', 'UINTMAX_C', -- + 'setlocale', 'localeconv', -- locale.h + -- math.h. + 'abs', 'div', 'fabs', 'fmod', 'exp', 'log', 'log10', 'pow', 'sqrt', 'sin', 'cos', 'tan', 'asin', + 'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'ceil', 'floor', 'frexp', 'ldexp', 'modf', + -- C99. + 'remainder', 'remquo', 'fma', 'fmax', 'fmin', 'fdim', 'nan', 'exp2', 'expm1', 'log2', 'log1p', + 'cbrt', 'hypot', 'asinh', 'acosh', 'atanh', 'erf', 'erfc', 'tgamma', 'lgamma', 'trunc', 'round', + 'nearbyint', 'rint', 'scalbn', 'ilogb', 'logb', 'nextafter', 'nexttoward', 'copysign', 'isfinite', + 'isinf', 'isnan', 'isnormal', 'signbit', 'isgreater', 'isgreaterequal', 'isless', 'islessequal', + 'islessgreater', 'isunordered', -- + 'strtoimax', 'strtoumax', -- inttypes.h C99 + 'signal', 'raise', -- signal.h + 'setjmp', 'longjmp', -- setjmp.h + 'va_start', 'va_arg', 'va_end', -- stdarg.h + -- stdio.h. + 'fopen', 'freopen', 'fclose', 'fflush', 'setbuf', 'setvbuf', 'fwide', 'fread', 'fwrite', 'fgetc', + 'getc', 'fgets', 'fputc', 'putc', 'getchar', 'gets', 'putchar', 'puts', 'ungetc', 'scanf', + 'fscanf', 'sscanf', 'printf', 'fprintf', 'sprintf', 'vprintf', 'vfprintf', 'vsprintf', 'ftell', + 'fgetpos', 'fseek', 'fsetpos', 'rewind', 'clearerr', 'feof', 'ferror', 'perror', 'remove', + 'rename', 'tmpfile', 'tmpnam', + -- stdlib.h. + 'abort', 'exit', 'atexit', 'system', 'getenv', 'malloc', 'calloc', 'realloc', 'free', 'atof', + 'atoi', 'atol', 'strtol', 'strtoul', 'strtod', 'mblen', 'mbsinit', 'mbrlen', 'qsort', 'bsearch', + 'rand', 'srand', -- + 'quick_exit', '_Exit', 'at_quick_exit', 'aligned_alloc', -- C11 + -- string.h. + 'strcpy', 'strncpy', 'strcat', 'strncat', 'strxfrm', 'strlen', 'strcmp', 'strncmp', 'strcoll', + 'strchr', 'strrchr', 'strspn', 'strcspn', 'strpbrk', 'strstr', 'strtok', 'memchr', 'memcmp', + 'memset', 'memcpy', 'memmove', 'strerror', + -- time.h. + 'difftime', 'time', 'clock', 'asctime', 'ctime', 'gmtime', 'localtime', 'mktime', -- + 'timespec_get' -- C11 +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'NULL', -- + '__DATE__', '__FILE__', '__LINE__', '__TIME__', '__func__', -- preprocessor + -- errno.h. + 'errno', -- + 'E2BIG', 'EACCES', 'EADDRINUSE', 'EADDRNOTAVAIL', 'EAFNOSUPPORT', 'EAGAIN', 'EALREADY', 'EBADF', + 'EBADMSG', 'EBUSY', 'ECANCELED', 'ECHILD', 'ECONNABORTED', 'ECONNREFUSED', 'ECONNRESET', + 'EDEADLK', 'EDESTADDRREQ', 'EDOM', 'EDQUOT', 'EEXIST', 'EFAULT', 'EFBIG', 'EHOSTUNREACH', 'EIDRM', + 'EILSEQ', 'EINPROGRESS', 'EINTR', 'EINVAL', 'EIO', 'EISCONN', 'EISDIR', 'ELOOP', 'EMFILE', + 'EMLINK', 'EMSGSIZE', 'EMULTIHOP', 'ENAMETOOLONG', 'ENETDOWN', 'ENETRESET', 'ENETUNREACH', + 'ENFILE', 'ENOBUFS', 'ENODATA', 'ENODEV', 'ENOENT', 'ENOEXEC', 'ENOLCK', 'ENOLINK', 'ENOMEM', + 'ENOMSG', 'ENOPROTOOPT', 'ENOSPC', 'ENOSR', 'ENOSTR', 'ENOSYS', 'ENOTCONN', 'ENOTDIR', + 'ENOTEMPTY', 'ENOTRECOVERABLE', 'ENOTSOCK', 'ENOTSUP', 'ENOTTY', 'ENXIO', 'EOPNOTSUPP', + 'EOVERFLOW', 'EOWNERDEAD', 'EPERM', 'EPIPE', 'EPROTO', 'EPROTONOSUPPORT', 'EPROTOTYPE', 'ERANGE', + 'EROFS', 'ESPIPE', 'ESRCH', 'ESTALE', 'ETIME', 'ETIMEDOUT', 'ETXTBSY', 'EWOULDBLOCK', 'EXDEV', + -- float.h. + 'FLT_MIN', 'DBL_MIN', 'LDBL_MIN', 'FLT_MAX', 'DBL_MAX', 'LDBL_MAX', + -- limits.h. + 'CHAR_BIT', 'MB_LEN_MAX', 'CHAR_MIN', 'CHAR_MAX', 'SCHAR_MIN', 'SHRT_MIN', 'INT_MIN', 'LONG_MIN', + 'SCHAR_MAX', 'SHRT_MAX', 'INT_MAX', 'LONG_MAX', 'UCHAR_MAX', 'USHRT_MAX', 'UINT_MAX', 'ULONG_MAX', + -- C99. + 'LLONG_MIN', 'ULLONG_MAX', 'PTRDIFF_MIN', 'PTRDIFF_MAX', 'SIZE_MAX', 'SIG_ATOMIC_MIN', + 'SIG_ATOMIC_MAX', 'WINT_MIN', 'WINT_MAX', 'WCHAR_MIN', 'WCHAR_MAX', -- + 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', -- locale.h + -- math.h. + 'HUGE_VAL', -- + 'INFINITY', 'NAN', -- C99 + -- stdint.h. + 'INT8_MIN', 'INT16_MIN', 'INT32_MIN', 'INT64_MIN', 'INT_FAST8_MIN', 'INT_FAST16_MIN', + 'INT_FAST32_MIN', 'INT_FAST64_MIN', 'INT_LEAST8_MIN', 'INT_LEAST16_MIN', 'INT_LEAST32_MIN', + 'INT_LEAST64_MIN', 'INTPTR_MIN', 'INTMAX_MIN', 'INT8_MAX', 'INT16_MAX', 'INT32_MAX', 'INT64_MAX', + 'INT_FAST8_MAX', 'INT_FAST16_MAX', 'INT_FAST32_MAX', 'INT_FAST64_MAX', 'INT_LEAST8_MAX', + 'INT_LEAST16_MAX', 'INT_LEAST32_MAX', 'INT_LEAST64_MAX', 'INTPTR_MAX', 'INTMAX_MAX', 'UINT8_MAX', + 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX', 'UINT_FAST8_MAX', 'UINT_FAST16_MAX', 'UINT_FAST32_MAX', + 'UINT_FAST64_MAX', 'UINT_LEAST8_MAX', 'UINT_LEAST16_MAX', 'UINT_LEAST32_MAX', 'UINT_LEAST64_MAX', + 'UINTPTR_MAX', 'UINTMAX_MAX', + -- stdio.h + 'stdin', 'stdout', 'stderr', 'EOF', 'FOPEN_MAX', 'FILENAME_MAX', 'BUFSIZ', '_IOFBF', '_IOLBF', + '_IONBF', 'SEEK_SET', 'SEEK_CUR', 'SEEK_END', 'TMP_MAX', -- + 'EXIT_SUCCESS', 'EXIT_FAILURE', 'RAND_MAX', -- stdlib.h + -- signal.h. + 'SIG_DFL', 'SIG_IGN', 'SIG_ERR', 'SIGABRT', 'SIGFPE', 'SIGILL', 'SIGINT', 'SIGSEGV', 'SIGTERM', -- + 'CLOCKS_PER_SEC' -- time.h. +}) + +lex:set_word_list(lexer.PREPROCESSOR, { + 'define', 'defined', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'line', 'pragma', + 'undef' +}) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/antlr.lua b/lua/lexers/antlr.lua index 32c9a77..ca63167 100644 --- a/lua/lexers/antlr.lua +++ b/lua/lexers/antlr.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- ANTLR LPeg lexer. local lexer = require('lexer') @@ -51,6 +51,7 @@ lex:add_fold_point(lexer.OPERATOR, ':', ';') lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/apdl.lua b/lua/lexers/apdl.lua index 6f29963..8adc02b 100644 --- a/lua/lexers/apdl.lua +++ b/lua/lexers/apdl.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- APDL LPeg lexer. local lexer = require('lexer') @@ -73,6 +73,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-*/$=,;()'))) lex:add_fold_point(lexer.KEYWORD, '*if', '*endif') lex:add_fold_point(lexer.KEYWORD, '*do', '*enddo') lex:add_fold_point(lexer.KEYWORD, '*dowhile', '*enddo') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('!')) + +lexer.property['scintillua.comment'] = '!' return lex diff --git a/lua/lexers/apl.lua b/lua/lexers/apl.lua index 9ee22cf..820d268 100644 --- a/lua/lexers/apl.lua +++ b/lua/lexers/apl.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 David B. Lamkins <david@lamkins.net>. See LICENSE. +-- Copyright 2015-2024 David B. Lamkins <david@lamkins.net>. See LICENSE. -- APL LPeg lexer. local lexer = require('lexer') @@ -52,4 +52,6 @@ lex:add_rule('special', token(lexer.TYPE, S('{}[]();') + '←' + '→' + '◊')) -- Nabla. lex:add_rule('nabla', token(lexer.PREPROCESSOR, P('∇') + '⍫')) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/applescript.lua b/lua/lexers/applescript.lua index e1c25d1..b4c1260 100644 --- a/lua/lexers/applescript.lua +++ b/lua/lexers/applescript.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Applescript LPeg lexer. local lexer = require('lexer') @@ -66,4 +66,6 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-^*/&<>=:,(){}'))) -- Fold points. lex:add_fold_point(lexer.COMMENT, '(*', '*)') +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/asm.lua b/lua/lexers/asm.lua index 416113c..0949fc2 100644 --- a/lua/lexers/asm.lua +++ b/lua/lexers/asm.lua @@ -1,17 +1,65 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- NASM Assembly LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('asm') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Instructions. +lex:add_rule('instruction', + lex:tag(lexer.FUNCTION_BUILTIN .. '.instruction', lex:word_match('instruction'))) + +-- Registers. +lex:add_rule('register', lex:tag(lexer.CONSTANT_BUILTIN .. '.register', lex:word_match('register'))) + +-- Types. +local sizes = lex:word_match('size') +local wrt_types = '..' * lex:word_match(lexer.TYPE .. '.wrt') +lex:add_rule('type', lex:tag(lexer.TYPE, sizes + wrt_types)) + +-- Constants. +local word = (lexer.alpha + S('$._?')) * (lexer.alnum + S('$._?#@~'))^0 +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, + lex:word_match(lexer.CONSTANT_BUILTIN) + '$' * P('$')^-1 * -word)) + +-- Labels. +lex:add_rule('label', lex:tag(lexer.LABEL, word * ':')) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, word)) + +-- Strings. +local sq_str = lexer.range("'", true) +local dq_str = lexer.range('"', true) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol(';'))) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number * S('hqb')^-1)) + +-- Preprocessor. +local pp_word = lex:word_match(lexer.PREPROCESSOR) +local pp_symbol = '??' + S('!$+?') + '%' * -lexer.space + lexer.digit^1 +lex:add_rule('preproc', lex:tag(lexer.PREPROCESSOR, '%' * (pp_word + pp_symbol))) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>!=^&|~:,()[]'))) + +-- Fold points. +lex:add_fold_point(lexer.PREPROCESSOR, '%if', '%endif') +lex:add_fold_point(lexer.PREPROCESSOR, '%macro', '%endmacro') +lex:add_fold_point(lexer.PREPROCESSOR, '%rep', '%endrep') +lex:add_fold_point(lexer.PREPROCESSOR, '%while', '%endwhile') +lex:add_fold_point(lexer.KEYWORD, 'struc', 'endstruc') + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { -- Preprocessor macros. 'struc', 'endstruc', 'istruc', 'at', 'iend', 'align', 'alignb', 'sectalign', '.nolist', -- Preprocessor Packages. @@ -29,12 +77,11 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ -- Operators. 'abs', 'rel', 'seg', 'wrt', 'strict', '__utf16__', '__utf16be__', '__utf16le__', '__utf32__', '__utf32be__', '__utf32le__' -})) +}) --- Instructions. -- awk '{print $1}'|uniq|tr '[:upper:]' '[:lower:]'| -- lua -e "for l in io.lines() do print(\"'\"..l..\"',\") end"|fmt -w 98 -lex:add_rule('instruction', token('instruction', word_match{ +lex:set_word_list('instruction', { -- Special Instructions. 'db', 'dd', 'do', 'dq', 'dt', 'dw', 'dy', 'resb', 'resd', 'reso', 'resq', 'rest', 'resw', 'resy', -- Conventional Instructions. @@ -304,11 +351,9 @@ lex:add_rule('instruction', token('instruction', word_match{ 'hint_nop49', 'hint_nop50', 'hint_nop51', 'hint_nop52', 'hint_nop53', 'hint_nop54', 'hint_nop55', 'hint_nop56', 'hint_nop57', 'hint_nop58', 'hint_nop59', 'hint_nop60', 'hint_nop61', 'hint_nop62', 'hint_nop63' -})) -lex:add_style('instruction', lexer.styles['function']) +}) --- Registers. -lex:add_rule('register', token('register', word_match{ +lex:set_word_list('register', { -- 32-bit registers. 'ah', 'al', 'ax', 'bh', 'bl', 'bp', 'bx', 'ch', 'cl', 'cx', 'dh', 'di', 'dl', 'dx', 'eax', 'ebx', 'ebx', 'ecx', 'edi', 'edx', 'esi', 'esp', 'fs', 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6', @@ -321,45 +366,21 @@ lex:add_rule('register', token('register', word_match{ 'r15w', 'rax', 'rbp', 'rbx', 'rcx', 'rdi', 'rdx', 'rsi', 'rsp', 'sil', 'xmm8', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14', 'xmm15', 'ymm8', 'ymm9', 'ymm10', 'ymm11', 'ymm12', 'ymm13', 'ymm14', 'ymm15' -})) -lex:add_style('register', lexer.styles.constant) +}) --- Types. -local sizes = word_match{ - 'byte', 'word', 'dword', 'qword', 'tword', 'oword', 'yword', - -- Instructions. - 'a16', 'a32', 'a64', 'o16', 'o32', 'o64' -} -local wrt_types = '..' * word_match('start gotpc gotoff gottpoff got plt sym tlsie') -lex:add_rule('type', token(lexer.TYPE, sizes + wrt_types)) +lex:set_word_list('size', { + 'byte', 'word', 'dword', 'qword', 'tword', 'oword', 'yword', -- + 'a16', 'a32', 'a64', 'o16', 'o32', 'o64' -- instructions +}) --- Constants. -local word = (lexer.alpha + S('$._?')) * (lexer.alnum + S('$._?#@~'))^0 -local constants = word_match{ +lex:set_word_list(lexer.TYPE .. '.wrt', 'start gotpc gotoff gottpoff got plt sym tlsie') + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { '__float128h__', '__float128l__', '__float16__', '__float32__', '__float64__', '__float8__', '__float80e__', '__float80m__', '__Infinity__', '__NaN__', '__QNaN__', '__SNaN__' -} -lex:add_rule('constant', token(lexer.CONSTANT, constants + '$' * P('$')^-1 * -word)) - --- Labels. -lex:add_rule('label', token(lexer.LABEL, word * ':')) - --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, word)) +}) --- Strings. -local sq_str = lexer.range("'", true) -local dq_str = lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) - --- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol(';'))) - --- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number * S('hqb')^-1)) - --- Preprocessor. -local pp_word = word_match{ +lex:set_word_list(lexer.PREPROCESSOR, { 'arg', 'assign', 'clear', 'define', 'defstr', 'deftok', 'depend', 'elif', 'elifctx', 'elifdef', 'elifempty', 'elifenv', 'elifid', 'elifidn', 'elifidni', 'elifmacro', 'elifn', 'elifnctx', 'elifndef', 'elifnempty', 'elifnenv', 'elifnid', 'elifnidn', 'elifnidni', 'elifnmacro', @@ -371,19 +392,8 @@ local pp_word = word_match{ 'include', 'ixdefine', 'line', 'local', 'macro', 'pathsearch', 'pop', 'push', 'rep', 'repl', 'rmacro', 'rotate', 'stacksize', 'strcat', 'strlen', 'substr', 'undef', 'unmacro', 'use', 'warning', 'while', 'xdefine' -} -local pp_symbol = '??' + S('!$+?') + '%' * -lexer.space + lexer.digit^1 -lex:add_rule('preproc', token(lexer.PREPROCESSOR, '%' * (pp_word + pp_symbol))) +}) --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|~:,()[]'))) - --- Fold points. -lex:add_fold_point(lexer.PREPROCESSOR, '%if', '%endif') -lex:add_fold_point(lexer.PREPROCESSOR, '%macro', '%endmacro') -lex:add_fold_point(lexer.PREPROCESSOR, '%rep', '%endrep') -lex:add_fold_point(lexer.PREPROCESSOR, '%while', '%endwhile') -lex:add_fold_point(lexer.KEYWORD, 'struc', 'endstruc') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines(';')) +lexer.property['scintillua.comment'] = ';' return lex diff --git a/lua/lexers/asp.lua b/lua/lexers/asp.lua index a16f4b6..b604a9b 100644 --- a/lua/lexers/asp.lua +++ b/lua/lexers/asp.lua @@ -1,31 +1,31 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- ASP LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S local html = lexer.load('html') -local lex = lexer.new('asp', {inherit = html}) -- proxy for HTML +local lex = lexer.new(..., {inherit = html}) -- proxy for HTML -- Embedded VB. local vb = lexer.load('vb') -local vb_start_rule = token('asp_tag', '<%' * P('=')^-1) -local vb_end_rule = token('asp_tag', '%>') +local vb_start_rule = lex:tag(lexer.PREPROCESSOR, '<%' * P('=')^-1) +local vb_end_rule = lex:tag(lexer.PREPROCESSOR, '%>') lex:embed(vb, vb_start_rule, vb_end_rule) -lex:add_style('asp_tag', lexer.styles.embedded) -- Embedded VBScript. local vbs = lexer.load('vb', 'vbscript') -local script_element = word_match('script', true) -local vbs_start_rule = #(P('<') * script_element * (P(function(input, index) +local script_element = lexer.word_match('script', true) +local vbs_start_rule = #('<' * script_element * (P(function(input, index) if input:find('^%s+language%s*=%s*(["\'])vbscript%1', index) or - input:find('^%s+type%s*=%s*(["\'])text/vbscript%1', index) then return index end + input:find('^%s+type%s*=%s*(["\'])text/vbscript%1', index) then return true end end) + '>')) * html.embed_start_tag -- <script language="vbscript"> -local vbs_end_rule = #('</' * script_element * lexer.space^0 * '>') * html.embed_end_tag -- </script> +local vbs_end_rule = #('</' * script_element * '>') * html.embed_end_tag -- </script> lex:embed(vbs, vbs_start_rule, vbs_end_rule) -- Fold points. -lex:add_fold_point('asp_tag', '<%', '%>') +lex:add_fold_point(lexer.PREPROCESSOR, '<%', '%>') + +lexer.property['scintillua.comment'] = '<!--|-->' return lex diff --git a/lua/lexers/autohotkey.lua b/lua/lexers/autohotkey.lua new file mode 100644 index 0000000..0980fb6 --- /dev/null +++ b/lua/lexers/autohotkey.lua @@ -0,0 +1,164 @@ +-- Copyright 2022-2024 Mitchell. See LICENSE. +-- AutoHotkey LPeg lexer. +-- Contributed by Snoopy. + +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B + +local lex = lexer.new(...) + +-- Keywords. +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD, true))) + +-- Variables. +lex:add_rule('variable', + lex:tag(lexer.VARIABLE_BUILTIN, 'A_' * lex:word_match(lexer.VARIABLE_BUILTIN, true))) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, S('fF') * lexer.digit * (lexer.digit)^-1 + + lex:word_match(lexer.CONSTANT_BUILTIN, true))) + +-- Functions. +local builtin_func = -B('.') * + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN, true)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '(')) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Comments. +local line_comment = lexer.to_eol(';') +local block_comment = lexer.range('/*', '*/') +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) + +-- Preprocessor. +lex:add_rule('preprocessor', + lex:tag(lexer.PREPROCESSOR, '#' * lex:word_match(lexer.PREPROCESSOR, true))) + +-- Strings. +local dq_str = lexer.range('"', true, false) +local sq_str = lexer.range("'", true, false) +lex:add_rule('string', lex:tag(lexer.STRING, dq_str + sq_str)) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('~+-^*/&<>=?:()[]{}'))) + +lex:set_word_list(lexer.KEYWORD, { + 'as', 'and', 'class', 'contains', 'extends', 'false', 'in', 'is', 'IsSet', 'not', 'or', 'super', + 'true', 'unset', 'Break', 'Catch', 'Continue', 'Else', 'Finally', 'For', 'Global', 'Goto', 'If', + 'Local', 'Loop', 'Return', 'Static', 'Throw', 'Try', 'Until', 'While' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'Abs', 'AutoTrim', 'Asc', 'ASin', 'ACos', 'ATan', 'BlockInput', 'Ceil', 'Chr', 'Click', + 'ClipWait', 'ComObjActive', 'ComObjArray', 'ComObjConnect', 'ComObjCreate', 'ComObject', + 'ComObjEnwrap', 'ComObjUnwrap', 'ComObjError', 'ComObjFlags', 'ComObjGet', 'ComObjMissing', + 'ComObjParameter', 'ComObjQuery', 'ComObjType', 'ComObjValue', 'Control', 'ControlClick', + 'ControlFocus', 'ControlGet', 'ControlGetFocus', 'ControlGetPos', 'ControlGetText', 'ControlMove', + 'ControlSend', 'ControlSendRaw', 'ControlSetText', 'CoordMode', 'Cos', 'Critical', + 'DetectHiddenText', 'DetectHiddenWindows', 'DllCall', 'Drive', 'DriveGet', 'DriveSpaceFree', + 'Edit', 'Else', 'EnvAdd', 'EnvDiv', 'EnvGet', 'EnvMult', 'EnvSet', 'EnvSub', 'EnvUpdate', + 'Exception', 'Exit', 'ExitApp', 'Exp', 'FileAppend', 'FileCopy', 'FileCopyDir', 'FileCreateDir', + 'FileCreateShortcut', 'FileDelete', 'FileEncoding', 'FileExist', 'FileInstall', 'FileGetAttrib', + 'FileGetShortcut', 'FileGetSize', 'FileGetTime', 'FileGetVersion', 'FileMove', 'FileMoveDir', + 'FileOpen', 'FileRead', 'FileReadLine', 'FileRecycle', 'FileRecycleEmpty', 'FileRemoveDir', + 'FileSelectFile', 'FileSelectFolder', 'FileSetAttrib', 'FileSetTime', 'Floor', 'Format', + 'FormatTime', 'Func', 'GetKeyName', 'GetKeyVK', 'GetKeySC', 'GetKeyState', 'GetKeyState', 'Gosub', + 'GroupActivate', 'GroupAdd', 'GroupClose', 'GroupDeactivate', 'Gui', 'GuiControl', + 'GuiControlGet', 'Hotkey', 'Hotstring', 'IfEqual', 'IfNotEqual', 'IfExist', 'IfNotExist', + 'IfGreater', 'IfGreaterOrEqual', 'IfInString', 'IfNotInString', 'IfLess', 'IfLessOrEqual', + 'IfMsgBox', 'IfWinActive', 'IfWinNotActive', 'IfWinExist', 'IfWinNotExist', 'IL_Create', 'IL_Add', + 'IL_Destroy', 'ImageSearch', 'IniDelete', 'IniRead', 'IniWrite', 'Input', 'InputBox', 'InputHook', + 'InStr', 'IsByRef', 'IsFunc', 'IsLabel', 'IsObject', 'IsSet', 'KeyHistory', 'KeyWait', + 'ListHotkeys', 'ListLines', 'ListVars', 'LoadPicture', 'Log', 'Ln', 'LV_Add', 'LV_Delete', + 'LV_DeleteCol', 'LV_GetCount', 'LV_GetNext', 'LV_GetText', 'LV_Insert', 'LV_InsertCol', + 'LV_Modify', 'LV_ModifyCol', 'LV_SetImageList', 'Max', 'Menu', 'MenuGetHandle', 'MenuGetName', + 'Min', 'Mod', 'MouseClick', 'MouseClickDrag', 'MouseGetPos', 'MouseMove', 'MsgBox', 'NumGet', + 'NumPut', 'ObjAddRef', 'ObjRelease', 'ObjBindMethod', 'ObjClone', 'ObjCount', 'ObjDelete', + 'ObjGetAddress', 'ObjGetCapacity', 'ObjHasKey', 'ObjInsert', 'ObjInsertAt', 'ObjLength', + 'ObjMaxIndex', 'ObjMinIndex', 'ObjNewEnum', 'ObjPop', 'ObjPush', 'ObjRemove', 'ObjRemoveAt', + 'ObjSetCapacity', 'ObjGetBase', 'ObjRawGet', 'ObjRawSet', 'ObjSetBase', 'OnClipboardChange', + 'OnError', 'OnExit', 'OnExit', 'OnMessage', 'Ord', 'OutputDebug', 'Pause', 'PixelGetColor', + 'PixelSearch', 'PostMessage', 'Process', 'Progress', 'Random', 'RegExMatch', 'RegExReplace', + 'RegDelete', 'RegRead', 'RegWrite', 'RegisterCallback', 'Reload', 'Round', 'Run', 'RunAs', + 'RunWait', 'SB_SetIcon', 'SB_SetParts', 'SB_SetText', 'Send', 'SendRaw', 'SendInput', 'SendPlay', + 'SendEvent', 'SendLevel', 'SendMessage', 'SendMode', 'SetBatchLines', 'SetCapsLockState', + 'SetControlDelay', 'SetDefaultMouseSpeed', 'SetEnv', 'SetFormat', 'SetKeyDelay', 'SetMouseDelay', + 'SetNumLockState', 'SetScrollLockState', 'SetRegView', 'SetStoreCapsLockMode', 'SetTimer', + 'SetTitleMatchMode', 'SetWinDelay', 'SetWorkingDir', 'Shutdown', 'Sin', 'Sleep', 'Sort', + 'SoundBeep', 'SoundGet', 'SoundGetWaveVolume', 'SoundPlay', 'SoundSet', 'SoundSetWaveVolume', + 'SplashImage', 'SplashTextOn', 'SplashTextOff', 'SplitPath', 'Sqrt', 'StatusBarGetText', + 'StatusBarWait', 'StrGet', 'StringCaseSense', 'StringGetPos', 'StringLeft', 'StringLen', + 'StringLower', 'StringMid', 'StringReplace', 'StringRight', 'StringSplit', 'StringTrimLeft', + 'StringTrimRight', 'StringUpper', 'StrLen', 'StrPut', 'StrReplace', 'StrSplit', 'SubStr', + 'Suspend', 'Switch', 'SysGet', 'Tan', 'Thread', 'ToolTip', 'Transform', 'TrayTip', 'Trim', + 'LTrim', 'RTrim', 'TV_Add', 'TV_Delete', 'TV_Get', 'TV_GetChild', 'TV_GetCount', 'TV_GetNext', + 'TV_GetParent', 'TV_GetPrev', 'TV_GetSelection', 'TV_GetText', 'TV_Modify', 'TV_SetImageList', + 'UrlDownloadToFile', 'VarSetCapacity', 'WinActivate', 'WinActivateBottom', 'WinActive', + 'WinClose', 'WinExist', 'WinGetActiveStats', 'WinGetActiveTitle', 'WinGetClass', 'WinGet', + 'WinGetPos', 'WinGetText', 'WinGetTitle', 'WinHide', 'WinKill', 'WinMaximize', + 'WinMenuSelectItem', 'WinMinimize', 'WinMinimizeAll', 'WinMinimizeAllUndo', 'WinMove', + 'WinRestore', 'WinSet', 'WinSetTitle', 'WinShow', 'WinWait', 'WinWaitActive', 'WinWaitNotActive', + 'WinWaitClose' +}) + +lex:set_word_list(lexer.PREPROCESSOR, { + 'ClipboardTimeout', 'CommentFlag', 'Delimiter', 'DerefChar', 'ErrorStdOut', 'EscapeChar', + 'HotkeyInterval', 'HotkeyModifierTimeout', 'Hotstring', 'If', 'IfTimeout', 'IfWinActive', + 'IfWinNotActive', 'IfWinExist', 'IfWinNotExist', 'Include', 'IncludeAgain', 'InputLevel', + 'InstallKeybdHook', 'InstallMouseHook', 'KeyHistory', 'LTrim', 'MaxHotkeysPerInterval', 'MaxMem', + 'MaxThreads', 'MaxThreadsBuffer', 'MaxThreadsPerHotkey', 'MenuMaskKey', 'NoEnv', 'NoTrayIcon', + 'Persistent', 'Requires', 'SingleInstance', 'UseHook', 'Warn', 'WinActivateForce' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'LButton', 'RButton', 'MButton', 'Advanced Buttons', 'XButton1', 'XButton2', 'Wheel', 'WheelDown', + 'WheelUp', 'WheelLeft', 'WheelRight', 'CapsLock', 'Space', 'Tab', 'Enter', 'Return', 'Esc', + 'Escape', 'BS', 'Backspace', 'ScrollLock', 'Del', 'Delete', 'Ins', 'Insert', 'Home', 'End', + 'PgUp', 'PgDn', 'Up', 'Down', 'Left', 'Right', 'Numpad0', 'NumpadIns', 'Numpad1', 'NumpadEnd', + 'Numpad2', 'NumpadDown', 'Numpad3', 'NumpadPgDn', 'Numpad4', 'NumpadLeft', 'Numpad5', + 'NumpadClear', 'Numpad6', 'NumpadRight', 'Numpad7', 'NumpadHome', 'Numpad8', 'NumpadUp', + 'Numpad9', 'NumpadPgUp', 'NumpadDot', 'NumpadDel', 'NumLock', 'NumpadDiv', 'NumpadMult', + 'NumpadAdd', 'NumpadSub', 'NumpadEnter', 'LWin', 'RWin', 'Ctrl', 'Control', 'Alt', 'Shift', + 'LCtrl', 'LControl', 'RCtrl', 'RControl', 'LShift', 'RShift', 'LAlt', 'RAlt', 'Browser_Back', + 'Browser_Forward', 'Browser_Refresh', 'Browser_Stop', 'Browser_Search', 'Browser_Favorites', + 'Browser_Home', 'Volume_Mute', 'Volume_Down', 'Volume_Up', 'Media_Next', 'Media_Prev', + 'Media_Stop', 'Media_Play_Pause', 'Launch_Mail', 'Launch_Media', 'Launch_App1', 'Launch_App2', + 'AppsKey', 'PrintScreen', 'CtrlBreak', 'Pause', 'Break', 'Help' +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'Space', 'Tab', 'Args', 'WorkingDir', 'InitialWorkingDir', 'ScriptDir', 'ScriptName', + 'ScriptFullPath', 'ScriptHwnd', 'LineNumber', 'LineFile', 'ThisFunc', 'ThisLabel', 'AhkVersion', + 'AhkPath', 'IsUnicode', 'IsCompiled', 'ExitReason', 'Year', 'MM', 'DD', 'MMMM', 'MMM', 'DDDD', + 'DDD', 'WDay', 'YDay', 'YWeek', 'Hour', 'Min', 'Sec', 'MSec', 'Now', 'NowUTC', 'TickCount', + 'IsSuspended', 'IsPaused', 'IsCritical', 'BatchLines', 'ListLines', 'TitleMatchMode', + 'TitleMatchModeSpeed', 'DetectHiddenWindows', 'DetectHiddenText', 'AutoTrim', 'StringCaseSense', + 'FileEncoding', 'FormatInteger', 'FormatFloat', 'SendMode', 'SendLevel', 'StoreCapsLockMode', + 'KeyDelay', 'KeyDuration', 'KeyDelayPlay', 'KeyDurationPlay', 'WinDelay', 'ControlDelay', + 'MouseDelay', 'MouseDelayPlay', 'DefaultMouseSpeed', 'CoordModeToolTip', 'CoordModePixel', + 'CoordModeMouse', 'CoordModeCaret', 'CoordModeMenu', 'RegView', 'IconHidden', 'IconTip', + 'IconFile', 'IconNumber', 'TimeIdle', 'TimeIdlePhysical', 'TimeIdleKeyboard', 'TimeIdleMouse', + 'DefaultGui', 'DefaultListView', 'DefaultTreeView', 'Gui', 'GuiControl', 'GuiWidth', 'GuiHeight', + 'GuiX', 'GuiY', 'GuiEvent', 'GuiControlEvent', 'EventInfo', 'ThisMenuItem', 'ThisMenu', + 'ThisMenuItemPos', 'ThisHotkey', 'PriorHotkey', 'PriorKey', 'TimeSinceThisHotkey', + 'TimeSincePriorHotkey', 'EndChar', 'ComSpec', 'Temp', 'OSType', 'OSVersion', 'Is64bitOS', + 'PtrSize', 'Language', 'ComputerName', 'UserName', 'WinDir', 'ProgramFiles', 'AppData', + 'AppDataCommon', 'Desktop', 'DesktopCommon', 'StartMenu', 'StartMenuCommon', 'Programs', + 'ProgramsCommon', 'Startup', 'StartupCommon', 'MyDocuments', 'IsAdmin', 'ScreenWidth', + 'ScreenHeight', 'ScreenDPI', 'Cursor', 'CaretX', 'CaretY', 'Clipboard', 'LastError', 'Index', + 'LoopFileName', 'LoopRegName', 'LoopReadLine', 'LoopField' +}) + +lexer.property['scintillua.comment'] = ';' + +-- Fold points. +lex:add_fold_point(lexer.OPERATOR, '{', '}') +lex:add_fold_point(lexer.COMMENT, '/*', '*/') + +return lex diff --git a/lua/lexers/autoit.lua b/lua/lexers/autoit.lua index 42b4bdf..6fd9f1b 100644 --- a/lua/lexers/autoit.lua +++ b/lua/lexers/autoit.lua @@ -1,27 +1,61 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- AutoIt LPeg lexer. -- Contributed by Jeff Stone. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('autoit') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match({ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD, true))) + +-- Functions. +local builtin_func = -B('.') * + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN, true)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '(')) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Comments. +local line_comment = lexer.to_eol(';') +local block_comment = lexer.range('#comments-start', '#comments-end') + lexer.range('#cs', '#ce') +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) + +-- Preprocessor. +lex:add_rule('preprocessor', + lex:tag(lexer.PREPROCESSOR, '#' * lex:word_match(lexer.PREPROCESSOR, true))) + +-- Strings. +local dq_str = lexer.range('"', true, false) +local sq_str = lexer.range("'", true, false) +local inc = lexer.range('<', '>', true, false, true) +lex:add_rule('string', lex:tag(lexer.STRING, dq_str + sq_str + inc)) + +-- Macros. +lex:add_rule('macro', lex:tag(lexer.CONSTANT_BUILTIN, '@' * (lexer.alnum + '_')^1)) + +-- Variables. +lex:add_rule('variable', lex:tag(lexer.VARIABLE, '$' * (lexer.alnum + '_')^1)) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-^*/&<>=?:()[]'))) + +lex:set_word_list(lexer.KEYWORD, { 'False', 'True', 'And', 'Or', 'Not', 'ContinueCase', 'ContinueLoop', 'Default', 'Dim', 'Global', 'Local', 'Const', 'Do', 'Until', 'Enum', 'Exit', 'ExitLoop', 'For', 'To', 'Step', 'Next', 'In', 'Func', 'Return', 'EndFunc', 'If', 'Then', 'ElseIf', 'Else', 'EndIf', 'Null', 'ReDim', 'Select', 'Case', 'EndSelect', 'Static', 'Switch', 'EndSwitch', 'Volatile', 'While', 'WEnd', 'With', 'EndWith' -}, true))) +}) --- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match({ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { 'Abs', 'ACos', 'AdlibRegister', 'AdlibUnRegister', 'Asc', 'AscW', 'ASin', 'Assign', 'ATan', 'AutoItSetOption', 'AutoItWinGetTitle', 'AutoItWinSetTitle', 'Beep', 'Binary', 'BinaryLen', 'BinaryMid', 'BinaryToString', 'BitAND', 'BitNOT', 'BitOR', 'BitRotate', 'BitShift', 'BitXOR', @@ -91,39 +125,13 @@ lex:add_rule('function', token(lexer.FUNCTION, word_match({ 'WinGetState', 'WinGetText', 'WinGetTitle', 'WinKill', 'WinList', 'WinMenuSelectItem', 'WinMinimizeAll', 'WinMinimizeAllUndo', 'WinMove', 'WinSetOnTop', 'WinSetState', 'WinSetTitle', 'WinSetTrans', 'WinWait', 'WinWaitActive', 'WinWaitClose', 'WinWaitNotActive' -}, true))) +}) --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) - --- Comments. -local line_comment = lexer.to_eol(';') -local block_comment = lexer.range('#comments-start', '#comments-end') + lexer.range('#cs', '#ce') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) - --- Preprocessor. -lex:add_rule('preprocessor', token(lexer.PREPROCESSOR, '#' * word_match({ +lex:set_word_list(lexer.PREPROCESSOR, { 'include-once', 'include', 'pragma', 'forceref', 'RequireAdmin', 'NoTrayIcon', 'OnAutoItStartRegister' -}, true))) - --- Strings. -local dq_str = lexer.range('"', true, false) -local sq_str = lexer.range("'", true, false) -local inc = lexer.range('<', '>', true, false, true) -lex:add_rule('string', token(lexer.STRING, dq_str + sq_str + inc)) - --- Macros. -lex:add_rule('macro', token('macro', '@' * (lexer.alnum + '_')^1)) -lex:add_style('macro', lexer.styles.preprocessor) +}) --- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, '$' * (lexer.alnum + '_')^1)) - --- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) - --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-^*/&<>=?:()[]'))) +lexer.property['scintillua.comment'] = ';' return lex diff --git a/lua/lexers/awk.lua b/lua/lexers/awk.lua index 0b3f9bf..47ff9b4 100644 --- a/lua/lexers/awk.lua +++ b/lua/lexers/awk.lua @@ -1,12 +1,11 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- AWK LPeg lexer. -- Modified by Wolfgang Seeberg 2012, 2013. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('awk') +local lex = lexer.new(...) local LEFTBRACKET = '[' local RIGHTBRACKET = ']' @@ -215,70 +214,82 @@ local function scanFieldDelimiters(input, index) return false end --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) - -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, '#' * P(scanComment))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, '#' * P(scanComment))) -- Strings. -lex:add_rule('string', token(lexer.STRING, DQUOTE * P(scanString))) +lex:add_rule('string', lex:tag(lexer.STRING, DQUOTE * P(scanString))) -- No leading sign because it might be binary. local float = ((lexer.digit^1 * ('.' * lexer.digit^0)^-1) + ('.' * lexer.digit^1)) * (S('eE') * S('+-')^-1 * lexer.digit^1)^-1 -- Fields. E.g. $1, $a, $(x), $a(x), $a[x], $"1", $$a, etc. -lex:add_rule('field', token('field', '$' * S('$+-')^0 * +lex:add_rule('field', lex:tag(lexer.VARIABLE .. '.field', '$' * S('$+-')^0 * (float + lexer.word^0 * '(' * P(scanFieldDelimiters) + lexer.word^1 * ('[' * P(scanFieldDelimiters))^-1 + '"' * P(scanString) + '/' * P(eatRegex) * '/'))) -lex:add_style('field', lexer.styles.label) -- Regular expressions. -- Slash delimited regular expressions are preceded by most operators or the keywords 'print' -- and 'case', possibly on a preceding line. They can contain unescaped slashes and brackets -- in brackets. Some escape sequences like '\S', '\s' have special meanings with Gawk. Tokens -- that contain them are displayed differently. -lex:add_rule('gawkRegex', token('gawkRegex', SLASH * P(scanGawkRegex))) -lex:add_style('gawkRegex', lexer.styles.preprocessor .. {underlined = true}) -lex:add_rule('regex', token(lexer.REGEX, SLASH * P(scanRegex))) +lex:add_rule('gawkRegex', lex:tag(lexer.REGEX .. '.gawk', SLASH * P(scanGawkRegex))) +lex:add_rule('regex', lex:tag(lexer.REGEX, SLASH * P(scanRegex))) -- Operators. -lex:add_rule('gawkOperator', token('gawkOperator', P("|&") + "@" + "**=" + "**")) -lex:add_style('gawkOperator', lexer.styles.operator .. {underlined = true}) -lex:add_rule('operator', token(lexer.OPERATOR, S('!%&()*+,-/:;<=>?[\\]^{|}~'))) +lex:add_rule('gawkOperator', lex:tag(lexer.OPERATOR .. '.gawk', P("|&") + "@" + "**=" + "**")) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('!%&()*+,-/:;<=>?[\\]^{|}~'))) -- Numbers. -lex:add_rule('gawkNumber', token('gawkNumber', lexer.hex_num + lexer.oct_num)) -lex:add_style('gawkNumber', lexer.styles.number .. {underlined = true}) -lex:add_rule('number', token(lexer.NUMBER, float)) +lex:add_rule('gawkNumber', lex:tag(lexer.NUMBER .. '.gawk', lexer.hex_num + lexer.oct_num)) +lex:add_rule('number', lex:tag(lexer.NUMBER, float)) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'BEGIN', 'END', 'atan2', 'break', 'close', 'continue', 'cos', 'delete', 'do', 'else', 'exit', - 'exp', 'fflush', 'for', 'function', 'getline', 'gsub', 'if', 'in', 'index', 'int', 'length', - 'log', 'match', 'next', 'nextfile', 'print', 'printf', 'rand', 'return', 'sin', 'split', - 'sprintf', 'sqrt', 'srand', 'sub', 'substr', 'system', 'tolower', 'toupper', 'while' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -lex:add_rule('builtInVariable', token('builtInVariable', word_match( - 'ARGC ARGV CONVFMT ENVIRON FILENAME FNR FS NF NR OFMT OFS ORS RLENGTH RS RSTART SUBSEP'))) -lex:add_style('builtInVariable', lexer.styles.constant) +lex:add_rule('builtInVariable', + lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))) -lex:add_rule('gawkBuiltInVariable', token('gawkBuiltInVariable', word_match{ - 'ARGIND', 'BINMODE', 'ERRNO', 'FIELDWIDTHS', 'FPAT', 'FUNCTAB', 'IGNORECASE', 'LINT', 'PREC', - 'PROCINFO', 'ROUNDMODE', 'RT', 'SYMTAB', 'TEXTDOMAIN' -})) -lex:add_style('gawkBuiltInVariable', lexer.styles.constant .. {underlined = true}) +lex:add_rule('gawkBuiltInVariable', lex:tag(lexer.VARIABLE_BUILTIN .. '.gawk', + lex:word_match(lexer.VARIABLE_BUILTIN .. '.gawk'))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, lexer.word * #P('('))) +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +lex:add_rule('function', (builtin_func + func) * #P('(')) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'BEGIN', 'END', 'break', 'continue', 'do', 'else', 'for', 'if', 'in', 'while', -- + 'delete', -- array + 'print', 'printf', 'getline', 'close', 'fflush', 'system', -- I/O + 'function', 'return', -- functions + 'next', 'nextfile', 'exit' -- program execution +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'gsub', 'index', 'length', 'match', 'split', 'sprintf', 'sub', 'substr', 'tolower', 'toupper', -- string + 'mktime', 'strftime', 'systime', -- time + 'atan2', 'cos', 'exp', 'int', 'log', 'rand', 'sin', 'sqrt', 'srand' -- arithmetic +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'ARGC', 'ARGV', 'CONVFMT', 'ENVIRON', 'FILENAME', 'FNR', 'FS', 'NF', 'NR', 'OFMT', 'OFS', 'ORS', + 'RLENGTH', 'RS', 'RSTART', 'SUBSEP' +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN .. '.gawk', { + 'ARGIND', 'BINMODE', 'ERRNO', 'FIELDWIDTHS', 'FPAT', 'FUNCTAB', 'IGNORECASE', 'LINT', 'PREC', + 'PROCINFO', 'ROUNDMODE', 'RT', 'SYMTAB', 'TEXTDOMAIN' +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/bash.lua b/lua/lexers/bash.lua index da4472e..2212ea6 100644 --- a/lua/lexers/bash.lua +++ b/lua/lexers/bash.lua @@ -1,58 +1,138 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Shell LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('bash') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'if', 'then', 'elif', 'else', 'fi', 'case', 'in', 'esac', 'while', 'for', 'do', 'done', - 'continue', 'local', 'return', 'select', - -- Operators. - '-a', '-b', '-c', '-d', '-e', '-f', '-g', '-h', '-k', '-p', '-r', '-s', '-t', '-u', '-w', '-x', - '-O', '-G', '-L', '-S', '-N', '-nt', '-ot', '-ef', '-o', '-z', '-n', '-eq', '-ne', '-lt', '-le', - '-gt', '-ge' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Builtins. +lex:add_rule('builtin', + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) * -P('=')) + +-- Variable assignment. +local assign = lex:tag(lexer.VARIABLE, lexer.word) * lex:tag(lexer.OPERATOR, '=') +lex:add_rule('assign', lexer.starts_line(assign, true)) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. -local sq_str = lexer.range("'", false, false) -local dq_str = lexer.range('"') -local ex_str = lexer.range('`') +local sq_str = -B('\\') * lexer.range("'", false, false) +local dq_str = -B('\\') * lexer.range('"') local heredoc = '<<' * P(function(input, index) - local _, e, _, delimiter = input:find('^%-?(["\']?)([%a_][%w_]*)%1[\n\r\f;]+', index) - if not delimiter then return end - _, e = input:find('[\n\r\f]+' .. delimiter, e) + local _, e, minus, _, delimiter = input:find('^(%-?)%s*(["\']?)([%w_]+)%2[^\n]*[\n\r\f;]+', index) + if not delimiter then return nil end + -- If the starting delimiter of a here-doc begins with "-", then spaces are allowed to come + -- before the closing delimiter. + _, e = + input:find((minus == '' and '[\n\r\f]+' or '[\n\r\f]+[ \t]*') .. delimiter .. '%f[^%w_]', e) return e and e + 1 or #input + 1 end) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str + ex_str + heredoc)) +local ex_str = -B('\\') * '`' +lex:add_rule('string', + lex:tag(lexer.STRING, sq_str + dq_str + heredoc) + lex:tag(lexer.EMBEDDED, ex_str)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, -B('\\') * lexer.to_eol('#'))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, '$' * - (S('!#?*@$') + lexer.digit^1 + lexer.word + lexer.range('{', '}', true, false, true)))) +local builtin_var = lex:tag(lexer.OPERATOR, '$' * P('{')^-1) * lex:tag(lexer.VARIABLE_BUILTIN, + lex:word_match(lexer.VARIABLE_BUILTIN) + S('!#?*@$-') * -lexer.alnum + lexer.digit^1) +local var_ref = lex:tag(lexer.OPERATOR, '$' * ('{' * S('!#')^-1)^-1) * + lex:tag(lexer.VARIABLE, lexer.word) +local patt_expansion = lex:tag(lexer.DEFAULT, '/#' + '#' * P('#')^-1) +lex:add_rule('variable', builtin_var + var_ref * patt_expansion^-1) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('=!<>+-/*^&|~.,:;?()[]{}'))) +local op = S('!<>&|;$()[]{}') + lpeg.B(lexer.space) * S('.:') * #lexer.space + +local function in_expr(constructs) + return P(function(input, index) + local line = input:sub(1, index):match('[^\r\n]*$') + for k, v in pairs(constructs) do + local s = line:find(k, 1, true) + if not s then goto continue end + local e = line:find(v, 1, true) + if not e or e < s then return true end + ::continue:: + end + return nil + end) +end + +local file_op = '-' * (S('abcdefghkprstuwxGLNOS') + 'ef' + 'nt' + 'ot') +local shell_op = '-o' +local var_op = '-' * S('vR') +local string_op = '-' * S('zn') + S('!=')^-1 * '=' + S('<>') +local num_op = '-' * lexer.word_match('eq ne lt le gt ge') +local in_cond_expr = in_expr{['[[ '] = ' ]]', ['[ '] = ' ]'} +local conditional_op = (num_op + file_op + shell_op + var_op + string_op) * #lexer.space * + in_cond_expr + +local in_arith_expr = in_expr{['(('] = '))'} +local arith_op = (S('+!~*/%<>=&^|?:,') + '--' + '-' * #S(' \t')) * in_arith_expr + +-- TODO: performance is terrible on large files. +-- lex:add_rule('operator', lex:tag(lexer.OPERATOR, op + conditional_op + arith_op)) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, op)) + +-- Flags/options. +lex:add_rule('flag', lex:tag(lexer.DEFAULT, '-' * P('-')^-1 * lexer.word * ('-' * lexer.word)^0)) -- Fold points. lex:add_fold_point(lexer.KEYWORD, 'if', 'fi') lex:add_fold_point(lexer.KEYWORD, 'case', 'esac') lex:add_fold_point(lexer.KEYWORD, 'do', 'done') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'if', 'then', 'elif', 'else', 'fi', 'time', 'for', 'in', 'until', 'while', 'do', 'done', 'case', + 'esac', 'coproc', 'select', 'function' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + -- Shell built-ins. + 'break', 'cd', 'continue', 'eval', 'exec', 'exit', 'export', 'getopts', 'hash', 'pwd', 'readonly', + 'return', 'shift', 'test', 'times', 'trap', 'umask', 'unset', + -- Bash built-ins. + 'alias', 'bind', 'builtin', 'caller', 'command', 'declare', 'echo', 'enable', 'help', 'let', + 'local', 'logout', 'mapfile', 'printf', 'read', 'readarray', 'source', 'type', 'typeset', + 'ulimit', 'unalias', -- + 'set', 'shopt', -- shell behavior + 'dirs', 'popd', 'pushd', -- directory stack + 'bg', 'fg', 'jobs', 'kill', 'wait', 'disown', 'suspend', -- job control + 'fc', 'history' -- history +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + -- Shell built-ins. + 'CDPATH', 'HOME', 'IFS', 'MAIL', 'MAILPATH', 'OPTARG', 'OPTIND', 'PATH', 'PS1', 'PS2', + -- Bash built-ins. + 'BASH', 'BASHOPTS', 'BASHPID', 'BASH_ALIASES', 'BASH_ARGC', 'BASH_ARGV', 'BASH_ARGV0', + 'BASH_CMDS', 'BASH_COMMAND', 'BASH_COMPAT', 'BASH_ENV', 'BASH_EXECUTION_STRING', 'BASH_LINENO', + 'BASH_LOADABLES_PATH', 'BASH_REMATCH', 'BASH_SOURCE', 'BASH_SUBSHELL', 'BASH_VERSINFO', + 'BASH_VERSION', 'BASH_XTRACEFD', 'CHILD_MAX', 'COLUMNS', 'COMP_CWORD', 'COMP_LINE', 'COMP_POINT', + 'COMP_TYPE', 'COMP_KEY', 'COMP_WORDBREAKS', 'COMP_WORDS', 'COMP_REPLY', 'COPROC', 'DIRSTACK', + 'EMACS', 'ENV', 'EPOCHREALTIME', 'EPOCHSECONDS', 'EUID', 'EXECIGNORE', 'FCEDIT', 'FIGNORE', + 'FUNCNAME', 'FUNCNEST', 'GLOBIGNORE', 'GROUPS', 'histchars', 'HISTCMD', 'HISTCONTROL', 'HISTFILE', + 'HISTFILESIZE', 'HISTIGNORE', 'HISTSIZE', 'HISTTIMEFORMAT', 'HOSTFILE', 'HOSTNAME', 'HOSTTYPE', + 'IGNOREEOF', 'INPUTRC', 'INSIDE_EMACS', 'LANG', 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', + 'LC_NUMERIC', 'LC_TIME', 'LINENO', 'LINES', 'MACHTYPE', 'MAILCHECK', 'MAPFILE', 'OLDPWD', + 'OPTERR', 'OSTYPE', 'PIPESTATUS', 'POSIXLY_CORRECT', 'PPID', 'PROMPT_COMMAND', 'PROMPT_DIRTRIM', + 'PSO', 'PS3', 'PS4', 'PWD', 'RANDOM', 'READLINE_LINE', 'READLINE_MARK', 'READLINE_POINT', 'REPLY', + 'SECONDS', 'SHELL', 'SHELLOPTS', 'SHLVL', 'SRANDOM', 'TIMEFORMAT', 'TMOUT', 'TMPDIR', 'UID', + -- Job control. + 'auto_resume' +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/batch.lua b/lua/lexers/batch.lua index 5468a1d..71e98b4 100644 --- a/lua/lexers/batch.lua +++ b/lua/lexers/batch.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Batch LPeg lexer. local lexer = require('lexer') @@ -50,4 +50,6 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+|&!<>='))) -- Fold points. lex:add_fold_point(lexer.KEYWORD, 'setlocal', 'endlocal') +lexer.property['scintillua.comment'] = 'REM ' + return lex diff --git a/lua/lexers/bibtex.lua b/lua/lexers/bibtex.lua index 06795e6..d9cf0d4 100644 --- a/lua/lexers/bibtex.lua +++ b/lua/lexers/bibtex.lua @@ -1,46 +1,48 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Bibtex LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('bibtex') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) +local lex = lexer.new(...) -- Fields. -lex:add_rule('field', token('field', word_match{ - 'author', 'title', 'journal', 'year', 'volume', 'number', 'pages', 'month', 'note', 'key', - 'publisher', 'editor', 'series', 'address', 'edition', 'howpublished', 'booktitle', - 'organization', 'chapter', 'school', 'institution', 'type', 'isbn', 'issn', 'affiliation', - 'issue', 'keyword', 'url' -})) -lex:add_style('field', lexer.styles.constant) +lex:add_rule('field', lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN, true))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local dq_str = lexer.range('"') local br_str = lexer.range('{', '}', false, false, true) -lex:add_rule('string', token(lexer.STRING, dq_str + br_str)) +lex:add_rule('string', lex:tag(lexer.STRING, dq_str + br_str)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S(',='))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S(',='))) -- Embedded in Latex. local latex = lexer.load('latex') -- Embedded Bibtex. -local entry = token('entry', '@' * word_match({ +local entry = lex:tag(lexer.PREPROCESSOR, '@' * lex:word_match('entry', true)) +local bibtex_start_rule = entry * lex:get_rule('whitespace')^0 * lex:tag(lexer.OPERATOR, '{') +local bibtex_end_rule = lex:tag(lexer.OPERATOR, '}') +latex:embed(lex, bibtex_start_rule, bibtex_end_rule) + +-- Word lists. +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'author', 'title', 'journal', 'year', 'volume', 'number', 'pages', 'month', 'note', 'key', + 'publisher', 'editor', 'series', 'address', 'edition', 'howpublished', 'booktitle', + 'organization', 'chapter', 'school', 'institution', 'type', 'isbn', 'issn', 'affiliation', + 'issue', 'keyword', 'url' +}) + +lex:set_word_list('entry', { + 'string', -- 'book', 'article', 'booklet', 'conference', 'inbook', 'incollection', 'inproceedings', 'manual', 'mastersthesis', 'lambda', 'misc', 'phdthesis', 'proceedings', 'techreport', 'unpublished' -}, true)) -lex:add_style('entry', lexer.styles.preprocessor) -local bibtex_start_rule = entry * ws^0 * token(lexer.OPERATOR, '{') -local bibtex_end_rule = token(lexer.OPERATOR, '}') -latex:embed(lex, bibtex_start_rule, bibtex_end_rule) +}) + +lexer.property['scintillua.comment'] = '%' return lex diff --git a/lua/lexers/boo.lua b/lua/lexers/boo.lua index 5b6a6e4..284bbf6 100644 --- a/lua/lexers/boo.lua +++ b/lua/lexers/boo.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Boo LPeg lexer. local lexer = require('lexer') @@ -46,7 +46,7 @@ local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) local tq_str = lexer.range('"""') local string = token(lexer.STRING, tq_str + sq_str + dq_str) -local regex_str = #P('/') * lexer.last_char_includes('!%^&*([{-=+|:;,?<>~') * lexer.range('/', true) +local regex_str = lexer.after_set('!%^&*([{-=+|:;,?<>~', lexer.range('/', true)) local regex = token(lexer.REGEX, regex_str) lex:add_rule('string', string + regex) @@ -61,4 +61,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number * (S('msdhsfFlL') + 'ms' -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('!%^&*()[]{}-=+/|:;.,?<>~`'))) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/caml.lua b/lua/lexers/caml.lua index fe70689..6fe0a10 100644 --- a/lua/lexers/caml.lua +++ b/lua/lexers/caml.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- OCaml LPeg lexer. local lexer = require('lexer') @@ -60,4 +60,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('=<>+-*/.,:;~!#%^&|?[](){}'))) +lexer.property['scintillua.comment'] = '(*|*)' + return lex diff --git a/lua/lexers/chuck.lua b/lua/lexers/chuck.lua index efd7a73..9c2433d 100644 --- a/lua/lexers/chuck.lua +++ b/lua/lexers/chuck.lua @@ -1,68 +1,102 @@ --- Copyright 2010-2022 Martin Morawetz. See LICENSE. +-- Copyright 2010-2024 Martin Morawetz. See LICENSE. -- ChucK LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local word_match = lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('chuck') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - -- Control structures. - 'break', 'continue', 'else', 'for', 'if', 'repeat', 'return', 'switch', 'until', 'while', - -- Other chuck keywords. - 'function', 'fun', 'spork', 'const', 'new' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match{ - -- Special values. - 'false', 'maybe', 'me', 'null', 'NULL', 'pi', 'true' -})) +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match('float int time dur void same'))) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) -- Classes. -lex:add_rule('class', token(lexer.CLASS, word_match{ - -- Class keywords. - 'class', 'extends', 'implements', 'interface', 'private', 'protected', 'public', 'pure', 'static', - 'super', 'this' -})) +lex:add_rule('class', lex:tag(lexer.CLASS, lex:word_match(lexer.CLASS))) + +-- Functions. +local std = 'Std.' * lex:word_match(lexer.FUNCTION_BUILTIN) +local machine = 'Machine.' * lex:word_match(lexer.FUNCTION_BUILTIN .. '.machine') +local math = 'Math.' * lex:word_match(lexer.FUNCTION_BUILTIN .. '.math') +local func = lex:tag(lexer.FUNCTION, lexer.word) * #P('(') +lex:add_rule('function', lex:tag(lexer.FUNCTION_BUILTIN, std + machine + math) + func) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, + 'Math.' * lex:word_match(lexer.CONSTANT_BUILTIN .. '.math'))) -- Global ugens. -lex:add_rule('ugen', token('ugen', word_match('dac adc blackhole'))) -lex:add_style('ugen', lexer.styles.constant) +lex:add_rule('ugen', lex:tag(lexer.CONSTANT_BUILTIN .. '.ugen', word_match('dac adc blackhole'))) -- Times. -lex:add_rule('time', token('time', word_match('samp ms second minute hour day week'))) -lex:add_style('time', lexer.styles.number) +lex:add_rule('time', lex:tag(lexer.NUMBER, word_match('samp ms second minute hour day week'))) -- Special special value. -lex:add_rule('now', token('now', 'now')) -lex:add_style('now', lexer.styles.constant .. {bold = true}) +lex:add_rule('now', lex:tag(lexer.CONSTANT_BUILTIN .. '.now', word_match('now'))) -- Strings. local sq_str = P('L')^-1 * lexer.range("'", true) local dq_str = P('L')^-1 * lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments. local line_comment = lexer.to_eol('//', true) local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}@'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}@'))) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + -- Control structures. + 'break', 'continue', 'else', 'for', 'if', 'repeat', 'return', 'switch', 'until', 'while', + -- Other chuck keywords. + 'function', 'fun', 'spork', 'const', 'new' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'false', 'maybe', 'me', 'null', 'NULL', 'pi', 'true' -- special values +}) + +lex:set_word_list(lexer.TYPE, 'float int time dur void same') + +-- Class keywords. +lex:set_word_list(lexer.CLASS, { + 'class', 'extends', 'implements', 'interface', 'private', 'protected', 'public', 'pure', 'static', + 'super', 'this' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'abs', 'fabs', 'sgn', 'system', 'atoi', 'atof', 'getenv', 'setenv', 'mtof', 'ftom', 'powtodb', + 'rmstodb', 'dbtopow', 'dbtorms' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN .. '.machine', { + 'add', 'spork', 'remove', 'replace', 'status', 'crash' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN .. '.math', { + 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'hypot', 'pow', + 'sqrt', 'exp', 'log', 'log2', 'log10', 'random', 'random2', 'randomf', 'random2f', 'srandom', + 'floor', 'ceil', 'round', 'trunc', 'fmod', 'remainder', 'min', 'max', 'nextpow2', 'isinf', 'isnan' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN .. '.math', { + 'PI', 'TWO_PI', 'e', 'E', 'i', 'I', 'j', 'J', 'RANDOM_MAX' +}) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/clojure.lua b/lua/lexers/clojure.lua index 270a058..d9eccbc 100644 --- a/lua/lexers/clojure.lua +++ b/lua/lexers/clojure.lua @@ -1,4 +1,4 @@ --- Copyright 2018-2022 Mitchell. See LICENSE. +-- Copyright 2018-2024 Mitchell. See LICENSE. -- Clojure LPeg lexer. -- Contributed by Christos Chatzifountas. @@ -139,9 +139,10 @@ lex:add_style('clojure_symbol', lexer.styles.type .. {bold = true}) -- Fold points. lex:add_fold_point(lexer.COMMENT, '#_(', ')') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines(';')) lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') +lexer.property['scintillua.comment'] = ';' + return lex diff --git a/lua/lexers/cmake.lua b/lua/lexers/cmake.lua index 8fcafeb..c16b4a0 100644 --- a/lua/lexers/cmake.lua +++ b/lua/lexers/cmake.lua @@ -1,132 +1,493 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- CMake LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('cmake') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) - --- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match( - 'IF ENDIF FOREACH ENDFOREACH WHILE ENDWHILE ELSE ELSEIF', true))) +local lex = lexer.new(..., {case_insensitive_fold_points = true}) -- Commands. -lex:add_rule('command', token(lexer.FUNCTION, word_match({ - 'ADD_CUSTOM_COMMAND', 'ADD_CUSTOM_TARGET', 'ADD_DEFINITIONS', 'ADD_DEPENDENCIES', - 'ADD_EXECUTABLE', 'ADD_LIBRARY', 'ADD_SUBDIRECTORY', 'ADD_TEST', 'AUX_SOURCE_DIRECTORY', - 'BUILD_COMMAND', 'BUILD_NAME', 'CMAKE_MINIMUM_REQUIRED', 'CONFIGURE_FILE', - 'CREATE_TEST_SOURCELIST', 'ENABLE_LANGUAGE', 'ENABLE_TESTING', 'ENDMACRO', 'EXEC_PROGRAM', - 'EXECUTE_PROCESS', 'EXPORT_LIBRARY_DEPENDENCIES', 'FILE', 'FIND_FILE', 'FIND_LIBRARY', - 'FIND_PACKAGE', 'FIND_PATH', 'FIND_PROGRAM', 'FLTK_WRAP_UI', 'GET_CMAKE_PROPERTY', - 'GET_DIRECTORY_PROPERTY', 'GET_FILENAME_COMPONENT', 'GET_SOURCE_FILE_PROPERTY', - 'GET_TARGET_PROPERTY', 'GET_TEST_PROPERTY', 'INCLUDE', 'INCLUDE_DIRECTORIES', - 'INCLUDE_EXTERNAL_MSPROJECT', 'INCLUDE_REGULAR_EXPRESSION', 'INSTALL', 'INSTALL_FILES', - 'INSTALL_PROGRAMS', 'INSTALL_TARGETS', 'LINK_DIRECTORIES', 'LINK_LIBRARIES', 'LIST', 'LOAD_CACHE', - 'LOAD_COMMAND', 'MACRO', 'MAKE_DIRECTORY', 'MARK_AS_ADVANCED', 'MATH', 'MESSAGE', 'OPTION', - 'OUTPUT_REQUIRED_FILES', 'PROJECT', 'QT_WRAP_CPP', 'QT_WRAP_UI', 'REMOVE', 'REMOVE_DEFINITIONS', - 'SEPARATE_ARGUMENTS', 'SET', 'SET_DIRECTORY_PROPERTIES', 'SET_SOURCE_FILES_PROPERTIES', - 'SET_TARGET_PROPERTIES', 'SET_TESTS_PROPERTIES', 'SITE_NAME', 'SOURCE_GROUP', 'STRING', - 'SUBDIR_DEPENDS', 'SUBDIRS', 'TARGET_LINK_LIBRARIES', 'TRY_COMPILE', 'TRY_RUN', - 'USE_MANGLED_MESA', 'UTILITY_SOURCE', 'VARIABLE_REQUIRES', 'VTK_MAKE_INSTANTIATOR', - 'VTK_WRAP_JAVA', 'VTK_WRAP_PYTHON', 'VTK_WRAP_TCL', 'WRITE_FILE' -}, true))) +local word = (lexer.alpha + S('_-')) * (lexer.alnum + S('_-'))^0 +local builtin_command = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match('command', true)) +local command = lex:tag(lexer.FUNCTION, word) +lex:add_rule('command', (builtin_command + command) * #P('(')) -- Constants. -lex:add_rule('constant', - token(lexer.CONSTANT, word_match('BOOL CACHE FALSE N NO ON OFF NOTFOUND TRUE', true))) +local constant = lex:word_match(lexer.CONSTANT_BUILTIN, true) +local module = lex:word_match('module') +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, constant + module)) -- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, word_match{ - 'APPLE', 'ARGS', 'BORLAND', 'CMAKE_AR', 'CMAKE_BACKWARDS_COMPATIBILITY', 'CMAKE_BASE_NAME', - 'CMAKE_BINARY_DIR', 'CMAKE_BUILD_TOOL', 'CMAKE_BUILD_TYPE', 'CMAKE_CACHEFILE_DIR', - 'CMAKE_CACHE_MAJOR_VERSION', 'CMAKE_CACHE_MINOR_VERSION', 'CMAKE_CACHE_RELEASE_VERSION', - 'CMAKE_C_COMPILE_OBJECT', 'CMAKE_C_COMPILER', 'CMAKE_C_COMPILER_ARG1', 'CMAKE_C_COMPILER_ENV_VAR', - 'CMAKE_C_COMPILER_FULLPATH', 'CMAKE_C_COMPILER_LOADED', 'CMAKE_C_COMPILER_WORKS', - 'CMAKE_C_CREATE_SHARED_LIBRARY', 'CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS', - 'CMAKE_C_CREATE_SHARED_MODULE', 'CMAKE_C_CREATE_STATIC_LIBRARY', 'CMAKE_CFG_INTDIR', - 'CMAKE_C_FLAGS', 'CMAKE_C_FLAGS_DEBUG', 'CMAKE_C_FLAGS_DEBUG_INIT', 'CMAKE_C_FLAGS_INIT', - 'CMAKE_C_FLAGS_MINSIZEREL', 'CMAKE_C_FLAGS_MINSIZEREL_INIT', 'CMAKE_C_FLAGS_RELEASE', - 'CMAKE_C_FLAGS_RELEASE_INIT', 'CMAKE_C_FLAGS_RELWITHDEBINFO', 'CMAKE_C_FLAGS_RELWITHDEBINFO_INIT', - 'CMAKE_C_IGNORE_EXTENSIONS', 'CMAKE_C_INFORMATION_LOADED', 'CMAKE_C_LINKER_PREFERENCE', - 'CMAKE_C_LINK_EXECUTABLE', 'CMAKE_C_LINK_FLAGS', 'CMAKE_COLOR_MAKEFILE', 'CMAKE_COMMAND', - 'CMAKE_COMPILER_IS_GNUCC', 'CMAKE_COMPILER_IS_GNUCC_RUN', 'CMAKE_COMPILER_IS_GNUCXX', - 'CMAKE_COMPILER_IS_GNUCXX_RUN', 'CMAKE_C_OUTPUT_EXTENSION', 'CMAKE_C_SOURCE_FILE_EXTENSIONS', - 'CMAKE_CTEST_COMMAND', 'CMAKE_CURRENT_BINARY_DIR', 'CMAKE_CURRENT_SOURCE_DIR', - 'CMAKE_CXX_COMPILE_OBJECT', 'CMAKE_CXX_COMPILER', 'CMAKE_CXX_COMPILER_ARG1', - 'CMAKE_CXX_COMPILER_ENV_VAR', 'CMAKE_CXX_COMPILER_FULLPATH', 'CMAKE_CXX_COMPILER_LOADED', - 'CMAKE_CXX_COMPILER_WORKS', 'CMAKE_CXX_CREATE_SHARED_LIBRARY', - 'CMAKE_CXX_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS', 'CMAKE_CXX_CREATE_SHARED_MODULE', - 'CMAKE_CXX_CREATE_STATIC_LIBRARY', 'CMAKE_CXX_FLAGS', 'CMAKE_CXX_FLAGS_DEBUG', - 'CMAKE_CXX_FLAGS_DEBUG_INIT', 'CMAKE_CXX_FLAGS_INIT', 'CMAKE_CXX_FLAGS_MINSIZEREL', - 'CMAKE_CXX_FLAGS_MINSIZEREL_INIT', 'CMAKE_CXX_FLAGS_RELEASE', 'CMAKE_CXX_FLAGS_RELEASE_INIT', - 'CMAKE_CXX_FLAGS_RELWITHDEBINFO', 'CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT', - 'CMAKE_CXX_IGNORE_EXTENSIONS', 'CMAKE_CXX_INFORMATION_LOADED', 'CMAKE_CXX_LINKER_PREFERENCE', - 'CMAKE_CXX_LINK_EXECUTABLE', 'CMAKE_CXX_LINK_FLAGS', 'CMAKE_CXX_OUTPUT_EXTENSION', - 'CMAKE_CXX_SOURCE_FILE_EXTENSIONS', 'CMAKE_DL_LIBS', 'CMAKE_EDIT_COMMAND', - 'CMAKE_EXECUTABLE_SUFFIX', 'CMAKE_EXE_LINKER_FLAGS', 'CMAKE_EXE_LINKER_FLAGS_DEBUG', - 'CMAKE_EXE_LINKER_FLAGS_MINSIZEREL', 'CMAKE_EXE_LINKER_FLAGS_RELEASE', - 'CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO', 'CMAKE_FILES_DIRECTORY', 'CMAKE_FIND_APPBUNDLE', - 'CMAKE_FIND_FRAMEWORK', 'CMAKE_FIND_LIBRARY_PREFIXES', 'CMAKE_FIND_LIBRARY_SUFFIXES', - 'CMAKE_GENERATOR', 'CMAKE_HOME_DIRECTORY', 'CMAKE_INCLUDE_FLAG_C', 'CMAKE_INCLUDE_FLAG_C_SEP', - 'CMAKE_INCLUDE_FLAG_CXX', 'CMAKE_INIT_VALUE', 'CMAKE_INSTALL_PREFIX', 'CMAKE_LIBRARY_PATH_FLAG', - 'CMAKE_LINK_LIBRARY_FLAG', 'CMAKE_LINK_LIBRARY_SUFFIX', 'CMAKE_MacOSX_Content_COMPILE_OBJECT', - 'CMAKE_MAJOR_VERSION', 'CMAKE_MAKE_PROGRAM', 'CMAKE_MINOR_VERSION', 'CMAKE_MODULE_EXISTS', - 'CMAKE_MODULE_LINKER_FLAGS', 'CMAKE_MODULE_LINKER_FLAGS_DEBUG', - 'CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL', 'CMAKE_MODULE_LINKER_FLAGS_RELEASE', - 'CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO', 'CMAKE_NUMBER_OF_LOCAL_GENERATORS', - 'CMAKE_OSX_ARCHITECTURES', '_CMAKE_OSX_MACHINE', 'CMAKE_OSX_SYSROOT', 'CMAKE_PARENT_LIST_FILE', - 'CMAKE_PATCH_VERSION', 'CMAKE_PLATFORM_HAS_INSTALLNAME', - 'CMAKE_PLATFORM_IMPLICIT_INCLUDE_DIRECTORIES', 'CMAKE_PLATFORM_ROOT_BIN', 'CMAKE_PROJECT_NAME', - 'CMAKE_RANLIB', 'CMAKE_ROOT', 'CMAKE_SHARED_LIBRARY_C_FLAGS', - 'CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS', 'CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS', - 'CMAKE_SHARED_LIBRARY_CXX_FLAGS', 'CMAKE_SHARED_LIBRARY_LINK_C_FLAGS', - 'CMAKE_SHARED_LIBRARY_PREFIX', 'CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG', - 'CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP', 'CMAKE_SHARED_LIBRARY_SONAME_C_FLAG', - 'CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG', 'CMAKE_SHARED_LIBRARY_SUFFIX', - 'CMAKE_SHARED_LINKER_FLAGS', 'CMAKE_SHARED_LINKER_FLAGS_DEBUG', - 'CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL', 'CMAKE_SHARED_LINKER_FLAGS_RELEASE', - 'CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO', 'CMAKE_SHARED_MODULE_CREATE_C_FLAGS', - 'CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS', 'CMAKE_SHARED_MODULE_PREFIX', - 'CMAKE_SHARED_MODULE_SUFFIX', 'CMAKE_SIZEOF_VOID_P', 'CMAKE_SKIP_RPATH', 'CMAKE_SOURCE_DIR', - 'CMAKE_STATIC_LIBRARY_PREFIX', 'CMAKE_STATIC_LIBRARY_SUFFIX', 'CMAKE_SYSTEM', - 'CMAKE_SYSTEM_AND_C_COMPILER_INFO_FILE', 'CMAKE_SYSTEM_AND_CXX_COMPILER_INFO_FILE', - 'CMAKE_SYSTEM_APPBUNDLE_PATH', 'CMAKE_SYSTEM_FRAMEWORK_PATH', 'CMAKE_SYSTEM_INCLUDE_PATH', - 'CMAKE_SYSTEM_INFO_FILE', 'CMAKE_SYSTEM_LIBRARY_PATH', 'CMAKE_SYSTEM_LOADED', 'CMAKE_SYSTEM_NAME', - 'CMAKE_SYSTEM_PROCESSOR', 'CMAKE_SYSTEM_PROGRAM_PATH', 'CMAKE_SYSTEM_SPECIFIC_INFORMATION_LOADED', - 'CMAKE_SYSTEM_VERSION', 'CMAKE_UNAME', 'CMAKE_USE_RELATIVE_PATHS', 'CMAKE_VERBOSE_MAKEFILE', - 'CYGWIN', 'EXECUTABLE_OUTPUT_PATH', 'FORCE', 'HAVE_CMAKE_SIZEOF_VOID_P', 'LIBRARY_OUTPUT_PATH', - 'LOCATION', 'MACOSX_BUNDLE', 'MINGW', 'MSVC', 'MSVC60', 'MSVC70', 'MSVC71', 'MSVC80', 'MSVC_IDE', - 'POST_BUILD', 'PRE_BUILD', 'PROJECT_BINARY_DIR', 'PROJECT_NAME', 'PROJECT_SOURCE_DIR', - 'RUN_CONFIGURE', 'TARGET', 'UNIX', 'WIN32' -} + P('$') * lexer.range('{', '}'))) +local builtin_var = lex:word_match(lexer.VARIABLE_BUILTIN) +local expansion_var = (P('CACHE') + 'ENV') * #P('{') +lex:add_rule('variable', lex:tag(lexer.VARIABLE_BUILTIN, builtin_var + expansion_var)) + +-- Generator expressions. +lex:add_rule('generator', + lex:tag(lexer.PREPROCESSOR, lpeg.B('$<') * lex:word_match(lexer.PREPROCESSOR))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, word_match{ - 'AND', 'COMMAND', 'DEFINED', 'DOC', 'EQUAL', 'EXISTS', 'GREATER', 'INTERNAL', 'LESS', 'MATCHES', - 'NAME', 'NAMES', 'NAME_WE', 'NOT', 'OR', 'PATH', 'PATHS', 'PROGRAM', 'STREQUAL', 'STRGREATER', - 'STRINGS', 'STRLESS' -} + S('=(){}'))) +lex:add_rule('operator', + lex:tag(lexer.OPERATOR, lex:word_match(lexer.OPERATOR, true) + S('=()${}<>'))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, word)) -- Strings. -lex:add_rule('string', token(lexer.STRING, lexer.range('"'))) +local bracket = lpeg.Cmt('[' * lpeg.C(P('=')^0) * '[', function(input, index, eq) + local _, e = input:find(']' .. eq .. ']', index, true) + return (e or #input) + 1 +end) +local quoted = lexer.range('"') +lex:add_rule('string', lex:tag(lexer.STRING, bracket + quoted)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +local line_comment = lexer.to_eol('#') +local bracket_comment = '#' * bracket +lex:add_rule('comment', lex:tag(lexer.COMMENT, bracket_comment + line_comment)) -- Fold points. -lex:add_fold_point(lexer.KEYWORD, 'IF', 'ENDIF') -lex:add_fold_point(lexer.KEYWORD, 'FOREACH', 'ENDFOREACH') -lex:add_fold_point(lexer.KEYWORD, 'WHILE', 'ENDWHILE') -lex:add_fold_point(lexer.FUNCTION, 'MACRO', 'ENDMACRO') +lex:add_fold_point(lexer.FUNCTION_BUILTIN, 'if', 'endif') +lex:add_fold_point(lexer.FUNCTION_BUILTIN, 'foreach', 'endforeach') +lex:add_fold_point(lexer.FUNCTION_BUILTIN, 'while', 'endwhile') +lex:add_fold_point(lexer.FUNCTION_BUILTIN, 'macro', 'endmacro') +lex:add_fold_point(lexer.FUNCTION_BUILTIN, 'function', 'endfunction') lex:add_fold_point(lexer.OPERATOR, '(', ')') -lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists. +lex:set_word_list('command', { + -- Scripting commands. + 'block', 'break', 'cmake_host_system_information', 'cmake_language', 'cmake_minimum_required', + 'cmake_parse_arguments', 'cmake_path', 'cmake_policy', 'configure_file', 'continue', 'else', + 'elseif', 'endblock', 'endforeach', 'endfunction', 'endif', 'endmacro', 'endwhile', + 'execute_process', 'file', 'find_file', 'find_library', 'find_package', 'find_path', + 'find_program', 'foreach', 'function', 'get_cmake_property', 'get_directory_property', + 'get_filename_component', 'get_property', 'if', 'include', 'include_guard', 'list', 'macro', + 'mark_as_advanced', 'math', 'message', 'option', 'return', 'separate_arguments', 'set', + 'set_directory_properties', 'set_property', 'site_name', 'string', 'unset', 'variable_watch', + 'while', + -- Project commands. + 'add_compile_definitions', 'add_compile_options', 'add_custom_command', 'add_custom_target', + 'add_definitions', 'add_dependencies', 'add_executable', 'add_library', 'add_link_options', + 'add_subdirectory', 'add_test', 'aux_source_directory', 'build_command', 'create_test_sourcelist', + 'define_property', 'enable_language', 'enable_testing', 'export', 'fltk_wrap_ui', + 'get_source_file_property', 'get_target_property', 'get_test_property', 'include_directories', + 'include_external_msproject', 'include_regular_expression', 'install', 'link_directories', + 'link_libraries', 'load_cache', 'project', 'remove_definitions', 'set_source_files_properties', + 'set_target_properties', 'set_tests_properties', 'source_group', 'target_compile_definitions', + 'target_compile_features', 'target_compile_options', 'target_include_directories', + 'target_link_directories', 'target_link_libraries', 'target_link_options', + 'target_precompile_headers', 'target_sources', 'try_compile', 'try_run', + -- CTest commands. + 'ctest_build', 'ctest_configure', 'ctest_coverage', 'ctest_empty_binary_directory', + 'ctest_memcheck', 'ctest_read_custom_files', 'ctest_run_script', 'ctest_sleep', 'ctest_start', + 'ctest_submit', 'ctest_test', 'ctest_update', 'ctest_upload' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, 'on yes true y off no false n ignore notfound') + +lex:set_word_list('module', { + 'AndroidTestUtilities', 'BundleUtilities', 'CheckCCompilerFlag', 'CheckCompilerFlag', + 'CheckCSourceCompiles', 'CheckCSourceRuns', 'CheckCXXCompilerFlag', 'CheckCXXSourceCompiles', + 'CheckCXXSourceRuns', 'CheckCXXSymbolExists', 'CheckFortranCompilerFlag', + 'CheckFortranFunctionExists', 'CheckFortranSourceCompiles', 'CheckFortranSourceRuns', + 'CheckFunctionExists', 'CheckIncludeFileCXX', 'CheckIncludeFile', 'CheckIncludeFiles', + 'CheckIPOSupported', 'CheckLanguage', 'CheckLibraryExists', 'CheckLinkerFlag', + 'CheckOBJCCompilerFlag', 'CheckOBJCSourceCompiles', 'CheckOBJCSourceRuns', + 'CheckOBJCXXCompilerFlag', 'CheckOBJCXXSourceCompiles', 'CheckOBJCXXSourceRuns', + 'CheckPIESupported', 'CheckPrototypeDefinition', 'CheckSourceCompiles', 'CheckSourceRuns', + 'CheckStructHasMember', 'CheckSymbolExists', 'CheckTypeSize', 'CheckVariableExists', + 'CMakeAddFortranSubdirectory', 'CMakeBackwardCompatibilityCXX', 'CMakeDependentOption', + 'CMakeFindDependencyMacro', 'CMakeFindFrameworks', 'CMakeFindPackageMode', 'CMakeGraphVizOptions', + 'CMakePackageConfigHelpers', 'CMakePrintHelpers', 'CMakePrintSystemInformation', + 'CMakePushCheckState', 'CMakeVerifyManifest', 'CPack', 'CPackComponent', 'CPackIFW', + 'CPackIFWConfigureFile', 'CSharpUtilities', 'CTest', 'CTestCoverageCollectGCOV', + 'CTestScriptMode', 'CTestUseLaunchers', 'Dart', 'DeployQt4', 'ExternalData', 'ExternalProject', + 'FeatureSummary', 'FetchContent', 'FindPackageHandleStandardArgs', 'FindPackageMessage', + 'FortranCInterface', 'GenerateExportHeader', 'GetPrerequisites', 'GNUInstallDirs', 'GoogleTest', + 'InstallRequiredSystemLibraries', 'ProcessorCount', 'SelectLibraryConfigurations', + 'SquishTestScript', 'TestBigEndian', 'TestForANSIForScope', 'TestForANSIStreamHeaders', + 'TestForSSTREAM', 'TestForSTDNamespace', 'UseEcos', 'UseJava', 'UseSWIG', 'UsewxWidgets' +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + -- Variables that provide information. + 'CMAKE_AR', 'CMAKE_ARGC', 'CMAKE_ARGV0', 'CMAKE_BINARY_DIR', 'CMAKE_BUILD_TOOL', + 'CMAKE_CACHE_MAJOR_VERSION', 'CMAKE_CACHE_MINOR_VERSION', 'CMAKE_CACHE_PATCH_VERSION', + 'CMAKE_CACHEFILE_DIR', 'CMAKE_CFG_INTDIR', 'CMAKE_COMMAND', 'CMAKE_CPACK_COMMAND', + 'CMAKE_CROSSCOMPILING', 'CMAKE_CROSSCOMPILING_EMULATOR', 'CMAKE_CTEST_COMMAND', + 'CMAKE_CURRENT_BINARY_DIR', 'CMAKE_CURRENT_FUNCTION', 'CMAKE_CURRENT_FUNCTION_LIST_DIR', + 'CMAKE_CURRENT_FUNCTION_LIST_FILE', 'CMAKE_CURRENT_FUNCTION_LIST_LINE', 'CMAKE_CURRENT_LIST_DIR', + 'CMAKE_CURRENT_LIST_FILE', 'CMAKE_CURRENT_LIST_LINE', 'CMAKE_CURRENT_SOURCE_DIR', + 'CMAKE_DEBUG_TARGET_PROPERTIES', 'CMAKE_DIRECTORY_LABELS', 'CMAKE_DL_LIBS', 'CMAKE_DOTNET_SDK', + 'CMAKE_DOTNET_TARGET_FRAMEWORK', 'CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION', 'CMAKE_EDIT_COMMAND', + 'CMAKE_EXECUTABLE_SUFFIX', + -- 'CMAKE_EXECUTABLE_SUFFIX_<LANG>', + 'CMAKE_EXTRA_GENERATOR', 'CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES', 'CMAKE_FIND_DEBUG_MODE', + 'CMAKE_FIND_PACKAGE_NAME', 'CMAKE_FIND_PACKAGE_REDIRECTS_DIR', + 'CMAKE_FIND_PACKAGE_SORT_DIRECTION', 'CMAKE_FIND_PACKAGE_SORT_ORDER', 'CMAKE_GENERATOR', + 'CMAKE_GENERATOR_INSTANCE', 'CMAKE_GENERATOR_PLATFORM', 'CMAKE_GENERATOR_TOOLSET', + 'CMAKE_IMPORT_LIBRARY_PREFIX', 'CMAKE_IMPORT_LIBRARY_SUFFIX', 'CMAKE_JOB_POOL_COMPILE', + 'CMAKE_JOB_POOL_LINK', 'CMAKE_JOB_POOL_PRECOMPILE_HEADER', 'CMAKE_JOB_POOLS', + -- 'CMAKE_<LANG>_COMPILER_AR', + -- 'CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT', + -- 'CMAKE_<LANG>_COMPILER_RANLIB', + -- 'CMAKE_<LANG>_LINK_LIBRARY_SUFFIX', + 'CMAKE_LINK_LIBRARY_SUFFIX', 'CMAKE_LINK_SEARCH_END_STATIC', 'CMAKE_LINK_SEARCH_START_STATIC', + 'CMAKE_MAJOR_VERSION', 'CMAKE_MAKE_PROGRAM', 'CMAKE_MATCH_COUNT', + -- 'CMAKE_MATCH_<n>', + 'CMAKE_MINIMUM_REQUIRED_VERSION', 'CMAKE_MINOR_VERSION', 'CMAKE_NETRC', 'CMAKE_NETRC_FILE', + 'CMAKE_PARENT_LIST_FILE', 'CMAKE_PATCH_VERSION', 'CMAKE_PROJECT_DESCRIPTION', + 'CMAKE_PROJECT_HOMEPAGE_URL', 'CMAKE_PROJECT_NAME', 'CMAKE_PROJECT_VERSION', + 'CMAKE_PROJECT_VERSION_MAJOR', 'CMAKE_PROJECT_VERSION_MINOR', 'CMAKE_PROJECT_VERSION_PATCH', + 'CMAKE_PROJECT_VERSION_TWEAK', 'CMAKE_RANLIB', 'CMAKE_ROOT', 'CMAKE_RULE_MESSAGES', + 'CMAKE_SCRIPT_MODE_FILE', 'CMAKE_SHARED_LIBRARY_PREFIX', 'CMAKE_SHARED_LIBRARY_SUFFIX', + 'CMAKE_SHARED_MODULE_PREFIX', 'CMAKE_SHARED_MODULE_SUFFIX', 'CMAKE_SIZEOF_VOID_P', + 'CMAKE_SKIP_INSTALL_RULES', 'CMAKE_SKIP_RPATH', 'CMAKE_SOURCE_DIR', 'CMAKE_STATIC_LIBRARY_PREFIX', + 'CMAKE_STATIC_LIBRARY_SUFFIX', 'CMAKE_Swift_MODULE_DIRECTORY', 'CMAKE_Swift_NUM_THREADS', + 'CMAKE_TOOLCHAIN_FILE', 'CMAKE_TWEAK_VERSION', 'CMAKE_VERBOSE_MAKEFILE', 'CMAKE_VERSION', + 'CMAKE_VS_DEVENV_COMMAND', 'CMAKE_VS_MSBUILD_COMMAND', 'CMAKE_VS_NsightTegra_VERSION', + 'CMAKE_VS_NUGET_PACKAGE_RESTORE', 'CMAKE_VS_PLATFORM_NAME', 'CMAKE_VS_PLATFORM_NAME_DEFAULT', + 'CMAKE_VS_PLATFORM_TOOLSET', 'CMAKE_VS_PLATFORM_TOOLSET_CUDA', + 'CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR', 'CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE', + 'CMAKE_VS_PLATFORM_TOOLSET_VERSION', 'CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER', + 'CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION', 'CMAKE_VS_TARGET_FRAMEWORK_VERSION', + 'CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION', 'CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM', + 'CMAKE_XCODE_BUILD_SYSTEM', 'CMAKE_XCODE_PLATFORM_TOOLSET', + -- '<PROJECT-NAME>_BINARY_DIR', + -- '<PROJECT-NAME>_DESCRIPTION', + -- '<PROJECT-NAME>_HOMEPAGE_URL', + -- '<PROJECT-NAME>_IS_TOP_LEVEL', + -- '<PROJECT-NAME>_SOURCE_DIR', + -- '<PROJECT-NAME>_VERSION', + -- '<PROJECT-NAME>_VERSION_MAJOR', + -- '<PROJECT-NAME>_VERSION_MINOR', + -- '<PROJECT-NAME>_VERSION_PATCH', + -- '<PROJECT-NAME>_VERSION_TWEAK', + 'PROJECT_BINARY_DIR', 'PROJECT_DESCRIPTION', 'PROJECT_HOMEPAGE_URL', 'PROJECT_IS_TOP_LEVEL', + 'PROJECT_NAME', 'PROJECT_SOURCE_DIR', 'PROJECT_VERSION', 'PROJECT_VERSION_MAJOR', + 'PROJECT_VERSION_MINOR', 'PROJECT_VERSION_PATCH', 'PROJECT_VERSION_TWEAK', + + -- Variables that change behavior. + 'BUILD_SHARED_LIBS', 'CMAKE_ABSOLUTE_DESTINATION_FILES', 'CMAKE_APPBUNDLE_PATH', + 'CMAKE_AUTOMOC_RELAXED_MODE', 'CMAKE_BACKWARDS_COMPATIBILITY', 'CMAKE_BUILD_TYPE', + 'CMAKE_CLANG_VFS_OVERLAY', 'CMAKE_CODEBLOCKS_COMPILER_ID', + 'CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES', 'CMAKE_CODELITE_USE_TARGETS', + 'CMAKE_COLOR_DIAGNOSTICS', 'CMAKE_COLOR_MAKEFILE', 'CMAKE_CONFIGURATION_TYPES', + 'CMAKE_DEPENDS_IN_PROJECT_ONLY', + -- 'CMAKE_DISABLE_FIND_PACKAGE_<PackageName>', + 'CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES', 'CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT', + 'CMAKE_ECLIPSE_MAKE_ARGUMENTS', 'CMAKE_ECLIPSE_RESOURCE_ENCODING', 'CMAKE_ECLIPSE_VERSION', + 'CMAKE_ERROR_DEPRECATED', 'CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION', + 'CMAKE_EXECUTE_PROCESS_COMMAND_ECHO', 'CMAKE_EXPORT_COMPILE_COMMANDS', + 'CMAKE_EXPORT_PACKAGE_REGISTRY', 'CMAKE_EXPORT_NO_PACKAGE_REGISTRY', 'CMAKE_FIND_APPBUNDLE', + 'CMAKE_FIND_FRAMEWORK', 'CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX', 'CMAKE_FIND_LIBRARY_PREFIXES', + 'CMAKE_FIND_LIBRARY_SUFFIXES', 'CMAKE_FIND_NO_INSTALL_PREFIX', + 'CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY', 'CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY', + 'CMAKE_FIND_PACKAGE_PREFER_CONFIG', 'CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS', + 'CMAKE_FIND_PACKAGE_TARGETS_GLOBAL', 'CMAKE_FIND_PACKAGE_WARN_NO_MODULE', 'CMAKE_FIND_ROOT_PATH', + 'CMAKE_FIND_ROOT_PATH_MODE_INCLUDE', 'CMAKE_FIND_ROOT_PATH_MODE_LIBRARY', + 'CMAKE_FIND_ROOT_PATH_MODE_PACKAGE', 'CMAKE_FIND_ROOT_PATH_MODE_PROGRAM', + 'CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH', 'CMAKE_FIND_USE_CMAKE_PATH', + 'CMAKE_FIND_USE_CMAKE_SYSTEM_PATH', 'CMAKE_FIND_USE_INSTALL_PREFIX', + 'CMAKE_FIND_USE_PACKAGE_REGISTRY', 'CMAKE_FIND_USE_PACKAGE_ROOT_PATH', + 'CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH', 'CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY', + 'CMAKE_FRAMEWORK_PATH', 'CMAKE_IGNORE_PATH', 'CMAKE_IGNORE_PREFIX_PATH', + 'CMAKE_INCLUDE_DIRECTORIES_BEFORE', 'CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE', + 'CMAKE_INCLUDE_PATH', 'CMAKE_INSTALL_DEFAULT_COMPONENT_NAME', + 'CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS', 'CMAKE_INSTALL_MESSAGE', 'CMAKE_INSTALL_PREFIX', + 'CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT', 'CMAKE_LIBRARY_PATH', + 'CMAKE_LINK_DIRECTORIES_BEFORE', 'CMAKE_LINK_LIBRARIES_ONLY_TARGETS', + 'CMAKE_MAXIMUM_RECURSION_DEPTH', 'CMAKE_MESSAGE_CONTEXT', 'CMAKE_MESSAGE_CONTEXT_SHOW', + 'CMAKE_MESSAGE_INDENT', 'CMAKE_MESSAGE_LOG_LEVEL', 'CMAKE_MFC_FLAG', 'CMAKE_MODULE_PATH', + -- 'CMAKE_POLICY_DEFAULT_CMP<NNNN>', + -- 'CMAKE_POLICY_WARNING_CMP<NNNN>', + 'CMAKE_PREFIX_PATH', 'CMAKE_PROGRAM_PATH', 'CMAKE_PROJECT_INCLUDE', + 'CMAKE_PROJECT_INCLUDE_BEFORE', + -- 'CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE', + -- 'CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE', + 'CMAKE_PROJECT_TOP_LEVEL_INCLUDES', + -- 'CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>', + 'CMAKE_SKIP_INSTALL_ALL_DEPENDENCY', 'CMAKE_STAGING_PREFIX', 'CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS', + 'CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE', 'CMAKE_SUPPRESS_REGENERATION', 'CMAKE_SYSROOT', + 'CMAKE_SYSROOT_COMPILE', 'CMAKE_SYSROOT_LINK', 'CMAKE_SYSTEM_APPBUNDLE_PATH', + 'CMAKE_SYSTEM_FRAMEWORK_PATH', 'CMAKE_SYSTEM_IGNORE_PATH', 'CMAKE_SYSTEM_IGNORE_PREFIX_PATH', + 'CMAKE_SYSTEM_INCLUDE_PATH', 'CMAKE_SYSTEM_LIBRARY_PATH', 'CMAKE_SYSTEM_PREFIX_PATH', + 'CMAKE_SYSTEM_PROGRAM_PATH', 'CMAKE_TLS_CAINFO', 'CMAKE_TLS_VERIFY', + 'CMAKE_USER_MAKE_RULES_OVERRIDE', 'CMAKE_WARN_DEPRECATED', + 'CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION', 'CMAKE_XCODE_GENERATE_SCHEME', + 'CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY', 'CMAKE_XCODE_LINK_BUILD_PHASE_MODE', + 'CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER', 'CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN', + 'CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING', 'CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER', + 'CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS', 'CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE', + 'CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION', + 'CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE', + 'CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION', 'CMAKE_XCODE_SCHEME_ENVIRONMENT', + 'CMAKE_XCODE_SCHEME_GUARD_MALLOC', 'CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION', + 'CMAKE_XCODE_SCHEME_LAUNCH_MODE', 'CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP', + 'CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES', 'CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE', + 'CMAKE_XCODE_SCHEME_MALLOC_STACK', 'CMAKE_XCODE_SCHEME_THREAD_SANITIZER', + 'CMAKE_XCODE_SCHEME_THREAD_SANITIZER_STOP', 'CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER', + 'CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP', 'CMAKE_XCODE_SCHEME_WORKING_DIRECTORY', + 'CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS', 'CMAKE_XCODE_XCCONFIG', + -- '<PackageName>_ROOT', + + -- Variables that describe the system. + 'ANDROID', 'APPLE', 'BORLAND', 'BSD', 'CMAKE_ANDROID_NDK_VERSION', 'CMAKE_CL_64', + 'CMAKE_COMPILER_2005', 'CMAKE_HOST_APPLE', 'CMAKE_HOST_BSD', 'CMAKE_HOST_LINUX', + 'CMAKE_HOST_SOLARIS', 'CMAKE_HOST_SYSTEM', 'CMAKE_HOST_SYSTEM_NAME', + 'CMAKE_HOST_SYSTEM_PROCESSOR', 'CMAKE_HOST_SYSTEM_VERSION', 'CMAKE_HOST_UNIX', 'CMAKE_HOST_WIN32', + 'CMAKE_LIBRARY_ARCHITECTURE', 'CMAKE_LIBRARY_ARCHITECTURE_REGEX', 'CMAKE_OBJECT_PATH_MAX', + 'CMAKE_SYSTEM', 'CMAKE_SYSTEM_NAME', 'CMAKE_SYSTEM_PROCESSOR', 'CMAKE_SYSTEM_VERSION', 'CYGWIN', + 'GHSMULTI', 'IOS', 'LINUX', 'MINGW', 'MSVC', 'MSVC10', 'MSVC11', 'MSVC12', 'MSVC14', 'MSVC60', + 'MSVC70', 'MSVC71', 'MSVC80', 'MSVC90', 'MSVC_IDE', 'MSVC_TOOLSET_VERSION', 'MSVC_VERSION', + 'MSYS', 'UNIX', 'WIN32', 'WINCE', 'WINDOWS_PHONE', 'WINDOWS_STORE', 'XCODE', 'XCODE_VERSION', + + -- Variables that control the build. + 'CMAKE_ADSP_ROOT', 'CMAKE_AIX_EXPORT_ALL_SYMBOLS', 'CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS', + 'CMAKE_ANDROID_API', 'CMAKE_ANDROID_API_MIN', 'CMAKE_ANDROID_ARCH', 'CMAKE_ANDROID_ARCH_ABI', + 'CMAKE_ANDROID_ARM_MODE', 'CMAKE_ANDROID_ARM_NEON', 'CMAKE_ANDROID_ASSETS_DIRECTORIES', + 'CMAKE_ANDROID_EXCEPTIONS', 'CMAKE_ANDROID_GUI', 'CMAKE_ANDROID_JAR_DEPENDENCIES', + 'CMAKE_ANDROID_JAR_DIRECTORIES', 'CMAKE_ANDROID_JAVA_SOURCE_DIR', + 'CMAKE_ANDROID_NATIVE_LIB_DEPENDENCIES', 'CMAKE_ANDROID_NATIVE_LIB_DIRECTORIES', + 'CMAKE_ANDROID_NDK', 'CMAKE_ANDROID_NDK_DEPRECATED_HEADERS', + 'CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG', 'CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION', + 'CMAKE_ANDROID_PROCESS_MAX', 'CMAKE_ANDROID_PROGUARD', 'CMAKE_ANDROID_PROGUARD_CONFIG_PATH', + 'CMAKE_ANDROID_RTTI', 'CMAKE_ANDROID_SECURE_PROPS_PATH', 'CMAKE_ANDROID_SKIP_ANT_STEP', + 'CMAKE_ANDROID_STANDALONE_TOOLCHAIN', 'CMAKE_ANDROID_STL_TYPE', 'CMAKE_APPLE_SILICON_PROCESSOR', + 'CMAKE_ARCHIVE_OUTPUT_DIRECTORY', + -- 'CMAKE_ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>', + 'CMAKE_AUTOGEN_ORIGIN_DEPENDS', 'CMAKE_AUTOGEN_PARALLEL', 'CMAKE_AUTOGEN_VERBOSE', + 'CMAKE_AUTOMOC', 'CMAKE_AUTOMOC_COMPILER_PREDEFINES', 'CMAKE_AUTOMOC_DEPEND_FILTERS', + 'CMAKE_AUTOMOC_MACRO_NAMES', 'CMAKE_AUTOMOC_MOC_OPTIONS', 'CMAKE_AUTOMOC_PATH_PREFIX', + 'CMAKE_AUTORCC', 'CMAKE_AUTORCC_OPTIONS', 'CMAKE_AUTOUIC', 'CMAKE_AUTOUIC_OPTIONS', + 'CMAKE_AUTOUIC_SEARCH_PATHS', 'CMAKE_BUILD_RPATH', 'CMAKE_BUILD_RPATH_USE_ORIGIN', + 'CMAKE_BUILD_WITH_INSTALL_NAME_DIR', 'CMAKE_BUILD_WITH_INSTALL_RPATH', + 'CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY', + -- 'CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG>', + 'CMAKE_COMPILE_WARNING_AS_ERROR', + -- 'CMAKE_<CONFIG>_POSTFIX', + 'CMAKE_CROSS_CONFIGS', 'CMAKE_CTEST_ARGUMENTS', 'CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS', + 'CMAKE_CUDA_RUNTIME_LIBRARY', 'CMAKE_CUDA_SEPARABLE_COMPILATION', 'CMAKE_DEBUG_POSTFIX', + 'CMAKE_DEFAULT_BUILD_TYPE', 'CMAKE_DEFAULT_CONFIGS', 'CMAKE_DEPENDS_USE_COMPILER', + 'CMAKE_DISABLE_PRECOMPILE_HEADERS', 'CMAKE_ENABLE_EXPORTS', 'CMAKE_EXE_LINKER_FLAGS', + -- 'CMAKE_EXE_LINKER_FLAGS_<CONFIG>', + -- 'CMAKE_EXE_LINKER_FLAGS_<CONFIG>_INIT', + 'CMAKE_EXE_LINKER_FLAGS_INIT', 'CMAKE_FOLDER', 'CMAKE_Fortran_FORMAT', + 'CMAKE_Fortran_MODULE_DIRECTORY', 'CMAKE_Fortran_PREPROCESS', 'CMAKE_FRAMEWORK', + -- 'CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>', + 'CMAKE_GHS_NO_SOURCE_GROUP_FILE', 'CMAKE_GLOBAL_AUTOGEN_TARGET', + 'CMAKE_GLOBAL_AUTOGEN_TARGET_NAME', 'CMAKE_GLOBAL_AUTORCC_TARGET', + 'CMAKE_GLOBAL_AUTORCC_TARGET_NAME', 'CMAKE_GNUtoMS', 'CMAKE_INCLUDE_CURRENT_DIR', + 'CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE', 'CMAKE_INSTALL_NAME_DIR', + 'CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH', 'CMAKE_INSTALL_RPATH', + 'CMAKE_INSTALL_RPATH_USE_LINK_PATH', 'CMAKE_INTERPROCEDURAL_OPTIMIZATION', + -- 'CMAKE_INTERPROCEDURAL_OPTIMIZATION_<CONFIG>', + 'CMAKE_IOS_INSTALL_COMBINED', + -- 'CMAKE_<LANG>_CLANG_TIDY', + -- 'CMAKE_<LANG>_COMPILER_LAUNCHER', + -- 'CMAKE_<LANG>_CPPCHECK', + -- 'CMAKE_<LANG>_CPPLINT', + -- 'CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE', + -- 'CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>', + -- 'CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED', + -- 'CMAKE_<LANG>_LINK_LIBRARY_FILE_FLAG', + -- 'CMAKE_<LANG>_LINK_LIBRARY_FLAG', + -- 'CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>', + -- 'CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED', + -- 'CMAKE_<LANG>_LINK_WHAT_YOU_USE_FLAG', + -- 'CMAKE_<LANG>_LINKER_LAUNCHER', + -- 'CMAKE_<LANG>_VISIBILITY_PRESET', + 'CMAKE_LIBRARY_OUTPUT_DIRECTORY', + -- 'CMAKE_LIBRARY_OUTPUT_DIRECTORY_<CONFIG>', + 'CMAKE_LIBRARY_PATH_FLAG', 'CMAKE_LINK_DEF_FILE_FLAG', 'CMAKE_LINK_DEPENDS_NO_SHARED', + -- 'CMAKE_LINK_GROUP_USING_<FEATURE>', + -- 'CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED', + 'CMAKE_LINK_INTERFACE_LIBRARIES', 'CMAKE_LINK_LIBRARY_FILE_FLAG', 'CMAKE_LINK_LIBRARY_FLAG', + -- 'CMAKE_LINK_LIBRARY_USING_<FEATURE>', + -- 'CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED', + 'CMAKE_LINK_WHAT_YOU_USE', 'CMAKE_LINK_WHAT_YOU_USE_CHECK', 'CMAKE_MACOSX_BUNDLE', + 'CMAKE_MACOSX_RPATH', + -- 'CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>', + 'CMAKE_MODULE_LINKER_FLAGS', + -- 'CMAKE_MODULE_LINKER_FLAGS_<CONFIG>', + -- 'CMAKE_MODULE_LINKER_FLAGS_<CONFIG>_INIT', + 'CMAKE_MODULE_LINKER_FLAGS_INIT', 'CMAKE_MSVC_DEBUG_INFORMATION_FORMAT', + 'CMAKE_MSVC_RUNTIME_LIBRARY', 'CMAKE_MSVCIDE_RUN_PATH', 'CMAKE_NINJA_OUTPUT_PATH_PREFIX', + 'CMAKE_NO_BUILTIN_CHRPATH', 'CMAKE_NO_SYSTEM_FROM_IMPORTED', 'CMAKE_OPTIMIZE_DEPENDENCIES', + 'CMAKE_OSX_ARCHITECTURES', 'CMAKE_OSX_DEPLOYMENT_TARGET', 'CMAKE_OSX_SYSROOT', + 'CMAKE_PCH_INSTANTIATE_TEMPLATES', 'CMAKE_PCH_WARN_INVALID', 'CMAKE_PDB_OUTPUT_DIRECTORY', + -- 'CMAKE_PDB_OUTPUT_DIRECTORY_<CONFIG>', + 'CMAKE_PLATFORM_NO_VERSIONED_SONAME', 'CMAKE_POSITION_INDEPENDENT_CODE', + 'CMAKE_RUNTIME_OUTPUT_DIRECTORY', + -- 'CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG>', + 'CMAKE_SHARED_LINKER_FLAGS', + -- 'CMAKE_SHARED_LINKER_FLAGS_<CONFIG>', + -- 'CMAKE_SHARED_LINKER_FLAGS_<CONFIG>_INIT', + 'CMAKE_SHARED_LINKER_FLAGS_INIT', 'CMAKE_SKIP_BUILD_RPATH', 'CMAKE_SKIP_INSTALL_RPATH', + 'CMAKE_STATIC_LINKER_FLAGS', + -- 'CMAKE_STATIC_LINKER_FLAGS_<CONFIG>', + -- 'CMAKE_STATIC_LINKER_FLAGS_<CONFIG>_INIT', + 'CMAKE_STATIC_LINKER_FLAGS_INIT', 'CMAKE_TASKING_TOOLSET', 'CMAKE_TRY_COMPILE_CONFIGURATION', + 'CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES', 'CMAKE_TRY_COMPILE_PLATFORM_VARIABLES', + 'CMAKE_TRY_COMPILE_TARGET_TYPE', 'CMAKE_UNITY_BUILD', 'CMAKE_UNITY_BUILD_BATCH_SIZE', + 'CMAKE_UNITY_BUILD_UNIQUE_ID', 'CMAKE_USE_RELATIVE_PATHS', 'CMAKE_VERIFY_INTERFACE_HEADER_SETS', + 'CMAKE_VISIBILITY_INLINES_HIDDEN', 'CMAKE_VS_GLOBALS', + 'CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD', 'CMAKE_VS_INCLUDE_PACKAGE_TO_DEFAULT_BUILD', + 'CMAKE_VS_JUST_MY_CODE_DEBUGGING', 'CMAKE_VS_NO_COMPILE_BATCHING', + 'CMAKE_VS_SDK_EXCLUDE_DIRECTORIES', 'CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES', + 'CMAKE_VS_SDK_INCLUDE_DIRECTORIES', 'CMAKE_VS_SDK_LIBRARY_DIRECTORIES', + 'CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES', 'CMAKE_VS_SDK_REFERENCE_DIRECTORIES', + 'CMAKE_VS_SDK_SOURCE_DIRECTORIES', 'CMAKE_VS_WINRT_BY_DEFAULT', 'CMAKE_WATCOM_RUNTIME_LIBRARY', + 'CMAKE_WIN32_EXECUTABLE', 'CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS', + -- 'CMAKE_XCODE_ATTRIBUTE_<an-attribute>', + 'EXECUTABLE_OUTPUT_PATH', 'LIBRARY_OUTPUT_PATH', + + -- Variables for languages. + 'CMAKE_C_COMPILE_FEATURES', 'CMAKE_C_EXTENSIONS', 'CMAKE_C_STANDARD', 'CMAKE_C_STANDARD_REQUIRED', + 'CMAKE_COMPILER_IS_GNUCC', 'CMAKE_COMPILER_IS_GNUCXX', 'CMAKE_COMPILER_IS_GNUG77', + 'CMAKE_CUDA_ARCHITECTURES', 'CMAKE_CUDA_COMPILE_FEATURES', 'CMAKE_CUDA_EXTENSIONS', + 'CMAKE_CUDA_HOST_COMPILER', 'CMAKE_CUDA_STANDARD', 'CMAKE_CUDA_STANDARD_REQUIRED', + 'CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES', 'CMAKE_CXX_COMPILE_FEATURES', 'CMAKE_CXX_EXTENSIONS', + 'CMAKE_CXX_STANDARD', 'CMAKE_CXX_STANDARD_REQUIRED', 'CMAKE_Fortran_MODDIR_DEFAULT', + 'CMAKE_Fortran_MODDIR_FLAG', 'CMAKE_Fortran_MODOUT_FLAG', 'CMAKE_HIP_ARCHITECTURES', + 'CMAKE_HIP_EXTENSIONS', 'CMAKE_HIP_STANDARD', 'CMAKE_HIP_STANDARD_REQUIRED', + 'CMAKE_ISPC_HEADER_DIRECTORY', 'CMAKE_ISPC_HEADER_SUFFIX', 'CMAKE_ISPC_INSTRUCTION_SETS', + -- 'CMAKE_<LANG>_ANDROID_TOOLCHAIN_MACHINE', + -- 'CMAKE_<LANG>_ANDROID_TOOLCHAIN_PREFIX', + -- 'CMAKE_<LANG>_ANDROID_TOOLCHAIN_SUFFIX', + -- 'CMAKE_<LANG>_ARCHIVE_APPEND', + -- 'CMAKE_<LANG>_ARCHIVE_CREATE', + -- 'CMAKE_<LANG>_ARCHIVE_FINISH', + -- 'CMAKE_<LANG>_BYTE_ORDER', + -- 'CMAKE_<LANG>_COMPILE_OBJECT', + -- 'CMAKE_<LANG>_COMPILER', + -- 'CMAKE_<LANG>_COMPILER_EXTERNAL_TOOLCHAIN', + -- 'CMAKE_<LANG>_COMPILER_ID', + -- 'CMAKE_<LANG>_COMPILER_LOADED', + -- 'CMAKE_<LANG>_COMPILER_PREDEFINES_COMMAND', + -- 'CMAKE_<LANG>_COMPILER_TARGET', + -- 'CMAKE_<LANG>_COMPILER_VERSION', + -- 'CMAKE_<LANG>_CREATE_SHARED_LIBRARY', + -- 'CMAKE_<LANG>_CREATE_SHARED_MODULE', + -- 'CMAKE_<LANG>_CREATE_STATIC_LIBRARY', + -- 'CMAKE_<LANG>_EXTENSIONS', + -- 'CMAKE_<LANG>_EXTENSIONS_DEFAULT', + -- 'CMAKE_<LANG>_FLAGS', + -- 'CMAKE_<LANG>_FLAGS_<CONFIG>', + -- 'CMAKE_<LANG>_FLAGS_<CONFIG>_INIT', + -- 'CMAKE_<LANG>_FLAGS_DEBUG', + -- 'CMAKE_<LANG>_FLAGS_DEBUG_INIT', + -- 'CMAKE_<LANG>_FLAGS_INIT', + -- 'CMAKE_<LANG>_FLAGS_MINSIZEREL', + -- 'CMAKE_<LANG>_FLAGS_MINSIZEREL_INIT', + -- 'CMAKE_<LANG>_FLAGS_RELEASE', + -- 'CMAKE_<LANG>_FLAGS_RELEASE_INIT', + -- 'CMAKE_<LANG>_FLAGS_RELWITHDEBINFO', + -- 'CMAKE_<LANG>_FLAGS_RELWITHDEBINFO_INIT', + -- 'CMAKE_<LANG>_IGNORE_EXTENSIONS', + -- 'CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES', + -- 'CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES', + -- 'CMAKE_<LANG>_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES', + -- 'CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES', + -- 'CMAKE_<LANG>_LIBRARY_ARCHITECTURE', + -- 'CMAKE_<LANG>_LINK_EXECUTABLE', + -- 'CMAKE_<LANG>_LINKER_WRAPPER_FLAG', + -- 'CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP', + -- 'CMAKE_<LANG>_OUTPUT_EXTENSION', + -- 'CMAKE_<LANG>_SIMULATE_ID', + -- 'CMAKE_<LANG>_SIMULATE_VERSION', + -- 'CMAKE_<LANG>_SIZEOF_DATA_PTR', + -- 'CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS', + -- 'CMAKE_<LANG>_STANDARD', + -- 'CMAKE_<LANG>_STANDARD_DEFAULT', + -- 'CMAKE_<LANG>_STANDARD_INCLUDE_DIRECTORIES', + -- 'CMAKE_<LANG>_STANDARD_LIBRARIES', + -- 'CMAKE_<LANG>_STANDARD_REQUIRED', + 'CMAKE_OBJC_EXTENSIONS', 'CMAKE_OBJC_STANDARD', 'CMAKE_OBJC_STANDARD_REQUIRED', + 'CMAKE_OBJCXX_EXTENSIONS', 'CMAKE_OBJCXX_STANDARD', 'CMAKE_OBJCXX_STANDARD_REQUIRED', + 'CMAKE_Swift_LANGUAGE_VERSION', + -- 'CMAKE_USER_MAKE_RULES_OVERRIDE_<LANG>', + + -- Variables for CTest. + 'CTEST_BINARY_DIRECTORY', 'CTEST_BUILD_COMMAND', 'CTEST_BUILD_NAME', 'CTEST_BZR_COMMAND', + 'CTEST_BZR_UPDATE_OPTIONS', 'CTEST_CHANGE_ID', 'CTEST_CHECKOUT_COMMAND', + 'CTEST_CONFIGURATION_TYPE', 'CTEST_CONFIGURE_COMMAND', 'CTEST_COVERAGE_COMMAND', + 'CTEST_COVERAGE_EXTRA_FLAGS', 'CTEST_CURL_OPTIONS', 'CTEST_CUSTOM_COVERAGE_EXCLUDE', + 'CTEST_CUSTOM_ERROR_EXCEPTION', 'CTEST_CUSTOM_ERROR_MATCH', 'CTEST_CUSTOM_ERROR_POST_CONTEXT', + 'CTEST_CUSTOM_ERROR_PRE_CONTEXT', 'CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE', + 'CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS', 'CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS', + 'CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE', 'CTEST_CUSTOM_MEMCHECK_IGNORE', + 'CTEST_CUSTOM_POST_MEMCHECK', 'CTEST_CUSTOM_POST_TEST', 'CTEST_CUSTOM_PRE_MEMCHECK', + 'CTEST_CUSTOM_PRE_TEST', 'CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION', 'CTEST_CUSTOM_TESTS_IGNORE', + 'CTEST_CUSTOM_WARNING_EXCEPTION', 'CTEST_CUSTOM_WARNING_MATCH', 'CTEST_CVS_CHECKOUT', + 'CTEST_CVS_COMMAND', 'CTEST_CVS_UPDATE_OPTIONS', 'CTEST_DROP_LOCATION', 'CTEST_DROP_METHOD', + 'CTEST_DROP_SITE', 'CTEST_DROP_SITE_CDASH', 'CTEST_DROP_SITE_PASSWORD', 'CTEST_DROP_SITE_USER', + 'CTEST_EXTRA_COVERAGE_GLOB', 'CTEST_GIT_COMMAND', 'CTEST_GIT_INIT_SUBMODULES', + 'CTEST_GIT_UPDATE_CUSTOM', 'CTEST_GIT_UPDATE_OPTIONS', 'CTEST_HG_COMMAND', + 'CTEST_HG_UPDATE_OPTIONS', 'CTEST_LABELS_FOR_SUBPROJECTS', 'CTEST_MEMORYCHECK_COMMAND', + 'CTEST_MEMORYCHECK_COMMAND_OPTIONS', 'CTEST_MEMORYCHECK_SANITIZER_OPTIONS', + 'CTEST_MEMORYCHECK_SUPPRESSIONS_FILE', 'CTEST_MEMORYCHECK_TYPE', 'CTEST_NIGHTLY_START_TIME', + 'CTEST_P4_CLIENT', 'CTEST_P4_COMMAND', 'CTEST_P4_OPTIONS', 'CTEST_P4_UPDATE_OPTIONS', + 'CTEST_RESOURCE_SPEC_FILE', 'CTEST_RUN_CURRENT_SCRIPT', 'CTEST_SCP_COMMAND', + 'CTEST_SCRIPT_DIRECTORY', 'CTEST_SITE', 'CTEST_SOURCE_DIRECTORY', + 'CTEST_SUBMIT_INACTIVITY_TIMEOUT', 'CTEST_SUBMIT_URL', 'CTEST_SVN_COMMAND', 'CTEST_SVN_OPTIONS', + 'CTEST_SVN_UPDATE_OPTIONS', 'CTEST_TEST_LOAD', 'CTEST_TEST_TIMEOUT', 'CTEST_TRIGGER_SITE', + 'CTEST_UPDATE_COMMAND', 'CTEST_UPDATE_OPTIONS', 'CTEST_UPDATE_VERSION_ONLY', + 'CTEST_UPDATE_VERSION_OVERRIDE', 'CTEST_USE_LAUNCHERS', + + -- Variables for CPack. + 'CPACK_ABSOLUTE_DESTINATION_FILES', 'CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY', + 'CPACK_CUSTOM_INSTALL_VARIABLES', 'CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION', + 'CPACK_INCLUDE_TOPLEVEL_DIRECTORY', 'CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS', + 'CPACK_PACKAGING_INSTALL_PREFIX', 'CPACK_SET_DESTDIR', + 'CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION' +}) + +lex:set_word_list(lexer.PREPROCESSOR, { + 'IF', 'BOOL', -- conditional + 'AND', 'OR', 'NOT', -- logical + 'STREQUAL', 'EQUAL', -- string comparison + 'VERSION_LESS', 'VERSION_GREATER', 'VERSION_EQUAL', 'VERSION_LESS_EQUAL', 'VERSION_GREATER_EQUAL', -- version comparison + 'LOWER_CASE', 'UPPER_CASE', 'MAKE_C_IDENTIFIER', -- string transformations + 'IN_LIST', 'JOIN', 'REMOVE_DUPLICATES', 'FILTER', -- list expressions + 'PATH_EQUAL', 'PATH', -- path expressions (note: cannot use ':' parts) + 'SHELL_PATH', -- shell paths + 'CONFIG', 'OUTPUT_CONFIG', 'COMMAND_CONFIG', -- configuration expressions + 'PLATFORM_ID', -- platform + 'C_COMPILER_VERSION', 'CXX_COMPILER_VERSION', 'CUDA_COMPILER_VERSION', 'OBJC_COMPILER_VERSION', + 'OBJCXX_COMPILER_VERSION', 'Fortran_COMPILER_VERSION', 'HIP_COMPILER_VERSION', + 'ISPC_COMPILER_VERSION', -- compiler version + 'C_COMPILER_ID', 'CXX_COMPILER_ID', 'CUDA_COMPILER_ID', 'OBJC_COMPILER_ID', 'OBJCXX_COMPILER_ID', + 'Fortran_COMPILER_ID', 'HIP_COMPILER_ID', 'ISPC_COMPILER_ID', 'COMPILE_LANGUAGE', + 'COMPILE_LANG_AND_ID', 'COMPILE_FEATURES', -- compile features + 'LINK_LANGUAGE', 'LINK_LANG_AND_ID', -- linker language and ID + 'LINK_LIBRARY', 'LINK_GROUP', -- link features + 'LINK_ONLY', 'DEVICE_LINK', 'HOST_LINK', -- link context + 'TARGET_EXISTS', 'TARGET_NAME_IF_EXISTS', 'TARGET_NAME', 'TARGET_PROPERTY', 'TARGET_OBJECTS', + 'TARGET_POLICY', 'TARGET_FILE', 'TARGET_FILE_BASE_NAME', 'TARGET_FILE_PREFIX', + 'TARGET_FILE_SUFFIX', 'TARGET_FILE_NAME', 'TARGET_FILE_DIR', 'TARGET_LINKER_FILE', + 'TARGET_LINKER_FILE_BASE_NAME', 'TARGET_LINKER_FILE_PREFIX', 'TARGET_LINKER_FILE_SUFFIX', + 'TARGET_LINKER_FILE_NAME', 'TARGET_LINKER_FILE_DIR', 'TARGET_SONAME_FILE', + 'TARGET_SONAME_FILE_NAME', 'TARGET_SONAME_FILE_DIR', 'TARGET_PDB_FILE', + 'TARGET_PDB_FILE_BASE_NAME', 'TARGET_PDB_FILE_NAME', 'TARGET_PDB_FILE_DIR', 'TARGET_BUNDLE_DIR', + 'TARGET_BUNDLE_DIR_NAME', 'TARGET_BUNDLE_CONTENT_DIR', 'TARGET_RUNTIME_DLLS', -- target-dependent expressions + 'INSTALL_INTERFACE', 'BUILD_INTERFACE', 'INSTALL_PREFIX', -- export and install expressions + 'GENEX_EVAL', 'TARGET_GENEX_EVAL', -- multi-level expression evaluation + 'ANGLE-R', 'COMMA', 'SEMICOLON' -- escaped characters +}) + +lex:set_word_list(lexer.OPERATOR, { + -- Unary test. + 'EXISTS', 'COMMAND', 'DEFINED', + -- Binary test. + 'EQUAL', 'LESS', 'LESS_EQUAL', 'GREATER', 'GREATER_EQUAL', 'STREQUAL', 'STRLESS', 'STRLESS_EQUAL', + 'STRGREATER', 'STRGREATER_EQUAL', 'VERSION_EQUAL', 'VERSION_LESS', 'VERSION_LESS_EQUAL', + 'VERSION_GREATER', 'VERSION_GREATER_EQUAL', 'PATH_EQUAL', 'MATCHES', + -- Logical. + 'NOT', 'AND', 'OR' +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/coffeescript.lua b/lua/lexers/coffeescript.lua index be4be45..c698d6e 100644 --- a/lua/lexers/coffeescript.lua +++ b/lua/lexers/coffeescript.lua @@ -1,17 +1,17 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- CoffeeScript LPeg lexer. local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local word_match = lexer.word_match local P, S = lpeg.P, lpeg.S local lex = lexer.new('coffeescript', {fold_by_indentation = true}) -- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +lex:add_rule('whitespace', lex:tag(lexer.WHITESPACE, lexer.space^1)) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, word_match{ 'all', 'and', 'bind', 'break', 'by', 'case', 'catch', 'class', 'const', 'continue', 'default', 'delete', 'do', 'each', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'is', 'isnt', 'let', 'loop', 'native', 'new', @@ -21,29 +21,30 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ -- Fields: object properties and methods. lex:add_rule('field', - token(lexer.FUNCTION, '.' * (S('_$') + lexer.alpha) * (S('_$') + lexer.alnum)^0)) + lex:tag(lexer.FUNCTION, '.' * (S('_$') + lexer.alpha) * (S('_$') + lexer.alnum)^0)) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local sq_str = lexer.range("'") local dq_str = lexer.range('"') -local string = token(lexer.STRING, sq_str + dq_str) -local regex_str = - #P('/') * lexer.last_char_includes('+-*%<>!=^&|?~:;,([{') * lexer.range('/', true) * S('igm')^0 -local regex = token(lexer.REGEX, regex_str) +local string = lex:tag(lexer.STRING, sq_str + dq_str) +local regex_str = lexer.after_set('+-*%<>!=^&|?~:;,([{', lexer.range('/', true) * S('igm')^0) +local regex = lex:tag(lexer.REGEX, regex_str) lex:add_rule('string', string + regex) -- Comments. local block_comment = lexer.range('###') local line_comment = lexer.to_eol('#', true) -lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;,.()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;,.()[]{}'))) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/container.lua b/lua/lexers/container.lua index 52edc0d..09490a4 100644 --- a/lua/lexers/container.lua +++ b/lua/lexers/container.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Container LPeg lexer. -- This is SciTE's plain text lexer. diff --git a/lua/lexers/context.lua b/lua/lexers/context.lua index 2124287..8ba2efc 100644 --- a/lua/lexers/context.lua +++ b/lua/lexers/context.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Robert Gieseke, Lars Otter. See LICENSE. +-- Copyright 2006-2024 Robert Gieseke, Lars Otter. See LICENSE. -- ConTeXt LPeg lexer. local lexer = require('lexer') @@ -42,7 +42,6 @@ lex:add_rule('operator', operator) lex:add_fold_point('environment', '\\start', '\\stop') lex:add_fold_point('environment', '\\begin', '\\end') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('%')) -- Embedded Lua. local luatex = lexer.load('lua') @@ -50,4 +49,6 @@ local luatex_start_rule = #P('\\startluacode') * environment local luatex_end_rule = #P('\\stopluacode') * environment lex:embed(luatex, luatex_start_rule, luatex_end_rule) +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/cpp.lua b/lua/lexers/cpp.lua index 73ee0b1..91ac0f8 100644 --- a/lua/lexers/cpp.lua +++ b/lua/lexers/cpp.lua @@ -1,68 +1,64 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- C++ LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('cpp') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'asm', 'auto', 'break', 'case', 'catch', 'class', 'const', 'const_cast', 'continue', 'default', - 'delete', 'do', 'dynamic_cast', 'else', 'explicit', 'export', 'extern', 'false', 'for', 'friend', - 'goto', 'if', 'inline', 'mutable', 'namespace', 'new', 'operator', 'private', 'protected', - 'public', 'register', 'reinterpret_cast', 'return', 'sizeof', 'static', 'static_cast', 'switch', - 'template', 'this', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'using', 'virtual', - 'volatile', 'while', - -- Operators. - 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq', - -- C++11. - 'alignas', 'alignof', 'constexpr', 'decltype', 'final', 'noexcept', 'override', 'static_assert', - 'thread_local' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ - 'bool', 'char', 'double', 'enum', 'float', 'int', 'long', 'short', 'signed', 'struct', 'union', - 'unsigned', 'void', 'wchar_t', - -- C++11. - 'char16_t', 'char32_t', 'nullptr' -})) +local basic_type = lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE)) +local stl_type = lex:tag(lexer.TYPE .. '.stl', 'std::' * lex:word_match(lexer.TYPE .. '.stl')) +lex:add_rule('type', basic_type + stl_type * -P('::')) + +-- Functions. +local non_member = -(B('.') + B('->') + B('::')) +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, + P('std::')^-1 * lex:word_match(lexer.FUNCTION_BUILTIN)) +local stl_func = lex:tag(lexer.FUNCTION_BUILTIN .. '.stl', + 'std::' * lex:word_match(lexer.FUNCTION_BUILTIN .. '.stl')) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = (B('.') + B('->')) * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', + (non_member * (stl_func + builtin_func) + method + func) * #(lexer.space^0 * '(')) + +-- Constants. +local const = + lex:tag(lexer.CONSTANT_BUILTIN, P('std::')^-1 * lex:word_match(lexer.CONSTANT_BUILTIN)) +local stl_const = lex:tag(lexer.CONSTANT_BUILTIN .. '.stl', + 'std::' * lex:word_match(lexer.CONSTANT_BUILTIN .. '.stl')) +lex:add_rule('constants', stl_const + const) -- Strings. -local sq_str = P('L')^-1 * lexer.range("'", true) -local dq_str = P('L')^-1 * lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +local sq_str = lexer.range("'", true) +local dq_str = lexer.range('"', true) +lex:add_rule('string', lex:tag(lexer.STRING, ('u8' + S('LuU'))^-1 * (sq_str + dq_str))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments. local line_comment = lexer.to_eol('//', true) local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) -- Numbers. -local dec = lexer.digit^1 * ("'" * lexer.digit^1)^0 -local hex = '0' * S('xX') * lexer.xdigit^1 * ("'" * lexer.xdigit^1)^0 -local bin = '0' * S('bB') * S('01')^1 * ("'" * S('01')^1)^0 * -lexer.xdigit -local integer = S('+-')^-1 * (hex + bin + dec) -lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number_("'"))) -- Preprocessor. -local include = token(lexer.PREPROCESSOR, '#' * S('\t ')^0 * 'include') * - (ws * token(lexer.STRING, lexer.range('<', '>', true)))^-1 -local preproc = token(lexer.PREPROCESSOR, '#' * S('\t ')^0 * - word_match('define elif else endif error if ifdef ifndef import line pragma undef using warning')) +local include = lex:tag(lexer.PREPROCESSOR, '#' * S('\t ')^0 * 'include') * + (lex:get_rule('whitespace') * lex:tag(lexer.STRING, lexer.range('<', '>', true)))^-1 +local preproc = lex:tag(lexer.PREPROCESSOR, '#' * S('\t ')^0 * lex:word_match(lexer.PREPROCESSOR)) lex:add_rule('preprocessor', include + preproc) +-- Attributes. +lex:add_rule('attribute', lex:tag(lexer.ATTRIBUTE, '[[' * lex:word_match(lexer.ATTRIBUTE) * ']]')) + -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;,.()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;,.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.PREPROCESSOR, 'if', 'endif') @@ -70,6 +66,222 @@ lex:add_fold_point(lexer.PREPROCESSOR, 'ifdef', 'endif') lex:add_fold_point(lexer.PREPROCESSOR, 'ifndef', 'endif') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'asm', 'auto', 'break', 'case', 'catch', 'class', 'const', 'const_cast', 'continue', 'default', + 'delete', 'do', 'dynamic_cast', 'else', 'explicit', 'export', 'extern', 'false', 'for', 'friend', + 'goto', 'if', 'inline', 'mutable', 'namespace', 'new', 'operator', 'private', 'protected', + 'public', 'register', 'reinterpret_cast', 'return', 'sizeof', 'static', 'static_cast', 'switch', + 'template', 'this', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'using', 'virtual', + 'volatile', 'while', + -- Operators. + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq', + -- C++11. + 'alignas', 'alignof', 'constexpr', 'decltype', 'final', 'noexcept', 'nullptr', 'override', + 'static_assert', 'thread_local', -- + 'consteval', 'constinit', 'co_await', 'co_return', 'co_yield', 'requires' -- C++20 +}) + +lex:set_word_list(lexer.TYPE, { + 'bool', 'char', 'double', 'enum', 'float', 'int', 'long', 'short', 'signed', 'struct', 'union', + 'unsigned', 'void', 'wchar_t', -- + 'char16_t', 'char32_t', -- C++11 + 'char8_t', -- C++20 + -- <cstddef> + 'size_t', 'ptrdiff_t', 'max_align_t', -- + 'byte', -- C++17 + -- <cstdint> + 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'int_fast8_t', 'int_fast16_t', 'int_fast32_t', + 'int_fast64_t', 'int_least8_t', 'int_least16_t', 'int_least32_t', 'int_least64_t', 'intmax_t', + 'intptr_t', 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', 'uint_fast8_t', 'uint_fast16_t', + 'uint_fast32_t', 'uint_fast64_t', 'uint_least8_t', 'uint_least16_t', 'uint_least32_t', + 'uint_least64_t', 'uintmax_t', 'uintptr_t' +}) + +lex:set_word_list(lexer.TYPE .. '.stl', { + 'any', 'bad_any_cast', -- <any> C++17 + 'array', -- <array> C++11 + 'atomic', -- <atomic> C++11 + 'barrier', -- <barrier> C++20 + 'bitset', -- <bitset> + -- <concepts> C++20 + 'same_as', 'derived_from', 'convertible_to', 'common_reference_with', 'common_with', 'integral', + 'signed_integral', 'unsigned_integral', 'floating_point', 'assignable_from', 'swappable', + 'swappable_with', 'destructible', 'constructible_from', 'default_initializable', + 'move_constructible', 'copy_constructible', 'equality_comparable', 'equality_comparable_with', + 'movable', 'copyable', 'semiregular', 'regular', 'invocable', 'regular_invocable', 'predicate', + 'relation', 'equivalence_relation', 'strict_weak_order', -- + 'complex', -- <complex> + 'deque', -- <deque> + 'exception', 'bad_exception', -- <exception> + 'forward_list', -- <forward_list> C++11 + 'function', 'hash', -- <functional> C++11 + -- <future> C++11 + 'promise', 'packaged_task', 'future', 'shared_future', 'launch', 'future_status', 'future_error', + 'future_errc', -- + 'initializer_list', -- <initializer_list> + 'istream', 'iostream', -- <istream> + -- <iterator> + 'reverse_iterator', 'back_insert_iterator', 'front_insert_iterator', 'insert_iterator', + 'istream_iterator', 'ostream_iterator', 'istreambuf_iterator', 'ostreambuf_iterator', -- + 'move_iterator', -- C++11 + 'latch', -- <latch> C++20 + 'list', -- <list> + -- <map> + 'map', 'multimap', -- + 'unordered_set', 'unordered_map', 'unordered_multiset', 'unordered_multimap', -- C++11 + 'unique_ptr', 'shared_ptr', 'weak_ptr', -- <memory> C++11 + -- <mutex> C++11 + 'mutex', 'timed_mutex', 'recursive_mutex', 'recursive_timed_mutex', 'lock_guard', 'unique_lock', -- + 'scoped_lock', -- C++17 + 'optional', 'bad_optional_access', -- <optional> C++17 + 'ostream', -- <ostream> + 'queue', 'priority_queue', -- <queue> + -- <random> C++11 + 'linear_congruential_engine', 'mersenne_twister_engine', 'subtract_with_carry_engine', + 'discard_block_engine', 'independent_bits_engine', 'shuffle_order_engine', 'random_device', + 'uniform_int_distribution', 'uniform_real_distribution', 'bernoulli_distribution', + 'binomial_distribution', 'negative_binomial_distribution', 'geometric_distribution', + 'poisson_distribution', 'exponential_distribution', 'gamma_distribution', 'weibull_distribution', + 'extreme_value_distribution', 'normal_distribution', 'lognormal_distribution', + 'chi_squared_distribution', 'cauchy_distribution', 'fisher_f_distribution', + 'student_t_distribution', 'discrete_distibution', 'piecewise_constant_distribution', + 'piecewise_linear_distribution', 'seed_seq', -- + 'ratio', -- <ratio> C++11 + -- <regex> C++11 + 'regex', 'csub_match', 'ssub_match', 'cmatch', 'smatch', 'cregex_iterator', 'sregex_iterator', + 'cregex_token_iterator', 'sregex_token_iterator', 'regex_error', 'regex_traits', -- + 'counting_semaphore', 'binary_semaphore', -- <semaphore> C++20 + 'set', 'multiset', -- <set> + 'span', -- <span> C++20 + 'stringbuf', 'istringstream', 'ostringstream', 'stringstream', -- <stringstream> + 'stack', -- <stack> + -- <stdexcept> + 'logic_error', 'invalid_argument', 'domain_error', 'length_error', 'out_of_range', + 'runtime_error', 'range_error', 'overflow_error', 'underflow_error', -- + 'streambuf', -- <streambuf> + -- <string> + 'string', -- + 'u16string', 'u32string', -- C++11 + 'u8string', -- C++20 + -- <string_view> C++17 + 'string_view', 'u16string_view', 'u32string_view', -- + 'u8string_view', -- C++20 + 'syncbuf', 'osyncstream', -- <syncstream> C++20 + 'thread', -- <thread> C++11 + 'tuple', 'tuple_size', 'tuple_element', -- <tuple> C++11 + 'pair', -- <utility> + 'variant', 'monostate', 'bad_variant_access', 'variant_size', 'variant_alternative', -- <variant> C++17 + 'vector' -- <vector> +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'assert', -- <cassert> + -- <cctype> + 'isalnum', 'isalpha', 'islower', 'isupper', 'isdigit', 'isxdigit', 'iscntrl', 'isgraph', + 'isspace', 'isprint', 'ispunct', 'tolower', 'toupper', -- + 'isblank', -- C++11 + 'va_start', 'va_arg', 'va_end', -- <cstdarg> + -- <cmath> + 'abs', 'fmod', 'exp', 'log', 'log10', 'pow', 'sqrt', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', + 'atan2', 'sinh', 'cosh', 'tanh', 'ceil', 'floor', 'frexp', 'ldexp', 'modf', + -- C++11. + 'remainder', 'remquo', 'exp2', 'expm1', 'log2', 'log1p', 'cbrt', 'hypot', 'asinh', 'acosh', + 'atanh', 'erf', 'erfc', 'tgamma', 'lgamma', 'trunc', 'round', 'nearbyint', 'rint', 'scalbn', + 'ilogb', 'logb', 'nextafter', 'copysign', 'isfinite', 'isinf', 'isnan', 'isnormal', 'signbit', + 'isgreater', 'isgreaterequal', 'isless', 'islessequal', 'islessgreater', 'isunordered', -- + -- C++17. + 'assoc_laguerre', 'assoc_legendre', 'beta', 'comp_ellint_1', 'comp_ellint_2', 'comp_ellint_3', + 'cyl_bessel_i', 'cyl_bessel_j', 'cyl_bessel_k', 'cyl_neumann', 'ellint_1', 'ellint_2', 'ellint_3', + 'expint', 'lhermite', 'lgendre', 'laguerre', 'riemann_zeta', 'sph_bessel', 'sph_legendre', + 'sph_neumann', -- + 'lerp', -- C++20 + -- <cstring> + 'strcpy', 'strncpy', 'strcat', 'strncat', 'strxfrm', 'strlen', 'strcmp', 'strncmp', 'strcoll', + 'strchr', 'strrchr', 'strspn', 'strcspn', 'strpbrk', 'strstr', 'strtok', 'memchr', 'memcmp', + 'memset', 'memcpy', 'memmove', 'strerror' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN .. '.stl', { + -- <algorithm> + 'for_each', 'count', 'count_if', 'mismatch', 'find', 'find_if', 'find_end', 'find_first_of', + 'adjacent_find', 'search', 'search_n', 'copy', 'copy_backward', 'fill', 'fill_n', 'transform', + 'generate', 'generate_n', 'remove', 'remove_if', 'remove_copy', 'remove_copy_if', 'replace', + 'replace_if', 'replace_copy', 'replace_copy_if', 'swap', 'swap_ranges', 'iter_swap', 'reverse', + 'reverse_copy', 'rotate', 'rotate_copy', 'unique_copy', 'partition', 'stable_partition', 'sort', + 'partial_sort', 'partial_sort_copy', 'stable_sort', 'nth_element', 'lower_bound', 'upper_bound', + 'binary_search', 'equal_range', 'merge', 'inplace_merge', 'includes', 'set_difference', + 'set_intersection', 'set_symmetric_difference', 'set_union', 'make_heap', 'push_heap', 'pop_heap', + 'sort_heap', 'max', 'max_element', 'min', 'min_element', 'equal', 'lexicographical_compare', + 'next_permutation', 'prev_permutation', -- + -- C++11. + 'all_of', 'any_of', 'none_of', 'find_if_not', 'copy_if', 'copy_n', 'move', 'move_backward', + 'shuffle', 'is_partitioned', 'partition_copy', 'partition_point', 'is_sorted', 'is_sorted_until', + 'is_heap', 'is_heap_until', 'minmax', 'minmax_element', 'is_permutation', -- + 'for_each_n', 'random_shuffle', 'sample', 'clamp', -- C++17 + 'shift_left', 'shift_right', 'lexicographical_compare_three_way', -- C++20 + 'make_any', 'any_cast', -- <any> C++17 + -- <bit> C++20 + 'bit_cast', 'byteswap', 'has_single_bit', 'bit_ceil', 'bit_floor', 'bit_width', 'rotl', 'rotr', + 'countl_zero', 'countl_one', 'countl_zero', 'countr_one', 'popcount', -- + 'from_chars', 'to_chars', -- <charconv> C++17 + -- <format> C++20 + 'format', 'format_to', 'format_to_n', 'formatted_size', 'vformat', 'vformat_to', + 'visit_format_arg', 'make_format_args', -- + 'async', 'future_category', -- <future> C++11 + -- <iterator> + 'front_inserter', 'back_inserter', 'inserter', -- + 'make_move_iterator', -- C++11 + 'make_reverse_iterator', -- C++14 + -- <memory> + 'make_shared', 'allocate_shared', 'static_pointer_cast', 'dynamic_pointer_cast', + 'const_pointer_cast', -- + 'make_unique', -- C++14 + 'reinterpret_pointer_cast', -- C++17 + 'try_lock', 'lock', 'call_once', -- <mutex> C++11 + -- <numeric> + 'accumulate', 'inner_product', 'adjacent_difference', 'partial_sum', -- + 'iota', -- C++11 + 'reduce', 'transform_reduce', 'inclusive_scan', 'exclusive_scan', 'gcd', 'lcm', -- C++17 + 'midpoint', -- C++20 + 'make_optional', -- <optional> C++17 + 'generate_canonical', -- <random> C++11 + 'regex_match', 'regex_search', 'regex_replace', -- <regex> C++11 + 'as_bytes', 'as_writable_bytes', -- <span> C++20 + -- <tuple> C++11 + 'make_tuple', 'tie', 'forward_as_tuple', 'tuple_cat', -- + 'apply', 'make_from_tuple', -- C++17 + -- <utility> + 'swap', 'make_pair', 'get', -- + 'forward', 'move', 'move_if_noexcept', 'declval', -- C++11 + 'exchange', -- C++14 + 'as_const', -- C++17 + -- C++20. + 'cmp_equal', 'cmp_not_equal', 'cmp_less', 'cmp_greater', 'cmp_less_equal', 'cmp_greater_equal', + 'in_range', -- + 'visit', 'holds_alternative', 'get_if' -- <variant> C++17 +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN .. '.stl', { + 'cin', 'cout', 'cerr', 'clog', -- <iostream> + 'endl', 'ends', 'flush', -- <ostream> + 'nullopt' -- <optional> C++17 +}) + +lex:set_word_list(lexer.PREPROCESSOR, { + 'define', 'defined', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'import', 'line', + 'pragma', 'undef', 'using', 'warning', -- + 'export', 'include', 'module' -- C++20 +}) + +lex:set_word_list(lexer.ATTRIBUTE, { + 'carries_dependency', 'noreturn', -- C++11 + 'deprecated', -- C++14 + 'fallthrough', 'maybe_unused', 'nodiscard', -- C++17 + 'likely', 'no_unique_address', 'unlikely' -- C++20 +}) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/crystal.lua b/lua/lexers/crystal.lua index f8b2211..da6cc08 100644 --- a/lua/lexers/crystal.lua +++ b/lua/lexers/crystal.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Copyright 2017 Michel Martens. -- Crystal LPeg lexer (based on Ruby). @@ -49,18 +49,13 @@ local heredoc = '<<' * P(function(input, index) end) local string = token(lexer.STRING, (sq_str + dq_str + heredoc + cmd_str) * S('f')^-1) -- TODO: regex_str fails with `obj.method /patt/` syntax. -local regex_str = - #P('/') * lexer.last_char_includes('!%^&*([{-=+|:;,?<>~') * lexer.range('/', true) * S('iomx')^0 +local regex_str = lexer.after_set('!%^&*([{-=+|:;,?<>~', lexer.range('/', true) * S('iomx')^0) local regex = token(lexer.REGEX, regex_str) lex:add_rule('string', string + regex) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 * S('ri')^-1 -local bin = '0b' * S('01')^1 * ('_' * S('01')^1)^0 -local integer = S('+-')^-1 * (bin + lexer.hex_num + lexer.oct_num + dec) --- TODO: meta, control, etc. for numeric_literal. -local numeric_literal = '?' * (lexer.any - lexer.space) * -word_char -lex:add_rule('number', token(lexer.NUMBER, lexer.float * S('ri')^-1 + integer + numeric_literal)) +local numeric_literal = '?' * (lexer.any - lexer.space) * -word_char -- TODO: meta, control, etc. +lex:add_rule('number', token(lexer.NUMBER, lexer.number_('_') * S('ri')^-1 + numeric_literal)) -- Variables. local global_var = '$' * @@ -71,7 +66,7 @@ lex:add_rule('variable', token(lexer.VARIABLE, global_var + class_var + inst_var -- Symbols. lex:add_rule('symbol', token('symbol', ':' * P(function(input, index) - if input:sub(index - 2, index - 2) ~= ':' then return index end + if input:sub(index - 2, index - 2) ~= ':' then return true end end) * (word_char^1 + sq_str + dq_str))) lex:add_style('symbol', lexer.styles.constant) @@ -97,6 +92,7 @@ lex:add_fold_point(lexer.KEYWORD, 'until', disambiguate) lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/csharp.lua b/lua/lexers/csharp.lua index 1d209ed..7e271e6 100644 --- a/lua/lexers/csharp.lua +++ b/lua/lexers/csharp.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- C# LPeg lexer. local lexer = require('lexer') @@ -59,6 +59,7 @@ lex:add_fold_point(lexer.PREPROCESSOR, 'ifndef', 'endif') lex:add_fold_point(lexer.PREPROCESSOR, 'region', 'endregion') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/css.lua b/lua/lexers/css.lua index de9fd96..c48d378 100644 --- a/lua/lexers/css.lua +++ b/lua/lexers/css.lua @@ -1,17 +1,78 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- CSS LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('css') +local lex = lexer.new(..., {no_user_word_lists = true}) --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +-- Tags. +local sel_prefix = B(S('#.')) +local sel_suffix = lexer.space^0 * S('!>+.,:[{') +lex:add_rule('tag', -sel_prefix * lex:tag(lexer.TAG, lex:word_match(lexer.TAG) * #sel_suffix)) -- Properties. -lex:add_rule('property', token('property', word_match{ +lex:add_rule('property', lex:tag('property', lex:word_match('property')) * #P(':')) + +-- Values. +lex:add_rule('value', + -sel_prefix * lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match('value')) * -sel_suffix) + +-- Functions. +lex:add_rule('function', lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))) + +-- Colors. +local color_name = lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match('color')) +local xdigit = lexer.xdigit +local color_value = lex:tag(lexer.NUMBER, + '#' * xdigit * xdigit * xdigit * (xdigit * xdigit * xdigit)^-1) +lex:add_rule('color', color_name + color_value) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + S('_-'))^0)) + +-- Pseudo classes and pseudo elements. +lex:add_rule('pseudoclass', ':' * lex:tag('pseudoclass', lex:word_match('pseudoclass'))) +lex:add_rule('pseudoelement', '::' * lex:tag('pseudoelement', lex:word_match('pseudoelement'))) + +-- Strings. +local sq_str = lexer.range("'") +local dq_str = lexer.range('"') +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.range('/*', '*/'))) + +-- Numbers. +local unit = lex:word_match('unit') + '%' +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number * unit^-1)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('~!#*>+=|.,:;()[]{}'))) + +-- At rule. +lex:add_rule('at_rule', lex:tag(lexer.PREPROCESSOR, '@' * lex:word_match('at_rule'))) + +-- Fold points. +lex:add_fold_point(lexer.OPERATOR, '{', '}') +lex:add_fold_point(lexer.COMMENT, '/*', '*/') + +-- Word lists. +lex:set_word_list(lexer.TAG, { + 'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'body', + 'button', 'canvas', 'caption', 'cite', 'code', 'colgroup', 'content', 'data', 'datalist', 'dd', + 'decorator', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', + 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', + 'html', 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'menu', + 'menuitem', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', + 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', + 'small', 'spacer', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', + 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'u', 'ul', 'var', 'video', -- + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', + 'param', 'source', 'track', 'wbr' +}) + +lex:set_word_list('property', { -- CSS 1. 'color', 'background-color', 'background-image', 'background-repeat', 'background-attachment', 'background-position', 'background', 'font-family', 'font-style', 'font-variant', 'font-weight', @@ -42,11 +103,9 @@ lex:add_rule('property', token('property', word_match{ 'align-content', 'align-items', 'align-self', 'justify-content', 'order', 'border-radius', 'transition', 'transform', 'box-shadow', 'filter', 'opacity', 'resize', 'word-break', 'word-wrap', 'box-sizing', 'animation', 'text-overflow' -})) -lex:add_style('property', lexer.styles.keyword) +}) --- Values. -lex:add_rule('value', token('value', word_match{ +lex:set_word_list('value', { -- CSS 1. 'auto', 'none', 'normal', 'italic', 'oblique', 'small-caps', 'bold', 'bolder', 'lighter', 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'larger', 'smaller', @@ -82,11 +141,9 @@ lex:add_rule('value', token('value', word_match{ 'female', 'child', 'x-low', 'low', 'high', 'x-high', 'code', 'digits', 'continous', -- CSS 3. 'flex', 'row', 'column', 'ellipsis', 'inline-block' -})) -lex:add_style('value', lexer.styles.constant) +}) --- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { 'attr', 'blackness', 'blend', 'blenda', 'blur', 'brightness', 'calc', 'circle', 'color-mod', 'contrast', 'counter', 'cubic-bezier', 'device-cmyk', 'drop-shadow', 'ellipse', 'gray', 'grayscale', 'hsl', 'hsla', 'hue', 'hue-rotate', 'hwb', 'image', 'inset', 'invert', 'lightness', @@ -95,11 +152,9 @@ lex:add_rule('function', token(lexer.FUNCTION, word_match{ 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'saturate', 'saturation', 'scale', 'scale3d', 'scaleX', 'scaleY', 'scaleZ', 'sepia', 'shade', 'skewX', 'skewY', 'steps', 'tint', 'toggle', 'translate', 'translate3d', 'translateX', 'translateY', 'translateZ', 'url', 'whiteness', 'var' -})) +}) --- Colors. -local xdigit = lexer.xdigit -lex:add_rule('color', token('color', word_match{ +lex:set_word_list('color', { 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', @@ -121,48 +176,26 @@ lex:add_rule('color', token('color', word_match{ 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'transparent', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen' -} + '#' * xdigit * xdigit * xdigit * (xdigit * xdigit * xdigit)^-1)) -lex:add_style('color', lexer.styles.number) - --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + S('_-'))^0)) +}) --- Pseudo classes and pseudo elements. -lex:add_rule('pseudoclass', ':' * token('pseudoclass', word_match{ +lex:set_word_list('pseudoclass', { 'active', 'checked', 'disabled', 'empty', 'enabled', 'first-child', 'first-of-type', 'focus', 'hover', 'in-range', 'invalid', 'lang', 'last-child', 'last-of-type', 'link', 'not', 'nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type', 'only-of-type', 'only-child', 'optional', 'out-of-range', 'read-only', 'read-write', 'required', 'root', 'target', 'valid', 'visited' -})) -lex:add_style('pseudoclass', lexer.styles.constant) -lex:add_rule('pseudoelement', '::' * - token('pseudoelement', word_match('after before first-letter first-line selection'))) -lex:add_style('pseudoelement', lexer.styles.constant) +}) --- Strings. -local sq_str = lexer.range("'") -local dq_str = lexer.range('"') -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) - --- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.range('/*', '*/'))) +lex:set_word_list('pseudoelement', 'after before first-letter first-line selection') --- Numbers. -local unit = token('unit', word_match( - 'ch cm deg dpcm dpi dppx em ex grad Hz in kHz mm ms pc pt px q rad rem s turn vh vmax vmin vw')) -lex:add_style('unit', lexer.styles.number) -lex:add_rule('number', token(lexer.NUMBER, lexer.dec_num) * unit^-1) +lex:set_word_list('unit', { + 'ch', 'cm', 'deg', 'dpcm', 'dpi', 'dppx', 'em', 'ex', 'grad', 'Hz', 'in', 'kHz', 'mm', 'ms', 'pc', + 'pt', 'px', 'q', 'rad', 'rem', 's', 'turn', 'vh', 'vmax', 'vmin', 'vw' +}) --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('~!#*>+=|.,:;()[]{}'))) +lex:set_word_list('at_rule', 'charset font-face media page import namespace keyframes') --- At rule. -lex:add_rule('at_rule', token('at_rule', '@' * - word_match('charset font-face media page import namespace keyframes'))) -lex:add_style('at_rule', lexer.styles.preprocessor) - --- Fold points. -lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, '/*', '*/') +lexer.property['scintillua.comment'] = '/*|*/' +lexer.property['scintillua.word.chars'] = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' return lex diff --git a/lua/lexers/cuda.lua b/lua/lexers/cuda.lua index 91818ae..84a7299 100644 --- a/lua/lexers/cuda.lua +++ b/lua/lexers/cuda.lua @@ -1,29 +1,21 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- CUDA LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('cuda', {inherit = lexer.load('cpp')}) +local lex = lexer.new(..., {inherit = lexer.load('cpp')}) --- Whitespace -lex:modify_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +-- Word lists. +lex:set_word_list(lexer.KEYWORD, '__global__ __host__ __device__ __constant__ __shared__', true) --- Keywords. -local keyword = token(lexer.KEYWORD, - word_match('__global__ __host__ __device__ __constant__ __shared__')) -lex:modify_rule('keyword', keyword + lex:get_rule('keyword')) - --- Types. -lex:modify_rule('type', token(lexer.TYPE, word_match{ +lex:set_word_list(lexer.TYPE, { 'uint', 'int1', 'uint1', 'int2', 'uint2', 'int3', 'uint3', 'int4', 'uint4', 'float1', 'float2', 'float3', 'float4', 'char1', 'char2', 'char3', 'char4', 'uchar1', 'uchar2', 'uchar3', 'uchar4', 'short1', 'short2', 'short3', 'short4', 'dim1', 'dim2', 'dim3', 'dim4' -}) + lex:get_rule('type') + +}, true) --- Functions. -token(lexer.FUNCTION, word_match{ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { -- Atom. 'atomicAdd', 'atomicAnd', 'atomicCAS', 'atomicDec', 'atomicExch', 'atomicInc', 'atomicMax', 'atomicMin', 'atomicOr', 'atomicSub', 'atomicXor', -- @@ -61,9 +53,10 @@ token(lexer.FUNCTION, word_match{ 'cudaMemcpyToSymbol', 'cudaMemset', 'cudaMemset2D', 'cudaMemset3D', 'cudaSetDevice', 'cudaSetupArgument', 'cudaStreamCreate', 'cudaStreamDestroy', 'cudaStreamQuery', 'cudaStreamSynchronize', 'cudaThreadExit', 'cudaThreadSynchronize', 'cudaUnbindTexture' -}) + +}, true) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, 'gridDim blockIdx blockDim threadIdx', true) --- Variables. -token(lexer.VARIABLE, word_match('gridDim blockIdx blockDim threadIdx'))) +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/dart.lua b/lua/lexers/dart.lua index ca253ff..f930d7c 100644 --- a/lua/lexers/dart.lua +++ b/lua/lexers/dart.lua @@ -1,4 +1,4 @@ --- Copyright 2013-2022 Mitchell. See LICENSE. +-- Copyright 2013-2024 Mitchell. See LICENSE. -- Dart LPeg lexer. -- Written by Brian Schott (@Hackerpilot on Github). @@ -45,12 +45,12 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) lex:add_rule('operator', token(lexer.OPERATOR, S('#?=!<>+-*$/%&|^~.,;()[]{}'))) -- Annotations. -lex:add_rule('annotation', token('annotation', '@' * lexer.word^1)) -lex:add_style('annotation', lexer.styles.preprocessor) +lex:add_rule('annotation', token(lexer.ANNOTATION, '@' * lexer.word^1)) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/desktop.lua b/lua/lexers/desktop.lua index 2c824e0..93e007f 100644 --- a/lua/lexers/desktop.lua +++ b/lua/lexers/desktop.lua @@ -1,53 +1,49 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Desktop Entry LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('desktop') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keys. -lex:add_rule('key', token('key', word_match{ - 'Type', 'Version', 'Name', 'GenericName', 'NoDisplay', 'Comment', 'Icon', 'Hidden', 'OnlyShowIn', - 'NotShowIn', 'TryExec', 'Exec', 'Exec', 'Path', 'Terminal', 'MimeType', 'Categories', - 'StartupNotify', 'StartupWMClass', 'URL' -})) -lex:add_style('key', lexer.styles.keyword) +lex:add_rule('key', lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))) -- Values. -lex:add_rule('value', token('value', word_match('true false'))) -lex:add_style('value', lexer.styles.constant) +lex:add_rule('value', lex:tag(lexer.CONSTANT_BUILTIN, lexer.word_match('true false'))) -- Identifiers. -lex:add_rule('identifier', lexer.token(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + S('_-'))^0)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + S('_-'))^0)) -- Group headers. local bracketed = lexer.range('[', ']') -lex:add_rule('header', lexer.starts_line(token('header', bracketed))) -lex:add_style('header', lexer.styles.label) +lex:add_rule('header', lexer.starts_line(lex:tag(lexer.HEADING, bracketed))) -- Locales. -lex:add_rule('locale', token('locale', bracketed)) -lex:add_style('locale', lexer.styles.class) +lex:add_rule('locale', lex:tag(lexer.TYPE, bracketed)) -- Strings. -lex:add_rule('string', token(lexer.STRING, lexer.range('"'))) +lex:add_rule('string', lex:tag(lexer.STRING, lexer.range('"'))) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Field codes. -lex:add_rule('code', lexer.token('code', '%' * S('fFuUdDnNickvm'))) -lex:add_style('code', lexer.styles.variable) +lex:add_rule('code', lex:tag(lexer.CONSTANT_BUILTIN, '%' * S('fFuUdDnNickvm'))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('='))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('='))) + +-- Word lists. +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'Type', 'Version', 'Name', 'GenericName', 'NoDisplay', 'Comment', 'Icon', 'Hidden', 'OnlyShowIn', + 'NotShowIn', 'TryExec', 'Exec', 'Exec', 'Path', 'Terminal', 'MimeType', 'Categories', + 'StartupNotify', 'StartupWMClass', 'URL' +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/diff.lua b/lua/lexers/diff.lua index 4c87dc2..4f779bf 100644 --- a/lua/lexers/diff.lua +++ b/lua/lexers/diff.lua @@ -1,29 +1,25 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Diff LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local to_eol, starts_line = lexer.to_eol, lexer.starts_line local P, S = lpeg.P, lpeg.S -local lex = lexer.new('diff', {lex_by_line = true}) +local lex = lexer.new(..., {lex_by_line = true}) --- Text, separators, and file headers. -lex:add_rule('index', token(lexer.COMMENT, 'Index: ' * lexer.any^0 * -1)) -lex:add_rule('separator', token(lexer.COMMENT, ('---' + P('*')^4 + P('=')^1) * lexer.space^0 * -1)) -lex:add_rule('header', token('header', (P('*** ') + '--- ' + '+++ ') * lexer.any^1)) -lex:add_style('header', lexer.styles.comment) +-- Text, file headers, and separators. +lex:add_rule('index', lex:tag(lexer.COMMENT, to_eol(starts_line('Index: ')))) +lex:add_rule('header', lex:tag(lexer.HEADING, to_eol(starts_line(P('*** ') + '--- ' + '+++ ')))) +lex:add_rule('separator', lex:tag(lexer.COMMENT, to_eol(starts_line(P('---') + '****' + '=')))) -- Location. -lex:add_rule('location', token(lexer.NUMBER, ('@@' + lexer.dec_num + '****') * lexer.any^1)) +lex:add_rule('location', lex:tag(lexer.NUMBER, to_eol(starts_line('@@' + lexer.dec_num + '****')))) -- Additions, deletions, and changes. -lex:add_rule('addition', token('addition', S('>+') * lexer.any^0)) -lex:add_style('addition', {fore = lexer.colors.green}) -lex:add_rule('deletion', token('deletion', S('<-') * lexer.any^0)) -lex:add_style('deletion', {fore = lexer.colors.red}) -lex:add_rule('change', token('change', '!' * lexer.any^0)) -lex:add_style('change', {fore = lexer.colors.yellow}) +lex:add_rule('addition', lex:tag('addition', to_eol(starts_line(S('>+'))))) +lex:add_rule('deletion', lex:tag('deletion', to_eol(starts_line(S('<-'))))) +lex:add_rule('change', lex:tag('change', to_eol(starts_line('!')))) -lex:add_rule('any_line', token(lexer.DEFAULT, lexer.any^1)) +lex:add_rule('any_line', lex:tag(lexer.DEFAULT, lexer.to_eol())) return lex diff --git a/lua/lexers/django.lua b/lua/lexers/django.lua index 2147853..75b2d28 100644 --- a/lua/lexers/django.lua +++ b/lua/lexers/django.lua @@ -1,55 +1,63 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Django LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('django') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'as', 'block', 'blocktrans', 'by', 'endblock', 'endblocktrans', 'comment', 'endcomment', 'cycle', - 'date', 'debug', 'else', 'extends', 'filter', 'endfilter', 'firstof', 'for', 'endfor', 'if', - 'endif', 'ifchanged', 'endifchanged', 'ifnotequal', 'endifnotequal', 'in', 'load', 'not', 'now', - 'or', 'parsed', 'regroup', 'ssi', 'trans', 'with', 'widthratio' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'add', 'addslashes', 'capfirst', 'center', 'cut', 'date', 'default', 'dictsort', - 'dictsortreversed', 'divisibleby', 'escape', 'filesizeformat', 'first', 'fix_ampersands', - 'floatformat', 'get_digit', 'join', 'length', 'length_is', 'linebreaks', 'linebreaksbr', - 'linenumbers', 'ljust', 'lower', 'make_list', 'phone2numeric', 'pluralize', 'pprint', 'random', - 'removetags', 'rjust', 'slice', 'slugify', 'stringformat', 'striptags', 'time', 'timesince', - 'title', 'truncatewords', 'unordered_list', 'upper', 'urlencode', 'urlize', 'urlizetrunc', - 'wordcount', 'wordwrap', 'yesno' -})) +lex:add_rule('function', + lpeg.B('|') * lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. -lex:add_rule('string', token(lexer.STRING, lexer.range('"', false, false))) +lex:add_rule('string', lex:tag(lexer.STRING, lexer.range('"', false, false))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S(':,.|'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S(':,.|'))) -- Embed Django in HTML. local html = lexer.load('html') -local html_comment = lexer.range('<!--', '-->') -local django_comment = lexer.range('{#', '#}', true) -html:modify_rule('comment', token(lexer.COMMENT, html_comment + django_comment)) -local django_start_rule = token('django_tag', '{' * S('{%')) -local django_end_rule = token('django_tag', S('%}') * '}') +html:add_rule('django_comment', lex:tag(lexer.COMMENT, lexer.range('{#', '#}', true))) +local django_start_rule = lex:tag(lexer.PREPROCESSOR, '{' * S('{%')) +local django_end_rule = lex:tag(lexer.PREPROCESSOR, S('%}') * '}') html:embed(lex, django_start_rule, django_end_rule) -lex:add_style('django_tag', lexer.styles.embedded) -- Fold points. -lex:add_fold_point('django_tag', '{{', '}}') -lex:add_fold_point('django_tag', '{%', '%}') +lex:add_fold_point(lexer.PREPROCESSOR, '{{', '}}') +lex:add_fold_point(lexer.PREPROCESSOR, '{%', '%}') + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'autoescape', 'endautoescape', 'block', 'endblock', 'comment', 'endcomment', 'csrf_token', + 'cycle', 'as', 'debug', 'extends', 'filter', 'endfilter', 'firstof', 'for', 'in', 'endfor', + 'empty', 'if', 'elif', 'else', 'endif', 'and', 'or', 'not', 'is', 'ifchanged', 'endifchanged', + 'include', 'load', 'lorem', 'now', 'regroup', 'resetcycle', 'spaceless', 'endspaceless', + 'templatetag', 'url', 'verbatim', 'endverbatim', 'widthratio', 'with', 'endwith', -- + 'blocktranslate', 'endblocktranslate', 'translate', 'language', 'get_available_languages', + 'get_current_language', 'get_current_language_bidi', 'get_language_info', + 'get_language_info_list', -- + 'get_static_prefix', 'get_media_prefix' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'add', 'addslashes', 'capfirst', 'center', 'cut', 'date', 'default', 'default_if_none', + 'dictsort', 'dictsortreversed', 'divisibleby', 'escape', 'escapejs', 'filesizeformat', 'first', + 'floatformat', 'force_escape', 'get_digit', 'iriencode', 'join', 'json_script', 'last', 'length', + 'length_is', 'linebreaks', 'linebreaksbr', 'linenumbers', 'ljust', 'lower', 'make_list', + 'phone2numeric', 'pluralize', 'pprint', 'random', 'rjust', 'safe', 'safeseq', 'slice', 'slugify', + 'stringformat', 'striptags', 'time', 'timesince', 'timeuntil', 'title', 'truncatechars_html', + 'truncatewords', 'truncatewords_html', 'unordered_list', 'upper', 'urlencode', 'urlize', + 'urlizetrunc', 'wordcount', 'wordwrap', 'yesno', -- + 'language_name', 'language_name_local', 'language_bidi', 'language_name_translated' +}) + +lexer.property['scintillua.comment'] = '{#|#}' return lex diff --git a/lua/lexers/dmd.lua b/lua/lexers/dmd.lua index 9fcacb5..cb4203d 100644 --- a/lua/lexers/dmd.lua +++ b/lua/lexers/dmd.lua @@ -1,99 +1,48 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- D LPeg lexer. -- Heavily modified by Brian Schott (@Hackerpilot on Github). -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('dmd') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(...) -- Class names. +local ws = lex:get_rule('whitespace') lex:add_rule('class', - token(lexer.TYPE, P('class') + 'struct') * ws^-1 * token(lexer.CLASS, lexer.word)) + lex:tag(lexer.TYPE, P('class') + 'struct') * ws^-1 * lex:tag(lexer.CLASS, lexer.word)) -- Versions. -local version = word_match{ - 'AArch64', 'AIX', 'all', 'Alpha', 'Alpha_HardFloat', 'Alpha_SoftFloat', 'Android', 'ARM', - 'ARM_HardFloat', 'ARM_SoftFloat', 'ARM_SoftFP', 'ARM_Thumb', 'assert', 'BigEndian', 'BSD', - 'Cygwin', 'D_Coverage', 'D_Ddoc', 'D_HardFloat', 'DigitalMars', 'D_InlineAsm_X86', - 'D_InlineAsm_X86_64', 'D_LP64', 'D_NoBoundsChecks', 'D_PIC', 'DragonFlyBSD', 'D_SIMD', - 'D_SoftFloat', 'D_Version2', 'D_X32', 'FreeBSD', 'GNU', 'Haiku', 'HPPA', 'HPPA64', 'Hurd', 'IA64', - 'LDC', 'linux', 'LittleEndian', 'MIPS32', 'MIPS64', 'MIPS_EABI', 'MIPS_HardFloat', 'MIPS_N32', - 'MIPS_N64', 'MIPS_O32', 'MIPS_O64', 'MIPS_SoftFloat', 'NetBSD', 'none', 'OpenBSD', 'OSX', 'Posix', - 'PPC', 'PPC64', 'PPC_HardFloat', 'PPC_SoftFloat', 'S390', 'S390X', 'SDC', 'SH', 'SH64', 'SkyOS', - 'Solaris', 'SPARC', 'SPARC64', 'SPARC_HardFloat', 'SPARC_SoftFloat', 'SPARC_V8Plus', 'SysV3', - 'SysV4', 'unittest', 'Win32', 'Win64', 'Windows', 'X86', 'X86_64' -} -local open_paren = token(lexer.OPERATOR, '(') -lex:add_rule('version', token(lexer.KEYWORD, 'version') * ws^-1 * open_paren * ws^-1 * - token('versions', version)) -lex:add_style('versions', lexer.styles.constant) +local open_paren = lex:tag(lexer.OPERATOR, '(') +lex:add_rule('version', lex:tag(lexer.KEYWORD, 'version') * ws^-1 * open_paren * ws^-1 * + lex:tag(lexer.CONSTANT_BUILTIN .. '.version', lex:word_match('version'))) -- Scopes. -local scope = word_match('exit success failure') -lex:add_rule('scope', - token(lexer.KEYWORD, 'scope') * ws^-1 * open_paren * ws^-1 * token('scopes', scope)) -lex:add_style('scopes', lexer.styles.constant) +lex:add_rule('scope', lex:tag(lexer.KEYWORD, 'scope') * ws^-1 * open_paren * ws^-1 * + lex:tag(lexer.CONSTANT_BUILTIN .. '.scope', lexer.word_match('exit success failure'))) -- Traits. -local trait = word_match{ - 'allMembers', 'classInstanceSize', 'compiles', 'derivedMembers', 'getAttributes', 'getMember', - 'getOverloads', 'getProtection', 'getUnitTests', 'getVirtualFunctions', 'getVirtualIndex', - 'getVirtualMethods', 'hasMember', 'identifier', 'isAbstractClass', 'isAbstractFunction', - 'isArithmetic', 'isAssociativeArray', 'isFinalClass', 'isFinalFunction', 'isFloating', - 'isIntegral', 'isLazy', 'isNested', 'isOut', 'isOverrideFunction', 'isPOD', 'isRef', 'isSame', - 'isScalar', 'isStaticArray', 'isStaticFunction', 'isUnsigned', 'isVirtualFunction', - 'isVirtualMethod', 'parent' -} -lex:add_rule('trait', - token(lexer.KEYWORD, '__traits') * ws^-1 * open_paren * ws^-1 * token('traits', trait)) -lex:add_style('traits', {fore = lexer.colors.yellow}) +lex:add_rule('trait', lex:tag(lexer.KEYWORD, '__traits') * ws^-1 * open_paren * ws^-1 * + lex:tag(lexer.VARIABLE_BUILTIN .. '.traits', lex:word_match('trait'))) -- Function names. -lex:add_rule('function', - token(lexer.FUNCTION, lexer.word) * #(ws^-1 * ('!' * lexer.word^-1 * ws^-1)^-1 * '(')) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = lpeg.B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (method + func) * #(ws^-1 * ('!' * lexer.word^-1 * ws^-1)^-1 * '(')) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'abstract', 'align', 'asm', 'assert', 'auto', 'body', 'break', 'case', 'cast', 'catch', 'const', - 'continue', 'debug', 'default', 'delete', 'deprecated', 'do', 'else', 'extern', 'export', 'false', - 'final', 'finally', 'for', 'foreach', 'foreach_reverse', 'goto', 'if', 'import', 'immutable', - 'in', 'inout', 'invariant', 'is', 'lazy', 'macro', 'mixin', 'new', 'nothrow', 'null', 'out', - 'override', 'pragma', 'private', 'protected', 'public', 'pure', 'ref', 'return', 'scope', - 'shared', 'static', 'super', 'switch', 'synchronized', 'this', 'throwtrue', 'try', 'typeid', - 'typeof', 'unittest', 'version', 'virtual', 'volatile', 'while', 'with', '__gshared', '__thread', - '__traits', '__vector', '__parameters' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Types. -local type = token(lexer.TYPE, word_match{ - 'alias', 'bool', 'byte', 'cdouble', 'cent', 'cfloat', 'char', 'class', 'creal', 'dchar', - 'delegate', 'double', 'enum', 'float', 'function', 'idouble', 'ifloat', 'int', 'interface', - 'ireal', 'long', 'module', 'package', 'ptrdiff_t', 'real', 'short', 'size_t', 'struct', - 'template', 'typedef', 'ubyte', 'ucent', 'uint', 'ulong', 'union', 'ushort', 'void', 'wchar', - 'string', 'wstring', 'dstring', 'hash_t', 'equals_t' -}) -lex:add_rule('type', type) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) -- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match{ - '__FILE__', '__LINE__', '__DATE__', '__EOF__', '__TIME__', '__TIMESTAMP__', '__VENDOR__', - '__VERSION__', '__FUNCTION__', '__PRETTY_FUNCTION__', '__MODULE__' -})) +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Properties. -local dot = token(lexer.OPERATOR, '.') -lex:add_rule('property', lpeg.B(lexer.alnum + ')') * dot * token(lexer.VARIABLE, word_match{ - 'alignof', 'dig', 'dup', 'epsilon', 'idup', 'im', 'init', 'infinity', 'keys', 'length', - 'mangleof', 'mant_dig', 'max', 'max_10_exp', 'max_exp', 'min', 'min_normal', 'min_10_exp', - 'min_exp', 'nan', 'offsetof', 'ptr', 're', 'rehash', 'reverse', 'sizeof', 'sort', 'stringof', - 'tupleof', 'values' -})) +local dot = lex:tag(lexer.OPERATOR, '.') +lex:add_rule('property', lpeg.B(lexer.alnum + ')') * dot * + lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match('property'))) -- Strings. local sq_str = lexer.range("'", true) * S('cwd')^-1 @@ -106,37 +55,87 @@ local str = sq_str + dq_str + lit_str + bt_str + hex_str + other_hex_str for left, right in pairs{['['] = ']', ['('] = ')', ['{'] = '}', ['<'] = '>'} do str = str + lexer.range('q"' .. left, right .. '"', false, false, true) * S('cwd')^-1 end -lex:add_rule('string', token(lexer.STRING, str)) +lex:add_rule('string', lex:tag(lexer.STRING, str)) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments. local line_comment = lexer.to_eol('//', true) local block_comment = lexer.range('/*', '*/') local nested_comment = lexer.range('/+', '+/', false, false, true) -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment + nested_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment + nested_comment)) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local hex_num = lexer.hex_num * ('_' * lexer.xdigit^1)^0 -local bin_num = '0' * S('bB') * S('01_')^1 * -lexer.xdigit -local oct_num = '0' * S('01234567_')^1 -local integer = S('+-')^-1 * (hex_num + oct_num + bin_num + dec) -lex:add_rule('number', token(lexer.NUMBER, (lexer.float + integer) * S('uULdDfFi')^-1)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number_('_') * S('uULdDfFi')^-1)) -- Preprocessor. -lex:add_rule('annotation', token('annotation', '@' * lexer.word^1)) -lex:add_style('annotation', lexer.styles.preprocessor) -lex:add_rule('preprocessor', token(lexer.PREPROCESSOR, lexer.to_eol('#'))) +lex:add_rule('annotation', lex:tag(lexer.ANNOTATION, '@' * lexer.word^1)) +lex:add_rule('preprocessor', lex:tag(lexer.PREPROCESSOR, lexer.to_eol('#'))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('?=!<>+-*$/%&|^~.,;:()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('?=!<>+-*$/%&|^~.,;:()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') lex:add_fold_point(lexer.COMMENT, '/+', '+/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +-- Word lists. +lex:set_word_list('version', { + 'AArch64', 'AIX', 'all', 'Alpha', 'Alpha_HardFloat', 'Alpha_SoftFloat', 'Android', 'ARM', + 'ARM_HardFloat', 'ARM_SoftFloat', 'ARM_SoftFP', 'ARM_Thumb', 'assert', 'BigEndian', 'BSD', + 'Cygwin', 'D_Coverage', 'D_Ddoc', 'D_HardFloat', 'DigitalMars', 'D_InlineAsm_X86', + 'D_InlineAsm_X86_64', 'D_LP64', 'D_NoBoundsChecks', 'D_PIC', 'DragonFlyBSD', 'D_SIMD', + 'D_SoftFloat', 'D_Version2', 'D_X32', 'FreeBSD', 'GNU', 'Haiku', 'HPPA', 'HPPA64', 'Hurd', 'IA64', + 'LDC', 'linux', 'LittleEndian', 'MIPS32', 'MIPS64', 'MIPS_EABI', 'MIPS_HardFloat', 'MIPS_N32', + 'MIPS_N64', 'MIPS_O32', 'MIPS_O64', 'MIPS_SoftFloat', 'NetBSD', 'none', 'OpenBSD', 'OSX', 'Posix', + 'PPC', 'PPC64', 'PPC_HardFloat', 'PPC_SoftFloat', 'S390', 'S390X', 'SDC', 'SH', 'SH64', 'SkyOS', + 'Solaris', 'SPARC', 'SPARC64', 'SPARC_HardFloat', 'SPARC_SoftFloat', 'SPARC_V8Plus', 'SysV3', + 'SysV4', 'unittest', 'Win32', 'Win64', 'Windows', 'X86', 'X86_64' +}) + +lex:set_word_list('trait', { + 'allMembers', 'classInstanceSize', 'compiles', 'derivedMembers', 'getAttributes', 'getMember', + 'getOverloads', 'getProtection', 'getUnitTests', 'getVirtualFunctions', 'getVirtualIndex', + 'getVirtualMethods', 'hasMember', 'identifier', 'isAbstractClass', 'isAbstractFunction', + 'isArithmetic', 'isAssociativeArray', 'isFinalClass', 'isFinalFunction', 'isFloating', + 'isIntegral', 'isLazy', 'isNested', 'isOut', 'isOverrideFunction', 'isPOD', 'isRef', 'isSame', + 'isScalar', 'isStaticArray', 'isStaticFunction', 'isUnsigned', 'isVirtualFunction', + 'isVirtualMethod', 'parent' +}) + +lex:set_word_list(lexer.KEYWORD, { + 'abstract', 'align', 'asm', 'assert', 'auto', 'body', 'break', 'case', 'cast', 'catch', 'const', + 'continue', 'debug', 'default', 'delete', 'deprecated', 'do', 'else', 'extern', 'export', 'false', + 'final', 'finally', 'for', 'foreach', 'foreach_reverse', 'goto', 'if', 'import', 'immutable', + 'in', 'inout', 'invariant', 'is', 'lazy', 'macro', 'mixin', 'new', 'nothrow', 'null', 'out', + 'override', 'pragma', 'private', 'protected', 'public', 'pure', 'ref', 'return', 'scope', + 'shared', 'static', 'super', 'switch', 'synchronized', 'this', 'throwtrue', 'try', 'typeid', + 'typeof', 'unittest', 'version', 'virtual', 'volatile', 'while', 'with', '__gshared', '__thread', + '__traits', '__vector', '__parameters' +}) + +lex:set_word_list(lexer.TYPE, { + 'alias', 'bool', 'byte', 'cdouble', 'cent', 'cfloat', 'char', 'class', 'creal', 'dchar', + 'delegate', 'double', 'enum', 'float', 'function', 'idouble', 'ifloat', 'int', 'interface', + 'ireal', 'long', 'module', 'package', 'ptrdiff_t', 'real', 'short', 'size_t', 'struct', + 'template', 'typedef', 'ubyte', 'ucent', 'uint', 'ulong', 'union', 'ushort', 'void', 'wchar', + 'string', 'wstring', 'dstring', 'hash_t', 'equals_t' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + '__FILE__', '__LINE__', '__DATE__', '__EOF__', '__TIME__', '__TIMESTAMP__', '__VENDOR__', + '__VERSION__', '__FUNCTION__', '__PRETTY_FUNCTION__', '__MODULE__' +}) + +lex:set_word_list('property', { + 'alignof', 'dig', 'dup', 'epsilon', 'idup', 'im', 'init', 'infinity', 'keys', 'length', + 'mangleof', 'mant_dig', 'max', 'max_10_exp', 'max_exp', 'min', 'min_normal', 'min_10_exp', + 'min_exp', 'nan', 'offsetof', 'ptr', 're', 'rehash', 'reverse', 'sizeof', 'sort', 'stringof', + 'tupleof', 'values' +}) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/dockerfile.lua b/lua/lexers/dockerfile.lua index 4b131df..9b335de 100644 --- a/lua/lexers/dockerfile.lua +++ b/lua/lexers/dockerfile.lua @@ -1,40 +1,47 @@ --- Copyright 2016-2022 Alejandro Baez (https://keybase.io/baez). See LICENSE. +-- Copyright 2016-2024 Alejandro Baez (https://keybase.io/baez). See LICENSE. -- Dockerfile LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('dockerfile', {fold_by_indentation = true}) - --- Whitespace -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(..., {fold_by_indentation = true}) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'ADD', 'ARG', 'CMD', 'COPY', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'FROM', 'LABEL', 'MAINTAINER', - 'ONBUILD', 'RUN', 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR' -})) +local keyword = lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD)) +lex:add_rule('keyword', keyword) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Variable. lex:add_rule('variable', - token(lexer.VARIABLE, S('$')^1 * (P('{')^1 * lexer.word * P('}')^1 + lexer.word))) +-B('\\') * lex:tag(lexer.OPERATOR, '$' * P('{')^-1) * lex:tag(lexer.VARIABLE, lexer.word)) -- Strings. local sq_str = lexer.range("'", false, false) local dq_str = lexer.range('"') -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('\\[],=:{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('\\[],=:{}'))) + +local bash = lexer.load('bash') +local start_rule = #P('RUN') * keyword * bash:get_rule('whitespace') +local end_rule = -B('\\') * #lexer.newline * lex:get_rule('whitespace') +lex:embed(bash, start_rule, end_rule) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'ADD', 'ARG', 'CMD', 'COPY', 'ENTRYPOINT', 'ENV', 'EXPOSE', 'FROM', 'LABEL', 'MAINTAINER', + 'ONBUILD', 'RUN', 'STOPSIGNAL', 'USER', 'VOLUME', 'WORKDIR' +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/dot.lua b/lua/lexers/dot.lua index 19c4b60..4451d1f 100644 --- a/lua/lexers/dot.lua +++ b/lua/lexers/dot.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Brian "Sir Alaran" Schott. See LICENSE. +-- Copyright 2006-2024 Brian "Sir Alaran" Schott. See LICENSE. -- Dot LPeg lexer. -- Based off of lexer code by Mitchell. @@ -51,6 +51,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('->()[]{};'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/eiffel.lua b/lua/lexers/eiffel.lua index 8f92250..20f5f44 100644 --- a/lua/lexers/eiffel.lua +++ b/lua/lexers/eiffel.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Eiffel LPeg lexer. local lexer = require('lexer') @@ -53,6 +53,7 @@ lex:add_fold_point(lexer.KEYWORD, 'inspect', 'end') lex:add_fold_point(lexer.KEYWORD, 'once', 'end') lex:add_fold_point(lexer.KEYWORD, 'class', function(text, pos, line, s) return line:find('deferred%s+class') and 0 or 1 end) -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('--')) + +lexer.property['scintillua.comment'] = '--' return lex diff --git a/lua/lexers/elixir.lua b/lua/lexers/elixir.lua index b06e6c0..0215bf0 100644 --- a/lua/lexers/elixir.lua +++ b/lua/lexers/elixir.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 Mitchell. See LICENSE. +-- Copyright 2015-2024 Mitchell. See LICENSE. -- Contributed by Richard Philips. -- Elixir LPeg lexer. @@ -94,4 +94,6 @@ local float = lexer.digit^1 * '.' * lexer.digit^1 * S('eE') * (S('+-')^-1 * lexe lex:add_rule('number', B(1 - (lexer.alpha + '_')) * S('+-')^-1 * token(lexer.NUMBER, float + integer)) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/elm.lua b/lua/lexers/elm.lua index d48e1e3..8e8e911 100644 --- a/lua/lexers/elm.lua +++ b/lua/lexers/elm.lua @@ -1,4 +1,4 @@ --- Copyright 2020-2022 Mitchell. See LICENSE. +-- Copyright 2020-2024 Mitchell. See LICENSE. -- Elm LPeg lexer -- Adapted from Haskell LPeg lexer by Karl Schultheisz. @@ -40,4 +40,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, op)) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/erlang.lua b/lua/lexers/erlang.lua index a0a3a7d..90201d9 100644 --- a/lua/lexers/erlang.lua +++ b/lua/lexers/erlang.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Erlang LPeg lexer. local lexer = require('lexer') @@ -49,12 +49,11 @@ lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.lower * ('_' + lexer.al lex:add_rule('variable', token(lexer.VARIABLE, P('_')^0 * lexer.upper * ('_' + lexer.alnum)^0)) -- Directives. -lex:add_rule('directive', token('directive', '-' * word_match{ +lex:add_rule('directive', token(lexer.PREPROCESSOR, '-' * word_match{ 'author', 'behaviour', 'behavior', 'compile', 'copyright', 'define', 'doc', 'else', 'endif', 'export', 'file', 'ifdef', 'ifndef', 'import', 'include', 'include_lib', 'module', 'record', 'spec', 'type', 'undef' })) -lex:add_style('directive', lexer.styles.preprocessor) -- Strings. local sq_str = lexer.range("'", true) @@ -85,6 +84,7 @@ lex:add_fold_point(lexer.KEYWORD, 'receive', 'end') lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('%')) + +lexer.property['scintillua.comment'] = '%' return lex diff --git a/lua/lexers/fantom.lua b/lua/lexers/fantom.lua index 776c597..47952da 100644 --- a/lua/lexers/fantom.lua +++ b/lua/lexers/fantom.lua @@ -1,4 +1,4 @@ --- Copyright 2018-2022 Simeon Maryasin (MarSoft). See LICENSE. +-- Copyright 2018-2024 Simeon Maryasin (MarSoft). See LICENSE. -- Fantom LPeg lexer. -- Based on Java LPeg lexer by Mitchell and Vim's Fantom syntax. @@ -73,12 +73,12 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number * S('LlFfDd')^-1)) lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}#'))) -- Annotations. -lex:add_rule('facet', token('facet', '@' * lexer.word)) -lex:add_style('facet', lexer.styles.preprocessor) +lex:add_rule('facet', token(lexer.ANNOTATION, '@' * lexer.word)) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/faust.lua b/lua/lexers/faust.lua index ff47149..c338533 100644 --- a/lua/lexers/faust.lua +++ b/lua/lexers/faust.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 David B. Lamkins <david@lamkins.net>. See LICENSE. +-- Copyright 2015-2024 David B. Lamkins <david@lamkins.net>. See LICENSE. -- Faust LPeg lexer, see http://faust.grame.fr/ local lexer = require('lexer') @@ -41,4 +41,6 @@ lex:add_rule('pragma', token(lexer.PREPROCESSOR, lexer.range('<mdoc>', '</mdoc>' -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>~!=^&|?~:;,.()[]{}@#$`\\\''))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/fennel.lua b/lua/lexers/fennel.lua index 3e5abbf..3303927 100644 --- a/lua/lexers/fennel.lua +++ b/lua/lexers/fennel.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Fennel LPeg lexer. -- Contributed by Momohime Honda. @@ -36,8 +36,10 @@ lex:modify_rule('string', token(lexer.STRING, dq_str + kw_str)) lex:modify_rule('comment', token(lexer.COMMENT, lexer.to_eol(';'))) -- Ignore these rules. -lex:modify_rule('longstring', P(false)) +-- lex:modify_rule('longstring', P(false)) lex:modify_rule('label', P(false)) lex:modify_rule('operator', P(false)) +lexer.property['scintillua.comment'] = ';' + return lex diff --git a/lua/lexers/fish.lua b/lua/lexers/fish.lua index e493e8f..5693322 100644 --- a/lua/lexers/fish.lua +++ b/lua/lexers/fish.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 Jason Schindler. See LICENSE. +-- Copyright 2015-2024 Jason Schindler. See LICENSE. -- Fish (http://fishshell.com/) script LPeg lexer. local lexer = require('lexer') @@ -34,8 +34,7 @@ local dq_str = lexer.range('"') lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) -- Shebang. -lex:add_rule('shebang', token('shebang', lexer.to_eol('#!/'))) -lex:add_style('shebang', lexer.styles.label) +lex:add_rule('shebang', token(lexer.COMMENT, lexer.to_eol('#!/'))) -- Comments. lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) @@ -54,4 +53,6 @@ lex:add_fold_point(lexer.KEYWORD, 'if', 'end') lex:add_fold_point(lexer.KEYWORD, 'switch', 'end') lex:add_fold_point(lexer.KEYWORD, 'while', 'end') +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/forth.lua b/lua/lexers/forth.lua index 9a1e6df..e1ad8f6 100644 --- a/lua/lexers/forth.lua +++ b/lua/lexers/forth.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Forth LPeg lexer. -- Contributions from Joseph Eib. @@ -53,4 +53,6 @@ lex:add_rule('number', token(lexer.NUMBER, P('-')^-1 * lexer.digit^1 * (S('./') -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S(':;<>+*-/[]#'))) +lexer.property['scintillua.comment'] = '\\' + return lex diff --git a/lua/lexers/fortran.lua b/lua/lexers/fortran.lua index 7d0480f..9457486 100644 --- a/lua/lexers/fortran.lua +++ b/lua/lexers/fortran.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Fortran LPeg lexer. local lexer = require('lexer') @@ -82,4 +82,6 @@ lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('<>=&+-/*,()'))) +lexer.property['scintillua.comment'] = '!' + return lex diff --git a/lua/lexers/fsharp.lua b/lua/lexers/fsharp.lua index 18df9bb..bdb8c94 100644 --- a/lua/lexers/fsharp.lua +++ b/lua/lexers/fsharp.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- F# LPeg lexer. local lexer = require('lexer') @@ -54,4 +54,6 @@ lex:add_rule('preproc', token(lexer.PREPROCESSOR, lexer.starts_line('#') * S('\t -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('=<>+-*/^.,:;~!@#%^&|?[](){}'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/fstab.lua b/lua/lexers/fstab.lua index 7c9d00e..0cf1536 100644 --- a/lua/lexers/fstab.lua +++ b/lua/lexers/fstab.lua @@ -1,17 +1,33 @@ --- Copyright 2016-2022 Christian Hesse. See LICENSE. +-- Copyright 2016-2024 Christian Hesse. See LICENSE. -- fstab LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('fstab', {lex_by_line = true}) - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(..., {lex_by_line = true}) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Numbers. +local uuid = lexer.xdigit^8 * ('-' * lexer.xdigit^4)^-3 * '-' * lexer.xdigit^12 +local integer = S('+-')^-1 * (lexer.hex_num + lexer.oct_num_('_') + lexer.dec_num_('_')) +lex:add_rule('number', lex:tag(lexer.NUMBER, uuid + lexer.float + integer)) + +-- Identifiers. +lex:add_rule('identifier', + lex:tag(lexer.IDENTIFIER, (lexer.alpha + '_') * (lexer.alnum + S('_.'))^0)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.starts_line(lexer.to_eol('#')))) + +-- Directories. +lex:add_rule('directory', lex:tag(lexer.VARIABLE, '/' * (1 - lexer.space)^0)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('=,'))) + +lex:set_word_list(lexer.KEYWORD, { -- Basic filesystem-independent mount options. 'async', 'atime', 'auto', 'comment', 'context', 'defaults', 'defcontext', 'dev', 'dirsync', 'exec', 'fscontext', 'group', 'iversion', 'lazytime', 'loud', 'mand', '_netdev', 'noatime', @@ -32,8 +48,8 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ 'compress', 'zlib', 'lzo', 'no', 'compress-force', 'degraded', 'device', 'discard', 'enospc_debug', 'fatal_errors', 'bug', 'panic', 'flushoncommit', 'inode_cache', 'max_inline', 'metadata_ratio', 'noacl', 'nobarrier', 'nodatacow', 'nodatasum', 'notreelog', 'recovery', - 'rescan_uuid_tree', 'skip_balance', 'nospace_cache', 'clear_cache', 'ssd', 'nossd', 'ssd_spread', - 'subvol', 'subvolid', 'subvolrootid', 'thread_pool', 'user_subvol_rm_allowed', + 'rescan_uuid_tree', 'skip_balance', 'space_cache', 'nospace_cache', 'clear_cache', 'ssd', 'nossd', + 'ssd_spread', 'subvol', 'subvolid', 'subvolrootid', 'thread_pool', 'user_subvol_rm_allowed', -- Mount options for devpts. 'uid', 'gid', 'mode', 'newinstance', 'ptmxmode', -- Mount options for ext2. @@ -91,6 +107,8 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ 'uni_xlate', 'posix', 'nonumtail', 'utf8', 'shortname', 'lower', 'win95', 'winnt', 'mixed', -- Mount options for usbfs. 'devuid', 'devgid', 'devmode', 'busuid', 'busgid', 'busmode', 'listuid', 'listgid', 'listmode', + -- Mount options for proc. + 'hidepid', -- Filesystems. 'adfs', 'ados', 'affs', 'anon_inodefs', 'atfs', 'audiofs', 'auto', 'autofs', 'bdev', 'befs', 'bfs', 'btrfs', 'binfmt_misc', 'cd9660', 'cfs', 'cgroup', 'cifs', 'coda', 'configfs', 'cpuset', @@ -102,25 +120,8 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ 'smbfs', 'squashfs', 'sockfs', 'sshfs', 'std', 'subfs', 'swap', 'sysfs', 'sysv', 'tcfs', 'tmpfs', 'udf', 'ufs', 'umap', 'umsdos', 'union', 'usbfs', 'userfs', 'vfat', 'vs3fs', 'vxfs', 'wrapfs', 'wvfs', 'xenfs', 'xfs', 'zisofs' -})) - --- Numbers. -local uuid = lexer.xdigit^8 * ('-' * lexer.xdigit^4)^-3 * '-' * lexer.xdigit^12 -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local oct_num = '0' * S('01234567_')^1 -local integer = S('+-')^-1 * (lexer.hex_num + oct_num + dec) -lex:add_rule('number', token(lexer.NUMBER, uuid + lexer.float + integer)) - --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, (lexer.alpha + '_') * (lexer.alnum + S('_.'))^0)) +}) --- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.starts_line(lexer.to_eol('#')))) - --- Directories. -lex:add_rule('directory', token(lexer.VARIABLE, '/' * (1 - lexer.space)^0)) - --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('=,'))) +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/gap.lua b/lua/lexers/gap.lua index 29cfdf9..444a0f6 100644 --- a/lua/lexers/gap.lua +++ b/lua/lexers/gap.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Gap LPeg lexer. local lexer = require('lexer') @@ -39,6 +39,7 @@ lex:add_fold_point(lexer.KEYWORD, 'function', 'end') lex:add_fold_point(lexer.KEYWORD, 'do', 'od') lex:add_fold_point(lexer.KEYWORD, 'if', 'fi') lex:add_fold_point(lexer.KEYWORD, 'repeat', 'until') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/gemini.lua b/lua/lexers/gemini.lua index dc529d4..a5f78a3 100644 --- a/lua/lexers/gemini.lua +++ b/lua/lexers/gemini.lua @@ -1,42 +1,23 @@ - --- Copyright 2006-2017 Mitchell mitchell.att.foicica.com. See LICENSE. --- Markdown LPeg lexer. --- Copyright 2020 Haelwenn (lanodan) Monnier <contact+gemini.lua@hacktivis.me> +-- Copyright 2020-2024 Haelwenn (lanodan) Monnier <contact+gemini.lua@hacktivis.me>. See LICENSE. -- Gemini / Gemtext LPeg lexer. -- See https://gemini.circumlunar.space/docs/specification.html -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, R, S = lpeg.P, lpeg.R, lpeg.S +local lexer = lexer +local P, S = lpeg.P, lpeg.S -local lex = lexer.new('gemini') +local lex = lexer.new(...) -local header = token('h3', lexer.starts_line('###') * lexer.nonnewline^0) + - token('h2', lexer.starts_line('##') * lexer.nonnewline^0) + - token('h1', lexer.starts_line('#') * lexer.nonnewline^0) +local header = lex:tag(lexer.HEADING .. '.h3', lexer.to_eol(lexer.starts_line('###'))) + + lex:tag(lexer.HEADING .. '.h2', lexer.to_eol(lexer.starts_line('##'))) + + lex:tag(lexer.HEADING .. '.h1', lexer.to_eol(lexer.starts_line('#'))) lex:add_rule('header', header) -lex:add_style('h1', {fore = lexer.colors.red, size = 15}) -lex:add_style('h2', {fore = lexer.colors.red, size = 14}) -lex:add_style('h3', {fore = lexer.colors.red, size = 13}) - -local list = token('list', lexer.starts_line('*') * lexer.nonnewline^0) -lex:add_rule('list', list) -lex:add_style('list', lexer.styles.constant) -local blockquote = token(lexer.STRING, lexer.starts_line('>') * lexer.nonnewline^0) -lex:add_rule('blockquote', blockquote) +lex:add_rule('list', lex:tag(lexer.LIST, lexer.to_eol(lexer.starts_line('*')))) --- Should only match ``` at start of line -local pre = token('pre', lexer.range('```', false, true)) -lex:add_rule('pre', pre) -lex:add_style('pre', lexer.styles.embedded .. {eolfilled = true}) +lex:add_rule('blockquote', lex:tag(lexer.STRING, lexer.to_eol(lexer.starts_line('>')))) --- Whitespace. -local ws = token(lexer.WHITESPACE, S(' \t')^1 + S('\v\r\n')^1) -lex:add_rule('whitespace', ws) +lex:add_rule('pre', lex:tag(lexer.CODE, lexer.to_eol(lexer.range('```', false, true)))) -local link = token('link', lexer.starts_line('=>') * lexer.nonnewline^0) -lex:add_rule('link', link) -lex:add_style('link', {underlined=true}) +lex:add_rule('link', lex:tag(lexer.LINK, lexer.to_eol(lexer.starts_line('=>')))) return lex diff --git a/lua/lexers/gettext.lua b/lua/lexers/gettext.lua index c4c1150..21a2c9b 100644 --- a/lua/lexers/gettext.lua +++ b/lua/lexers/gettext.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Gettext LPeg lexer. local lexer = require('lexer') @@ -26,4 +26,6 @@ lex:add_rule('string', token(lexer.STRING, lexer.range('"', true))) -- Comments. lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#' * S(': .~')))) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/gherkin.lua b/lua/lexers/gherkin.lua index c876fe3..73c9e7d 100644 --- a/lua/lexers/gherkin.lua +++ b/lua/lexers/gherkin.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 Jason Schindler. See LICENSE. +-- Copyright 2015-2024 Jason Schindler. See LICENSE. -- Gherkin (https://github.com/cucumber/cucumber/wiki/Gherkin) LPeg lexer. local lexer = require('lexer') @@ -26,15 +26,14 @@ lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) -- lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Tags. -lex:add_rule('tag', token('tag', '@' * lexer.word^0)) -lex:add_style('tag', lexer.styles.label) +lex:add_rule('tag', token(lexer.LABEL, '@' * lexer.word^0)) -- Placeholders. -lex:add_rule('placeholder', token('placeholder', lexer.range('<', '>', false, false, true))) -lex:add_style('placeholder', lexer.styles.variable) +lex:add_rule('placeholder', token(lexer.VARIABLE, lexer.range('<', '>', false, false, true))) -- Examples. -lex:add_rule('example', token('example', lexer.to_eol('|'))) -lex:add_style('example', lexer.styles.number) +lex:add_rule('example', token(lexer.DEFAULT, lexer.to_eol('|'))) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/git-rebase.lua b/lua/lexers/git-rebase.lua index ee68040..27e9815 100644 --- a/lua/lexers/git-rebase.lua +++ b/lua/lexers/git-rebase.lua @@ -1,20 +1,28 @@ --- Copyright 2017-2021 Marc André Tanner +-- Copyright 2017-2024 Marc André Tanner. See LICENSE. -- git-rebase(1) LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, R = lpeg.P, lpeg.R -local lex = lexer.new('git-rebase', {lex_by_line = true}) - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(..., {lex_by_line = true}) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.starts_line('#') * lexer.nonnewline^0)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol(lexer.starts_line('#')))) -- Keywords. -local keywords = lexer.starts_line(word_match[[ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lexer.starts_line(lex:word_match(lexer.KEYWORD)))) + +-- Commit SHA1. +local function patn(pat, min, max) + return -pat^(max + 1) * pat^min +end + +lex:add_rule('commit', lex:tag(lexer.NUMBER, patn(R('09', 'af'), 7, 40))) + +lex:add_rule('message', lex:tag('message', lexer.to_eol())) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, [[ p pick r reword e edit @@ -27,15 +35,5 @@ local keywords = lexer.starts_line(word_match[[ t reset m merge ]]) -lex:add_rule('keyword', token(lexer.KEYWORD, keywords)) - --- Commit SHA1. -local function patn(pat, min, max) - return -pat^(max + 1) * pat^min -end - -lex:add_rule('commit', token(lexer.NUMBER, patn(R('09', 'af'), 7, 40))) - -lex:add_rule('message', token(lexer.STRING, lexer.nonnewline^1)) return lex diff --git a/lua/lexers/gleam.lua b/lua/lexers/gleam.lua index 6aee725..19eb2d8 100644 --- a/lua/lexers/gleam.lua +++ b/lua/lexers/gleam.lua @@ -1,4 +1,4 @@ --- Copyright 2021-2022 Mitchell. See LICENSE. +-- Copyright 2021-2024 Mitchell. See LICENSE. -- Gleam LPeg lexer -- https://gleam.run/ -- Contributed by Tynan Beatty @@ -88,17 +88,16 @@ local err_tok = token(lexer.ERROR, lexer.any) lex:add_rule('error', err_tok) -- Fold points. -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '(', ')') -- Embedded Bit Strings. -- Mimic lexer.load() by creating a bitstring-specific whitespace style. -local bitstring = lexer.new(lex._NAME .. '_bitstring') -local bitstring_ws = token(bitstring._NAME .. '_whitespace', lexer.space^1) +local bitstring = lexer.new(lex._name .. '_bitstring') +local bitstring_ws = token(bitstring._name .. '_whitespace', lexer.space^1) bitstring:add_rule('whitespace', bitstring_ws) -bitstring:add_style(bitstring._NAME .. '_whitespace', lexer.styles.whitespace) +bitstring:add_style(bitstring._name .. '_whitespace', lexer.styles.whitespace) bitstring:add_rule('type', typ_tok) bitstring:add_rule('module', mod_tok(bitstring_ws)) bitstring:add_rule('keyword', key_tok + token(KEY, word_match{ @@ -116,4 +115,6 @@ bitstring:add_rule('operator', op_tok) bitstring:add_rule('error', err_tok) lex:embed(bitstring, token(OP, '<<'), token(OP, '>>')) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/glsl.lua b/lua/lexers/glsl.lua index 9997a13..60aad9d 100644 --- a/lua/lexers/glsl.lua +++ b/lua/lexers/glsl.lua @@ -1,39 +1,49 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- GLSL LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S, R = lpeg.P, lpeg.S, lpeg.R +local lexer = lexer +local P, S = lpeg.P, lpeg.S -local lex = lexer.new('glsl', {inherit = lexer.load('cpp')}) +local lex = lexer.new(..., {inherit = lexer.load('ansi_c')}) --- Whitespace. -lex:modify_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'attribute', 'const', 'uniform', 'varying', 'buffer', 'shared', 'coherent', 'volatile', + 'restrict', 'readonly', 'writeonly', 'layout', 'centroid', 'flat', 'smooth', 'noperspective', + 'patch', 'sample', 'break', 'continue', 'do', 'for', 'while', 'switch', 'case', 'default', 'if', + 'else', 'subroutine', 'in', 'inout', 'out', 'true', 'false', 'invariant', 'precise', 'discard', + 'return', 'lowp', 'mediump', 'highp', 'precision', 'struct', -- + -- Reserved. + 'common', 'partition', 'active', 'asm', 'class', 'union', 'enum', 'typedef', 'template', 'this', + 'resource', 'goto', 'inline', 'noinline', 'public', 'static', 'extern', 'external', 'interface', + 'superp', 'input', 'output', 'filter', 'sizeof', 'cast', 'namespace', 'using' +}) --- Keywords. -lex:modify_rule('keyword', token(lexer.KEYWORD, word_match{ - 'attribute', 'const', 'in', 'inout', 'out', 'uniform', 'varying', 'invariant', 'centroid', 'flat', - 'smooth', 'noperspective', 'layout', 'patch', 'sample', 'subroutine', 'lowp', 'mediump', 'highp', - 'precision', - -- Macros. - '__VERSION__', '__LINE__', '__FILE__' -}) + lex:get_rule('keyword')) +lex:set_word_list(lexer.TYPE, { + 'atomic_uint', 'float', 'double', 'int', 'void', 'bool', 'mat2', 'mat3', 'mat4', 'dmat2', 'dmat3', + 'dmat4', 'mat2x2', 'mat2x3', 'mat2x4', 'dmat2x2', 'dmat2x3', 'dmat2x4', 'mat3x2', 'mat3x3', + 'mat3x4', 'dmat3x2', 'dmat3x3', 'dmat3x4', 'mat4x2', 'mat4x3', 'mat4x4', 'dmat4x2', 'dmat4x3', + 'dmat4x4', 'vec2', 'vec3', 'vec4', 'ivec2', 'ivec3', 'ivec4', 'bvec2', 'bvec3', 'bvec4', 'dvec2', + 'dvec3', 'dvec4', 'uint', 'uvec2', 'uvec3', 'uvec4', 'sampler1D', 'sampler2D', 'sampler3D', + 'samplerCube', 'sampler1DShadow', 'sampler2DShadow', 'samplerCubeShadow', 'sampler1DArray', + 'sampler2DArray', 'sampler1DArrayShadow', 'sampler2DArrayShadow', 'isampler1D', 'isampler2D', + 'isampler3D', 'isamplerCube', 'isampler1DArray', 'isampler2DArray', 'usampler1D', 'usampler2D', + 'usampler3D', 'usamplerCube', 'usampler1DArray', 'usampler2DArray', 'sampler2DRect', + 'sampler2DRectShadow', 'isampler2DRect', 'usampler2DRect', 'samplerBuffer', 'isamplerBuffer', + 'usamplerBuffer', 'sampler2DMS', 'isampler2DMS', 'usampler2DMS', 'sampler2DMSArray', + 'isampler2DMSArray', 'usampler2DMSArray', 'samplerCubeArray', 'samplerCubeArrayShadow', + 'isamplerCubeArray', 'usamplerCubeArray', 'image1D', 'iimage1D', 'uimage1D', 'image2D', + 'iimage2D', 'uimage2D', 'image3D', 'iimage3D', 'uimage3D', 'image2DRect', 'iimage2DRect', + 'uimage2DRect', 'imageCube', 'iimageCube', 'uimageCube', 'imageBuffer', 'iimageBuffer', + 'uimageBuffer', 'image1DArray', 'iimage1DArray', 'uimage1DArray', 'image2DArray', 'iimage2DArray', + 'uimage2DArray', 'imageCubeArray', 'iimageCubeArray', 'uimageCubeArray', 'image2DMS', + 'iimage2DMS', 'uimage2DMS', 'image2DMSArray', 'iimage2DMSArray', 'uimage2DMSArray', + -- Reserved. + 'long', 'short', 'half', 'fixed', 'unsigned', 'hvec2', 'hvec3', 'hvec4', 'fvec2', 'fvec3', + 'fvec4', 'sampler3DRect' +}) --- Types. --- LuaFormatter off -lex:modify_rule('type', token(lexer.TYPE, - S('bdiu')^-1 * 'vec' * R('24') + - P('d')^-1 * 'mat' * R('24') * ('x' * R('24')^-1) + - S('iu')^-1 * 'sampler' * R('13') * 'D' + - 'sampler' * R('12') * 'D' * P('Array')^-1 * 'Shadow' + - (S('iu')^-1 * 'sampler' * (R('12') * 'DArray' + - word_match('Cube 2DRect Buffer 2DMS 2DMSArray 2DMSCubeArray'))) + - word_match('samplerCubeShadow sampler2DRectShadow samplerCubeArrayShadow')) + --- LuaFormatter on - lex:get_rule('type') + - --- Functions. -token(lexer.FUNCTION, word_match{ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { 'radians', 'degrees', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', 'pow', 'exp', 'log', 'exp2', 'log2', 'sqrt', 'inversesqrt', 'abs', 'sign', 'floor', 'trunc', 'round', 'roundEven', 'ceil', 'fract', 'mod', 'modf', 'min', 'max', @@ -56,10 +66,9 @@ token(lexer.FUNCTION, word_match{ 'interpolateAtCentroid', 'interpolateAtSample', 'interpolateAtOffset', 'noise1', 'noise2', 'noise3', 'noise4', 'EmitStreamVertex', 'EndStreamPrimitive', 'EmitVertex', 'EndPrimitive', 'barrier' -}) + +}) --- Variables. -token(lexer.VARIABLE, word_match{ +lex:set_word_list(lexer.VARIABLE, { 'gl_VertexID', 'gl_InstanceID', 'gl_Position', 'gl_PointSize', 'gl_ClipDistance', 'gl_PrimitiveIDIn', 'gl_InvocationID', 'gl_PrimitiveID', 'gl_Layer', 'gl_PatchVerticesIn', 'gl_TessLevelOuter', 'gl_TessLevelInner', 'gl_TessCoord', 'gl_FragCoord', 'gl_FrontFacing', @@ -69,10 +78,10 @@ token(lexer.VARIABLE, word_match{ 'gl_SecondaryColor', 'gl_Normal', 'gl_Vertex', 'gl_MultiTexCoord0', 'gl_MultiTexCoord1', 'gl_MultiTexCoord2', 'gl_MultiTexCoord3', 'gl_MultiTexCoord4', 'gl_MultiTexCoord5', 'gl_MultiTexCoord6', 'gl_MultiTexCoord7', 'gl_FogCoord' -}) + +}) --- Constants. -token(lexer.CONSTANT, word_match{ +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + '__LINE__', '__FILE__', '__VERSION__', -- 'gl_MaxVertexAttribs', 'gl_MaxVertexUniformComponents', 'gl_MaxVaryingFloats', 'gl_MaxVaryingComponents', 'gl_MaxVertexOutputComponents', 'gl_MaxGeometryInputComponents', 'gl_MaxGeometryOutputComponents', 'gl_MaxFragmentInputComponents', @@ -99,6 +108,10 @@ token(lexer.CONSTANT, word_match{ 'gl_BackLightProduct', 'gl_TextureEnvColor', 'gl_EyePlaneS', 'gl_EyePlaneT', 'gl_EyePlaneR', 'gl_EyePlaneQ', 'gl_ObjectPlaneS', 'gl_ObjectPlaneT', 'gl_ObjectPlaneR', 'gl_ObjectPlaneQ', 'gl_Fog' -})) +}) + +lex:set_word_list(lexer.PREPROCESSOR, 'extension version', true) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/gnuplot.lua b/lua/lexers/gnuplot.lua index a3d93dd..aab557c 100644 --- a/lua/lexers/gnuplot.lua +++ b/lua/lexers/gnuplot.lua @@ -1,33 +1,55 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Gnuplot LPeg lexer. -local lexer = require('lexer') +local lexer = lexer local token, word_match = lexer.token, lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('gnuplot') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Functions. +lex:add_rule('function', lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))) + +-- Variables. +lex:add_rule('variable', lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Strings. +local sq_str = lexer.range("'") +local dq_str = lexer.range('"') +local br_str = lexer.range('[', ']', true) + lexer.range('{', '}', true) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + br_str)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('-+~!$*%=<>&|^?:;()'))) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { 'cd', 'call', 'clear', 'exit', 'fit', 'help', 'history', 'if', 'load', 'pause', 'plot', 'using', 'with', 'index', 'every', 'smooth', 'thru', 'print', 'pwd', 'quit', 'replot', 'reread', 'reset', 'save', 'set', 'show', 'unset', 'shell', 'splot', 'system', 'test', 'unset', 'update' -})) +}) --- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { 'abs', 'acos', 'acosh', 'arg', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'besj0', 'besj1', 'besy0', 'besy1', 'ceil', 'cos', 'cosh', 'erf', 'erfc', 'exp', 'floor', 'gamma', 'ibeta', 'inverf', 'igamma', 'imag', 'invnorm', 'int', 'lambertw', 'lgamma', 'log', 'log10', 'norm', 'rand', 'real', 'sgn', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'column', 'defined', 'tm_hour', 'tm_mday', 'tm_min', 'tm_mon', 'tm_sec', 'tm_wday', 'tm_yday', 'tm_year', 'valid' -})) +}) --- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, word_match{ +lex:set_word_list(lexer.VARIABLE_BUILTIN, { 'angles', 'arrow', 'autoscale', 'bars', 'bmargin', 'border', 'boxwidth', 'clabel', 'clip', 'cntrparam', 'colorbox', 'contour', 'datafile', 'decimalsign', 'dgrid3d', 'dummy', 'encoding', 'fit', 'fontpath', 'format', 'functions', 'function', 'grid', 'hidden3d', 'historysize', @@ -41,21 +63,8 @@ lex:add_rule('variable', token(lexer.VARIABLE, word_match{ 'y2mtics', 'y2range', 'y2tics', 'y2zeroaxis', 'ydata', 'ydtics', 'ylabel', 'ymtics', 'yrange', 'ytics', 'yzeroaxis', 'zdata', 'zdtics', 'cbdata', 'cbdtics', 'zero', 'zeroaxis', 'zlabel', 'zmtics', 'zrange', 'ztics', 'cblabel', 'cbmtics', 'cbrange', 'cbtics' -})) - --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +}) --- Strings. -local sq_str = lexer.range("'") -local dq_str = lexer.range('"') -local br_str = lexer.range('[', ']', true) + lexer.range('{', '}', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str + br_str)) - --- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) - --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('-+~!$*%=<>&|^?:()'))) +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/go.lua b/lua/lexers/go.lua index e440422..844ba77 100644 --- a/lua/lexers/go.lua +++ b/lua/lexers/go.lua @@ -1,61 +1,71 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Go LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('go') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'break', 'case', 'chan', 'const', 'continue', 'default', 'defer', 'else', 'fallthrough', 'for', - 'func', 'go', 'goto', 'if', 'import', 'interface', 'map', 'package', 'range', 'return', 'select', - 'struct', 'switch', 'type', 'var' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match('true false iota nil'))) +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ - 'any', 'bool', 'byte', 'comparable', 'complex64', 'complex128', 'error', 'float32', 'float64', - 'int', 'int8', 'int16', 'int32', 'int64', 'rune', 'string', 'uint', 'uint8', 'uint16', 'uint32', - 'uint64', 'uintptr' -})) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'append', 'cap', 'close', 'complex', 'copy', 'delete', 'imag', 'len', 'make', 'new', 'panic', - 'print', 'println', 'real', 'recover' -})) +local builtin_func = -lpeg.B('.') * + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = lpeg.B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '(')) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) local raw_str = lexer.range('`', false, false) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str + raw_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + raw_str)) -- Comments. local line_comment = lexer.to_eol('//') local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number * P('i')^-1)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number * P('i')^-1)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-*/%&|^<>=!~:;.,()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-*/%&|^<>=!~:;.,()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'break', 'case', 'chan', 'const', 'continue', 'default', 'defer', 'else', 'fallthrough', 'for', + 'func', 'go', 'goto', 'if', 'import', 'interface', 'map', 'package', 'range', 'return', 'select', + 'struct', 'switch', 'type', 'var' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, 'true false iota nil') + +lex:set_word_list(lexer.TYPE, { + 'any', 'bool', 'byte', 'comparable', 'complex64', 'complex128', 'error', 'float32', 'float64', + 'int', 'int8', 'int16', 'int32', 'int64', 'rune', 'string', 'uint', 'uint8', 'uint16', 'uint32', + 'uint64', 'uintptr' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'append', 'cap', 'close', 'complex', 'copy', 'delete', 'imag', 'len', 'make', 'new', 'panic', + 'print', 'println', 'real', 'recover' +}) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/groovy.lua b/lua/lexers/groovy.lua index 42ab49d..f891bd5 100644 --- a/lua/lexers/groovy.lua +++ b/lua/lexers/groovy.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Groovy LPeg lexer. local lexer = require('lexer') @@ -49,7 +49,7 @@ local sq_str = lexer.range("'") local dq_str = lexer.range('"') local tq_str = lexer.range("'''") + lexer.range('"""') local string = token(lexer.STRING, tq_str + sq_str + dq_str) -local regex_str = #P('/') * lexer.last_char_includes('=~|!<>+-*?&,:;([{') * lexer.range('/', true) +local regex_str = lexer.after_set('=~|!<>+-*?&,:;([{', lexer.range('/', true)) local regex = token(lexer.REGEX, regex_str) lex:add_rule('string', string + regex) @@ -62,6 +62,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=~|!<>+-/*?&.,:;()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/gtkrc.lua b/lua/lexers/gtkrc.lua index 0dcbbd0..76f93b0 100644 --- a/lua/lexers/gtkrc.lua +++ b/lua/lexers/gtkrc.lua @@ -1,52 +1,57 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Gtkrc LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local word_match = lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('gtkrc') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match( +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, word_match( 'binding class include module_path pixmap_path im_module_file style widget widget_class'))) -- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, word_match{ - 'bg', 'fg', 'base', 'text', 'xthickness', 'ythickness', 'bg_pixmap', 'font', 'fontset', - 'font_name', 'stock', 'color', 'engine' -})) +lex:add_rule('variable', lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))) -- States. -lex:add_rule('state', - token('state', word_match('ACTIVE SELECTED NORMAL PRELIGHT INSENSITIVE TRUE FALSE'))) -lex:add_style('state', lexer.styles.constant) +lex:add_rule('state', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match('mix shade lighter darker'))) +lex:add_rule('function', lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + S('_-'))^0)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.alpha * (lexer.alnum + S('_-'))^0)) -- Strings. local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.digit^1 * ('.' * lexer.digit^1)^-1)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.digit^1 * ('.' * lexer.digit^1)^-1)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S(':=,*()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S(':=,*()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists. +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'bg', 'fg', 'base', 'text', 'xthickness', 'ythickness', 'bg_pixmap', 'font', 'fontset', + 'font_name', 'stock', 'color', 'engine' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'ACTIVE', 'SELECTED', 'NORMAL', 'PRELIGHT', 'INSENSITIVE', 'TRUE', 'FALSE' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, {'mix', 'shade', 'lighter', 'darker'}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/hare.lua b/lua/lexers/hare.lua index 73068bf..fcf6e6a 100644 --- a/lua/lexers/hare.lua +++ b/lua/lexers/hare.lua @@ -1,59 +1,81 @@ --- Copyright 2021-2022 Mitchell. See LICENSE. +-- Copyright 2021-2024 Mitchell. See LICENSE. -- Hare LPeg lexer --- https://harelang.org --- Contributed by Qiu -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, R, S = lpeg.P, lpeg.R, lpeg.S -local lex = lexer.new('hare') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'as', 'break', 'case', 'const', 'continue', 'def', 'defer', 'else', 'export', 'false', 'fn', - 'for', 'if', 'is', 'let', 'match', 'null', 'nullable', 'return', 'static', 'struct', 'switch', - 'true', 'type', 'use', 'yield' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Types. +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'len', 'alloc', 'free', 'assert', 'abort', 'size', 'append', 'insert', 'delete', 'vastart', - 'vaarg', 'vaend' -})) +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, + lex:word_match(lexer.FUNCTION_BUILTIN) + 'size' * #(lexer.space^0 * '(')) +local func = lex:tag(lexer.FUNCTION, lex:tag(lexer.FUNCTION, lexer.word * ('::' * lexer.word)^0 * + #(lexer.space^0 * '('))) +lex:add_rule('function', builtin_func + func) --- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ - 'bool', 'enum', 'f32', 'f64', 'i16', 'i32', 'i64', 'i8', 'int', 'u16', 'u32', 'u64', 'u8', 'uint', - 'uintptr', 'union', 'void', 'rune', 'str', 'char' -})) +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. +local sq_str = lexer.range("'", true) local dq_str = lexer.range('"') -local raw_str = lexer.range('`') -lex:add_rule('string', token(lexer.STRING, dq_str + raw_str)) +local raw_str = lexer.range('`', false, false) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + raw_str)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('//'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('//'))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +local bin_num = '0b' * R('01')^1 * -lexer.xdigit +local oct_num = '0o' * R('07')^1 * -lexer.xdigit +local hex_num = '0x' * lexer.xdigit^1 +local int_suffix = lexer.word_match('i u z i8 i16 i32 i64 u8 u16 u32 u64') +local float_suffix = lexer.word_match('f32 f64') +local suffix = int_suffix + float_suffix +local integer = S('+-')^-1 * + ((hex_num + oct_num + bin_num) * int_suffix^-1 + lexer.dec_num * suffix^-1) +local float = lexer.float * float_suffix^-1 +lex:add_rule('number', lex:tag(lexer.NUMBER, integer + float)) + +-- Error assertions +lex:add_rule('error_assert', lex:tag(lexer.ERROR .. '.assert', lpeg.B(')') * P('!'))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%^!=&|?:;,.()[]{}<>'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%^!=&|?~:;,.()[]{}<>'))) --- At rule. -lex:add_rule('at_rule', token('at_rule', '@' * word_match('noreturn offset init fini test symbol'))) -lex:add_style('at_rule', lexer.styles.preprocessor) +-- Attributes. +lex:add_rule('attribute', lex:tag(lexer.ANNOTATION, '@' * lexer.word)) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'as', 'break', 'case', 'const', 'continue', 'def', 'defer', 'else', 'export', 'fn', 'for', 'if', + 'is', 'let', 'match', 'nullable', 'return', 'static', 'switch', 'type', 'use', 'yield', '_' +}) + +lex:set_word_list(lexer.TYPE, { + 'bool', 'enum', 'f32', 'f64', 'i16', 'i32', 'i64', 'i8', 'int', 'opaque', 'never', 'rune', 'size', + 'str', 'struct', 'u16', 'u32', 'u64', 'u8', 'uint', 'uintptr', 'union', 'valist' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'abort', 'align', 'alloc', 'append', 'assert', 'delete', 'free', 'insert', 'len', 'offset', + 'vaarg', 'vaend', 'vastart' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, 'false null true void') + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/haskell.lua b/lua/lexers/haskell.lua index 9fd6631..0242423 100644 --- a/lua/lexers/haskell.lua +++ b/lua/lexers/haskell.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Haskell LPeg lexer. -- Modified by Alex Suraci. @@ -42,4 +42,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, op)) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/html.lua b/lua/lexers/html.lua index 0cb3c2f..9146b63 100644 --- a/lua/lexers/html.lua +++ b/lua/lexers/html.lua @@ -1,99 +1,63 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- HTML LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local word_match = lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('html') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(..., {no_user_word_lists = true}) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.range('<!--', '-->'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.range('<!--', '-->'))) -- Doctype. -lex:add_rule('doctype', token('doctype', lexer.range('<!' * word_match('doctype', true), '>'))) -lex:add_style('doctype', lexer.styles.comment) - --- Elements. -local single_element = token('single_element', '<' * P('/')^-1 * word_match( - { - 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', - 'param', 'source', 'track', 'wbr' - }, true)) -local paired_element = token('element', '<' * P('/')^-1 * word_match({ - 'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'body', - 'button', 'canvas', 'caption', 'cite', 'code', 'colgroup', 'content', 'data', 'datalist', 'dd', - 'decorator', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', - 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', - 'html', 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'menu', - 'menuitem', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', - 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', - 'small', 'spacer', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', - 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'u', 'ul', 'var', 'video' -}, true)) -local known_element = single_element + paired_element -local unknown_element = token('unknown_element', '<' * P('/')^-1 * (lexer.alnum + '-')^1) -local element = (known_element + unknown_element) * -P(':') -lex:add_rule('element', element) -lex:add_style('single_element', lexer.styles.keyword) -lex:add_style('element', lexer.styles.keyword) -lex:add_style('unknown_element', lexer.styles.keyword .. {italics = true}) +lex:add_rule('doctype', + lex:tag(lexer.TAG .. '.doctype', lexer.range('<!' * word_match('doctype', true), '>'))) + +-- Tags. +local paired_tag = lex:tag(lexer.TAG, lex:word_match(lexer.TAG, true)) +local single_tag = lex:tag(lexer.TAG .. '.single', lex:word_match(lexer.TAG .. '.single', true)) +local known_tag = paired_tag + single_tag +local unknown_tag = lex:tag(lexer.TAG .. '.unknown', (lexer.alnum + '-')^1) +local tag = lex:tag(lexer.TAG .. '.chars', '<' * P('/')^-1) * (known_tag + unknown_tag) * -P(':') +lex:add_rule('tag', tag) -- Closing tags. -local tag_close = token('element', P('/')^-1 * '>') +local tag_close = lex:tag(lexer.TAG .. '.chars', P('/')^-1 * '>') lex:add_rule('tag_close', tag_close) --- Attributes. -local known_attribute = token('attribute', word_match({ - 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'alt', 'async', 'autocomplete', - 'autofocus', 'autoplay', 'bgcolor', 'border', 'buffered', 'challenge', 'charset', 'checked', - 'cite', 'class', 'code', 'codebase', 'color', 'cols', 'colspan', 'content', 'contenteditable', - 'contextmenu', 'controls', 'coords', 'data', 'data-', 'datetime', 'default', 'defer', 'dir', - 'dirname', 'disabled', 'download', 'draggable', 'dropzone', 'enctype', 'for', 'form', 'headers', - 'height', 'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id', 'ismap', 'itemprop', - 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'loop', 'low', 'manifest', 'max', - 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'novalidate', 'open', 'optimum', - 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', - 'required', 'reversed', 'role', 'rows', 'rowspan', 'sandbox', 'scope', 'scoped', 'seamless', - 'selected', 'shape', 'size', 'sizes', 'span', 'spellcheck', 'src', 'srcdoc', 'srclang', 'start', - 'step', 'style', 'summary', 'tabindppex', 'target', 'title', 'type', 'usemap', 'value', 'width', - 'wrap' -}, true) + ((P('data-') + 'aria-') * (lexer.alnum + '-')^1)) -local unknown_attribute = token('unknown_attribute', (lexer.alnum + '-')^1) -local attribute = (known_attribute + unknown_attribute) * #(lexer.space^0 * '=') -lex:add_rule('attribute', attribute) -lex:add_style('attribute', lexer.styles.type) -lex:add_style('unknown_attribute', lexer.styles.type .. {italics = true}) - -- Equals. -- TODO: performance is terrible on large files. local in_tag = P(function(input, index) local before = input:sub(1, index - 1) local s, e = before:find('<[^>]-$'), before:find('>[^<]-$') - if s and e then return s > e and index or nil end - if s then return index end - return input:find('^[^<]->', index) and index or nil + if s and e then return s > e end + if s then return true end + return input:find('^[^<]->', index) ~= nil end) -local equals = token(lexer.OPERATOR, '=') -- * in_tag +local equals = lex:tag(lexer.OPERATOR, '=') -- * in_tag -- lex:add_rule('equals', equals) +-- Attributes. +local known_attribute = lex:tag(lexer.ATTRIBUTE, lex:word_match(lexer.ATTRIBUTE, true) + + ((P('data-') + 'aria-') * (lexer.alnum + '-')^1)) +local unknown_attribute = lex:tag(lexer.ATTRIBUTE .. '.unknown', (lexer.alnum + '-')^1) +local ws = lex:get_rule('whitespace') +local attribute_eq = (known_attribute + unknown_attribute) * ws^-1 * equals +lex:add_rule('attribute', attribute_eq) + -- Strings. -local string = #S('\'"') * lexer.last_char_includes('=') * - token(lexer.STRING, lexer.range("'") + lexer.range('"')) +local string = lex:tag(lexer.STRING, lexer.after_set('=', lexer.range("'") + lexer.range('"'))) lex:add_rule('string', string) -- Numbers. -local number = token(lexer.NUMBER, lexer.dec_num * P('%')^-1) -lex:add_rule('number', #lexer.digit * lexer.last_char_includes('=') * number) -- *in_tag) +local number = lex:tag(lexer.NUMBER, lexer.dec_num * P('%')^-1) +lex:add_rule('number', lexer.after_set('=', number)) -- *in_tag) -- Entities. -lex:add_rule('entity', token('entity', '&' * (lexer.any - lexer.space - ';')^1 * ';')) -lex:add_style('entity', lexer.styles.comment) +lex:add_rule('entity', lex:tag(lexer.CONSTANT_BUILTIN .. '.entity', + '&' * (lexer.any - lexer.space - ';')^1 * ';')) -- Fold points. local function disambiguate_lt(text, pos, line, s) @@ -105,44 +69,84 @@ local function disambiguate_lt(text, pos, line, s) return 1 end end -lex:add_fold_point('element', '<', disambiguate_lt) -lex:add_fold_point('unknown_element', '<', disambiguate_lt) +lex:add_fold_point(lexer.TAG .. '.chars', '<', disambiguate_lt) lex:add_fold_point(lexer.COMMENT, '<!--', '-->') -- Tags that start embedded languages. -- Export these patterns for proxy lexers (e.g. ASP) that need them. -lex.embed_start_tag = element * (ws * attribute * ws^-1 * equals * ws^-1 * string)^0 * ws^-1 * - tag_close -lex.embed_end_tag = element * tag_close +lex.embed_start_tag = tag * (ws * attribute_eq * ws^-1 * string)^0 * ws^-1 * tag_close +lex.embed_end_tag = tag * tag_close -- Embedded CSS (<style type="text/css"> ... </style>). local css = lexer.load('css') -local style_element = word_match('style', true) -local css_start_rule = #('<' * style_element * ('>' + P(function(input, index) - if input:find('^%s+type%s*=%s*(["\'])text/css%1', index) then return index end +local style_tag = word_match('style', true) +local css_start_rule = #('<' * style_tag * ('>' + P(function(input, index) + if input:find('^%s+type%s*=%s*(["\'])text/css%1', index) then return true end end))) * lex.embed_start_tag -local css_end_rule = #('</' * style_element * ws^-1 * '>') * lex.embed_end_tag +local css_end_rule = #('</' * style_tag * '>') * lex.embed_end_tag lex:embed(css, css_start_rule, css_end_rule) +-- Embedded CSS in style="" attribute. +local style = lexer.load('css', 'css.style') +css_start_rule = #(P('style') * lexer.space^0 * '=') * attribute_eq * ws^-1 * + lex:tag(lexer.STRING, '"') +css_end_rule = lex:tag(lexer.STRING, '"') +lex:embed(style, css_start_rule, css_end_rule) -- only double-quotes for now -- Embedded JavaScript (<script type="text/javascript"> ... </script>). local js = lexer.load('javascript') -local script_element = word_match('script', true) -local js_start_rule = #('<' * script_element * ('>' + P(function(input, index) - if input:find('^%s+type%s*=%s*(["\'])text/javascript%1', index) then return index end +local script_tag = word_match('script', true) +local js_start_rule = #('<' * script_tag * ('>' + P(function(input, index) + if input:find('^%s+type%s*=%s*(["\'])text/javascript%1', index) then return true end end))) * lex.embed_start_tag -local js_end_rule = #('</' * script_element * ws^-1 * '>') * lex.embed_end_tag -local js_line_comment = '//' * (lexer.nonnewline - js_end_rule)^0 -local js_block_comment = '/*' * (lexer.any - '*/' - js_end_rule)^0 * P('*/')^-1 -js:modify_rule('comment', token(lexer.COMMENT, js_line_comment + js_block_comment)) +local js_end_rule = #('</' * script_tag * '>') * lex.embed_end_tag lex:embed(js, js_start_rule, js_end_rule) -- Embedded CoffeeScript (<script type="text/coffeescript"> ... </script>). local cs = lexer.load('coffeescript') -script_element = word_match('script', true) -local cs_start_rule = #('<' * script_element * P(function(input, index) - if input:find('^[^>]+type%s*=%s*(["\'])text/coffeescript%1', index) then return index end +script_tag = word_match('script', true) +local cs_start_rule = #('<' * script_tag * P(function(input, index) + if input:find('^[^>]+type%s*=%s*(["\'])text/coffeescript%1', index) then return true end end)) * lex.embed_start_tag -local cs_end_rule = #('</' * script_element * ws^-1 * '>') * lex.embed_end_tag +local cs_end_rule = #('</' * script_tag * '>') * lex.embed_end_tag lex:embed(cs, cs_start_rule, cs_end_rule) +-- Word lists. +lex:set_word_list(lexer.TAG .. '.single', { + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', + 'param', 'source', 'track', 'wbr' +}) + +lex:set_word_list(lexer.TAG, { + 'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'body', + 'button', 'canvas', 'caption', 'cite', 'code', 'colgroup', 'content', 'data', 'datalist', 'dd', + 'decorator', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', + 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', + 'html', 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'menu', + 'menuitem', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', + 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'shadow', + 'small', 'spacer', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', + 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'u', 'ul', 'var', 'video' +}) + +lex:set_word_list(lexer.ATTRIBUTE, { + 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'alt', 'async', 'autocomplete', + 'autofocus', 'autoplay', 'bgcolor', 'border', 'buffered', 'challenge', 'charset', 'checked', + 'cite', 'class', 'code', 'codebase', 'color', 'cols', 'colspan', 'content', 'contenteditable', + 'contextmenu', 'controls', 'coords', 'data', 'data-', 'datetime', 'default', 'defer', 'dir', + 'dirname', 'disabled', 'download', 'draggable', 'dropzone', 'enctype', 'for', 'form', 'headers', + 'height', 'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id', 'ismap', 'itemprop', + 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'loop', 'low', 'manifest', 'max', + 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'novalidate', 'open', 'optimum', + 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', + 'required', 'reversed', 'role', 'rows', 'rowspan', 'sandbox', 'scope', 'scoped', 'seamless', + 'selected', 'shape', 'size', 'sizes', 'span', 'spellcheck', 'src', 'srcdoc', 'srclang', 'start', + 'step', 'style', 'summary', 'tabindex', 'target', 'title', 'type', 'usemap', 'value', 'width', + 'wrap' +}) + +lexer.property['scintillua.comment'] = '<!--|-->' +lexer.property['scintillua.angle.braces'] = '1' +lexer.property['scintillua.word.chars'] = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' + return lex diff --git a/lua/lexers/icon.lua b/lua/lexers/icon.lua index b66312c..4881628 100644 --- a/lua/lexers/icon.lua +++ b/lua/lexers/icon.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- LPeg lexer for the Icon programming language. -- http://www.cs.arizona.edu/icon -- Contributed by Carl Sturtivant. @@ -55,6 +55,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>~!=^&|?~@:;,.()[]{}'))) lex:add_fold_point(lexer.PREPROCESSOR, 'ifdef', 'endif') lex:add_fold_point(lexer.PREPROCESSOR, 'ifndef', 'endif') lex:add_fold_point(lexer.KEYWORD, 'procedure', 'end') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/idl.lua b/lua/lexers/idl.lua index 9a2250c..5ba0863 100644 --- a/lua/lexers/idl.lua +++ b/lua/lexers/idl.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- IDL LPeg lexer. local lexer = require('lexer') @@ -47,4 +47,6 @@ lex:add_rule('preproc', token(lexer.PREPROCESSOR, lexer.starts_line('#') * -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('!<>=+-/*%&|^~.,:;?()[]{}'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/inform.lua b/lua/lexers/inform.lua index 728cbfc..df3353a 100644 --- a/lua/lexers/inform.lua +++ b/lua/lexers/inform.lua @@ -1,5 +1,5 @@ --- Copyright 2010-2022 Jeff Stone. See LICENSE. --- Inform LPeg lexer for Scintillua. +-- Copyright 2010-2024 Jeff Stone. See LICENSE. +-- Inform 6 LPeg lexer for Scintillua. -- JMS 2010-04-25. local lexer = require('lexer') @@ -39,7 +39,7 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ })) -- Library actions. -lex:add_rule('action', token('action', word_match{ +lex:add_rule('action', token(lexer.FUNCTION_BUILTIN, word_match{ 'Answer', 'Ask', 'AskFor', 'Attack', 'Blow', 'Burn', 'Buy', 'Climb', 'Close', 'Consult', 'Cut', 'Dig', 'Disrobe', 'Drink', 'Drop', 'Eat', 'Empty', 'EmptyT', 'Enter', 'Examine', 'Exit', 'Fill', 'FullScore', 'GetOff', 'Give', 'Go', 'GoIn', 'Insert', 'Inv', 'InvTall', 'InvWide', 'Jump', @@ -51,7 +51,6 @@ lex:add_rule('action', token('action', word_match{ 'Tell', 'Think', 'ThrowAt', 'ThrownAt', 'Tie', 'Touch', 'Transfer', 'Turn', 'Unlock', 'VagueGo', 'Verify', 'Version', 'Wait', 'Wake', 'WakeOther', 'Wave', 'WaveHands', 'Wear', 'Yes' })) -lex:add_style('action', lexer.styles.variable) -- Identifiers. lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) @@ -72,4 +71,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.integer + inform_hex + inform_b -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('@~=+-*/%^#=<>;:,.{}[]()&|?'))) +lexer.property['scintillua.comment'] = '!' + return lex diff --git a/lua/lexers/ini.lua b/lua/lexers/ini.lua index a2b107e..15ebd06 100644 --- a/lua/lexers/ini.lua +++ b/lua/lexers/ini.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Ini LPeg lexer. local lexer = require('lexer') @@ -28,12 +28,12 @@ lex:add_rule('label', token(lexer.LABEL, lexer.range('[', ']', true))) lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol(lexer.starts_line(S(';#'))))) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local oct_num = '0' * S('01234567_')^1 -local integer = S('+-')^-1 * (lexer.hex_num + oct_num + dec) +local integer = S('+-')^-1 * (lexer.hex_num + lexer.oct_num_('_') + lexer.dec_num_('_')) lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, '=')) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/io_lang.lua b/lua/lexers/io_lang.lua index 18648fc..fb7f03a 100644 --- a/lua/lexers/io_lang.lua +++ b/lua/lexers/io_lang.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Io LPeg lexer. local lexer = require('lexer') @@ -45,6 +45,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('`~@$%^&*-+/=\\<>?.,:;()[]{}')) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/java.lua b/lua/lexers/java.lua index df83b61..5c8bff2 100644 --- a/lua/lexers/java.lua +++ b/lua/lexers/java.lua @@ -1,67 +1,142 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Java LPeg lexer. -- Modified by Brian Schott. local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('java') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(...) -- Classes. -lex:add_rule('classdef', token(lexer.KEYWORD, 'class') * ws * token(lexer.CLASS, lexer.word)) +lex:add_rule('classdef', lex:tag(lexer.KEYWORD, 'class') * lex:get_rule('whitespace') * + lex:tag(lexer.CLASS, lexer.word)) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'abstract', 'assert', 'break', 'case', 'catch', 'class', 'const', 'continue', 'default', 'do', - 'else', 'enum', 'extends', 'final', 'finally', 'for', 'goto', 'if', 'implements', 'import', - 'instanceof', 'interface', 'native', 'new', 'package', 'private', 'protected', 'public', 'return', - 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', - 'try', 'while', 'volatile', - -- Literals. - 'true', 'false', 'null' -})) - --- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ - 'boolean', 'byte', 'char', 'double', 'float', 'int', 'long', 'short', 'void', 'Boolean', 'Byte', - 'Character', 'Double', 'Float', 'Integer', 'Long', 'Short', 'String' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, lexer.word) * #P('(')) +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = lpeg.B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '(')) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN) + + 'System.' * lexer.word_match('err in out'))) + +-- Types. +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Comments. local line_comment = lexer.to_eol('//', true) local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number * S('LlFfDd')^-1)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number * S('LlFfDd')^-1)) -- Annotations. -lex:add_rule('annotation', token('annotation', '@' * lexer.word)) -lex:add_style('annotation', lexer.styles.preprocessor) +lex:add_rule('annotation', lex:tag(lexer.ANNOTATION, '@' * lexer.word)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) -lex:add_fold_point(lexer.KEYWORD, lexer.fold_consecutive_lines('import')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'abstract', 'assert', 'break', 'case', 'catch', 'class', 'const', 'continue', 'default', 'do', + 'else', 'enum', 'extends', 'final', 'finally', 'for', 'goto', 'if', 'implements', 'import', + 'instanceof', 'interface', 'native', 'new', 'package', 'private', 'protected', 'public', 'return', + 'static', 'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', + 'try', 'while', 'volatile', -- + 'true', 'false', 'null' -- literals +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'clone', 'equals', 'finalize', 'getClass', 'hashCode', 'notify', 'notifyAll', 'toString', 'wait', -- + 'Boolean.compare', 'Boolean.getBoolean', 'Boolean.parseBoolean', 'Boolean.valueOf', -- + 'Byte.compare', 'Byte.decode', 'Byte.parseByte', 'Byte.valueOf', -- + 'Character.charCount', 'Character.codePointAt', 'Character.codePointBefore', + 'Character.codePointCount', 'Character.compare', 'Character.digit', 'Character.forDigit', + 'Character.getName', 'Character.getNumericValue', 'Character.getType', 'Character.isAlphabetic', + 'Character.isDefined', 'Character.isDigit', 'Character.isIdentifierIgnorable', + 'Character.isIdeographic', 'Character.isISOControl', 'Character.isJavaIdentifierPart', + 'Character.isJavaIdentifierStart', 'Character.isLetter', 'Character.isLetterOrDigit', + 'Character.isLowerCase', 'Character.isMirrored', 'Character.isSpaceChar', + 'Character.isSupplementaryCodePoint', 'Character.isSurrogate', 'Character.isSurrogatePair', + 'Character.isTitleCase', 'Character.isUnicodeIdentifierPart', + 'Character.isUnicodeIdentifierStart', 'Character.isUpperCase', 'Character.isValidCodePoint', + 'Character.isWhitespace', 'Character.offsetByCodePoints', 'Character.reverseBytes', + 'Character.toChars', 'Character.toCodePoint', 'Character.toLowerCase', 'Character.toTitleCase', + 'Character.toUpperCase', 'Character.valueOf', -- + 'Double.compare', 'Double.doubleToLongBits', 'Double.doubleToRawLongBits', 'Double.isInfinite', + 'Double.longBitsToDouble', 'Double.parseDouble', 'Double.toHexString', 'Double.valueOf', -- + 'Integer.bitCount', 'Integer.compare', 'Integer.decode', 'Integer.getInteger', + 'Integer.highestOneBit', 'Integer.lowestOneBit', 'Integer.numberOfLeadingZeros', + 'Integer.numberOfTrailingZeros', 'Integer.parseInt', 'Integer.reverse', 'Integer.reverseBytes', + 'Integer.rotateLeft', 'Integer.rotateRight', 'Integer.signum', 'Integer.toBinaryString', + 'Integer.toHexString', 'Integer.toOctalString', 'Integer.valueOf', -- + 'Math.abs', 'Math.acos', 'Math.asin', 'Math.atan', 'Math.atan2', 'Math.cbrt', 'Math.ceil', + 'Math.copySign', 'Math.cos', 'Math.cosh', 'Math.exp', 'Math.expm1', 'Math.floor', + 'Math.getExponent', 'Math.hypot', 'Math.IEEEremainder', 'Math.log', 'Math.log10', 'Math.log1p', + 'Math.max', 'Math.min', 'Math.nextAfter', 'Math.nextUp', 'Math.pow', 'Math.random', 'Math.rint', + 'Math.round', 'Math.scalb', 'Math.signum', 'Math.sin', 'Math.sinh', 'Math.sqrt', 'Math.tan', + 'Math.tanh', 'Math.toDegrees', 'Math.toRadians', 'Math.ulp', -- + 'Runtime.getRuntime', -- + 'String.copyValueOf', 'String.format', 'String.valueOf', -- + 'System.arraycopy', 'System.clearProperty', 'System.console', 'System.currentTimeMillis', + 'System.exit', 'System.gc', 'System.getenv', 'System.getProperties', 'System.getProperty', + 'System.getSecurityManager', 'System.identityHashCode', 'System.inheritedChannel', + 'System.lineSeparator', 'System.load', 'System.loadLibrary', 'System.mapLibraryName', + 'System.nanoTime', 'System.runFinalization', 'System.setErr', 'System.setIn', 'System.setOut', + 'System.setProperties', 'System.setProperty', 'System.setSecurityManager', -- + 'Thread.activeCount', 'Thread.currentThread', 'Thread.dumpStack', 'Thread.enumerate', + 'Thread.getAllStackTraces', 'Thread.getDefaultUncaughtExceptionHandler', 'Thread.holdsLock', + 'Thread.interrupted', 'Thread.setDefaultUncaughtExceptionHandler', 'Thread.sleep', 'Thread.yield' -- +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'Double.MAX_EXPONENT', 'Double.MAX_VALUE', 'Double.MIN_EXPONENT', 'Double.MIN_NORMAL', + 'Double.MIN_VALUE', 'Double.NaN', 'Double.NEGATIVE_INFINITY', 'Double.POSITIVE_INFINITY', -- + 'Integer.MAX_VALUE', 'Integer.MIN_VALUE', -- + 'Math.E', 'Math.PI', -- + 'Thread.MAX_PRIORITY', 'Thread.MIN_PRIORITY', 'Thread.NORM_PRIORITY' +}) + +lex:set_word_list(lexer.TYPE, { + 'boolean', 'byte', 'char', 'double', 'float', 'int', 'long', 'short', 'void', 'Boolean', 'Byte', + 'Character', 'Class', 'Double', 'Enum', 'Float', 'Integer', 'Long', 'Object', 'Process', + 'Runtime', 'Short', 'String', 'StringBuffer', 'StringBuilder', 'Thread', 'Throwable', 'Void', + -- Exceptions. + 'ArithmeticException', 'ArrayIndexOutOfBoundsException', 'ArrayStoreException', + 'ClassCastException', 'ClassNotFoundException', 'CloneNotSupportedException', + 'EnumConstantNotPresentException', 'Exception', 'IllegalAccessException', + 'IllegalArgumentException', 'IllegalMonitorStateException', 'IllegalStateException', + 'IllegalThreadStateException', 'IndexOutOfBoundsException', 'InstantiationException', + 'InterruptedException', 'NegativeArraySizeException', 'NoSuchFieldException', + 'NoSuchMethodException', 'NullPointerException', 'NumberFormatException', + 'ReflectiveOperationException', 'RuntimeException', 'SecurityException', + 'StringIndexOutOfBoundsException', 'TypeNotPresentException', 'UnsupportedOperationException', + -- Errors. + 'AbstractMethodError', 'AssertionError', 'BootstrapMethodError', 'ClassCircularityError', + 'ClassFormatError', 'Error', 'ExceptionInInitializerError', 'IllegalAccessError', + 'IncompatibleClassChangeError', 'InstantiationError', 'InternalError', 'LinkageError', + 'NoClassDefFoundError', 'NoSuchFieldError', 'NoSuchMethodError', 'OutOfMemoryError', + 'StackOverflowError', 'ThreadDeath', 'UnknownError', 'UnsatisfiedLinkError', + 'UnsupportedClassVersionError', 'VerifyError', 'VirtualMachineError' +}) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/javascript.lua b/lua/lexers/javascript.lua index d94baed..e5cce7e 100644 --- a/lua/lexers/javascript.lua +++ b/lua/lexers/javascript.lua @@ -1,17 +1,56 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- JavaScript LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('javascript') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Types. +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) + +-- Functions. +local builtin_func = -B('.') * + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + method + func) * #(lexer.space^0 * '(')) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Comments. +local line_comment = lexer.to_eol('//', true) +local block_comment = lexer.range('/*', '*/') +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) + +-- Strings. +local sq_str = lexer.range("'") +local dq_str = lexer.range('"') +local bq_str = lexer.range('`') +local string = lex:tag(lexer.STRING, sq_str + dq_str + bq_str) +local regex_str = lexer.after_set('+-*%^!=&|?:;,([{<>', lexer.range('/', true) * S('igm')^0) +local regex = lex:tag(lexer.REGEX, regex_str) +lex:add_rule('string', string + regex) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%^!=&|?:;,.()[]{}<>'))) + +-- Fold points. +lex:add_fold_point(lexer.OPERATOR, '{', '}') +lex:add_fold_point(lexer.COMMENT, '/*', '*/') + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { 'abstract', 'async', 'await', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'get', 'goto', 'if', @@ -19,10 +58,9 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ 'null', 'of', 'package', 'private', 'protected', 'public', 'return', 'set', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'yield' -})) +}) --- Types. -lex:add_rule('type', token(lexer.TYPE, word_match{ +lex:set_word_list(lexer.TYPE, { -- Fundamental objects. 'Object', 'Function', 'Boolean', 'Symbol', -- Error Objects. @@ -46,45 +84,15 @@ lex:add_rule('type', token(lexer.TYPE, word_match{ 'Reflect', 'Proxy', -- Other. 'Intl', 'WebAssembly' -})) +}) --- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { 'eval', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent' -})) +}) --- Constants. -lex:add_rule('constant', - token(lexer.CONSTANT, word_match('Infinity NaN undefined globalThis arguments'))) +lex:set_word_list(lexer.CONSTANT_BUILTIN, 'Infinity NaN undefined globalThis arguments') --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) - --- Comments. -local line_comment = lexer.to_eol('//', true) -local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) - --- Strings. -local sq_str = lexer.range("'") -local dq_str = lexer.range('"') -local bq_str = lexer.range('`') -local string = token(lexer.STRING, sq_str + dq_str + bq_str) -local regex_str = - #P('/') * lexer.last_char_includes('+-*%^!=&|?:;,([{<>') * lexer.range('/', true) * S('igm')^0 -local regex = token(lexer.REGEX, regex_str) -lex:add_rule('string', string + regex) - --- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) - --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%^!=&|?:;,.()[]{}<>'))) - --- Fold points. -lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/jq.lua b/lua/lexers/jq.lua index 3797e8d..cf2ea3e 100644 --- a/lua/lexers/jq.lua +++ b/lua/lexers/jq.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- jq 1.6 Lua lexer -- https://stedolan.github.io/jq/wiki -- Anonymously contributed. @@ -78,6 +78,7 @@ lex:add_rule('variable', token(lexer.VARIABLE, '$' * lexer.word)) lex:add_fold_point(lexer.KEYWORD, 'if', 'end') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/json.lua b/lua/lexers/json.lua index 10b6406..9879053 100644 --- a/lua/lexers/json.lua +++ b/lua/lexers/json.lua @@ -1,40 +1,28 @@ --- Copyright 2006-2022 Brian "Sir Alaran" Schott. See LICENSE. +-- Copyright 2006-2024 Brian "Sir Alaran" Schott. See LICENSE. -- JSON LPeg lexer. -- Based off of lexer code by Mitchell. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('json') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Strings. local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match('true false null'))) - --- Comments. -local line_comment = lexer.to_eol('//', true) -local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lexer.word_match('true false null'))) -- Numbers. -local integer = S('+-')^-1 * lexer.dec_num * S('Ll')^-1 -lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('[]{}:,'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('[]{}:,'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) return lex diff --git a/lua/lexers/jsp.lua b/lua/lexers/jsp.lua index b92b886..ef16a1d 100644 --- a/lua/lexers/jsp.lua +++ b/lua/lexers/jsp.lua @@ -1,20 +1,20 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- JSP LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('jsp', {inherit = lexer.load('html')}) +local lex = lexer.new(..., {inherit = lexer.load('html')}) -- Embedded Java. local java = lexer.load('java') -local java_start_rule = token('jsp_tag', '<%' * P('=')^-1) -local java_end_rule = token('jsp_tag', '%>') +local java_start_rule = lex:tag(lexer.PREPROCESSOR, '<%' * P('=')^-1) +local java_end_rule = lex:tag(lexer.PREPROCESSOR, '%>') lex:embed(java, java_start_rule, java_end_rule, true) -lex:add_style('jsp_tag', lexer.styles.embedded) -- Fold points. -lex:add_fold_point('jsp_tag', '<%', '%>') +lex:add_fold_point(lexer.PREPROCESSOR, '<%', '%>') + +lexer.property['scintillua.comment'] = '<!--|-->' return lex diff --git a/lua/lexers/julia.lua b/lua/lexers/julia.lua index 0e751a0..6b5334b 100644 --- a/lua/lexers/julia.lua +++ b/lua/lexers/julia.lua @@ -1,4 +1,4 @@ --- Copyright 2020-2022 Tobias Frilling. See LICENSE. +-- Copyright 2020-2024 Tobias Frilling. See LICENSE. -- Julia lexer. local lexer = require('lexer') @@ -53,12 +53,11 @@ lex:add_rule('type', token(lexer.TYPE, type_builtin_range + type_builtin_array)) -- Macro -lex:add_rule('macro', token('macro', '@' * (id + '.'))) -lex:add_style('macro', lexer.styles.preprocessor) +lex:add_rule('macro', token(lexer.PREPROCESSOR, '@' * (id + '.'))) -- Symbol lex:add_rule('symbol', token('symbol', -B(P(':') + '<') * ':' * id)) -lex:add_style('symbol', lexer.styles.constant) +lex:add_style('symbol', lexer.styles.string) -- Function lex:add_rule('function', token(lexer.FUNCTION, id * #(P('.')^-1 * '('))) @@ -105,6 +104,9 @@ lex:add_rule('character', token('character', char)) lex:add_style('character', lexer.styles.constant) -- Operator -lex:add_rule('operator', token(lexer.OPERATOR, S('+-*/÷<>=!≠≈≤≥%^&|⊻~\\\':?.√'))) +lex:add_rule('operator', token(lexer.OPERATOR, S('+-*/<>=!%^&|~\\\':?.') + '÷' + '≠' + '≈' + + '≤' + '≥' + '⊻' + '√')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/latex.lua b/lua/lexers/latex.lua index a0b8995..c7e63be 100644 --- a/lua/lexers/latex.lua +++ b/lua/lexers/latex.lua @@ -1,50 +1,44 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Latex LPeg lexer. -- Modified by Brian Schott. -- Modified by Robert Gieseke. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local word_match = lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('latex') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Comments. local line_comment = lexer.to_eol('%') local block_comment = lexer.range('\\begin' * P(' ')^0 * '{comment}', '\\end' * P(' ')^0 * '{comment}') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) -- Math environments. local math_word = word_match('align displaymath eqnarray equation gather math multline') local math_begin_end = (P('begin') + P('end')) * P(' ')^0 * '{' * math_word * P('*')^-1 * '}' -lex:add_rule('math', token('math', '$' + '\\' * (S('[]()') + math_begin_end))) -lex:add_style('math', lexer.styles['function']) +lex:add_rule('math', lex:tag('environment.math', '$' + '\\' * (S('[]()') + math_begin_end))) -- LaTeX environments. -lex:add_rule('environment', token('environment', '\\' * (P('begin') + 'end') * P(' ')^0 * '{' * +lex:add_rule('environment', lex:tag('environment', '\\' * (P('begin') + 'end') * P(' ')^0 * '{' * lexer.word * P('*')^-1 * '}')) -lex:add_style('environment', lexer.styles.keyword) -- Sections. -lex:add_rule('section', token('section', '\\' * +lex:add_rule('section', lex:tag('command.section', '\\' * word_match('part chapter section subsection subsubsection paragraph subparagraph') * P('*')^-1)) -lex:add_style('section', lexer.styles.class) -- Commands. -lex:add_rule('command', token('command', '\\' * (lexer.alpha^1 + S('#$&~_^%{}\\')))) -lex:add_style('command', lexer.styles.keyword) +lex:add_rule('command', lex:tag('command', '\\' * (lexer.alpha^1 + S('#$&~_^%{}\\')))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('&#{}[]'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('&#{}[]'))) -- Fold points. lex:add_fold_point(lexer.COMMENT, '\\begin', '\\end') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('%')) lex:add_fold_point('environment', '\\begin', '\\end') lex:add_fold_point(lexer.OPERATOR, '{', '}') +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/ledger.lua b/lua/lexers/ledger.lua index 27ca588..d33ac76 100644 --- a/lua/lexers/ledger.lua +++ b/lua/lexers/ledger.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 Charles Lehner. See LICENSE. +-- Copyright 2015-2024 Charles Lehner. See LICENSE. -- ledger journal LPeg lexer, see http://www.ledger-cli.org/ local lexer = require('lexer') @@ -40,4 +40,6 @@ local directive_word = word_match{ } + S('AYNDCIiOobh') lex:add_rule('directive', token(lexer.KEYWORD, lexer.starts_line(S('!@')^-1 * directive_word))) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/less.lua b/lua/lexers/less.lua index 317d252..e54d868 100644 --- a/lua/lexers/less.lua +++ b/lua/lexers/less.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Robert Gieseke. See LICENSE. +-- Copyright 2006-2024 Robert Gieseke. See LICENSE. -- Less CSS LPeg lexer. -- http://lesscss.org @@ -14,7 +14,6 @@ lex:add_rule('line_comment', token(lexer.COMMENT, lexer.to_eol('//'))) -- Variables. lex:add_rule('variable', token(lexer.VARIABLE, '@' * (lexer.alnum + S('_-{}'))^1)) --- Fold points. -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/lexer.lua b/lua/lexers/lexer.lua index 15ff432..0c76272 100644 --- a/lua/lexers/lexer.lua +++ b/lua/lexers/lexer.lua @@ -1,23 +1,20 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -local M = {} - ---[=[ This comment is for LuaDoc. ---- --- Lexes Scintilla documents and source code with Lua and LPeg. +--- Lexes Scintilla documents and source code with Lua and LPeg. -- -- ### Writing Lua Lexers -- --- Lexers highlight the syntax of source code. Scintilla (the editing component behind --- [Textadept][] and [SciTE][]) traditionally uses static, compiled C++ lexers which are --- notoriously difficult to create and/or extend. On the other hand, Lua makes it easy to to --- rapidly create new lexers, extend existing ones, and embed lexers within one another. Lua --- lexers tend to be more readable than C++ lexers too. +-- Lexers recognize and tag elements of source code for syntax highlighting. Scintilla (the +-- editing component behind [Textadept][] and [SciTE][]) traditionally uses static, compiled C++ +-- lexers which are notoriously difficult to create and/or extend. On the other hand, Lua makes +-- it easy to to rapidly create new lexers, extend existing ones, and embed lexers within one +-- another. Lua lexers tend to be more readable than C++ lexers too. -- --- Lexers are Parsing Expression Grammars, or PEGs, composed with the Lua [LPeg library][]. The --- following table comes from the LPeg documentation and summarizes all you need to know about --- constructing basic LPeg patterns. This module provides convenience functions for creating --- and working with other more advanced patterns and concepts. +-- While lexers can be written in plain Lua, Scintillua prefers using Parsing Expression +-- Grammars, or PEGs, composed with the Lua [LPeg library][]. As a result, this document is +-- devoted to writing LPeg lexers. The following table comes from the LPeg documentation and +-- summarizes all you need to know about constructing basic LPeg patterns. This module provides +-- convenience functions for creating and working with other more advanced patterns and concepts. -- -- Operator | Description -- -|- @@ -30,15 +27,15 @@ local M = {} -- `patt1 * patt2` | Matches `patt1` followed by `patt2`. -- `patt1 + patt2` | Matches `patt1` or `patt2` (ordered choice). -- `patt1 - patt2` | Matches `patt1` if `patt2` does not also match. --- `-patt` | Equivalent to `("" - patt)`. +-- `-patt` | Matches if `patt` does not match, consuming no input. -- `#patt` | Matches `patt` but consumes no input. -- -- The first part of this document deals with rapidly constructing a simple lexer. The next part --- deals with more advanced techniques, such as custom coloring and embedding lexers within one --- another. Following that is a discussion about code folding, or being able to tell Scintilla --- which code blocks are "foldable" (temporarily hideable from view). After that are instructions --- on how to use Lua lexers with the aforementioned Textadept and SciTE editors. Finally there --- are comments on lexer performance and limitations. +-- deals with more advanced techniques, such as embedding lexers within one another. Following +-- that is a discussion about code folding, or being able to tell Scintilla which code blocks +-- are "foldable" (temporarily hideable from view). After that are instructions on how to use +-- Lua lexers with the aforementioned Textadept and SciTE editors. Finally there are comments +-- on lexer performance and limitations. -- -- [LPeg library]: http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html -- [Textadept]: https://orbitalquark.github.io/textadept @@ -46,12 +43,12 @@ local M = {} -- -- ### Lexer Basics -- --- The *lexers/* directory contains all lexers, including your new one. Before attempting to --- write one from scratch though, first determine if your programming language is similar to --- any of the 100+ languages supported. If so, you may be able to copy and modify that lexer, --- saving some time and effort. The filename of your lexer should be the name of your programming --- language in lower case followed by a *.lua* extension. For example, a new Lua lexer has the --- name *lua.lua*. +-- The *lexers/* directory contains all of Scintillua's Lua lexers, including any new ones you +-- write. Before attempting to write one from scratch though, first determine if your programming +-- language is similar to any of the 100+ languages supported. If so, you may be able to copy +-- and modify, or inherit from that lexer, saving some time and effort. The filename of your +-- lexer should be the name of your programming language in lower case followed by a *.lua* +-- extension. For example, a new Lua lexer has the name *lua.lua*. -- -- Note: Try to refrain from using one-character language names like "c", "d", or "r". For -- example, Scintillua uses "ansi_c", "dmd", and "rstats", respectively. @@ -59,116 +56,164 @@ local M = {} -- #### New Lexer Template -- -- There is a *lexers/template.txt* file that contains a simple template for a new lexer. Feel --- free to use it, replacing the '?'s with the name of your lexer. Consider this snippet from +-- free to use it, replacing the '?' with the name of your lexer. Consider this snippet from -- the template: -- -- -- ? LPeg lexer. -- --- local lexer = require('lexer') --- local token, word_match = lexer.token, lexer.word_match +-- local lexer = lexer -- local P, S = lpeg.P, lpeg.S -- --- local lex = lexer.new('?') +-- local lex = lexer.new(...) -- --- -- Whitespace. --- local ws = token(lexer.WHITESPACE, lexer.space^1) --- lex:add_rule('whitespace', ws) +-- [... lexer rules ...] -- --- [...] +-- -- Identifier. +-- local identifier = lex:tag(lexer.IDENTIFIER, lexer.word) +-- lex:add_rule('identifier', identifier) +-- +-- [... more lexer rules ...] -- -- return lex -- --- The first 3 lines of code simply define often used convenience variables. The fourth and --- last lines [define](#lexer.new) and return the lexer object Scintilla uses; they are very --- important and must be part of every lexer. The fifth line defines something called a "token", --- an essential building block of lexers. You will learn about tokens shortly. The sixth line --- defines a lexer grammar rule, which you will learn about later, as well as token styles. (Be --- aware that it is common practice to combine these two lines for short rules.) Note, however, --- the `local` prefix in front of variables, which is needed so-as not to affect Lua's global --- environment. All in all, this is a minimal, working lexer that you can build on. --- --- #### Tokens --- --- Take a moment to think about your programming language's structure. What kind of key --- elements does it have? In the template shown earlier, one predefined element all languages --- have is whitespace. Your language probably also has elements like comments, strings, and --- keywords. Lexers refer to these elements as "tokens". Tokens are the fundamental "building --- blocks" of lexers. Lexers break down source code into tokens for coloring, which results --- in the syntax highlighting familiar to you. It is up to you how specific your lexer is --- when it comes to tokens. Perhaps only distinguishing between keywords and identifiers is --- necessary, or maybe recognizing constants and built-in functions, methods, or libraries is --- desirable. The Lua lexer, for example, defines 11 tokens: whitespace, keywords, built-in --- functions, constants, built-in libraries, identifiers, strings, comments, numbers, labels, --- and operators. Even though constants, built-in functions, and built-in libraries are subsets --- of identifiers, Lua programmers find it helpful for the lexer to distinguish between them --- all. It is perfectly acceptable to just recognize keywords and identifiers. --- --- In a lexer, tokens consist of a token name and an LPeg pattern that matches a sequence of --- characters recognized as an instance of that token. Create tokens using the [`lexer.token()`]() --- function. Let us examine the "whitespace" token defined in the template shown earlier: --- --- local ws = token(lexer.WHITESPACE, lexer.space^1) +-- The first line of code is a Lua convention to store a global variable into a local variable +-- for quick access. The second line simply defines often used convenience variables. The third +-- and last lines [define](#lexer.new) and return the lexer object Scintillua uses; they are +-- very important and must be part of every lexer. Note the `...` passed to `lexer.new()` is +-- literal: the lexer will assume the name of its filename or an alternative name specified +-- by `lexer.load()` in embedded lexer applications. The fourth line uses something called a +-- "tag", an essential component of lexers. You will learn about tags shortly. The fifth line +-- defines a lexer grammar rule, which you will learn about later. (Be aware that it is common +-- practice to combine these two lines for short rules.) Note, however, the `local` prefix in +-- front of variables, which is needed so-as not to affect Lua's global environment. All in all, +-- this is a minimal, working lexer that you can build on. +-- +-- #### Tags +-- +-- Take a moment to think about your programming language's structure. What kind of key elements +-- does it have? Most languages have elements like keywords, strings, and comments. The +-- lexer's job is to break down source code into these elements and "tag" them for syntax +-- highlighting. Therefore, tags are an essential component of lexers. It is up to you how +-- specific your lexer is when it comes to tagging elements. Perhaps only distinguishing between +-- keywords and identifiers is necessary, or maybe recognizing constants and built-in functions, +-- methods, or libraries is desirable. The Lua lexer, for example, tags the following elements: +-- keywords, functions, constants, identifiers, strings, comments, numbers, labels, attributes, +-- and operators. Even though functions and constants are subsets of identifiers, Lua programmers +-- find it helpful for the lexer to distinguish between them all. It is perfectly acceptable +-- to just recognize keywords and identifiers. +-- +-- In a lexer, LPeg patterns that match particular sequences of characters are tagged with a +-- tag name using the the `lexer.tag()` function. Let us examine the "identifier" tag used in +-- the template shown earlier: +-- +-- local identifier = lex:tag(lexer.IDENTIFIER, lexer.word) -- -- At first glance, the first argument does not appear to be a string name and the second -- argument does not appear to be an LPeg pattern. Perhaps you expected something like: -- --- local ws = token('whitespace', S('\t\v\f\n\r ')^1) +-- lex:tag('identifier', (lpeg.R('AZ', 'az') + '_') * (lpeg.R('AZ', 'az', '09') + '_')^0) +-- +-- The `lexer` module actually provides a convenient list of common tag names and common LPeg +-- patterns for you to use. Tag names for programming languages include (but are not limited +-- to) `lexer.DEFAULT`, `lexer.COMMENT`, `lexer.STRING`, `lexer.NUMBER`, `lexer.KEYWORD`, +-- `lexer.IDENTIFIER`, `lexer.OPERATOR`, `lexer.ERROR`, `lexer.PREPROCESSOR`, `lexer.CONSTANT`, +-- `lexer.CONSTANT_BUILTIN`, `lexer.VARIABLE`, `lexer.VARIABLE_BUILTIN`, `lexer.FUNCTION`, +-- `lexer.FUNCTION_BUILTIN`, `lexer.FUNCTION_METHOD`, `lexer.CLASS`, `lexer.TYPE`, `lexer.LABEL`, +-- `lexer.REGEX`, `lexer.EMBEDDED`, and `lexer.ANNOTATION`. Tag names for markup languages include +-- (but are not limited to) `lexer.TAG`, `lexer.ATTRIBUTE`, `lexer.HEADING`, `lexer.BOLD`, +-- `lexer.ITALIC`, `lexer.UNDERLINE`, `lexer.CODE`, `lexer.LINK`, `lexer.REFERENCE`, and +-- `lexer.LIST`. Patterns include `lexer.any`, `lexer.alpha`, `lexer.digit`, `lexer.alnum`, +-- `lexer.lower`, `lexer.upper`, `lexer.xdigit`, `lexer.graph`, `lexer.punct`, `lexer.space`, +-- `lexer.newline`, `lexer.nonnewline`, `lexer.dec_num`, `lexer.hex_num`, `lexer.oct_num`, +-- `lexer.bin_num`, `lexer.integer`, `lexer.float`, `lexer.number`, and `lexer.word`. You may +-- use your own tag names if none of the above fit your language, but an advantage to using +-- predefined tag names is that the language elements your lexer recognizes will inherit any +-- universal syntax highlighting color theme that your editor uses. You can also "subclass" +-- existing tag names by appending a '.*subclass*' string to them. For example, the HTML lexer +-- tags unknown tags as `lexer.TAG .. '.unknown'`. This gives editors the opportunity to style +-- those subclassed tags in a different way than normal tags, or fall back to styling them as +-- normal tags. +-- +-- ##### Example Tags +-- +-- So, how might you recognize and tag elements like keywords, comments, and strings? Here are +-- some examples. -- --- The `lexer` module actually provides a convenient list of common token names and common LPeg --- patterns for you to use. Token names include [`lexer.DEFAULT`](), [`lexer.WHITESPACE`](), --- [`lexer.COMMENT`](), [`lexer.STRING`](), [`lexer.NUMBER`](), [`lexer.KEYWORD`](), --- [`lexer.IDENTIFIER`](), [`lexer.OPERATOR`](), [`lexer.ERROR`](), [`lexer.PREPROCESSOR`](), --- [`lexer.CONSTANT`](), [`lexer.VARIABLE`](), [`lexer.FUNCTION`](), [`lexer.CLASS`](), --- [`lexer.TYPE`](), [`lexer.LABEL`](), [`lexer.REGEX`](), and [`lexer.EMBEDDED`](). Patterns --- include [`lexer.any`](), [`lexer.alpha`](), [`lexer.digit`](), [`lexer.alnum`](), --- [`lexer.lower`](), [`lexer.upper`](), [`lexer.xdigit`](), [`lexer.graph`](), [`lexer.print`](), --- [`lexer.punct`](), [`lexer.space`](), [`lexer.newline`](), [`lexer.nonnewline`](), --- [`lexer.dec_num`](), [`lexer.hex_num`](), [`lexer.oct_num`](), [`lexer.integer`](), --- [`lexer.float`](), [`lexer.number`](), and [`lexer.word`](). You may use your own token names --- if none of the above fit your language, but an advantage to using predefined token names is --- that your lexer's tokens will inherit the universal syntax highlighting color theme used by --- your text editor. +-- **Keywords** -- --- ##### Example Tokens +-- Instead of matching _n_ keywords with _n_ `P('keyword_`_`n`_`')` ordered choices, use one +-- of of the following methods: -- --- So, how might you define other tokens like keywords, comments, and strings? Here are some --- examples. +-- 1. Use the convenience function `lexer.word_match()` optionally coupled with +-- `lexer.set_word_list()`. It is much easier and more efficient to write word matches like: -- --- **Keywords** +-- local keyword = lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD)) +-- [...] +-- lex:set_word_list(lexer.KEYWORD, { +-- 'keyword_1', 'keyword_2', ..., 'keyword_n' +-- }) +-- +-- local case_insensitive_word = lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD, true)) +-- [...] +-- lex:set_word_list(lexer.KEYWORD, { +-- 'KEYWORD_1', 'keyword_2', ..., 'KEYword_n' +-- }) +-- +-- local hyphenated_keyword = lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD)) +-- [...] +-- lex:set_word_list(lexer.KEYWORD, { +-- 'keyword-1', 'keyword-2', ..., 'keyword-n' +-- }) -- --- Instead of matching _n_ keywords with _n_ `P('keyword_`_`n`_`')` ordered choices, use another --- convenience function: [`lexer.word_match()`](). It is much easier and more efficient to --- write word matches like: +-- The benefit of using this method is that other lexers that inherit from, embed, or embed +-- themselves into your lexer can set, replace, or extend these word lists. For example, +-- the TypeScript lexer inherits from JavaScript, but extends JavaScript's keyword and type +-- lists with more options. -- --- local keyword = token(lexer.KEYWORD, lexer.word_match{ --- 'keyword_1', 'keyword_2', ..., 'keyword_n' --- }) +-- This method also allows applications that use your lexer to extend or replace your word +-- lists. For example, the Lua lexer includes keywords and functions for the latest version +-- of Lua (5.4 at the time of writing). However, editors using that lexer might want to use +-- keywords from Lua version 5.1, which is still quite popular. -- --- local case_insensitive_keyword = token(lexer.KEYWORD, lexer.word_match({ --- 'KEYWORD_1', 'keyword_2', ..., 'KEYword_n' --- }, true)) +-- Note that calling `lex:set_word_list()` is completely optional. Your lexer is allowed to +-- expect the editor using it to supply word lists. Scintilla-based editors can do so via +-- Scintilla's `ILexer5` interface. -- --- local hyphened_keyword = token(lexer.KEYWORD, lexer.word_match{ --- 'keyword-1', 'keyword-2', ..., 'keyword-n' --- }) +-- 2. Use the lexer-agnostic form of `lexer.word_match()`: -- --- For short keyword lists, you can use a single string of words. For example: +-- local keyword = lex:tag(lexer.KEYWORD, lexer.word_match{ +-- 'keyword_1', 'keyword_2', ..., 'keyword_n' +-- }) -- --- local keyword = token(lexer.KEYWORD, lexer.word_match('key_1 key_2 ... key_n')) +-- local case_insensitive_keyword = lex:tag(lexer.KEYWORD, lexer.word_match({ +-- 'KEYWORD_1', 'keyword_2', ..., 'KEYword_n' +-- }, true)) +-- +-- local hyphened_keyword = lex:tag(lexer.KEYWORD, lexer.word_match{ +-- 'keyword-1', 'keyword-2', ..., 'keyword-n' +-- }) +-- +-- For short keyword lists, you can use a single string of words. For example: +-- +-- local keyword = lex:tag(lexer.KEYWORD, lexer.word_match('key_1 key_2 ... key_n')) +-- +-- You can use this method for static word lists that do not change, or where it does not +-- make sense to allow applications or other lexers to extend or replace a word list. -- -- **Comments** -- --- Line-style comments with a prefix character(s) are easy to express with LPeg: +-- Line-style comments with a prefix character(s) are easy to express: -- --- local shell_comment = token(lexer.COMMENT, lexer.to_eol('#')) --- local c_line_comment = token(lexer.COMMENT, lexer.to_eol('//', true)) +-- local shell_comment = lex:tag(lexer.COMMENT, lexer.to_eol('#')) +-- local c_line_comment = lex:tag(lexer.COMMENT, lexer.to_eol('//', true)) -- --- The comments above start with a '#' or "//" and go to the end of the line. The second comment --- recognizes the next line also as a comment if the current line ends with a '\' escape character. +-- The comments above start with a '#' or "//" and go to the end of the line (EOL). The second +-- comment recognizes the next line also as a comment if the current line ends with a '\' +-- escape character. -- -- C-style "block" comments with a start and end delimiter are also easy to express: -- --- local c_comment = token(lexer.COMMENT, lexer.range('/*', '*/')) +-- local c_comment = lex:tag(lexer.COMMENT, lexer.range('/*', '*/')) -- -- This comment starts with a "/\*" sequence and contains anything up to and including an ending -- "\*/" sequence. The ending "\*/" is optional so the lexer can recognize unfinished comments @@ -178,77 +223,83 @@ local M = {} -- -- Most programming languages allow escape sequences in strings such that a sequence like -- "\\"" in a double-quoted string indicates that the '"' is not the end of the --- string. [`lexer.range()`]() handles escapes inherently. +-- string. `lexer.range()` handles escapes inherently. -- -- local dq_str = lexer.range('"') -- local sq_str = lexer.range("'") --- local string = token(lexer.STRING, dq_str + sq_str) +-- local string = lex:tag(lexer.STRING, dq_str + sq_str) -- -- In this case, the lexer treats '\' as an escape character in a string sequence. -- -- **Numbers** -- --- Most programming languages have the same format for integer and float tokens, so it might --- be as simple as using a predefined LPeg pattern: +-- Most programming languages have the same format for integers and floats, so it might be as +-- simple as using a predefined LPeg pattern: -- --- local number = token(lexer.NUMBER, lexer.number) +-- local number = lex:tag(lexer.NUMBER, lexer.number) -- -- However, some languages allow postfix characters on integers. -- -- local integer = P('-')^-1 * (lexer.dec_num * S('lL')^-1) --- local number = token(lexer.NUMBER, lexer.float + lexer.hex_num + integer) +-- local number = lex:tag(lexer.NUMBER, lexer.float + lexer.hex_num + integer) +-- +-- Other languages allow separaters within numbers for better readability. +-- +-- local number = lex:tag(lexer.NUMBER, lexer.number_('_')) -- recognize 1_000_000 -- -- Your language may need other tweaks, but it is up to you how fine-grained you want your -- highlighting to be. After all, you are not writing a compiler or interpreter! -- -- #### Rules -- --- Programming languages have grammars, which specify valid token structure. For example, --- comments usually cannot appear within a string. Grammars consist of rules, which are simply --- combinations of tokens. Recall from the lexer template the [`lexer.add_rule()`]() call, --- which adds a rule to the lexer's grammar: +-- Programming languages have grammars, which specify valid syntactic structure. For example, +-- comments usually cannot appear within a string, and valid identifiers (like variable names) +-- cannot be keywords. In Lua lexers, grammars consist of LPeg pattern rules, many of which +-- are tagged. Recall from the lexer template the `lexer.add_rule()` call, which adds a rule +-- to the lexer's grammar: -- --- lex:add_rule('whitespace', ws) +-- lex:add_rule('identifier', identifier) -- -- Each rule has an associated name, but rule names are completely arbitrary and serve only to -- identify and distinguish between different rules. Rule order is important: if text does not -- match the first rule added to the grammar, the lexer tries to match the second rule added, and --- so on. Right now this lexer simply matches whitespace tokens under a rule named "whitespace". +-- so on. Right now this lexer simply matches identifiers under a rule named "identifier". -- -- To illustrate the importance of rule order, here is an example of a simplified Lua lexer: -- --- lex:add_rule('whitespace', token(lexer.WHITESPACE, ...)) --- lex:add_rule('keyword', token(lexer.KEYWORD, ...)) --- lex:add_rule('identifier', token(lexer.IDENTIFIER, ...)) --- lex:add_rule('string', token(lexer.STRING, ...)) --- lex:add_rule('comment', token(lexer.COMMENT, ...)) --- lex:add_rule('number', token(lexer.NUMBER, ...)) --- lex:add_rule('label', token(lexer.LABEL, ...)) --- lex:add_rule('operator', token(lexer.OPERATOR, ...)) --- --- Note how identifiers come after keywords. In Lua, as with most programming languages, --- the characters allowed in keywords and identifiers are in the same set (alphanumerics --- plus underscores). If the lexer added the "identifier" rule before the "keyword" rule, --- all keywords would match identifiers and thus incorrectly highlight as identifiers instead --- of keywords. The same idea applies to function, constant, etc. tokens that you may want to --- distinguish between: their rules should come before identifiers. --- --- So what about text that does not match any rules? For example in Lua, the '!' character is +-- lex:add_rule('keyword', lex:tag(lexer.KEYWORD, ...)) +-- lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, ...)) +-- lex:add_rule('string', lex:tag(lexer.STRING, ...)) +-- lex:add_rule('comment', lex:tag(lexer.COMMENT, ...)) +-- lex:add_rule('number', lex:tag(lexer.NUMBER, ...)) +-- lex:add_rule('label', lex:tag(lexer.LABEL, ...)) +-- lex:add_rule('operator', lex:tag(lexer.OPERATOR, ...)) +-- +-- Notice how identifiers come _after_ keywords. In Lua, as with most programming languages, +-- the characters allowed in keywords and identifiers are in the same set (alphanumerics plus +-- underscores). If the lexer added the "identifier" rule before the "keyword" rule, all keywords +-- would match identifiers and thus would be incorrectly tagged (and likewise incorrectly +-- highlighted) as identifiers instead of keywords. The same idea applies to function names, +-- constants, etc. that you may want to distinguish between: their rules should come before +-- identifiers. +-- +-- So what about text that does not match any rules? For example in Lua, the '!' character is -- meaningless outside a string or comment. Normally the lexer skips over such text. If instead --- you want to highlight these "syntax errors", add an additional end rule: +-- you want to highlight these "syntax errors", add a final rule: -- --- lex:add_rule('whitespace', ws) +-- lex:add_rule('keyword', keyword) -- ... --- lex:add_rule('error', token(lexer.ERROR, lexer.any)) +-- lex:add_rule('error', lex:tag(lexer.ERROR, lexer.any)) -- --- This identifies and highlights any character not matched by an existing rule as a `lexer.ERROR` --- token. +-- This identifies and tags any character not matched by an existing rule as a `lexer.ERROR`. -- --- Even though the rules defined in the examples above contain a single token, rules may --- consist of multiple tokens. For example, a rule for an HTML tag could consist of a tag token --- followed by an arbitrary number of attribute tokens, allowing the lexer to highlight all --- tokens separately. That rule might look something like this: +-- Even though the rules defined in the examples above contain a single tagged pattern, rules may +-- consist of multiple tagged patterns. For example, the rule for an HTML tag could consist of a +-- tagged tag followed by an arbitrary number of tagged attributes, separated by whitespace. This +-- allows the lexer to produce all tags separately, but in a single, convenient rule. That rule +-- might look something like this: -- +-- local ws = lex:get_rule('whitespace') -- predefined rule for all lexers -- lex:add_rule('tag', tag_start * (ws * attributes)^0 * tag_end^-1) -- -- Note however that lexers with complex rules like these are more prone to lose track of their @@ -256,79 +307,16 @@ local M = {} -- -- #### Summary -- --- Lexers primarily consist of tokens and grammar rules. At your disposal are a number of --- convenience patterns and functions for rapidly creating a lexer. If you choose to use --- predefined token names for your tokens, you do not have to define how the lexer highlights --- them. The tokens will inherit the default syntax highlighting color theme your editor uses. +-- Lexers primarily consist of tagged patterns and grammar rules. These patterns match language +-- elements like keywords, comments, and strings, and rules dictate the order in which patterns +-- are matched. At your disposal are a number of convenience patterns and functions for rapidly +-- creating a lexer. If you choose to use predefined tag names (or perhaps even subclassed +-- names) for your patterns, you do not have to update your editor's theme to specify how to +-- syntax-highlight those patterns. Your language's elements will inherit the default syntax +-- highlighting color theme your editor uses. -- -- ### Advanced Techniques -- --- #### Styles and Styling --- --- The most basic form of syntax highlighting is assigning different colors to different --- tokens. Instead of highlighting with just colors, Scintilla allows for more rich highlighting, --- or "styling", with different fonts, font sizes, font attributes, and foreground and background --- colors, just to name a few. The unit of this rich highlighting is called a "style". Styles --- are simply Lua tables of properties. By default, lexers associate predefined token names like --- `lexer.WHITESPACE`, `lexer.COMMENT`, `lexer.STRING`, etc. with particular styles as part --- of a universal color theme. These predefined styles are contained in [`lexer.styles`](), --- and you may define your own styles. See that table's documentation for more information. As --- with token names, LPeg patterns, and styles, there is a set of predefined color names, --- but they vary depending on the current color theme in use. Therefore, it is generally not --- a good idea to manually define colors within styles in your lexer since they might not fit --- into a user's chosen color theme. Try to refrain from even using predefined colors in a --- style because that color may be theme-specific. Instead, the best practice is to either use --- predefined styles or derive new color-agnostic styles from predefined ones. For example, Lua --- "longstring" tokens use the existing `lexer.styles.string` style instead of defining a new one. --- --- ##### Example Styles --- --- Defining styles is pretty straightforward. An empty style that inherits the default theme --- settings is simply an empty table: --- --- local style_nothing = {} --- --- A similar style but with a bold font face looks like this: --- --- local style_bold = {bold = true} --- --- You can derive new styles from predefined ones without having to rewrite them. This operation --- leaves the old style unchanged. For example, if you had a "static variable" token whose --- style you wanted to base off of `lexer.styles.variable`, it would probably look like: --- --- local style_static_var = lexer.styles.variable .. {italics = true} --- --- The color theme files in the *lexers/themes/* folder give more examples of style definitions. --- --- #### Token Styles --- --- Lexers use the [`lexer.add_style()`]() function to assign styles to particular tokens. Recall --- the token definition and from the lexer template: --- --- local ws = token(lexer.WHITESPACE, lexer.space^1) --- lex:add_rule('whitespace', ws) --- --- Why is a style not assigned to the `lexer.WHITESPACE` token? As mentioned earlier, lexers --- automatically associate tokens that use predefined token names with a particular style. Only --- tokens with custom token names need manual style associations. As an example, consider a --- custom whitespace token: --- --- local ws = token('custom_whitespace', lexer.space^1) --- --- Assigning a style to this token looks like: --- --- lex:add_style('custom_whitespace', lexer.styles.whitespace) --- --- Do not confuse token names with rule names. They are completely different entities. In the --- example above, the lexer associates the "custom_whitespace" token with the existing style --- for `lexer.WHITESPACE` tokens. If instead you prefer to color the background of whitespace --- a shade of grey, it might look like: --- --- lex:add_style('custom_whitespace', lexer.styles.whitespace .. {back = lexer.colors.grey}) --- --- Remember to refrain from assigning specific colors in styles, but in this case, all user --- color themes probably define `colors.grey`. --- -- #### Line Lexers -- -- By default, lexers match the arbitrary chunks of text passed to them by Scintilla. These @@ -338,16 +326,16 @@ local M = {} -- line accordingly. To indicate that your lexer matches by line, create the lexer with an -- extra parameter: -- --- local lex = lexer.new('?', {lex_by_line = true}) +-- local lex = lexer.new(..., {lex_by_line = true}) -- -- Now the input text for the lexer is a single line at a time. Keep in mind that line lexers --- do not have the ability to look ahead at subsequent lines. +-- do not have the ability to look ahead to subsequent lines. -- -- #### Embedded Lexers -- --- Lexers embed within one another very easily, requiring minimal effort. In the following --- sections, the lexer being embedded is called the "child" lexer and the lexer a child is --- being embedded in is called the "parent". For example, consider an HTML lexer and a CSS +-- Scintillua lexers embed within one another very easily, requiring minimal effort. In the +-- following sections, the lexer being embedded is called the "child" lexer and the lexer a child +-- is being embedded in is called the "parent". For example, consider an HTML lexer and a CSS -- lexer. Either lexer stands alone for styling their respective HTML and CSS files. However, CSS -- can be embedded inside HTML. In this specific case, the CSS lexer is the "child" lexer with -- the HTML lexer being the "parent". Now consider an HTML lexer and a PHP lexer. This sounds @@ -359,7 +347,7 @@ local M = {} -- ##### Parent Lexer -- -- Before embedding a child lexer into a parent lexer, the parent lexer needs to load the child --- lexer. This is done with the [`lexer.load()`]() function. For example, loading the CSS lexer +-- lexer. This is done with the `lexer.load()` function. For example, loading the CSS lexer -- within the HTML lexer looks like: -- -- local css = lexer.load('css') @@ -371,15 +359,14 @@ local M = {} -- tag with a "type" attribute whose value is "text/css": -- -- local css_tag = P('<style') * P(function(input, index) --- if input:find('^[^>]+type="text/css"', index) then return index end +-- if input:find('^[^>]+type="text/css"', index) then return true end -- end) -- -- This pattern looks for the beginning of a "style" tag and searches its attribute list for -- the text "`type="text/css"`". (In this simplified example, the Lua pattern does not consider -- whitespace between the '=' nor does it consider that using single quotes is valid.) If there --- is a match, the functional pattern returns a value instead of `nil`. In this case, the value --- returned does not matter because we ultimately want to style the "style" tag as an HTML tag, --- so the actual start rule looks like this: +-- is a match, the functional pattern returns `true`. However, we ultimately want to style the +-- "style" tag as an HTML tag, so the actual start rule looks like this: -- -- local css_start_rule = #css_tag * tag -- @@ -390,7 +377,7 @@ local M = {} -- local css_end_rule = #P('</style>') * tag -- -- Once the parent loads the child lexer and defines the child's start and end rules, it embeds --- the child with the [`lexer.embed()`]() function: +-- the child with the `lexer.embed()` function: -- -- lex:embed(css, css_start_rule, css_end_rule) -- @@ -398,23 +385,26 @@ local M = {} -- -- The process for instructing a child lexer to embed itself into a parent is very similar to -- embedding a child into a parent: first, load the parent lexer into the child lexer with the --- [`lexer.load()`]() function and then create start and end rules for the child lexer. However, --- in this case, call [`lexer.embed()`]() with switched arguments. For example, in the PHP lexer: +-- `lexer.load()` function and then create start and end rules for the child lexer. However, +-- in this case, call `lexer.embed()` with switched arguments. For example, in the PHP lexer: -- -- local html = lexer.load('html') --- local php_start_rule = token('php_tag', '<?php ') --- local php_end_rule = token('php_tag', '?>') --- lex:add_style('php_tag', lexer.styles.embedded) +-- local php_start_rule = lex:tag('php_tag', '<?php' * lexer.space) +-- local php_end_rule = lex:tag('php_tag', '?>') -- html:embed(lex, php_start_rule, php_end_rule) -- +-- Note that the use of a 'php_tag' tag will require the editor using the lexer to specify how +-- to highlight text with that tag. In order to avoid this, you could use the `lexer.PREPROCESSOR` +-- tag instead. +-- -- #### Lexers with Complex State -- -- A vast majority of lexers are not stateful and can operate on any chunk of text in a -- document. However, there may be rare cases where a lexer does need to keep track of some -- sort of persistent state. Rather than using `lpeg.P` function patterns that set state -- variables, it is recommended to make use of Scintilla's built-in, per-line state integers via --- [`lexer.line_state`](). It was designed to accommodate up to 32 bit flags for tracking state. --- [`lexer.line_from_position()`]() will return the line for any position given to an `lpeg.P` +-- `lexer.line_state`. It was designed to accommodate up to 32 bit-flags for tracking state. +-- `lexer.line_from_position()` will return the line for any position given to an `lpeg.P` -- function pattern. (Any positions derived from that position argument will also work.) -- -- Writing stateful lexers is beyond the scope of this document. @@ -432,17 +422,17 @@ local M = {} -- fold keywords are "if" and "end" in Lua and examples of fold character sequences are '{', -- '}', "/\*", and "\*/" in C for code block and comment delimiters, respectively. However, -- these fold points cannot occur just anywhere. For example, lexers should not recognize fold --- keywords that appear within strings or comments. The [`lexer.add_fold_point()`]() function --- allows you to conveniently define fold points with such granularity. For example, consider C: +-- keywords that appear within strings or comments. The `lexer.add_fold_point()` function allows +-- you to conveniently define fold points with such granularity. For example, consider C: -- -- lex:add_fold_point(lexer.OPERATOR, '{', '}') -- lex:add_fold_point(lexer.COMMENT, '/*', '*/') -- --- The first assignment states that any '{' or '}' that the lexer recognized as an `lexer.OPERATOR` --- token is a fold point. Likewise, the second assignment states that any "/\*" or "\*/" that --- the lexer recognizes as part of a `lexer.COMMENT` token is a fold point. The lexer does --- not consider any occurrences of these characters outside their defined tokens (such as in --- a string) as fold points. How do you specify fold keywords? Here is an example for Lua: +-- The first assignment states that any '{' or '}' that the lexer tagged as an `lexer.OPERATOR` +-- is a fold point. Likewise, the second assignment states that any "/\*" or "\*/" that the +-- lexer tagged as part of a `lexer.COMMENT` is a fold point. The lexer does not consider any +-- occurrences of these characters outside their tagged elements (such as in a string) as fold +-- points. How do you specify fold keywords? Here is an example for Lua: -- -- lex:add_fold_point(lexer.KEYWORD, 'if', 'end') -- lex:add_fold_point(lexer.KEYWORD, 'do', 'end') @@ -450,15 +440,16 @@ local M = {} -- lex:add_fold_point(lexer.KEYWORD, 'repeat', 'until') -- -- If your lexer has case-insensitive keywords as fold points, simply add a --- `case_insensitive_fold_points = true` option to [`lexer.new()`](), and specify keywords in +-- `case_insensitive_fold_points = true` option to `lexer.new()`, and specify keywords in -- lower case. -- --- If your lexer needs to do some additional processing in order to determine if a token is --- a fold point, pass a function that returns an integer to `lex:add_fold_point()`. Returning --- `1` indicates the token is a beginning fold point and returning `-1` indicates the token is --- an ending fold point. Returning `0` indicates the token is not a fold point. For example: +-- If your lexer needs to do some additional processing in order to determine if a tagged element +-- is a fold point, pass a function to `lex:add_fold_point()` that returns an integer. A return +-- value of `1` indicates the element is a beginning fold point and a return value of `-1` +-- indicates the element is an ending fold point. A return value of `0` indicates the element +-- is not a fold point. For example: -- --- local function fold_strange_token(text, pos, line, s, symbol) +-- local function fold_strange_element(text, pos, line, s, symbol) -- if ... then -- return 1 -- beginning fold point -- elseif ... then @@ -467,13 +458,13 @@ local M = {} -- return 0 -- end -- --- lex:add_fold_point('strange_token', '|', fold_strange_token) +-- lex:add_fold_point('strange_element', '|', fold_strange_element) -- --- Any time the lexer encounters a '|' that is a "strange_token", it calls the `fold_strange_token` --- function to determine if '|' is a fold point. The lexer calls these functions with the --- following arguments: the text to identify fold points in, the beginning position of the --- current line in the text to fold, the current line's text, the position in the current line --- the fold point text starts at, and the fold point text itself. +-- Any time the lexer encounters a '|' that is tagged as a "strange_element", it calls the +-- `fold_strange_element` function to determine if '|' is a fold point. The lexer calls these +-- functions with the following arguments: the text to identify fold points in, the beginning +-- position of the current line in the text to fold, the current line's text, the position in +-- the current line the fold point text starts at, and the fold point text itself. -- -- #### Fold by Indentation -- @@ -481,17 +472,17 @@ local M = {} -- your lexer falls into this category and you would like to mark fold points based on changes -- in indentation, create the lexer with a `fold_by_indentation = true` option: -- --- local lex = lexer.new('?', {fold_by_indentation = true}) +-- local lex = lexer.new(..., {fold_by_indentation = true}) -- -- ### Using Lexers -- -- **Textadept** -- --- Put your lexer in your *~/.textadept/lexers/* directory so you do not overwrite it when +-- Place your lexer in your *~/.textadept/lexers/* directory so you do not overwrite it when -- upgrading Textadept. Also, lexers in this directory override default lexers. Thus, Textadept -- loads a user *lua* lexer instead of the default *lua* lexer. This is convenient for tweaking --- a default lexer to your liking. Then add a [file type](#textadept.file_types) for your lexer --- if necessary. +-- a default lexer to your liking. Then add a [file extension](#lexer.detect_extensions) for +-- your lexer if necessary. -- -- **SciTE** -- @@ -499,107 +490,82 @@ local M = {} -- or *SciTEGlobal.properties*. The contents of the *.properties* file should contain: -- -- file.patterns.[lexer_name]=[file_patterns] --- lexer.$(file.patterns.[lexer_name])=[lexer_name] +-- lexer.$(file.patterns.[lexer_name])=scintillua.[lexer_name] +-- keywords.$(file.patterns.[lexer_name])=scintillua +-- keywords2.$(file.patterns.[lexer_name])=scintillua +-- ... +-- keywords9.$(file.patterns.[lexer_name])=scintillua -- -- where `[lexer_name]` is the name of your lexer (minus the *.lua* extension) and --- `[file_patterns]` is a set of file extensions to use your lexer for. +-- `[file_patterns]` is a set of file extensions to use your lexer for. The `keyword` settings are +-- only needed if another SciTE properties file has defined keyword sets for `[file_patterns]`. +-- The `scintillua` keyword setting instructs Scintillua to use the keyword sets defined within +-- the lexer. You can override a lexer's keyword set(s) by specifying your own in the same order +-- that the lexer calls `lex:set_word_list()`. For example, the Lua lexer's first set of keywords +-- is for reserved words, the second is for built-in global functions, the third is for library +-- functions, the fourth is for built-in global constants, and the fifth is for library constants. +-- +-- SciTE assigns styles to tag names in order to perform syntax highlighting. Since the set of +-- tag names used for a given language changes, your *.properties* file should specify styles +-- for tag names instead of style numbers. For example: -- --- Please note that Lua lexers ignore any styling information in *.properties* files. Your --- theme file in the *lexers/themes/* directory contains styling information. +-- scintillua.styles.my_tag=$(scintillua.styles.keyword),bold -- -- ### Migrating Legacy Lexers -- -- Legacy lexers are of the form: -- --- local l = require('lexer') --- local token, word_match = l.token, l.word_match --- local P, R, S = lpeg.P, lpeg.R, lpeg.S +-- local lexer = require('lexer') +-- local token, word_match = lexer.token, lexer.word_match +-- local P, S = lpeg.P, lpeg.S -- --- local M = {_NAME = '?'} +-- local lex = lexer.new('?') -- --- [... token and pattern definitions ...] +-- -- Whitespace. +-- lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) -- --- M._rules = { --- {'rule', pattern}, +-- -- Keywords. +-- lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ -- [...] --- } +-- })) -- --- M._tokenstyles = { --- 'token' = 'style', --- [...] --- } +-- [... other rule definitions ...] -- --- M._foldsymbols = { --- _patterns = {...}, --- ['token'] = {['start'] = 1, ['end'] = -1}, --- [...] --- } +-- -- Custom. +-- lex:add_rule('custom_rule', token('custom_token', ...)) +-- lex:add_style('custom_token', lexer.styles.keyword .. {bold = true}) -- --- return M +-- -- Fold points. +-- lex:add_fold_point(lexer.OPERATOR, '{', '}') -- --- While Scintillua will handle such legacy lexers just fine without any changes, it is +-- return lex +-- +-- While Scintillua will mostly handle such legacy lexers just fine without any changes, it is -- recommended that you migrate yours. The migration process is fairly straightforward: -- --- 1. Replace all instances of `l` with `lexer`, as it's better practice and results in less --- confusion. --- 2. Replace `local M = {_NAME = '?'}` with `local lex = lexer.new('?')`, where `?` is the --- name of your legacy lexer. At the end of the lexer, change `return M` to `return lex`. --- 3. Instead of defining rules towards the end of your lexer, define your rules as you define --- your tokens and patterns using [`lex:add_rule()`](#lexer.add_rule). --- 4. Similarly, any custom token names should have their styles immediately defined using --- [`lex:add_style()`](#lexer.add_style). --- 5. Optionally convert any table arguments passed to [`lexer.word_match()`]() to a --- space-separated string of words. --- 6. Replace any calls to `lexer.embed(M, child, ...)` and `lexer.embed(parent, M, ...)` with --- [`lex:embed`](#lexer.embed)`(child, ...)` and `parent:embed(lex, ...)`, respectively. --- 7. Define fold points with simple calls to [`lex:add_fold_point()`](#lexer.add_fold_point). No --- need to mess with Lua patterns anymore. --- 8. Any legacy lexer options such as `M._FOLDBYINDENTATION`, `M._LEXBYLINE`, `M._lexer`, --- etc. should be added as table options to [`lexer.new()`](). --- 9. Any external lexer rule fetching and/or modifications via `lexer._RULES` should be changed --- to use [`lexer.get_rule()`]() and [`lexer.modify_rule()`](). +-- 1. `lexer` exists in the default lexer environment, so `require('lexer')` should be replaced +-- by simply `lexer`. (Keep in mind `local lexer = lexer` is a Lua idiom.) +-- 2. Every lexer created using `lexer.new()` should no longer specify a lexer name by string, +-- but should instead use `...` (three dots), which evaluates to the lexer's filename or +-- alternative name in embedded lexer applications. +-- 3. Every lexer created using `lexer.new()` now includes a rule to match whitespace. Unless +-- your lexer has significant whitespace, you can remove your legacy lexer's whitespace +-- token and rule. Otherwise, your defined whitespace rule will replace the default one. +-- 4. The concept of tokens has been replaced with tags. Instead of calling a `token()` function, +-- call [`lex:tag()`](#lexer.tag) instead. +-- 5. Lexers now support replaceable word lists. Instead of calling `lexer.word_match()` with +-- large word lists, call it as an instance method with an identifier string (typically +-- something like `lexer.KEYWORD`). Then at the end of the lexer (before `return lex`), call +-- [`lex:set_word_list()`](#lexer.set_word_list) with the same identifier and the usual +-- list of words to match. This allows users of your lexer to call `lex:set_word_list()` +-- with their own set of words should they wish to. +-- 6. Lexers no longer specify styling information. Remove any calls to `lex:add_style()`. You +-- may need to add styling information for custom tags to your editor's theme. +-- 7. `lexer.last_char_includes()` has been deprecated in favor of the new `lexer.after_set()`. +-- Use the character set and pattern as arguments to that new function. -- -- As an example, consider the following sample legacy lexer: -- --- local l = require('lexer') --- local token, word_match = l.token, l.word_match --- local P, R, S = lpeg.P, lpeg.R, lpeg.S --- --- local M = {_NAME = 'legacy'} --- --- local ws = token(l.WHITESPACE, l.space^1) --- local comment = token(l.COMMENT, '#' * l.nonnewline^0) --- local string = token(l.STRING, l.delimited_range('"')) --- local number = token(l.NUMBER, l.float + l.integer) --- local keyword = token(l.KEYWORD, word_match{'foo', 'bar', 'baz'}) --- local custom = token('custom', P('quux')) --- local identifier = token(l.IDENTIFIER, l.word) --- local operator = token(l.OPERATOR, S('+-*/%^=<>,.()[]{}')) --- --- M._rules = { --- {'whitespace', ws}, --- {'keyword', keyword}, --- {'custom', custom}, --- {'identifier', identifier}, --- {'string', string}, --- {'comment', comment}, --- {'number', number}, --- {'operator', operator} --- } --- --- M._tokenstyles = { --- 'custom' = l.STYLE_KEYWORD .. ',bold' --- } --- --- M._foldsymbols = { --- _patterns = {'[{}]'}, --- [l.OPERATOR] = {['{'] = 1, ['}'] = -1} --- } --- --- return M --- --- Following the migration steps would yield: --- -- local lexer = require('lexer') -- local token, word_match = lexer.token, lexer.word_match -- local P, S = lpeg.P, lpeg.S @@ -620,15 +586,39 @@ local M = {} -- -- return lex -- +-- Following the migration steps would yield: +-- +-- local lexer = lexer +-- local P, S = lpeg.P, lpeg.S +-- +-- local lex = lexer.new(...) +-- +-- lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) +-- lex:add_rule('custom', lex:tag('custom', 'quux')) +-- lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) +-- lex:add_rule('string', lex:tag(lexer.STRING, lexer.range('"'))) +-- lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) +-- lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) +-- lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-*/%^=<>,.()[]{}'))) +-- +-- lex:add_fold_point(lexer.OPERATOR, '{', '}') +-- +-- lex:set_word_list(lexer.KEYWORD, {'foo', 'bar', 'baz'}) +-- +-- return lex +-- +-- Any editors using this lexer would have to add a style for the 'custom' tag. +-- -- ### Considerations -- -- #### Performance -- -- There might be some slight overhead when initializing a lexer, but loading a file from disk --- into Scintilla is usually more expensive. On modern computer systems, I see no difference in --- speed between Lua lexers and Scintilla's C++ ones. Optimize lexers for speed by re-arranging --- `lexer.add_rule()` calls so that the most common rules match first. Do keep in mind that --- order matters for similar rules. +-- into Scintilla is usually more expensive. Actually painting the syntax highlighted text to +-- the screen is often more expensive than the lexing operation. On modern computer systems, +-- I see no difference in speed between Lua lexers and Scintilla's C++ ones. Optimize lexers for +-- speed by re-arranging `lexer.add_rule()` calls so that the most common rules match first. Do +-- keep in mind that order matters for similar rules. -- -- In some cases, folding may be far more expensive than lexing, particularly in lexers with a -- lot of potential fold points. If your lexer is exhibiting signs of slowness, try disabling @@ -638,13 +628,23 @@ local M = {} -- -- #### Limitations -- --- Embedded preprocessor languages like PHP cannot completely embed in their parent languages --- in that the parent's tokens do not support start and end rules. This mostly goes unnoticed, --- but code like +-- Embedded preprocessor languages like PHP cannot completely embed themselves into their parent +-- languages because the parent's tagged patterns do not support start and end rules. This +-- mostly goes unnoticed, but code like -- -- <div id="<?php echo $id; ?>"> -- --- will not style correctly. +-- will not style correctly. Also, these types of languages cannot currently embed themselves +-- into their parent's child languages either. +-- +-- A language cannot embed itself into something like an interpolated string because it is +-- possible that if lexing starts within the embedded entity, it will not be detected as such, +-- so a child to parent transition cannot happen. For example, the following Ruby code will +-- not style correctly: +-- +-- sum = "1 + 2 = #{1 + 2}" +-- +-- Also, there is the potential for recursion for languages embedding themselves within themselves. -- -- #### Troubleshooting -- @@ -656,9 +656,9 @@ local M = {} -- -- Poorly written lexers have the ability to crash Scintilla (and thus its containing application), -- so unsaved data might be lost. However, I have only observed these crashes in early lexer --- development, when syntax errors or pattern errors are present. Once the lexer actually starts --- styling text (either correctly or incorrectly, it does not matter), I have not observed --- any crashes. +-- development, when syntax errors or pattern errors are present. Once the lexer actually +-- starts processing and tagging text (either correctly or incorrectly, it does not matter), +-- I have not observed any crashes. -- -- #### Acknowledgements -- @@ -666,591 +666,626 @@ local M = {} -- and thanks to Roberto Ierusalimschy for LPeg. -- -- [lexer post]: http://lua-users.org/lists/lua-l/2007-04/msg00116.html --- @field DEFAULT (string) --- The token name for default tokens. --- @field WHITESPACE (string) --- The token name for whitespace tokens. --- @field COMMENT (string) --- The token name for comment tokens. --- @field STRING (string) --- The token name for string tokens. --- @field NUMBER (string) --- The token name for number tokens. --- @field KEYWORD (string) --- The token name for keyword tokens. --- @field IDENTIFIER (string) --- The token name for identifier tokens. --- @field OPERATOR (string) --- The token name for operator tokens. --- @field ERROR (string) --- The token name for error tokens. --- @field PREPROCESSOR (string) --- The token name for preprocessor tokens. --- @field CONSTANT (string) --- The token name for constant tokens. --- @field VARIABLE (string) --- The token name for variable tokens. --- @field FUNCTION (string) --- The token name for function tokens. --- @field CLASS (string) --- The token name for class tokens. --- @field TYPE (string) --- The token name for type tokens. --- @field LABEL (string) --- The token name for label tokens. --- @field REGEX (string) --- The token name for regex tokens. --- @field any (pattern) --- A pattern that matches any single character. --- @field ascii (pattern) --- A pattern that matches any ASCII character (codes 0 to 127). --- @field extend (pattern) --- A pattern that matches any ASCII extended character (codes 0 to 255). --- @field alpha (pattern) --- A pattern that matches any alphabetic character ('A'-'Z', 'a'-'z'). --- @field digit (pattern) --- A pattern that matches any digit ('0'-'9'). --- @field alnum (pattern) --- A pattern that matches any alphanumeric character ('A'-'Z', 'a'-'z', '0'-'9'). --- @field lower (pattern) --- A pattern that matches any lower case character ('a'-'z'). --- @field upper (pattern) --- A pattern that matches any upper case character ('A'-'Z'). --- @field xdigit (pattern) --- A pattern that matches any hexadecimal digit ('0'-'9', 'A'-'F', 'a'-'f'). --- @field cntrl (pattern) --- A pattern that matches any control character (ASCII codes 0 to 31). --- @field graph (pattern) --- A pattern that matches any graphical character ('!' to '~'). --- @field print (pattern) --- A pattern that matches any printable character (' ' to '~'). --- @field punct (pattern) --- A pattern that matches any punctuation character ('!' to '/', ':' to '@', '[' to ''', --- '{' to '~'). --- @field space (pattern) --- A pattern that matches any whitespace character ('\t', '\v', '\f', '\n', '\r', space). --- @field newline (pattern) --- A pattern that matches a sequence of end of line characters. --- @field nonnewline (pattern) --- A pattern that matches any single, non-newline character. --- @field dec_num (pattern) --- A pattern that matches a decimal number. --- @field hex_num (pattern) --- A pattern that matches a hexadecimal number. --- @field oct_num (pattern) --- A pattern that matches an octal number. --- @field integer (pattern) --- A pattern that matches either a decimal, hexadecimal, or octal number. --- @field float (pattern) --- A pattern that matches a floating point number. --- @field number (pattern) --- A pattern that matches a typical number, either a floating point, decimal, hexadecimal, --- or octal number. --- @field word (pattern) --- A pattern that matches a typical word. Words begin with a letter or underscore and consist --- of alphanumeric and underscore characters. --- @field FOLD_BASE (number) --- The initial (root) fold level. --- @field FOLD_BLANK (number) --- Flag indicating that the line is blank. --- @field FOLD_HEADER (number) --- Flag indicating the line is fold point. --- @field fold_level (table, Read-only) --- Table of fold level bit-masks for line numbers starting from 1. --- Fold level masks are composed of an integer level combined with any of the following bits: --- --- * `lexer.FOLD_BASE` --- The initial fold level. --- * `lexer.FOLD_BLANK` --- The line is blank. --- * `lexer.FOLD_HEADER` --- The line is a header, or fold point. --- @field indent_amount (table, Read-only) --- Table of indentation amounts in character columns, for line numbers starting from 1. --- @field line_state (table) --- Table of integer line states for line numbers starting from 1. --- Line states can be used by lexers for keeping track of persistent states. --- @field property (table) --- Map of key-value string pairs. --- @field property_expanded (table, Read-only) --- Map of key-value string pairs with `$()` and `%()` variable replacement performed in values. --- @field property_int (table, Read-only) --- Map of key-value pairs with values interpreted as numbers, or `0` if not found. --- @field style_at (table, Read-only) --- Table of style names at positions in the buffer starting from 1. --- @field folding (boolean) --- Whether or not folding is enabled for the lexers that support it. --- This option is disabled by default. --- This is an alias for `lexer.property['fold'] = '1|0'`. --- @field fold_on_zero_sum_lines (boolean) --- Whether or not to mark as a fold point lines that contain both an ending and starting fold --- point. For example, `} else {` would be marked as a fold point. --- This option is disabled by default. This is an alias for --- `lexer.property['fold.on.zero.sum.lines'] = '1|0'`. --- @field fold_compact (boolean) --- Whether or not blank lines after an ending fold point are included in that --- fold. --- This option is disabled by default. --- This is an alias for `lexer.property['fold.compact'] = '1|0'`. --- @field fold_by_indentation (boolean) --- Whether or not to fold based on indentation level if a lexer does not have --- a folder. --- Some lexers automatically enable this option. It is disabled by default. --- This is an alias for `lexer.property['fold.by.indentation'] = '1|0'`. --- @field fold_line_groups (boolean) --- Whether or not to fold multiple, consecutive line groups (such as line comments and import --- statements) and only show the top line. --- This option is disabled by default. --- This is an alias for `lexer.property['fold.line.groups'] = '1|0'`. -module('lexer')]=] - -if not require then - -- Substitute for Lua's require() function, which does not require the package module to - -- be loaded. - -- Note: all modules must be in the global namespace, which is the case in LexerLPeg's default - -- Lua State. - function require(name) return name == 'lexer' and M or _G[name] end -end +-- @module lexer +local M = {} + +--- The tag name for default elements. +-- @field DEFAULT + +--- The tag name for comment elements. +-- @field COMMENT + +--- The tag name for string elements. +-- @field STRING + +--- The tag name for number elements. +-- @field NUMBER + +--- The tag name for keyword elements. +-- @field KEYWORD + +--- The tag name for identifier elements. +-- @field IDENTIFIER + +--- The tag name for operator elements. +-- @field OPERATOR + +--- The tag name for error elements. +-- @field ERROR + +--- The tag name for preprocessor elements. +-- @field PREPROCESSOR + +--- The tag name for constant elements. +-- @field CONSTANT + +--- The tag name for variable elements. +-- @field VARIABLE + +--- The tag name for function elements. +-- @field FUNCTION -local print = function(...) - local args = table.pack(...) - local msg = {} - for i = 1, args.n do - msg[#msg + 1] = tostring(args[i]) +--- The tag name for class elements. +-- @field CLASS + +--- The tag name for type elements. +-- @field TYPE + +--- The tag name for label elements. +-- @field LABEL + +--- The tag name for regex elements. +-- @field REGEX + +--- The tag name for embedded elements. +-- @field EMBEDDED + +--- The tag name for builtin function elements. +-- @field FUNCTION_BUILTIN + +--- The tag name for builtin constant elements. +-- @field CONSTANT_BUILTIN + +--- The tag name for function method elements. +-- @field FUNCTION_METHOD + +--- The tag name for function tag elements, typically in markup. +-- @field TAG + +--- The tag name for function attribute elements, typically in markup. +-- @field ATTRIBUTE + +--- The tag name for builtin variable elements. +-- @field VARIABLE_BUILTIN + +--- The tag name for heading elements, typically in markup. +-- @field HEADING + +--- The tag name for bold elements, typically in markup. +-- @field BOLD + +--- The tag name for builtin italic elements, typically in markup. +-- @field ITALIC + +--- The tag name for underlined elements, typically in markup. +-- @field UNDERLINE + +--- The tag name for code elements, typically in markup. +-- @field CODE + +--- The tag name for link elements, typically in markup. +-- @field LINK + +--- The tag name for reference elements, typically in markup. +-- @field REFERENCE + +--- The tag name for annotation elements. +-- @field ANNOTATION + +--- The tag name for list item elements, typically in markup. +-- @field LIST + +--- The initial (root) fold level. +-- @field FOLD_BASE + +--- Flag indicating that the line is blank. +-- @field FOLD_BLANK + +--- Flag indicating the line is fold point. +-- @field FOLD_HEADER + +-- This comment is needed for LDoc to process the previous field. + +if not lpeg then lpeg = require('lpeg') end -- Scintillua's Lua environment defines _G.lpeg +local lpeg = lpeg +local P, R, S, V, B = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.B +local Ct, Cc, Cp, Cmt, C = lpeg.Ct, lpeg.Cc, lpeg.Cp, lpeg.Cmt, lpeg.C + +--- Default tags. +local default = { + 'whitespace', 'comment', 'string', 'number', 'keyword', 'identifier', 'operator', 'error', + 'preprocessor', 'constant', 'variable', 'function', 'class', 'type', 'label', 'regex', 'embedded', + 'function.builtin', 'constant.builtin', 'function.method', 'tag', 'attribute', 'variable.builtin', + 'heading', 'bold', 'italic', 'underline', 'code', 'link', 'reference', 'annotation', 'list' +} +for _, name in ipairs(default) do M[name:upper():gsub('%.', '_')] = name end +--- Names for predefined Scintilla styles. +-- Having these here simplifies style number handling between Scintillua and Scintilla. +local predefined = { + 'default', 'line.number', 'brace.light', 'brace.bad', 'control.char', 'indent.guide', 'call.tip', + 'fold.display.text' +} +for _, name in ipairs(predefined) do M[name:upper():gsub('%.', '_')] = name end + +--- Creates and returns a pattern that tags pattern *patt* with name *name* in lexer *lexer*. +-- If *name* is not a predefined tag name, its Scintilla style will likely need to be defined +-- by the editor or theme using this lexer. +-- @param lexer The lexer to tag the given pattern in. +-- @param name The name to use. +-- @param patt The LPeg pattern to tag. +-- @return pattern +-- @usage local number = lex:tag(lexer.NUMBER, lexer.number) +-- @usage local addition = lex:tag('addition', '+' * lexer.word) +function M.tag(lexer, name, patt) + if not lexer._TAGS then + -- Create the initial maps for tag names to style numbers and styles. + local tags = {} + for i, name in ipairs(default) do tags[name], tags[i] = i, name end + for i, name in ipairs(predefined) do tags[name], tags[i + 32] = i + 32, name end + lexer._TAGS, lexer._num_styles = tags, #default + 1 + lexer._extra_tags = {} end - vis:info(table.concat(msg, ' ')) + if not assert(lexer._TAGS, 'not a lexer instance')[name] then + local num_styles = lexer._num_styles + if num_styles == 33 then num_styles = num_styles + 8 end -- skip predefined + assert(num_styles <= 256, 'too many styles defined (256 MAX)') + lexer._TAGS[name], lexer._TAGS[num_styles], lexer._num_styles = num_styles, name, num_styles + 1 + lexer._extra_tags[name] = true + -- If the lexer is a proxy or a child that embedded itself, make this tag name known to + -- the parent lexer. + if lexer._lexer then lexer._lexer:tag(name, false) end + end + return Cc(name) * (P(patt) / 0) * Cp() end -lpeg = require('lpeg') -local lpeg_P, lpeg_R, lpeg_S, lpeg_V = lpeg.P, lpeg.R, lpeg.S, lpeg.V -local lpeg_Ct, lpeg_Cc, lpeg_Cp = lpeg.Ct, lpeg.Cc, lpeg.Cp -local lpeg_Cmt, lpeg_C = lpeg.Cmt, lpeg.C -local lpeg_match = lpeg.match +--- Returns a unique grammar rule name for the given lexer's i-th word list. +local function word_list_id(lexer, i) return lexer._name .. '_wordlist' .. i end --- Searches for the given *name* in the given *path*. --- This is a safe implementation of Lua 5.2's `package.searchpath()` function that does not --- require the package module to be loaded. -local function searchpath(name, path) - local tried = {} - for part in path:gmatch('[^;]+') do - local filename = part:gsub('%?', name) - local ok, errmsg = loadfile(filename) - if ok or not errmsg:find('cannot open') then return filename end - tried[#tried + 1] = string.format("no file '%s'", filename) +--- Either returns a pattern for lexer *lexer* (if given) that matches one word in the word list +-- identified by string *word_list*, ignoring case if *case_sensitive* is `true`, or, if *lexer* +-- is not given, creates and returns a pattern that matches any single word in list or string +-- *word_list*, ignoring case if *case_insensitive* is `true`. +-- This is a convenience function for simplifying a set of ordered choice word patterns and +-- potentially allowing downstream users to configure word lists. +-- If there is ultimately no word list set via `set_word_list()`, no error will be raised, +-- but the returned pattern will not match anything. +-- @param[opt] lexer Optional lexer to match a word in a wordlist for. This parameter may be +-- omitted for lexer-agnostic matching. +-- @param word_list Either a string name of the word list to match from if *lexer* is given, +-- or, if *lexer* is omitted, a list of words or a string list of words separated by spaces. +-- @param[opt] case_insensitive Optional boolean flag indicating whether or not the word match +-- is case-insensitive. The default value is `false`. +-- @return pattern +-- @usage lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) +-- @usage local keyword = lex:tag(lexer.KEYWORD, lexer.word_match{'foo', 'bar', 'baz'}) +-- @usage local keyword = lex:tag(lexer.KEYWORD, lexer.word_match({'foo-bar', 'foo-baz', +-- 'bar-foo', 'bar-baz', 'baz-foo', 'baz-bar'}, true)) +-- @usage local keyword = lex:tag(lexer.KEYWORD, lexer.word_match('foo bar baz')) +function M.word_match(lexer, word_list, case_insensitive) + if type(lexer) == 'table' and getmetatable(lexer) then + if lexer._lexer then + -- If this lexer is a proxy (e.g. rails), get the true parent (ruby) in order to get the + -- parent's word list. If this lexer is a child embedding itself (e.g. php), continue + -- getting its word list, not the parent's (html). + local parent = lexer._lexer + if not parent._CHILDREN or not parent._CHILDREN[lexer] then lexer = parent end + end + + if not lexer._WORDLISTS then lexer._WORDLISTS = {case_insensitive = {}} end + local i = lexer._WORDLISTS[word_list] or #lexer._WORDLISTS + 1 + lexer._WORDLISTS[word_list], lexer._WORDLISTS[i] = i, '' -- empty placeholder word list + lexer._WORDLISTS.case_insensitive[i] = case_insensitive + return V(word_list_id(lexer, i)) end - return nil, table.concat(tried, '\n') -end ---- --- Map of color name strings to color values in `0xBBGGRR` or `"#RRGGBB"` format. --- Note: for applications running within a terminal emulator, only 16 color values are recognized, --- regardless of how many colors a user's terminal actually supports. (A terminal emulator's --- settings determines how to actually display these recognized color values, which may end up --- being mapped to a completely different color set.) In order to use the light variant of a --- color, some terminals require a style's `bold` attribute must be set along with that normal --- color. Recognized color values are black (0x000000), red (0x000080), green (0x008000), yellow --- (0x008080), blue (0x800000), magenta (0x800080), cyan (0x808000), white (0xC0C0C0), light black --- (0x404040), light red (0x0000FF), light green (0x00FF00), light yellow (0x00FFFF), light blue --- (0xFF0000), light magenta (0xFF00FF), light cyan (0xFFFF00), and light white (0xFFFFFF). --- @name colors --- @class table -M.colors = setmetatable({}, { - __index = function(_, name) - local color = M.property['color.' .. name] - return tonumber(color) or color - end, __newindex = function(_, name, color) M.property['color.' .. name] = color end -}) + -- Lexer-agnostic word match. + word_list, case_insensitive = lexer, word_list --- A style object that distills into a property string that can be read by the LPeg lexer. -local style_obj = {} -style_obj.__index = style_obj - --- Create a style object from a style name, property table, or legacy style string. -function style_obj.new(name_or_props) - local prop_string = tostring(name_or_props) - if type(name_or_props) == 'string' and name_or_props:find('^[%w_]+$') then - prop_string = string.format('$(style.%s)', name_or_props) - elseif type(name_or_props) == 'table' then - local settings = {} - for k, v in pairs(name_or_props) do - settings[#settings + 1] = type(v) ~= 'boolean' and string.format('%s:%s', k, v) or - string.format('%s%s', v and '' or 'not', k) + if type(word_list) == 'string' then + local words = word_list -- space-separated list of words + word_list = {} + for word in words:gmatch('%S+') do word_list[#word_list + 1] = word end + end + + local word_chars = M.alnum + '_' + local extra_chars = '' + for _, word in ipairs(word_list) do + word_list[case_insensitive and word:lower() or word] = true + for char in word:gmatch('[^%w_%s]') do + if not extra_chars:find(char, 1, true) then extra_chars = extra_chars .. char end end - prop_string = table.concat(settings, ',') end - return setmetatable({prop_string = prop_string}, style_obj) -end + if extra_chars ~= '' then word_chars = word_chars + S(extra_chars) end + + -- Optimize small word sets as ordered choice. "Small" is arbitrary. + if #word_list <= 6 and not case_insensitive then + local choice = P(false) + for _, word in ipairs(word_list) do choice = choice + word:match('%S+') end + return choice * -word_chars + end --- Returns a new style based on this one with the properties defined in the given table or --- legacy style string. -function style_obj.__concat(self, props) - if type(props) == 'table' then props = tostring(style_obj.new(props)) end - return setmetatable({prop_string = string.format('%s,%s', self.prop_string, props)}, style_obj) + return Cmt(word_chars^1, function(input, index, word) + if case_insensitive then word = word:lower() end + return word_list[word] + end) end --- Returns this style object as property string for use with the LPeg lexer. -function style_obj.__tostring(self) return self.prop_string end - ---- --- Map of style names to style definition tables. --- --- Style names consist of the following default names as well as the token names defined by lexers. --- --- * `default`: The default style all others are based on. --- * `line_number`: The line number margin style. --- * `control_char`: The style of control character blocks. --- * `indent_guide`: The style of indentation guides. --- * `call_tip`: The style of call tip text. Only the `font`, `size`, `fore`, and `back` style --- definition fields are supported. --- * `fold_display_text`: The style of text displayed next to folded lines. --- * `class`, `comment`, `constant`, `embedded`, `error`, `function`, `identifier`, `keyword`, --- `label`, `number`, `operator`, `preprocessor`, `regex`, `string`, `type`, `variable`, --- `whitespace`: Some token names used by lexers. Some lexers may define more token names, --- so this list is not exhaustive. --- * *`lang`*`_whitespace`: A special style for whitespace tokens in lexer name *lang*. It --- inherits from `whitespace`, and is used in place of it for all lexers. --- --- Style definition tables may contain the following fields: --- --- * `font`: String font name. --- * `size`: Integer font size. --- * `bold`: Whether or not the font face is bold. The default value is `false`. --- * `weight`: Integer weight or boldness of a font, between 1 and 999. --- * `italics`: Whether or not the font face is italic. The default value is `false`. --- * `underlined`: Whether or not the font face is underlined. The default value is `false`. --- * `fore`: Font face foreground color in `0xBBGGRR` or `"#RRGGBB"` format. --- * `back`: Font face background color in `0xBBGGRR` or `"#RRGGBB"` format. --- * `eolfilled`: Whether or not the background color extends to the end of the line. The +--- Sets in lexer *lexer* the word list identified by string or number *name* to string or +-- list *word_list*, appending to any existing word list if *append* is `true`. +-- This only has an effect if *lexer* uses `word_match()` to reference the given list. +-- Case-insensitivity is specified by `word_match()`. +-- @param lexer The lexer to add the given word list to. +-- @param name The string name or number of the word list to set. +-- @param word_list A list of words or a string list of words separated by spaces. +-- @param append Whether or not to append *word_list* to the existing word list (if any). The -- default value is `false`. --- * `case`: Font case: `'u'` for upper, `'l'` for lower, and `'m'` for normal, mixed case. The --- default value is `'m'`. --- * `visible`: Whether or not the text is visible. The default value is `true`. --- * `changeable`: Whether the text is changeable instead of read-only. The default value is --- `true`. --- @class table --- @name styles -M.styles = setmetatable({}, { - __index = function(_, name) return style_obj.new(name) end, __newindex = function(_, name, style) - if getmetatable(style) ~= style_obj then style = style_obj.new(style) end - M.property['style.' .. name] = tostring(style) +function M.set_word_list(lexer, name, word_list, append) + if word_list == 'scintillua' then return end -- for SciTE + if lexer._lexer then + -- If this lexer is a proxy (e.g. rails), get the true parent (ruby) in order to set the + -- parent's word list. If this lexer is a child embedding itself (e.g. php), continue + -- setting its word list, not the parent's (html). + local parent = lexer._lexer + if not parent._CHILDREN or not parent._CHILDREN[lexer] then lexer = parent end end -}) --- Default styles. -local default = { - 'nothing', 'whitespace', 'comment', 'string', 'number', 'keyword', 'identifier', 'operator', - 'error', 'preprocessor', 'constant', 'variable', 'function', 'class', 'type', 'label', 'regex', - 'embedded' -} -for _, name in ipairs(default) do - M[name:upper()] = name - M['STYLE_' .. name:upper()] = style_obj.new(name) -- backward compatibility -end --- Predefined styles. -local predefined = { - 'default', 'line_number', 'brace_light', 'brace_bad', 'control_char', 'indent_guide', 'call_tip', - 'fold_display_text' -} -for _, name in ipairs(predefined) do - M[name:upper()] = name - M['STYLE_' .. name:upper()] = style_obj.new(name) -- backward compatibility + assert(lexer._WORDLISTS, 'lexer has no word lists') + local i = tonumber(lexer._WORDLISTS[name]) or name -- lexer._WORDLISTS[name] --> i + if type(i) ~= 'number' or i > #lexer._WORDLISTS then return end -- silently return + + if type(word_list) == 'string' then + local list = {} + for word in word_list:gmatch('%S+') do list[#list + 1] = word end + word_list = list + end + + if not append or lexer._WORDLISTS[i] == '' then + lexer._WORDLISTS[i] = word_list + else + local list = lexer._WORDLISTS[i] + for _, word in ipairs(word_list) do list[#list + 1] = word end + end + + lexer._grammar_table = nil -- invalidate end ---- --- Adds pattern *rule* identified by string *id* to the ordered list of rules for lexer *lexer*. +--- Adds pattern *rule* identified by string *id* to the ordered list of rules for lexer *lexer*. -- @param lexer The lexer to add the given rule to. -- @param id The id associated with this rule. It does not have to be the same as the name --- passed to `token()`. +-- passed to `tag()`. -- @param rule The LPeg pattern of the rule. -- @see modify_rule --- @name add_rule function M.add_rule(lexer, id, rule) if lexer._lexer then lexer = lexer._lexer end -- proxy; get true parent - if not lexer._RULES then - lexer._RULES = {} - -- Contains an ordered list (by numerical index) of rule names. This is used in conjunction - -- with lexer._RULES for building _TOKENRULE. - lexer._RULEORDER = {} + if not lexer._rules then lexer._rules = {} end + if id == 'whitespace' and lexer._rules[id] then -- legacy + lexer:modify_rule(id, rule) + return end - lexer._RULES[id] = rule - lexer._RULEORDER[#lexer._RULEORDER + 1] = id - lexer:build_grammar() + lexer._rules[#lexer._rules + 1], lexer._rules[id] = id, rule + lexer._grammar_table = nil -- invalidate end ---- --- Replaces in lexer *lexer* the existing rule identified by string *id* with pattern *rule*. +--- Replaces in lexer *lexer* the existing rule identified by string *id* with pattern *rule*. -- @param lexer The lexer to modify. -- @param id The id associated with this rule. -- @param rule The LPeg pattern of the rule. --- @name modify_rule function M.modify_rule(lexer, id, rule) if lexer._lexer then lexer = lexer._lexer end -- proxy; get true parent - lexer._RULES[id] = rule - lexer:build_grammar() + assert(lexer._rules[id], 'rule does not exist') + lexer._rules[id] = rule + lexer._grammar_table = nil -- invalidate end ---- --- Returns the rule identified by string *id*. +--- Returns a unique grammar rule name for the given lexer's rule name. +local function rule_id(lexer, name) return lexer._name .. '.' .. name end + +--- Returns the rule identified by string *id*. -- @param lexer The lexer to fetch a rule from. -- @param id The id of the rule to fetch. -- @return pattern --- @name get_rule function M.get_rule(lexer, id) if lexer._lexer then lexer = lexer._lexer end -- proxy; get true parent - return lexer._RULES[id] + if id == 'whitespace' then return V(rule_id(lexer, id)) end -- special case + return assert(lexer._rules[id], 'rule does not exist') end ---- --- Associates string *token_name* in lexer *lexer* with style table *style*. --- *style* may have the following fields: --- --- * `font`: String font name. --- * `size`: Integer font size. --- * `bold`: Whether or not the font face is bold. The default value is `false`. --- * `weight`: Integer weight or boldness of a font, between 1 and 999. --- * `italics`: Whether or not the font face is italic. The default value is `false`. --- * `underlined`: Whether or not the font face is underlined. The default value is `false`. --- * `fore`: Font face foreground color in `0xBBGGRR` or `"#RRGGBB"` format. --- * `back`: Font face background color in `0xBBGGRR` or `"#RRGGBB"` format. --- * `eolfilled`: Whether or not the background color extends to the end of the line. The --- default value is `false`. --- * `case`: Font case, `'u'` for upper, `'l'` for lower, and `'m'` for normal, mixed case. The --- default value is `'m'`. --- * `visible`: Whether or not the text is visible. The default value is `true`. --- * `changeable`: Whether the text is changeable instead of read-only. The default value is --- `true`. --- --- Field values may also contain "$(property.name)" expansions for properties defined in Scintilla, --- theme files, etc. --- @param lexer The lexer to add a style to. --- @param token_name The name of the token to associated with the style. --- @param style A style string for Scintilla. --- @usage lex:add_style('longstring', lexer.styles.string) --- @usage lex:add_style('deprecated_func', lexer.styles['function'] .. {italics = true} --- @usage lex:add_style('visible_ws', lexer.styles.whitespace .. {back = lexer.colors.grey} --- @name add_style -function M.add_style(lexer, token_name, style) - local num_styles = lexer._numstyles - if num_styles == 33 then num_styles = num_styles + 8 end -- skip predefined - if num_styles >= 256 then print('Too many styles defined (256 MAX)') end - lexer._TOKENSTYLES[token_name], lexer._numstyles = num_styles, num_styles + 1 - if type(style) == 'table' and not getmetatable(style) then style = style_obj.new(style) end - lexer._EXTRASTYLES[token_name] = tostring(style) - -- If the lexer is a proxy or a child that embedded itself, copy this style to the parent lexer. - if lexer._lexer then lexer._lexer:add_style(token_name, style) end +--- Embeds child lexer *child* in parent lexer *lexer* using patterns *start_rule* and *end_rule*, +-- which signal the beginning and end of the embedded lexer, respectively. +-- @param lexer The parent lexer. +-- @param child The child lexer. +-- @param start_rule The pattern that signals the beginning of the embedded lexer. +-- @param end_rule The pattern that signals the end of the embedded lexer. +-- @usage html:embed(css, css_start_rule, css_end_rule) +-- @usage html:embed(lex, php_start_rule, php_end_rule) -- from php lexer +function M.embed(lexer, child, start_rule, end_rule) + if lexer._lexer then lexer = lexer._lexer end -- proxy; get true parent + + -- Add child rules. + assert(child._rules, 'cannot embed lexer with no rules') + if not child._start_rules then child._start_rules = {} end + if not child._end_rules then child._end_rules = {} end + child._start_rules[lexer], child._end_rules[lexer] = start_rule, end_rule + if not lexer._CHILDREN then lexer._CHILDREN = {} end + lexer._CHILDREN[#lexer._CHILDREN + 1], lexer._CHILDREN[child] = child, true + + -- Add child tags. + for name in pairs(child._extra_tags) do lexer:tag(name, true) end + + -- Add child fold symbols. + if child._fold_points then + for tag_name, symbols in pairs(child._fold_points) do + if tag_name ~= '_symbols' then + for symbol, v in pairs(symbols) do lexer:add_fold_point(tag_name, symbol, v) end + end + end + end + + -- Add child word lists. + if child._WORDLISTS then + for name, i in pairs(child._WORDLISTS) do + if type(name) == 'string' and type(i) == 'number' then + name = child._name .. '.' .. name + lexer:word_match(name) -- for side effects + lexer:set_word_list(name, child._WORDLISTS[i]) + end + end + end + + child._lexer = lexer -- use parent's rules if child is embedding itself end ---- --- Adds to lexer *lexer* a fold point whose beginning and end tokens are string *token_name* --- tokens with string content *start_symbol* and *end_symbol*, respectively. +--- Adds to lexer *lexer* a fold point whose beginning and end points are tagged with string +-- *tag_name* tags and have string content *start_symbol* and *end_symbol*, respectively. -- In the event that *start_symbol* may or may not be a fold point depending on context, and that -- additional processing is required, *end_symbol* may be a function that ultimately returns -- `1` (indicating a beginning fold point), `-1` (indicating an ending fold point), or `0` -- (indicating no fold point). That function is passed the following arguments: -- --- * `text`: The text being processed for fold points. --- * `pos`: The position in *text* of the beginning of the line currently being processed. --- * `line`: The text of the line currently being processed. --- * `s`: The position of *start_symbol* in *line*. --- * `symbol`: *start_symbol* itself. +-- - `text`: The text being processed for fold points. +-- - `pos`: The position in *text* of the beginning of the line currently being processed. +-- - `line`: The text of the line currently being processed. +-- - `s`: The position of *start_symbol* in *line*. +-- - `symbol`: *start_symbol* itself. -- @param lexer The lexer to add a fold point to. --- @param token_name The token name of text that indicates a fold point. +-- @param tag_name The tag name for text that indicates a fold point. -- @param start_symbol The text that indicates the beginning of a fold point. -- @param end_symbol Either the text that indicates the end of a fold point, or a function that -- returns whether or not *start_symbol* is a beginning fold point (1), an ending fold point -- (-1), or not a fold point at all (0). -- @usage lex:add_fold_point(lexer.OPERATOR, '{', '}') -- @usage lex:add_fold_point(lexer.KEYWORD, 'if', 'end') --- @usage lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) -- @usage lex:add_fold_point('custom', function(text, pos, line, s, symbol) ... end) --- @name add_fold_point -function M.add_fold_point(lexer, token_name, start_symbol, end_symbol) - if not lexer._FOLDPOINTS then lexer._FOLDPOINTS = {_SYMBOLS = {}} end - local symbols = lexer._FOLDPOINTS._SYMBOLS - if not lexer._FOLDPOINTS[token_name] then lexer._FOLDPOINTS[token_name] = {} end - if lexer._CASEINSENSITIVEFOLDPOINTS then +function M.add_fold_point(lexer, tag_name, start_symbol, end_symbol) + if not start_symbol and not end_symbol then return end -- from legacy fold_consecutive_lines() + if not lexer._fold_points then lexer._fold_points = {_symbols = {}} end + local symbols = lexer._fold_points._symbols + if not lexer._fold_points[tag_name] then lexer._fold_points[tag_name] = {} end + if lexer._case_insensitive_fold_points then start_symbol = start_symbol:lower() if type(end_symbol) == 'string' then end_symbol = end_symbol:lower() end end + if type(end_symbol) == 'string' then if not symbols[end_symbol] then symbols[#symbols + 1], symbols[end_symbol] = end_symbol, true end - lexer._FOLDPOINTS[token_name][start_symbol] = 1 - lexer._FOLDPOINTS[token_name][end_symbol] = -1 + lexer._fold_points[tag_name][start_symbol] = 1 + lexer._fold_points[tag_name][end_symbol] = -1 else - lexer._FOLDPOINTS[token_name][start_symbol] = end_symbol -- function or int + lexer._fold_points[tag_name][start_symbol] = end_symbol -- function or int end if not symbols[start_symbol] then symbols[#symbols + 1], symbols[start_symbol] = start_symbol, true end + -- If the lexer is a proxy or a child that embedded itself, copy this fold point to the -- parent lexer. - if lexer._lexer then lexer._lexer:add_fold_point(token_name, start_symbol, end_symbol) end + if lexer._lexer then lexer._lexer:add_fold_point(tag_name, start_symbol, end_symbol) end end --- (Re)constructs `lexer._TOKENRULE`. -local function join_tokens(lexer) - local patterns, order = lexer._RULES, lexer._RULEORDER - local token_rule = patterns[order[1]] - for i = 2, #order do token_rule = token_rule + patterns[order[i]] end - lexer._TOKENRULE = token_rule + M.token(M.DEFAULT, M.any) - return lexer._TOKENRULE -end +--- Recursively adds the rules for the given lexer and its children to the given grammar. +-- @param g The grammar to add rules to. +-- @param lexer The lexer whose rules to add. +local function add_lexer(g, lexer) + local rule = P(false) --- Metatable for Scintillua grammars. --- These grammars are just tables ultimately passed to `lpeg.P()`. -local grammar_mt = { - __index = { - -- Adds lexer *lexer* and any of its embedded lexers to this grammar. - -- @param lexer The lexer to add. - add_lexer = function(self, lexer) - local lexer_name = lexer._PARENTNAME or lexer._NAME - local token_rule = lexer:join_tokens() - for _, child in ipairs(lexer._CHILDREN) do - if child._CHILDREN then self:add_lexer(child) end - local rules = child._EMBEDDEDRULES[lexer_name] - local rules_token_rule = self['__' .. child._NAME] or rules.token_rule - self[child._NAME] = (-rules.end_rule * rules_token_rule)^0 * rules.end_rule^-1 * - lpeg_V(lexer_name) - local embedded_child = '_' .. child._NAME - self[embedded_child] = rules.start_rule * (-rules.end_rule * rules_token_rule)^0 * - rules.end_rule^-1 - token_rule = lpeg_V(embedded_child) + token_rule - end - self['__' .. lexer_name] = token_rule -- can contain embedded lexer rules - self[lexer_name] = token_rule^0 + -- Add this lexer's rules. + for _, name in ipairs(lexer._rules) do + local id = rule_id(lexer, name) + g[id] = lexer._rules[name] -- ['lua.keyword'] = keyword_patt + rule = rule + V(id) -- V('lua.keyword') + V('lua.function') + V('lua.constant') + ... + end + local any_id = lexer._name .. '_fallback' + g[any_id] = lexer:tag(M.DEFAULT, M.any) -- ['lua_fallback'] = any_char + rule = rule + V(any_id) -- ... + V('lua.operator') + V('lua_fallback') + + -- Add this lexer's word lists. + if lexer._WORDLISTS then + for i = 1, #lexer._WORDLISTS do + local id = word_list_id(lexer, i) + local list, case_insensitive = lexer._WORDLISTS[i], lexer._WORDLISTS.case_insensitive[i] + local patt = list ~= '' and M.word_match(list, case_insensitive) or P(false) + g[id] = patt -- ['lua_wordlist.1'] = word_match_patt or P(false) end - } -} + end --- (Re)constructs `lexer._GRAMMAR`. --- @param initial_rule The name of the rule to start lexing with. The default value is --- `lexer._NAME`. Multilang lexers use this to start with a child rule if necessary. -local function build_grammar(lexer, initial_rule) - if not lexer._RULES then return end - if lexer._CHILDREN then - if not initial_rule then initial_rule = lexer._NAME end - local grammar = setmetatable({initial_rule}, grammar_mt) - grammar:add_lexer(lexer) - lexer._INITIALRULE = initial_rule - lexer._GRAMMAR = lpeg_Ct(lpeg_P(grammar)) - else - lexer._GRAMMAR = lpeg_Ct(lexer:join_tokens()^0) + -- Add this child lexer's end rules. + if lexer._end_rules then + for parent, end_rule in pairs(lexer._end_rules) do + local back_id = lexer._name .. '_to_' .. parent._name + g[back_id] = end_rule -- ['css_to_html'] = css_end_rule + rule = rule - V(back_id) + -- (V('css.property') + ... + V('css_fallback')) - V('css_to_html') + V(back_id) * V(parent._name) -- V('css_to_html') * V('html') + end + end + + -- Add this child lexer's start rules. + if lexer._start_rules then + for parent, start_rule in pairs(lexer._start_rules) do + local to_id = parent._name .. '_to_' .. lexer._name + g[to_id] = start_rule * V(lexer._name) -- ['html_to_css'] = css_start_rule * V('css') + end + end + + -- Finish adding this lexer's rules. + local rule_id = lexer._name .. '_rule' + g[rule_id] = rule -- ['lua_rule'] = V('lua.keyword') + ... + V('lua_fallback') + g[lexer._name] = V(rule_id)^0 -- ['lua'] = V('lua_rule')^0 + + -- Add this lexer's children's rules. + -- TODO: preprocessor languages like PHP should also embed themselves into their parent's + -- children like HTML's CSS and Javascript. + if not lexer._CHILDREN then return end + for _, child in ipairs(lexer._CHILDREN) do + add_lexer(g, child) + local to_id = lexer._name .. '_to_' .. child._name + g[rule_id] = V(to_id) + g[rule_id] -- ['html_rule'] = V('html_to_css') + V('html.comment') + ... + + -- Add a child's inherited parent's rules (e.g. rhtml parent with rails child inheriting ruby). + if child._parent_name then + local name = child._name + child._name = child._parent_name -- ensure parent and transition rule names are correct + add_lexer(g, child) + child._name = name -- restore + local to_id = lexer._name .. '_to_' .. child._parent_name + g[rule_id] = V(to_id) + g[rule_id] -- ['html_rule'] = V('html_to_ruby') + V('html.comment') + ... + end end end ---- --- Embeds child lexer *child* in parent lexer *lexer* using patterns *start_rule* and *end_rule*, --- which signal the beginning and end of the embedded lexer, respectively. --- @param lexer The parent lexer. --- @param child The child lexer. --- @param start_rule The pattern that signals the beginning of the embedded lexer. --- @param end_rule The pattern that signals the end of the embedded lexer. --- @usage html:embed(css, css_start_rule, css_end_rule) --- @usage html:embed(lex, php_start_rule, php_end_rule) -- from php lexer --- @name embed -function M.embed(lexer, child, start_rule, end_rule) - if lexer._lexer then lexer = lexer._lexer end -- proxy; get true parent - -- Add child rules. - if not child._EMBEDDEDRULES then child._EMBEDDEDRULES = {} end - if not child._RULES then error('Cannot embed lexer with no rules') end - child._EMBEDDEDRULES[lexer._NAME] = { - start_rule = start_rule, token_rule = child:join_tokens(), end_rule = end_rule - } - if not lexer._CHILDREN then lexer._CHILDREN = {} end - local children = lexer._CHILDREN - children[#children + 1] = child - -- Add child styles. - for token, style in pairs(child._EXTRASTYLES) do lexer:add_style(token, style) end - -- Add child fold symbols. - if child._FOLDPOINTS then - for token_name, symbols in pairs(child._FOLDPOINTS) do - if token_name ~= '_SYMBOLS' then - for symbol, v in pairs(symbols) do lexer:add_fold_point(token_name, symbol, v) end +--- Returns a grammar for the given lexer and initial rule, (re)constructing it if necessary. +-- @param lexer The lexer to build a grammar for. +-- @param init_style The current style. Multiple-language lexers use this to determine which +-- language to start lexing in. +local function build_grammar(lexer, init_style) + if not lexer._rules then return end + if not lexer._initial_rule then lexer._initial_rule = lexer._parent_name or lexer._name end + if not lexer._grammar_table then + local grammar = {lexer._initial_rule} + if not lexer._parent_name then + add_lexer(grammar, lexer) + -- {'lua', + -- ['lua.keyword'] = patt, ['lua.function'] = patt, ..., + -- ['lua_wordlist.1'] = patt, ['lua_wordlist.2'] = patt, ..., + -- ['lua_rule'] = V('lua.keyword') + ... + V('lua_fallback'), + -- ['lua'] = V('lua_rule')^0 + -- } + -- {'html' + -- ['html.comment'] = patt, ['html.doctype'] = patt, ..., + -- ['html_wordlist.1'] = patt, ['html_wordlist.2'] = patt, ..., + -- ['html_rule'] = V('html_to_css') * V('css') + V('html.comment') + ... + V('html_fallback'), + -- ['html'] = V('html')^0, + -- ['css.property'] = patt, ['css.value'] = patt, ..., + -- ['css_wordlist.1'] = patt, ['css_wordlist.2'] = patt, ..., + -- ['css_to_html'] = patt, + -- ['css_rule'] = ((V('css.property') + ... + V('css_fallback')) - V('css_to_html')) + + -- V('css_to_html') * V('html'), + -- ['html_to_css'] = patt, + -- ['css'] = V('css_rule')^0 + -- } + else + local name = lexer._name + lexer._name = lexer._parent_name -- ensure parent and transition rule names are correct + add_lexer(grammar, lexer) + lexer._name = name -- restore + -- {'html', + -- ... + -- ['html_rule'] = V('html_to_php') * V('php') + V('html_to_css') * V('css') + + -- V('html.comment') + ... + V('html_fallback'), + -- ... + -- ['php.keyword'] = patt, ['php.type'] = patt, ..., + -- ['php_wordlist.1'] = patt, ['php_wordlist.2'] = patt, ..., + -- ['php_to_html'] = patt, + -- ['php_rule'] = ((V('php.keyword') + ... + V('php_fallback')) - V('php_to_html')) + + -- V('php_to_html') * V('html') + -- ['html_to_php'] = patt, + -- ['php'] = V('php_rule')^0 + -- } + end + lexer._grammar, lexer._grammar_table = Ct(P(grammar)), grammar + end + + -- For multilang lexers, build a new grammar whose initial rule is the current language + -- if necessary. LPeg does not allow a variable initial rule. + if lexer._CHILDREN then + for style_num, tag in ipairs(lexer._TAGS) do + if style_num == init_style then + local lexer_name = tag:match('^whitespace%.(.+)$') or lexer._parent_name or lexer._name + if lexer._initial_rule == lexer_name then break end + if not lexer._grammar_table[lexer_name] then + -- For proxy lexers like RHTML, the 'whitespace.rhtml' tag would produce the 'rhtml' + -- lexer name, but there is no 'rhtml' rule. It should be the 'html' rule (parent) + -- instead. + lexer_name = lexer._parent_name or lexer._name + end + lexer._initial_rule = lexer_name + lexer._grammar_table[1] = lexer._initial_rule + lexer._grammar = Ct(P(lexer._grammar_table)) + return lexer._grammar end end end - lexer:build_grammar() - child._lexer = lexer -- use parent's tokens if child is embedding itself + + return lexer._grammar end ---- --- Lexes a chunk of text *text* (that has an initial style number of *init_style*) using lexer --- *lexer*, returning a table of token names and positions. +--- Lexes a chunk of text *text* (that has an initial style number of *init_style*) using lexer +-- *lexer*, returning a list of tag names and positions. -- @param lexer The lexer to lex text with. -- @param text The text in the buffer to lex. -- @param init_style The current style. Multiple-language lexers use this to determine which -- language to start lexing in. --- @return table of token names and positions. --- @name lex +-- @return list of tag names and positions. function M.lex(lexer, text, init_style) - if not lexer._GRAMMAR then return {M.DEFAULT, #text + 1} end - if not lexer._LEXBYLINE then - -- For multilang lexers, build a new grammar whose initial_rule is the current language. - if lexer._CHILDREN then - for style, style_num in pairs(lexer._TOKENSTYLES) do - if style_num == init_style then - local lexer_name = style:match('^(.+)_whitespace') or lexer._PARENTNAME or lexer._NAME - if lexer._INITIALRULE ~= lexer_name then lexer:build_grammar(lexer_name) end - break - end - end - end - return lpeg_match(lexer._GRAMMAR, text) - else - local function append(tokens, line_tokens, offset) - for i = 1, #line_tokens, 2 do - tokens[#tokens + 1] = line_tokens[i] - tokens[#tokens + 1] = line_tokens[i + 1] + offset + local grammar = build_grammar(lexer, init_style) + if not grammar then return {M.DEFAULT, #text + 1} end + if M._standalone then M._text, M.line_state = text, {} end + + if lexer._lex_by_line then + local line_from_position = M.line_from_position + local function append(tags, line_tags, offset) + for i = 1, #line_tags, 2 do + tags[#tags + 1], tags[#tags + 2] = line_tags[i], line_tags[i + 1] + offset end end - local tokens = {} + local tags = {} local offset = 0 - local grammar = lexer._GRAMMAR + rawset(M, 'line_from_position', function(pos) return line_from_position(pos + offset) end) for line in text:gmatch('[^\r\n]*\r?\n?') do - local line_tokens = lpeg_match(grammar, line) - if line_tokens then append(tokens, line_tokens, offset) end + local line_tags = grammar:match(line) + if line_tags then append(tags, line_tags, offset) end offset = offset + #line - -- Use the default style to the end of the line if none was specified. - if tokens[#tokens] ~= offset then - tokens[#tokens + 1], tokens[#tokens + 2] = 'default', offset + 1 + -- Use the default tag to the end of the line if none was specified. + if tags[#tags] ~= offset + 1 then + tags[#tags + 1], tags[#tags + 2] = 'default', offset + 1 end end - return tokens + rawset(M, 'line_from_position', line_from_position) + return tags end + + return grammar:match(text) end ---- --- Determines fold points in a chunk of text *text* using lexer *lexer*, returning a table of +--- Determines fold points in a chunk of text *text* using lexer *lexer*, returning a table of -- fold levels associated with line numbers. --- *text* starts at position *start_pos* on line number *start_line* with a beginning fold --- level of *start_level* in the buffer. +-- *text* starts on line number *start_line* with a beginning fold level of *start_level* +-- in the buffer. -- @param lexer The lexer to fold text with. -- @param text The text in the buffer to fold. --- @param start_pos The position in the buffer *text* starts at, counting from 1. -- @param start_line The line number *text* starts on, counting from 1. -- @param start_level The fold level *text* starts on. -- @return table of fold levels associated with line numbers. --- @name fold -function M.fold(lexer, text, start_pos, start_line, start_level) +function M.fold(lexer, text, start_line, start_level) local folds = {} if text == '' then return folds end local fold = M.property_int['fold'] > 0 - local FOLD_BASE = M.FOLD_BASE - local FOLD_HEADER, FOLD_BLANK = M.FOLD_HEADER, M.FOLD_BLANK - if fold and lexer._FOLDPOINTS then + local FOLD_BASE = M.FOLD_BASE or 0x400 + local FOLD_HEADER, FOLD_BLANK = M.FOLD_HEADER or 0x2000, M.FOLD_BLANK or 0x1000 + if M._standalone then M._text, M.line_state = text, {} end + if fold and lexer._fold_points then local lines = {} for p, l in (text .. '\n'):gmatch('()(.-)\r?\n') do lines[#lines + 1] = {p, l} end - local fold_zero_sum_lines = M.property_int['fold.on.zero.sum.lines'] > 0 - local fold_compact = M.property_int['fold.compact'] > 0 - local fold_points = lexer._FOLDPOINTS - local fold_point_symbols = fold_points._SYMBOLS + local fold_zero_sum_lines = M.property_int['fold.scintillua.on.zero.sum.lines'] > 0 + local fold_compact = M.property_int['fold.scintillua.compact'] > 0 + local fold_points = lexer._fold_points + local fold_point_symbols = fold_points._symbols local style_at, fold_level = M.style_at, M.fold_level local line_num, prev_level = start_line, start_level local current_level = prev_level for _, captures in ipairs(lines) do local pos, line = captures[1], captures[2] if line ~= '' then - if lexer._CASEINSENSITIVEFOLDPOINTS then line = line:lower() end + if lexer._case_insensitive_fold_points then line = line:lower() end local ranges = {} local function is_valid_range(s, e) if not s or not e then return false end @@ -1273,7 +1308,11 @@ function M.fold(lexer, text, start_pos, start_line, start_level) local word_before = s > 1 and line:find('^[%w_]', s - 1) local word_after = line:find('^[%w_]', e + 1) if not word or not (word_before or word_after) then - local symbols = fold_points[style_at[start_pos + pos - 1 + s - 1]] + local style_name = style_at[pos + s - 1] + local symbols = fold_points[style_name] + if not symbols and style_name:find('%.') then + symbols = fold_points[style_name:match('^[^.]+')] + end local level = symbols and symbols[symbol] if type(level) == 'function' then level = level(text, pos, line, s, symbol) @@ -1312,7 +1351,8 @@ function M.fold(lexer, text, start_pos, start_line, start_level) end line_num = line_num + 1 end - elseif fold and (lexer._FOLDBYINDENTATION or M.property_int['fold.by.indentation'] > 0) then + elseif fold and + (lexer._fold_by_indentation or M.property_int['fold.scintillua.by.indentation'] > 0) then -- Indentation based folding. -- Calculate indentation per line. local indentation = {} @@ -1366,221 +1406,456 @@ function M.fold(lexer, text, start_pos, start_line, start_level) return folds end ---- --- Creates a returns a new lexer with the given name. +--- Creates a returns a new lexer with the given name. -- @param name The lexer's name. -- @param opts Table of lexer options. Options currently supported: --- * `lex_by_line`: Whether or not the lexer only processes whole lines of text (instead of +-- - `lex_by_line`: Whether or not the lexer only processes whole lines of text (instead of -- arbitrary chunks of text) at a time. Line lexers cannot look ahead to subsequent lines. -- The default value is `false`. --- * `fold_by_indentation`: Whether or not the lexer does not define any fold points and that +-- - `fold_by_indentation`: Whether or not the lexer does not define any fold points and that -- fold points should be calculated based on changes in line indentation. The default value -- is `false`. --- * `case_insensitive_fold_points`: Whether or not fold points added via +-- - `case_insensitive_fold_points`: Whether or not fold points added via -- `lexer.add_fold_point()` ignore case. The default value is `false`. --- * `inherit`: Lexer to inherit from. The default value is `nil`. +-- - `no_user_word_lists`: Does not automatically allocate word lists that can be set by +-- users. This should really only be set by non-programming languages like markup languages. +-- - `inherit`: Lexer to inherit from. The default value is `nil`. -- @usage lexer.new('rhtml', {inherit = lexer.load('html')}) --- @name new function M.new(name, opts) - local lexer = { - _NAME = assert(name, 'lexer name expected'), _LEXBYLINE = opts and opts['lex_by_line'], - _FOLDBYINDENTATION = opts and opts['fold_by_indentation'], - _CASEINSENSITIVEFOLDPOINTS = opts and opts['case_insensitive_fold_points'], - _lexer = opts and opts['inherit'] - } - - -- Create the initial maps for token names to style numbers and styles. - local token_styles = {} - for i = 1, #default do token_styles[default[i]] = i end - for i = 1, #predefined do token_styles[predefined[i]] = i + 32 end - lexer._TOKENSTYLES, lexer._numstyles = token_styles, #default + 1 - lexer._EXTRASTYLES = {} - - return setmetatable(lexer, { + local lexer = setmetatable({ + _name = assert(name, 'lexer name expected'), _lex_by_line = opts and opts['lex_by_line'], + _fold_by_indentation = opts and opts['fold_by_indentation'], + _case_insensitive_fold_points = opts and opts['case_insensitive_fold_points'], + _no_user_word_lists = opts and opts['no_user_word_lists'], _lexer = opts and opts['inherit'] + }, { __index = { + tag = M.tag, word_match = M.word_match, set_word_list = M.set_word_list, add_rule = M.add_rule, modify_rule = M.modify_rule, get_rule = M.get_rule, - add_style = M.add_style, add_fold_point = M.add_fold_point, join_tokens = join_tokens, - build_grammar = build_grammar, embed = M.embed, lex = M.lex, fold = M.fold + add_fold_point = M.add_fold_point, embed = M.embed, lex = M.lex, fold = M.fold, -- + add_style = function() end -- legacy } }) + + -- Add initial whitespace rule. + -- Use a unique whitespace tag name since embedded lexing relies on these unique names. + lexer:add_rule('whitespace', lexer:tag('whitespace.' .. name, M.space^1)) + + return lexer end --- Legacy support for older lexers. --- Processes the `lex._rules`, `lex._tokenstyles`, and `lex._foldsymbols` tables. Since legacy --- lexers may be processed up to twice, ensure their default styles and rules are not processed --- more than once. -local function process_legacy_lexer(lexer) - local function warn(msg) --[[io.stderr:write(msg, "\n")]]end - if not lexer._LEGACY then - lexer._LEGACY = true - warn("lexers as tables are deprecated; use 'lexer.new()'") - local token_styles = {} - for i = 1, #default do token_styles[default[i]] = i end - for i = 1, #predefined do token_styles[predefined[i]] = i + 32 end - lexer._TOKENSTYLES, lexer._numstyles = token_styles, #default + 1 - lexer._EXTRASTYLES = {} - setmetatable(lexer, getmetatable(M.new(''))) - if lexer._rules then - warn("lexer '_rules' table is deprecated; use 'add_rule()'") - for _, rule in ipairs(lexer._rules) do lexer:add_rule(rule[1], rule[2]) end - end - end - if lexer._tokenstyles then - warn("lexer '_tokenstyles' table is deprecated; use 'add_style()'") - for token, style in pairs(lexer._tokenstyles) do - -- If this legacy lexer is being processed a second time, only add styles added since - -- the first processing. - if not lexer._TOKENSTYLES[token] then lexer:add_style(token, style) end +--- Creates a substitute for some Scintilla tables and functions that Scintillua depends on +-- when using it as a standalone module. +local function initialize_standalone_library() + M.property = setmetatable({['scintillua.lexers'] = package.path:gsub('/%?%.lua', '')}, { + __index = function() return '' end, __newindex = function(t, k, v) rawset(t, k, tostring(v)) end + }) + + M.line_from_position = function(pos) + local line = 1 + for s in M._text:gmatch('[^\n]*()') do + if pos <= s then return line end + line = line + 1 end + return line - 1 -- should not get to here end - if lexer._foldsymbols then - warn("lexer '_foldsymbols' table is deprecated; use 'add_fold_point()'") - for token_name, symbols in pairs(lexer._foldsymbols) do - if type(symbols) == 'table' and token_name ~= '_patterns' then - for symbol, v in pairs(symbols) do lexer:add_fold_point(token_name, symbol, v) end + + M.indent_amount = setmetatable({}, { + __index = function(_, line) + local current_line = 1 + for s in M._text:gmatch('()[^\n]*') do + if current_line == line then + return #M._text:match('^[ \t]*', s):gsub('\t', string.rep(' ', 8)) + end + current_line = current_line + 1 end end - if lexer._foldsymbols._case_insensitive then lexer._CASEINSENSITIVEFOLDPOINTS = true end - elseif lexer._fold then - lexer.fold = function(self, ...) return lexer._fold(...) end + }) + + M._standalone = true +end + +--- Searches for the given *name* in the given *path*. +-- This is a safe implementation of Lua 5.2's `package.searchpath()` function that does not +-- require the package module to be loaded. +local function searchpath(name, path) + local tried = {} + for part in path:gmatch('[^;]+') do + local filename = part:gsub('%?', name) + local ok, errmsg = loadfile(filename) + if ok or not errmsg:find('cannot open') then return filename end + tried[#tried + 1] = string.format("no file '%s'", filename) end + return nil, table.concat(tried, '\n') end -local lexers = {} -- cache of loaded lexers ---- --- Initializes or loads and returns the lexer of string name *name*. +--- Initializes or loads and then returns the lexer of string name *name*. -- Scintilla calls this function in order to load a lexer. Parent lexers also call this function -- in order to load child lexers and vice-versa. The user calls this function in order to load -- a lexer when using Scintillua as a Lua library. -- @param name The name of the lexing language. --- @param alt_name The alternate name of the lexing language. This is useful for embedding the --- same child lexer with multiple sets of start and end tokens. --- @param cache Flag indicating whether or not to load lexers from the cache. This should only --- be `true` when initially loading a lexer (e.g. not from within another lexer for embedding --- purposes). The default value is `false`. +-- @param[opt] alt_name Optional alternate name of the lexing language. This is useful for +-- embedding the same child lexer with multiple sets of start and end tags. -- @return lexer object --- @name load -function M.load(name, alt_name, cache) - if cache and lexers[alt_name or name] then return lexers[alt_name or name] end - - -- When using Scintillua as a stand-alone module, the `property`, `property_int`, and - -- `property_expanded` tables do not exist (they are not useful). Create them in order prevent - -- errors from occurring. - if not M.property then - M.property = setmetatable({['lexer.lpeg.home'] = package.path:gsub('/%?%.lua', '')}, { - __index = function() return '' end, - __newindex = function(t, k, v) rawset(t, k, tostring(v)) end - }) +function M.load(name, alt_name) + assert(name, 'no lexer given') + if not M.property then initialize_standalone_library() end + if not M.property_int then + -- Separate from initialize_standalone_library() so applications that choose to define + -- M.property do not also have to define this. M.property_int = setmetatable({}, { __index = function(t, k) return tonumber(M.property[k]) or 0 end, __newindex = function() error('read-only property') end }) - M.property_expanded = setmetatable({}, { - __index = function(t, key) - return M.property[key]:gsub('[$%%](%b())', function(key) return t[key:sub(2, -2)] end) - end, __newindex = function() error('read-only property') end - }) end - -- Load the language lexer with its rules, styles, etc. - -- However, replace the default `WHITESPACE` style name with a unique whitespace style name - -- (and then automatically add it afterwards), since embedded lexing relies on these unique - -- whitespace style names. Note that loading embedded lexers changes `WHITESPACE` again, - -- so when adding it later, do not reference the potentially incorrect value. - M.WHITESPACE = (alt_name or name) .. '_whitespace' - local path = M.property['lexer.lpeg.home']:gsub(';', '/?.lua;') .. '/?.lua' - local lexer = dofile(assert(searchpath('lexers/'..name, path))) + -- Load the language lexer with its rules, tags, etc. + local path = M.property['scintillua.lexers']:gsub(';', '/?.lua;') .. '/?.lua' + local ro_lexer = setmetatable({ + WHITESPACE = 'whitespace.' .. (alt_name or name) -- legacy + }, {__index = M}) + local env = { + 'assert', 'error', 'ipairs', 'math', 'next', 'pairs', 'print', 'select', 'string', 'table', + 'tonumber', 'tostring', 'type', 'utf8', '_VERSION', lexer = ro_lexer, lpeg = lpeg, -- + require = function() return ro_lexer end -- legacy + } + for _, name in ipairs(env) do env[name] = _G[name] end + local lexer = assert(loadfile(assert(searchpath(name, path)), 't', env))(alt_name or name) assert(lexer, string.format("'%s.lua' did not return a lexer", name)) - if alt_name then lexer._NAME = alt_name end - if not getmetatable(lexer) or lexer._LEGACY then - -- A legacy lexer may need to be processed a second time in order to pick up any `_tokenstyles` - -- or `_foldsymbols` added after `lexer.embed_lexer()`. - process_legacy_lexer(lexer) - if lexer._lexer and lexer._lexer._LEGACY then - process_legacy_lexer(lexer._lexer) -- mainly for `_foldsymbols` edits - end - end - lexer:add_style((alt_name or name) .. '_whitespace', M.styles.whitespace) -- If the lexer is a proxy or a child that embedded itself, set the parent to be the main - -- lexer. Keep a reference to the old parent name since embedded child rules reference and - -- use that name. + -- lexer. Keep a reference to the old parent name since embedded child start and end rules + -- reference and use that name. if lexer._lexer then lexer = lexer._lexer - lexer._PARENTNAME, lexer._NAME = lexer._NAME, alt_name or name + lexer._parent_name, lexer._name = lexer._name, alt_name or name end - if cache then lexers[alt_name or name] = lexer end + M.property['scintillua.comment.' .. (alt_name or name)] = M.property['scintillua.comment'] + return lexer end +--- Returns a list of all known lexer names. +-- This function is not available to lexers and requires the LuaFileSystem (`lfs`) module to +-- be available. +-- @param[opt] path Optional ';'-delimited list of directories to search for lexers in. The +-- default value is Scintillua's configured lexer path. +-- @return lexer name list +function M.names(path) + local lfs = require('lfs') + if not path then path = M.property and M.property['scintillua.lexers'] end + if not path or path == '' then + for part in package.path:gmatch('[^;]+') do + local dir = part:match('^(.-[/\\]?lexers)[/\\]%?%.lua$') + if dir then + path = dir + break + end + end + end + local lexers = {} + for dir in assert(path, 'lexer path not configured or found'):gmatch('[^;]+') do + if lfs.attributes(dir, 'mode') == 'directory' then + for file in lfs.dir(dir) do + local name = file:match('^(.+)%.lua$') + if name and name ~= 'lexer' and not lexers[name] then + lexers[#lexers + 1], lexers[name] = name, true + end + end + end + end + table.sort(lexers) + return lexers +end + +--- Map of file extensions, without the '.' prefix, to their associated lexer names. +-- This map has precedence over Scintillua's built-in map. +-- @see detect +M.detect_extensions = {} + +--- Map of line patterns to their associated lexer names. +-- These are Lua string patterns, not LPeg patterns. +-- This map has precedence over Scintillua's built-in map. +-- @see detect +M.detect_patterns = {} + +--- Returns the name of the lexer often associated with filename *filename* and/or content +-- line *line*. +-- @param[opt] filename Optional string filename. The default value is read from the +-- 'lexer.scintillua.filename' property. +-- @param[opt] line Optional string first content line, such as a shebang line. The default +-- value is read from the 'lexer.scintillua.line' property. +-- @return string lexer name to pass to `load()`, or `nil` if none was detected +-- @see detect_extensions +-- @see detect_patterns +function M.detect(filename, line) + if not filename then filename = M.property and M.property['lexer.scintillua.filename'] or '' end + if not line then line = M.property and M.property['lexer.scintillua.line'] or '' end + + -- Locally scoped in order to avoid persistence in memory. + local extensions = { + as = 'actionscript', asc = 'actionscript', -- + adb = 'ada', ads = 'ada', -- + g = 'antlr', g4 = 'antlr', -- + ans = 'apdl', inp = 'apdl', mac = 'apdl', -- + apl = 'apl', -- + applescript = 'applescript', -- + asm = 'asm', ASM = 'asm', s = 'asm', S = 'asm', -- + asa = 'asp', asp = 'asp', hta = 'asp', -- + ahk = 'autohotkey', -- + au3 = 'autoit', a3x = 'autoit', -- + awk = 'awk', -- + bat = 'batch', cmd = 'batch', -- + bib = 'bibtex', -- + boo = 'boo', -- + cs = 'csharp', -- + c = 'ansi_c', C = 'ansi_c', cc = 'cpp', cpp = 'cpp', cxx = 'cpp', ['c++'] = 'cpp', h = 'cpp', + hh = 'cpp', hpp = 'cpp', hxx = 'cpp', ['h++'] = 'cpp', -- + ck = 'chuck', -- + clj = 'clojure', cljs = 'clojure', cljc = 'clojure', edn = 'clojure', -- + ['CMakeLists.txt'] = 'cmake', cmake = 'cmake', ['cmake.in'] = 'cmake', ctest = 'cmake', + ['ctest.in'] = 'cmake', -- + coffee = 'coffeescript', -- + cr = 'crystal', -- + css = 'css', -- + cu = 'cuda', cuh = 'cuda', -- + d = 'dmd', di = 'dmd', -- + dart = 'dart', -- + desktop = 'desktop', -- + diff = 'diff', patch = 'diff', -- + Dockerfile = 'dockerfile', -- + dot = 'dot', -- + e = 'eiffel', eif = 'eiffel', -- + ex = 'elixir', exs = 'elixir', -- + elm = 'elm', -- + erl = 'erlang', hrl = 'erlang', -- + fs = 'fsharp', -- + fan = 'fantom', -- + dsp = 'faust', -- + fnl = 'fennel', -- + fish = 'fish', -- + forth = 'forth', frt = 'forth', -- + f = 'fortran', ['for'] = 'fortran', ftn = 'fortran', fpp = 'fortran', f77 = 'fortran', + f90 = 'fortran', f95 = 'fortran', f03 = 'fortran', f08 = 'fortran', -- + fstab = 'fstab', -- + gd = 'gap', gi = 'gap', gap = 'gap', -- + gmi = 'gemini', -- + po = 'gettext', pot = 'gettext', -- + feature = 'gherkin', -- + gleam = 'gleam', -- + glslf = 'glsl', glslv = 'glsl', -- + dem = 'gnuplot', plt = 'gnuplot', -- + go = 'go', -- + groovy = 'groovy', gvy = 'groovy', -- + gtkrc = 'gtkrc', -- + ha = 'hare', -- + hs = 'haskell', -- + htm = 'html', html = 'html', shtm = 'html', shtml = 'html', xhtml = 'html', vue = 'html', -- + icn = 'icon', -- + idl = 'idl', odl = 'idl', -- + ni = 'inform', -- + cfg = 'ini', cnf = 'ini', inf = 'ini', ini = 'ini', reg = 'ini', -- + io = 'io_lang', -- + bsh = 'java', java = 'java', -- + js = 'javascript', jsfl = 'javascript', -- + jq = 'jq', -- + json = 'json', -- + jsp = 'jsp', -- + jl = 'julia', -- + bbl = 'latex', dtx = 'latex', ins = 'latex', ltx = 'latex', tex = 'latex', sty = 'latex', -- + ledger = 'ledger', journal = 'ledger', -- + less = 'less', -- + lily = 'lilypond', ly = 'lilypond', -- + cl = 'lisp', el = 'lisp', lisp = 'lisp', lsp = 'lisp', -- + litcoffee = 'litcoffee', -- + lgt = 'logtalk', -- + lua = 'lua', -- + GNUmakefile = 'makefile', iface = 'makefile', mak = 'makefile', makefile = 'makefile', + Makefile = 'makefile', -- + md = 'markdown', -- + ['meson.build'] = 'meson', -- + moon = 'moonscript', -- + myr = 'myrddin', -- + n = 'nemerle', -- + link = 'networkd', network = 'networkd', netdev = 'networkd', -- + nim = 'nim', -- + nsh = 'nsis', nsi = 'nsis', nsis = 'nsis', -- + obs = 'objeck', -- + m = 'objective_c', mm = 'objective_c', objc = 'objective_c', -- + caml = 'caml', ml = 'caml', mli = 'caml', mll = 'caml', mly = 'caml', -- + dpk = 'pascal', dpr = 'pascal', p = 'pascal', pas = 'pascal', -- + al = 'perl', perl = 'perl', pl = 'perl', pm = 'perl', pod = 'perl', -- + inc = 'php', php = 'php', php3 = 'php', php4 = 'php', phtml = 'php', -- + p8 = 'pico8', -- + pike = 'pike', pmod = 'pike', -- + PKGBUILD = 'pkgbuild', -- + pony = 'pony', -- + eps = 'ps', ps = 'ps', -- + ps1 = 'powershell', -- + prolog = 'prolog', -- + props = 'props', properties = 'props', -- + proto = 'protobuf', -- + pure = 'pure', -- + sc = 'python', py = 'python', pyw = 'python', -- + R = 'rstats', Rout = 'rstats', Rhistory = 'rstats', Rt = 'rstats', ['Rout.save'] = 'rstats', + ['Rout.fail'] = 'rstats', -- + re = 'reason', -- + r = 'rebol', reb = 'rebol', -- + rst = 'rest', -- + orx = 'rexx', rex = 'rexx', -- + erb = 'rhtml', rhtml = 'rhtml', -- + rsc = 'routeros', -- + spec = 'rpmspec', -- + Rakefile = 'ruby', rake = 'ruby', rb = 'ruby', rbw = 'ruby', -- + rs = 'rust', -- + sass = 'sass', scss = 'sass', -- + scala = 'scala', -- + sch = 'scheme', scm = 'scheme', -- + bash = 'bash', bashrc = 'bash', bash_profile = 'bash', configure = 'bash', csh = 'bash', + ksh = 'bash', mksh = 'bash', sh = 'bash', zsh = 'bash', -- + changes = 'smalltalk', st = 'smalltalk', sources = 'smalltalk', -- + sml = 'sml', fun = 'sml', sig = 'sml', -- + sno = 'snobol4', SNO = 'snobol4', -- + spin = 'spin', -- + ddl = 'sql', sql = 'sql', -- + automount = 'systemd', device = 'systemd', mount = 'systemd', path = 'systemd', + scope = 'systemd', service = 'systemd', slice = 'systemd', socket = 'systemd', swap = 'systemd', + target = 'systemd', timer = 'systemd', -- + taskpaper = 'taskpaper', -- + tcl = 'tcl', tk = 'tcl', -- + texi = 'texinfo', -- + toml = 'toml', -- + ['1'] = 'troff', ['2'] = 'troff', ['3'] = 'troff', ['4'] = 'troff', ['5'] = 'troff', + ['6'] = 'troff', ['7'] = 'troff', ['8'] = 'troff', ['9'] = 'troff', ['1x'] = 'troff', + ['2x'] = 'troff', ['3x'] = 'troff', ['4x'] = 'troff', ['5x'] = 'troff', ['6x'] = 'troff', + ['7x'] = 'troff', ['8x'] = 'troff', ['9x'] = 'troff', -- + t2t = 'txt2tags', -- + ts = 'typescript', -- + vala = 'vala', -- + vcf = 'vcard', vcard = 'vcard', -- + v = 'verilog', ver = 'verilog', -- + vh = 'vhdl', vhd = 'vhdl', vhdl = 'vhdl', -- + bas = 'vb', cls = 'vb', ctl = 'vb', dob = 'vb', dsm = 'vb', dsr = 'vb', frm = 'vb', pag = 'vb', + vb = 'vb', vba = 'vb', vbs = 'vb', -- + wsf = 'wsf', -- + dtd = 'xml', svg = 'xml', xml = 'xml', xsd = 'xml', xsl = 'xml', xslt = 'xml', xul = 'xml', -- + xs = 'xs', xsin = 'xs', xsrc = 'xs', -- + xtend = 'xtend', -- + yaml = 'yaml', yml = 'yaml', -- + zig = 'zig' + } + local patterns = { + ['^#!.+[/ ][gm]?awk'] = 'awk', ['^#!.+[/ ]lua'] = 'lua', ['^#!.+[/ ]octave'] = 'matlab', + ['^#!.+[/ ]perl'] = 'perl', ['^#!.+[/ ]php'] = 'php', ['^#!.+[/ ]python'] = 'python', + ['^#!.+[/ ]ruby'] = 'ruby', ['^#!.+[/ ]bash'] = 'bash', ['^#!.+/m?ksh'] = 'bash', + ['^#!.+/sh'] = 'bash', ['^%s*class%s+%S+%s*<%s*ApplicationController'] = 'rails', + ['^%s*class%s+%S+%s*<%s*ActionController::Base'] = 'rails', + ['^%s*class%s+%S+%s*<%s*ActiveRecord::Base'] = 'rails', + ['^%s*class%s+%S+%s*<%s*ActiveRecord::Migration'] = 'rails', ['^%s*<%?xml%s'] = 'xml', + ['^#cloud%-config'] = 'yaml' + } + + for patt, name in pairs(M.detect_patterns) do if line:find(patt) then return name end end + for patt, name in pairs(patterns) do if line:find(patt) then return name end end + local name, ext = filename:match('[^/\\]+$'), filename:match('[^.]*$') + return M.detect_extensions[name] or extensions[name] or M.detect_extensions[ext] or + extensions[ext] +end + -- The following are utility functions lexers will have access to. -- Common patterns. -M.any = lpeg_P(1) -M.alpha = lpeg_R('AZ', 'az') -M.digit = lpeg_R('09') -M.alnum = lpeg_R('AZ', 'az', '09') -M.lower = lpeg_R('az') -M.upper = lpeg_R('AZ') -M.xdigit = lpeg_R('09', 'AF', 'af') -M.graph = lpeg_R('!~') -M.punct = lpeg_R('!/', ':@', '[\'', '{~') -M.space = lpeg_S('\t\v\f\n\r ') - -M.newline = lpeg_P('\r')^-1 * '\n' -M.nonnewline = 1 - M.newline -M.dec_num = M.digit^1 -M.hex_num = '0' * lpeg_S('xX') * M.xdigit^1 -M.oct_num = '0' * lpeg_R('07')^1 -M.integer = lpeg_S('+-')^-1 * (M.hex_num + M.oct_num + M.dec_num) -M.float = lpeg_S('+-')^-1 * - ((M.digit^0 * '.' * M.digit^1 + M.digit^1 * '.' * M.digit^0 * -lpeg_P('.')) * - (lpeg_S('eE') * lpeg_S('+-')^-1 * M.digit^1)^-1 + - (M.digit^1 * lpeg_S('eE') * lpeg_S('+-')^-1 * M.digit^1)) -M.number = M.float + M.integer +--- A pattern that matches any single character. +M.any = P(1) +--- A pattern that matches any alphabetic character ('A'-'Z', 'a'-'z'). +M.alpha = R('AZ', 'az') +--- A pattern that matches any digit ('0'-'9'). +M.digit = R('09') +--- A pattern that matches any alphanumeric character ('A'-'Z', 'a'-'z', '0'-'9'). +M.alnum = R('AZ', 'az', '09') +--- A pattern that matches any lower case character ('a'-'z'). +M.lower = R('az') +--- A pattern that matches any upper case character ('A'-'Z'). +M.upper = R('AZ') +--- A pattern that matches any hexadecimal digit ('0'-'9', 'A'-'F', 'a'-'f'). +M.xdigit = R('09', 'AF', 'af') +--- A pattern that matches any graphical character ('!' to '~'). +M.graph = R('!~') +--- A pattern that matches any punctuation character ('!' to '/', ':' to '@', '[' to ''', '{' +-- to '~'). +M.punct = R('!/', ':@', '[\'', '{~') +--- A pattern that matches any whitespace character ('\t', '\v', '\f', '\n', '\r', space). +M.space = S('\t\v\f\n\r ') -M.word = (M.alpha + '_') * (M.alnum + '_')^0 +--- A pattern that matches a sequence of end of line characters. +M.newline = P('\r')^-1 * '\n' +--- A pattern that matches any single, non-newline character. +M.nonnewline = 1 - M.newline --- Deprecated. -M.nonnewline_esc = 1 - (M.newline + '\\') + '\\' * M.any -M.ascii = lpeg_R('\000\127') -M.extend = lpeg_R('\000\255') -M.cntrl = lpeg_R('\000\031') -M.print = lpeg_R(' ~') - ---- --- Creates and returns a token pattern with token name *name* and pattern *patt*. --- If *name* is not a predefined token name, its style must be defined via `lexer.add_style()`. --- @param name The name of token. If this name is not a predefined token name, then a style --- needs to be assiciated with it via `lexer.add_style()`. --- @param patt The LPeg pattern associated with the token. --- @return pattern --- @usage local ws = token(lexer.WHITESPACE, lexer.space^1) --- @usage local annotation = token('annotation', '@' * lexer.word) --- @name token -function M.token(name, patt) - return lpeg_Cc(name) * patt * lpeg_Cp() +--- Returns a pattern that matches a decimal number, whose digits may be separated by character +-- *c*. +function M.dec_num_(c) return M.digit * (P(c)^-1 * M.digit)^0 end +--- Returns a pattern that matches a hexadecimal number, whose digits may be separated by +-- character *c*. +function M.hex_num_(c) return '0' * S('xX') * (P(c)^-1 * M.xdigit)^1 end +--- Returns a pattern that matches an octal number, whose digits may be separated by character *c*. +function M.oct_num_(c) return '0' * (P(c)^-1 * R('07'))^1 * -M.xdigit end +--- Returns a pattern that matches a binary number, whose digits may be separated by character *c*. +function M.bin_num_(c) return '0' * S('bB') * (P(c)^-1 * S('01'))^1 * -M.xdigit end +--- Returns a pattern that matches either a decimal, hexadecimal, octal, or binary number, +-- whose digits may be separated by character *c*. +function M.integer_(c) + return S('+-')^-1 * (M.hex_num_(c) + M.bin_num_(c) + M.oct_num_(c) + M.dec_num_(c)) +end +local function exp_(c) return S('eE') * S('+-')^-1 * M.digit * (P(c)^-1 * M.digit)^0 end +--- Returns a pattern that matches a floating point number, whose digits may be separated by +-- character *c*. +function M.float_(c) + return S('+-')^-1 * + ((M.dec_num_(c)^-1 * '.' * M.dec_num_(c) + M.dec_num_(c) * '.' * M.dec_num_(c)^-1 * -P('.')) * + exp_(c)^-1 + (M.dec_num_(c) * exp_(c))) end +--- Returns a pattern that matches a typical number, either a floating point, decimal, hexadecimal, +-- octal, or binary number, and whose digits may be separated by character *c*. +function M.number_(c) return M.float_(c) + M.integer_(c) end + +--- A pattern that matches a decimal number. +M.dec_num = M.dec_num_(false) +--- A pattern that matches a hexadecimal number. +M.hex_num = M.hex_num_(false) +--- A pattern that matches an octal number. +M.oct_num = M.oct_num_(false) +--- A pattern that matches a binary number. +M.bin_num = M.bin_num_(false) +--- A pattern that matches either a decimal, hexadecimal, octal, or binary number. +M.integer = M.integer_(false) +--- A pattern that matches a floating point number. +M.float = M.float_(false) +--- A pattern that matches a typical number, either a floating point, decimal, hexadecimal, +-- octal, or binary number. +M.number = M.number_(false) + +--- A pattern that matches a typical word. Words begin with a letter or underscore and consist +-- of alphanumeric and underscore characters. +M.word = (M.alpha + '_') * (M.alnum + '_')^0 ---- --- Creates and returns a pattern that matches from string or pattern *prefix* until the end of +--- Creates and returns a pattern that matches from string or pattern *prefix* until the end of -- the line. -- *escape* indicates whether the end of the line can be escaped with a '\' character. --- @param prefix String or pattern prefix to start matching at. --- @param escape Optional flag indicating whether or not newlines can be escaped by a '\' +-- @param[opt] prefix Optional string or pattern prefix to start matching at. The default value +-- is any non-newline character. +-- @param[opt] escape Optional flag indicating whether or not newlines can be escaped by a '\' -- character. The default value is `false`. -- @return pattern -- @usage local line_comment = lexer.to_eol('//') -- @usage local line_comment = lexer.to_eol(S('#;')) --- @name to_eol function M.to_eol(prefix, escape) - return prefix * (not escape and M.nonnewline or M.nonnewline_esc)^0 + return (prefix or M.nonnewline) * + (not escape and M.nonnewline or 1 - (M.newline + '\\') + '\\' * M.any)^0 end ---- --- Creates and returns a pattern that matches a range of text bounded by strings or patterns *s* +--- Creates and returns a pattern that matches a range of text bounded by strings or patterns *s* -- and *e*. -- This is a convenience function for matching more complicated ranges like strings with escape -- characters, balanced parentheses, and block comments (nested or not). *e* is optional and @@ -1589,20 +1864,19 @@ end -- indicates whether or not to handle balanced ranges like parentheses, and requires *s* and *e* -- to be different. -- @param s String or pattern start of a range. --- @param e Optional string or pattern end of a range. The default value is *s*. --- @param single_line Optional flag indicating whether or not the range must be on a single +-- @param[opt] e Optional string or pattern end of a range. The default value is *s*. +-- @param[opt] single_line Optional flag indicating whether or not the range must be on a single -- line. The default value is `false`. --- @param escapes Optional flag indicating whether or not the range end may be escaped by a '\' --- character. The default value is `false` unless *s* and *e* are identical, single-character --- strings. In that case, the default value is `true`. --- @param balanced Optional flag indicating whether or not to match a balanced range, like the --- "%b" Lua pattern. This flag only applies if *s* and *e* are different. +-- @param[opt] escapes Optional flag indicating whether or not the range end may be escaped +-- by a '\' character. The default value is `false` unless *s* and *e* are identical, +-- single-character strings. In that case, the default value is `true`. +-- @param[opt] balanced Optional flag indicating whether or not to match a balanced range, +-- like the "%b" Lua pattern. This flag only applies if *s* and *e* are different. -- @return pattern -- @usage local dq_str_escapes = lexer.range('"') -- @usage local dq_str_noescapes = lexer.range('"', false, false) -- @usage local unbalanced_parens = lexer.range('(', ')') -- @usage local balanced_parens = lexer.range('(', ')', false, false, true) --- @name range function M.range(s, e, single_line, escapes, balanced) if type(e) ~= 'string' and type(e) ~= 'userdata' then e, single_line, escapes, balanced = s, e, single_line, escapes @@ -1610,246 +1884,106 @@ function M.range(s, e, single_line, escapes, balanced) local any = M.any - e if single_line then any = any - '\n' end if balanced then any = any - s end - if escapes == nil then - -- Only allow escapes by default for ranges with identical, single-character string delimiters. - escapes = type(s) == 'string' and #s == 1 and s == e - end + -- Only allow escapes by default for ranges with identical, single-character string delimiters. + if escapes == nil then escapes = type(s) == 'string' and #s == 1 and s == e end if escapes then any = any - '\\' + '\\' * M.any end - if balanced and s ~= e then - return lpeg_P{s * (any + lpeg_V(1))^0 * lpeg_P(e)^-1} - else - return s * any^0 * lpeg_P(e)^-1 - end -end - --- Deprecated function. Use `lexer.range()` instead. --- Creates and returns a pattern that matches a range of text bounded by *chars* characters. --- This is a convenience function for matching more complicated delimited ranges like strings --- with escape characters and balanced parentheses. *single_line* indicates whether or not the --- range must be on a single line, *no_escape* indicates whether or not to ignore '\' as an --- escape character, and *balanced* indicates whether or not to handle balanced ranges like --- parentheses and requires *chars* to be composed of two characters. --- @param chars The character(s) that bound the matched range. --- @param single_line Optional flag indicating whether or not the range must be on a single line. --- @param no_escape Optional flag indicating whether or not the range end character may be --- escaped by a '\\' character. --- @param balanced Optional flag indicating whether or not to match a balanced range, like the --- "%b" Lua pattern. This flag only applies if *chars* consists of two different characters --- (e.g. "()"). --- @return pattern --- @usage local dq_str_escapes = lexer.delimited_range('"') --- @usage local dq_str_noescapes = lexer.delimited_range('"', false, true) --- @usage local unbalanced_parens = lexer.delimited_range('()') --- @usage local balanced_parens = lexer.delimited_range('()', false, false, true) --- @see range --- @name delimited_range -function M.delimited_range(chars, single_line, no_escape, balanced) - print("lexer.delimited_range() is deprecated, use lexer.range()") - local s = chars:sub(1, 1) - local e = #chars == 2 and chars:sub(2, 2) or s - local range - local b = balanced and s or '' - local n = single_line and '\n' or '' - if no_escape then - local invalid = lpeg_S(e .. n .. b) - range = M.any - invalid - else - local invalid = lpeg_S(e .. n .. b) + '\\' - range = M.any - invalid + '\\' * M.any - end - if balanced and s ~= e then - return lpeg_P{s * (range + lpeg_V(1))^0 * e} - else - return s * range^0 * lpeg_P(e)^-1 - end + if balanced and s ~= e then return P{s * (any + V(1))^0 * P(e)^-1} end + return s * any^0 * P(e)^-1 end ---- --- Creates and returns a pattern that matches pattern *patt* only at the beginning of a line. --- @param patt The LPeg pattern to match on the beginning of a line. --- @return pattern --- @usage local preproc = token(lexer.PREPROCESSOR, lexer.starts_line(lexer.to_eol('#'))) --- @name starts_line -function M.starts_line(patt) - return lpeg_Cmt(lpeg_C(patt), function(input, index, match, ...) +--- Creates and returns a pattern that matches pattern *patt* only when it comes after one of +-- the characters in string *set* (or when there are no characters behind *patt*), skipping +-- over any characters in string *skip*, which is whitespace by default. +-- @param set String character set like one passed to `lpeg.S()`. +-- @param patt The LPeg pattern to match after a set character. +-- @param skip String character set to skip over. The default value is ' \t\r\n\v\f' (whitespace). +-- @usage local regex = lexer.after_set('+-*!%^&|=,([{', lexer.range('/')) +function M.after_set(set, patt, skip) + if not skip then skip = ' \t\r\n\v\f' end + local set_chars, skip_chars = {}, {} + -- Note: cannot use utf8.codes() because Lua 5.1 is still supported. + for char in set:gmatch('.') do set_chars[string.byte(char)] = true end + for char in skip:gmatch('.') do skip_chars[string.byte(char)] = true end + return (B(S(set)) + -B(1)) * patt + Cmt(C(patt), function(input, index, match, ...) local pos = index - #match - if pos == 1 then return index, ... end - local char = input:sub(pos - 1, pos - 1) - if char == '\n' or char == '\r' or char == '\f' then return index, ... end + if #skip > 0 then while pos > 1 and skip_chars[input:byte(pos - 1)] do pos = pos - 1 end end + if pos == 1 or set_chars[input:byte(pos - 1)] then return index, ... end + return nil end) end ---- --- Creates and returns a pattern that verifies the first non-whitespace character behind the --- current match position is in string set *s*. --- @param s String character set like one passed to `lpeg.S()`. +--- Creates and returns a pattern that matches pattern *patt* only at the beginning of a line, +-- or after any line indentation if *allow_indent* is `true`. +-- @param patt The LPeg pattern to match on the beginning of a line. +-- @param allow_indent Whether or not to consider line indentation as the start of a line. The +-- default value is `false`. -- @return pattern --- @usage local regex = lexer.last_char_includes('+-*!%^&|=,([{') * lexer.range('/') --- @name last_char_includes -function M.last_char_includes(s) - s = string.format('[%s]', s:gsub('[-%%%[]', '%%%1')) - return lpeg_P(function(input, index) - if index == 1 then return index end - local i = index - while input:sub(i - 1, i - 1):match('[ \t\r\n\f]') do i = i - 1 end - if input:sub(i - 1, i - 1):match(s) then return index end - end) +-- @usage local preproc = lex:tag(lexer.PREPROCESSOR, lexer.starts_line(lexer.to_eol('#'))) +function M.starts_line(patt, allow_indent) + return M.after_set('\r\n\v\f', patt, allow_indent and ' \t' or '') end --- Deprecated function. Use `lexer.range()` instead. --- Returns a pattern that matches a balanced range of text that starts with string *start_chars* --- and ends with string *end_chars*. --- With single-character delimiters, this function is identical to `delimited_range(start_chars .. --- end_chars, false, true, true)`. --- @param start_chars The string starting a nested sequence. --- @param end_chars The string ending a nested sequence. +M.colors = {} -- legacy +M.styles = setmetatable({}, { -- legacy + __index = function() return setmetatable({}, {__concat = function() return nil end}) end, + __newindex = function() end +}) +M.property_expanded = setmetatable({}, {__index = function() return '' end}) -- legacy + +-- Legacy function for creates and returns a token pattern with token name *name* and pattern +-- *patt*. +-- Use `tag()` instead. +-- @param name The name of token. +-- @param patt The LPeg pattern associated with the token. -- @return pattern --- @usage local nested_comment = lexer.nested_pair('/*', '*/') --- @see range --- @name nested_pair -function M.nested_pair(start_chars, end_chars) - print("lexer.nested_pair() is deprecated, use lexer.range()") - local s, e = start_chars, lpeg_P(end_chars)^-1 - return lpeg_P{s * (M.any - s - end_chars + lpeg_V(1))^0 * e} -end +-- @usage local number = token(lexer.NUMBER, lexer.number) +-- @usage local addition = token('addition', '+' * lexer.word) +function M.token(name, patt) return Cc(name) * (P(patt) / 0) * Cp() end ---- --- Creates and returns a pattern that matches any single word in list or string *words*. --- *case_insensitive* indicates whether or not to ignore case when matching words. --- This is a convenience function for simplifying a set of ordered choice word patterns. --- @param word_list A list of words or a string list of words separated by spaces. --- @param case_insensitive Optional boolean flag indicating whether or not the word match is --- case-insensitive. The default value is `false`. --- @param word_chars Unused legacy parameter. +-- Legacy function that creates and returns a pattern that verifies the first non-whitespace +-- character behind the current match position is in string set *s*. +-- @param s String character set like one passed to `lpeg.S()`. -- @return pattern --- @usage local keyword = token(lexer.KEYWORD, word_match{'foo', 'bar', 'baz'}) --- @usage local keyword = token(lexer.KEYWORD, word_match({'foo-bar', 'foo-baz', 'bar-foo', --- 'bar-baz', 'baz-foo', 'baz-bar'}, true)) --- @usage local keyword = token(lexer.KEYWORD, word_match('foo bar baz')) --- @name word_match -function M.word_match(word_list, case_insensitive, word_chars) - if type(case_insensitive) == 'string' or type(word_chars) == 'boolean' then - -- Legacy `word_match(word_list, word_chars, case_insensitive)` form. - word_chars, case_insensitive = case_insensitive, word_chars - elseif type(word_list) == 'string' then - local words = word_list -- space-separated list of words - word_list = {} - for word in words:gsub('%-%-[^\n]+', ''):gmatch('%S+') do word_list[#word_list + 1] = word end - end - if not word_chars then word_chars = '' end - for _, word in ipairs(word_list) do - word_list[case_insensitive and word:lower() or word] = true - for char in word:gmatch('[^%w_%s]') do - if not word_chars:find(char, 1, true) then word_chars = word_chars .. char end - end - end - local chars = M.alnum + '_' - if word_chars ~= '' then chars = chars + lpeg_S(word_chars) end - return lpeg_Cmt(chars^1, function(input, index, word) - if case_insensitive then word = word:lower() end - return word_list[word] and index or nil - end) -end +-- @usage local regex = #P('/') * lexer.last_char_includes('+-*!%^&|=,([{') * lexer.range('/') +function M.last_char_includes(s) return M.after_set(s, true) end --- Deprecated legacy function. Use `parent:embed()` instead. --- Embeds child lexer *child* in parent lexer *parent* using patterns *start_rule* and *end_rule*, --- which signal the beginning and end of the embedded lexer, respectively. --- @param parent The parent lexer. --- @param child The child lexer. --- @param start_rule The pattern that signals the beginning of the embedded lexer. --- @param end_rule The pattern that signals the end of the embedded lexer. --- @usage lexer.embed_lexer(M, css, css_start_rule, css_end_rule) --- @usage lexer.embed_lexer(html, M, php_start_rule, php_end_rule) --- @usage lexer.embed_lexer(html, ruby, ruby_start_rule, ruby_end_rule) --- @see embed --- @name embed_lexer -function M.embed_lexer(parent, child, start_rule, end_rule) - if not getmetatable(parent) then process_legacy_lexer(parent) end - if not getmetatable(child) then process_legacy_lexer(child) end - parent:embed(child, start_rule, end_rule) -end +function M.fold_consecutive_lines() end -- legacy --- Determines if the previous line is a comment. --- This is used for determining if the current comment line is a fold point. --- @param prefix The prefix string defining a comment. --- @param text The text passed to a fold function. --- @param pos The pos passed to a fold function. --- @param line The line passed to a fold function. --- @param s The s passed to a fold function. -local function prev_line_is_comment(prefix, text, pos, line, s) - local start = line:find('%S') - if start < s and not line:find(prefix, start, true) then return false end - local p = pos - 1 - if text:sub(p, p) == '\n' then - p = p - 1 - if text:sub(p, p) == '\r' then p = p - 1 end - if text:sub(p, p) ~= '\n' then - while p > 1 and text:sub(p - 1, p - 1) ~= '\n' do p = p - 1 end - while text:sub(p, p):find('^[\t ]$') do p = p + 1 end - return text:sub(p, p + #prefix - 1) == prefix - end - end - return false -end +-- The functions and fields below were defined in C. --- Determines if the next line is a comment. --- This is used for determining if the current comment line is a fold point. --- @param prefix The prefix string defining a comment. --- @param text The text passed to a fold function. --- @param pos The pos passed to a fold function. --- @param line The line passed to a fold function. --- @param s The s passed to a fold function. -local function next_line_is_comment(prefix, text, pos, line, s) - local p = text:find('\n', pos + s) - if p then - p = p + 1 - while text:sub(p, p):find('^[\t ]$') do p = p + 1 end - return text:sub(p, p + #prefix - 1) == prefix - end - return false -end +--- Table of fold level bit-masks for line numbers starting from 1. (Read-only) +-- Fold level masks are composed of an integer level combined with any of the following bits: +-- +-- - `lexer.FOLD_BASE` +-- The initial fold level. +-- - `lexer.FOLD_BLANK` +-- The line is blank. +-- - `lexer.FOLD_HEADER` +-- The line is a header, or fold point. +-- @table fold_level ---- --- Returns for `lexer.add_fold_point()` the parameters needed to fold consecutive lines that --- start with string *prefix*. --- @param prefix The prefix string (e.g. a line comment). --- @usage lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('--')) --- @usage lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) --- @usage lex:add_fold_point(lexer.KEYWORD, lexer.fold_consecutive_lines('import')) --- @name fold_consecutive_lines -function M.fold_consecutive_lines(prefix) - local property_int = M.property_int - return prefix, function(text, pos, line, s) - if property_int['fold.line.groups'] == 0 then return 0 end - if s > 1 and line:match('^%s*()') < s then return 0 end - local prev_line_comment = prev_line_is_comment(prefix, text, pos, line, s) - local next_line_comment = next_line_is_comment(prefix, text, pos, line, s) - if not prev_line_comment and next_line_comment then return 1 end - if prev_line_comment and not next_line_comment then return -1 end - return 0 - end -end +--- Table of indentation amounts in character columns, for line numbers starting from +-- 1. (Read-only) +-- @table indent_amount --- Deprecated legacy function. Use `lexer.fold_consecutive_lines()` instead. --- Returns a fold function (to be passed to `lexer.add_fold_point()`) that folds consecutive --- line comments that start with string *prefix*. --- @param prefix The prefix string defining a line comment. --- @usage lex:add_fold_point(lexer.COMMENT, '--', lexer.fold_line_comments('--')) --- @usage lex:add_fold_point(lexer.COMMENT, '//', lexer.fold_line_comments('//')) --- @name fold_line_comments -function M.fold_line_comments(prefix) - print('lexer.fold_line_comments() is deprecated, use lexer.fold_consecutive_lines()') - return select(2, M.fold_consecutive_lines(prefix)) -end +--- Table of integer line states for line numbers starting from 1. +-- Line states can be used by lexers for keeping track of persistent states. For example, +-- the output lexer uses this to mark lines that have warnings or errors. +-- @table line_state + +--- Map of key-value string pairs. +-- @table property + +--- Map of key-value pairs with values interpreted as numbers, or `0` if not found. (Read-only) +-- @table property_int ---[[ The functions and fields below were defined in C. +--- Table of style names at positions in the buffer starting from 1. (Read-only) +-- @table style_at ---- --- Returns the line number (starting from 1) of the line that contains position *pos*, which +--- Returns the line number (starting from 1) of the line that contains position *pos*, which -- starts from 1. -- @param pos The position to get the line number of. -- @return number -local function line_from_position(pos) end -]] +-- @function line_from_position return M diff --git a/lua/lexers/lilypond.lua b/lua/lexers/lilypond.lua index 9fca571..9c3515b 100644 --- a/lua/lexers/lilypond.lua +++ b/lua/lexers/lilypond.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Robert Gieseke. See LICENSE. +-- Copyright 2006-2024 Robert Gieseke. See LICENSE. -- Lilypond LPeg lexer. -- TODO Embed Scheme; Notes?, Numbers? @@ -27,4 +27,6 @@ lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('%'))) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S("{}'~<>|"))) +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/lisp.lua b/lua/lexers/lisp.lua index 6a0e680..e5aa270 100644 --- a/lua/lexers/lisp.lua +++ b/lua/lexers/lisp.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Lisp LPeg lexer. local lexer = require('lexer') @@ -31,7 +31,7 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ })) -- Identifiers. -local word = lexer.alpha * (lexer.alnum + '_' + '-')^0 +local word = lexer.alpha * (lexer.alnum + S('_-'))^0 lex:add_rule('identifier', token(lexer.IDENTIFIER, word)) -- Strings. @@ -45,10 +45,6 @@ lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) -- Numbers. lex:add_rule('number', token(lexer.NUMBER, P('-')^-1 * lexer.digit^1 * (S('./') * lexer.digit^1)^-1)) --- Entities. -lex:add_rule('entity', token('entity', '&' * word)) -lex:add_style('entity', lexer.styles.variable) - -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('<>=*/+-`@%()'))) @@ -57,6 +53,7 @@ lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '#|', '|#') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines(';')) + +lexer.property['scintillua.comment'] = ';' return lex diff --git a/lua/lexers/litcoffee.lua b/lua/lexers/litcoffee.lua index f097e5f..1b03501 100644 --- a/lua/lexers/litcoffee.lua +++ b/lua/lexers/litcoffee.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Robert Gieseke. See LICENSE. +-- Copyright 2006-2024 Robert Gieseke. See LICENSE. -- Literate CoffeeScript LPeg lexer. -- http://coffeescript.org/#literate @@ -10,8 +10,8 @@ local lex = lexer.new('litcoffee', {inherit = lexer.load('markdown')}) -- Embedded CoffeeScript. local coffeescript = lexer.load('coffeescript') -local coffee_start_rule = token(lexer.STYLE_EMBEDDED, (P(' ')^4 + P('\t'))) -local coffee_end_rule = token(lexer.STYLE_EMBEDDED, lexer.newline) +local coffee_start_rule = token(lexer.EMBEDDED, (P(' ')^4 + P('\t'))) +local coffee_end_rule = token(lexer.EMBEDDED, lexer.newline) lex:embed(coffeescript, coffee_start_rule, coffee_end_rule) -- Use 'markdown_whitespace' instead of lexer.WHITESPACE since the latter would expand to diff --git a/lua/lexers/logtalk.lua b/lua/lexers/logtalk.lua index e33e80a..ffa7595 100644 --- a/lua/lexers/logtalk.lua +++ b/lua/lexers/logtalk.lua @@ -1,4 +1,4 @@ --- Copyright © 2017-2022 Michael T. Richter <ttmrichter@gmail.com>. See LICENSE. +-- Copyright © 2017-2024 Michael T. Richter <ttmrichter@gmail.com>. See LICENSE. -- Logtalk LPeg lexer. local lexer = require('lexer') @@ -59,4 +59,6 @@ local operators = { } lex:modify_rule('operator', token(lexer.OPERATOR, word_match(operators)) + lex:get_rule('operator')) +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/lua.lua b/lua/lexers/lua.lua index 03c37e0..ff749ec 100644 --- a/lua/lexers/lua.lua +++ b/lua/lexers/lua.lua @@ -1,118 +1,32 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Lua LPeg lexer. -- Original written by Peter Odding, 2007/04/04. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local B, P, S = lpeg.B, lpeg.P, lpeg.S -local lex = lexer.new('lua') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if', 'in', 'local', - 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while', - -- Added in 5.2. - 'goto' -})) - --- Functions and deprecated functions. -local func = token(lexer.FUNCTION, word_match{ - 'assert', 'collectgarbage', 'dofile', 'error', 'getmetatable', 'ipairs', 'load', 'loadfile', - 'next', 'pairs', 'pcall', 'print', 'rawequal', 'rawget', 'rawset', 'require', 'select', - 'setmetatable', 'tonumber', 'tostring', 'type', 'xpcall', - -- Added in 5.2. - 'rawlen', - -- Added in 5.4. - 'warn' -}) -local deprecated_func = token('deprecated_function', word_match{ - -- Deprecated in 5.2. - 'getfenv', 'loadstring', 'module', 'setfenv', 'unpack' -}) -lex:add_rule('function', -B('.') * (func + deprecated_func)) -lex:add_style('deprecated_function', lexer.styles['function'] .. {italics = true}) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Functions. +local non_field = -B('.') + B('_G.') + B('..') +local builtin_func = lex:word_match(lexer.FUNCTION_BUILTIN) +local lib_func = lex:word_match(lexer.FUNCTION_BUILTIN .. '.library') +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = B(':') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', + method + ((non_field * lex:tag(lexer.FUNCTION_BUILTIN, builtin_func + lib_func)) + func) * + #(lexer.space^0 * S('({\'"'))) -- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, -B('.') * word_match{ - '_G', '_VERSION', - -- Added in 5.2. - '_ENV' -})) - --- Libraries and deprecated libraries. -local library = token('library', word_match{ - -- Coroutine. - 'coroutine', 'coroutine.create', 'coroutine.resume', 'coroutine.running', 'coroutine.status', - 'coroutine.wrap', 'coroutine.yield', - -- Coroutine added in 5.3. - 'coroutine.isyieldable', - -- Coroutine added in 5.4. - 'coroutine.close', - -- Module. - 'package', 'package.cpath', 'package.loaded', 'package.loadlib', 'package.path', - 'package.preload', - -- Module added in 5.2. - 'package.config', 'package.searchers', 'package.searchpath', - -- UTF-8 added in 5.3. - 'utf8', 'utf8.char', 'utf8.charpattern', 'utf8.codepoint', 'utf8.codes', 'utf8.len', - 'utf8.offset', - -- String. - 'string', 'string.byte', 'string.char', 'string.dump', 'string.find', 'string.format', - 'string.gmatch', 'string.gsub', 'string.len', 'string.lower', 'string.match', 'string.rep', - 'string.reverse', 'string.sub', 'string.upper', - -- String added in 5.3. - 'string.pack', 'string.packsize', 'string.unpack', - -- Table. - 'table', 'table.concat', 'table.insert', 'table.remove', 'table.sort', - -- Table added in 5.2. - 'table.pack', 'table.unpack', - -- Table added in 5.3. - 'table.move', - -- Math. - 'math', 'math.abs', 'math.acos', 'math.asin', 'math.atan', 'math.ceil', 'math.cos', 'math.deg', - 'math.exp', 'math.floor', 'math.fmod', 'math.huge', 'math.log', 'math.max', 'math.min', - 'math.modf', 'math.pi', 'math.rad', 'math.random', 'math.randomseed', 'math.sin', 'math.sqrt', - 'math.tan', - -- Math added in 5.3. - 'math.maxinteger', 'math.mininteger', 'math.tointeger', 'math.type', 'math.ult', - -- IO. - 'io', 'io.close', 'io.flush', 'io.input', 'io.lines', 'io.open', 'io.output', 'io.popen', - 'io.read', 'io.stderr', 'io.stdin', 'io.stdout', 'io.tmpfile', 'io.type', 'io.write', - -- OS. - 'os', 'os.clock', 'os.date', 'os.difftime', 'os.execute', 'os.exit', 'os.getenv', 'os.remove', - 'os.rename', 'os.setlocale', 'os.time', 'os.tmpname', - -- Debug. - 'debug', 'debug.debug', 'debug.gethook', 'debug.getinfo', 'debug.getlocal', 'debug.getmetatable', - 'debug.getregistry', 'debug.getupvalue', 'debug.sethook', 'debug.setlocal', 'debug.setmetatable', - 'debug.setupvalue', 'debug.traceback', - -- Debug added in 5.2. - 'debug.getuservalue', 'debug.setuservalue', 'debug.upvalueid', 'debug.upvaluejoin' -}) -local deprecated_library = token('deprecated_library', word_match{ - -- Module deprecated in 5.2. - 'package.loaders', 'package.seeall', - -- Table deprecated in 5.2. - 'table.maxn', - -- Math deprecated in 5.2. - 'math.log10', - -- Math deprecated in 5.3. - 'math.atan2', 'math.cosh', 'math.frexp', 'math.ldexp', 'math.pow', 'math.sinh', 'math.tanh', - -- Bit32 deprecated in 5.3. - 'bit32', 'bit32.arshift', 'bit32.band', 'bit32.bnot', 'bit32.bor', 'bit32.btest', 'bit32.extract', - 'bit32.lrotate', 'bit32.lshift', 'bit32.replace', 'bit32.rrotate', 'bit32.rshift', 'bit32.xor', - -- Debug deprecated in 5.2. - 'debug.getfenv', 'debug.setfenv' -}) -lex:add_rule('library', -B('.') * (library + deprecated_library)) -lex:add_style('library', lexer.styles.type) -lex:add_style('deprecated_library', lexer.styles.type .. {italics = true}) +local builtin_const = lex:word_match(lexer.CONSTANT_BUILTIN) +local lib_const = lex:word_match(lexer.CONSTANT_BUILTIN .. '.library') +lex:add_rule('constant', non_field * lex:tag(lexer.CONSTANT_BUILTIN, builtin_const + lib_const)) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local sq_str = lexer.range("'") @@ -121,28 +35,27 @@ local longstring = lpeg.Cmt('[' * lpeg.C(P('=')^0) * '[', function(input, index, local _, e = input:find(']' .. eq .. ']', index, true) return (e or #input) + 1 end) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str) + token('longstring', longstring)) -lex:add_style('longstring', lexer.styles.string) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str) + + lex:tag(lexer.STRING .. '.longstring', longstring)) -- Comments. local line_comment = lexer.to_eol('--') local block_comment = '--' * longstring -lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) -- Numbers. local lua_integer = P('-')^-1 * (lexer.hex_num + lexer.dec_num) -lex:add_rule('number', token(lexer.NUMBER, lexer.float + lua_integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.float + lua_integer)) -- Labels. -lex:add_rule('label', token(lexer.LABEL, '::' * lexer.word * '::')) +lex:add_rule('label', lex:tag(lexer.LABEL, '::' * lexer.word * '::')) -- Attributes. -lex:add_rule('attribute', token('attribute', '<' * lexer.space^0 * word_match('const close') * - lexer.space^0 * '>')) -lex:add_style('attribute', lexer.styles.class) +lex:add_rule('attribute', lex:tag(lexer.ATTRIBUTE, '<' * lexer.space^0 * + lexer.word_match('const close') * lexer.space^0 * '>')) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, '..' + S('+-*/%^#=<>&|~;:,.{}[]()'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, '..' + S('+-*/%^#=<>&|~;:,.{}[]()'))) -- Fold points. local function fold_longcomment(text, pos, line, s, symbol) @@ -159,10 +72,73 @@ lex:add_fold_point(lexer.KEYWORD, 'function', 'end') lex:add_fold_point(lexer.KEYWORD, 'repeat', 'until') lex:add_fold_point(lexer.COMMENT, '[', fold_longcomment) lex:add_fold_point(lexer.COMMENT, ']', fold_longcomment) -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('--')) -lex:add_fold_point('longstring', '[', ']') +lex:add_fold_point(lexer.FUNCTION .. '.longstring', '[', ']') lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if', 'in', 'local', + 'or', 'nil', 'not', 'repeat', 'return', 'then', 'true', 'until', 'while', -- + 'goto' -- 5.2 +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'assert', 'collectgarbage', 'dofile', 'error', 'getmetatable', 'ipairs', 'load', 'loadfile', + 'next', 'pairs', 'pcall', 'print', 'rawequal', 'rawget', 'rawset', 'require', 'select', + 'setmetatable', 'tonumber', 'tostring', 'type', 'xpcall', -- + 'rawlen', -- 5.2 + 'warn' -- 5.4 +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN .. '.library', { + 'coroutine.create', 'coroutine.resume', 'coroutine.running', 'coroutine.status', 'coroutine.wrap', + 'coroutine.yield', -- + 'coroutine.isyieldable', -- 5.3 + 'coroutine.close', -- 5.4 + 'package.loadlib', -- + 'package.searchpath', -- 5.2 + 'utf8.char', 'utf8.codepoint', 'utf8.codes', 'utf8.len', 'utf8.offset', -- 5.3 + 'string.byte', 'string.char', 'string.dump', 'string.find', 'string.format', 'string.gmatch', + 'string.gsub', 'string.len', 'string.lower', 'string.match', 'string.rep', 'string.reverse', + 'string.sub', 'string.upper', -- + 'string.pack', 'string.packsize', 'string.unpack', -- 5.3 + 'table.concat', 'table.insert', 'table.remove', 'table.sort', -- + 'table.pack', 'table.unpack', -- 5.2 + 'table.move', -- 5.3 + 'math.abs', 'math.acos', 'math.asin', 'math.atan', 'math.ceil', 'math.cos', 'math.deg', + 'math.exp', 'math.floor', 'math.fmod', 'math.log', 'math.max', 'math.min', 'math.modf', + 'math.rad', 'math.random', 'math.randomseed', 'math.sin', 'math.sqrt', 'math.tan', -- + 'math.tointeger', 'math.type', 'math.ult', -- 5.3 + 'io.close', 'io.flush', 'io.input', 'io.lines', 'io.open', 'io.output', 'io.popen', 'io.read', + 'io.tmpfile', 'io.type', 'io.write', -- + 'os.clock', 'os.date', 'os.difftime', 'os.execute', 'os.exit', 'os.getenv', 'os.remove', + 'os.rename', 'os.setlocale', 'os.time', 'os.tmpname', -- + 'debug', 'debug.debug', 'debug.gethook', 'debug.getinfo', 'debug.getlocal', 'debug.getmetatable', + 'debug.getregistry', 'debug.getupvalue', 'debug.sethook', 'debug.setlocal', 'debug.setmetatable', + 'debug.setupvalue', 'debug.traceback', -- + 'debug.getuservalue', 'debug.setuservalue', 'debug.upvalueid', 'debug.upvaluejoin' -- 5.2 +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + '_G', '_VERSION', -- + '_ENV' -- 5.2 +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN .. '.library', { + 'coroutine', -- + 'package', 'package.cpath', 'package.loaded', 'package.path', 'package.preload', -- + 'package.config', 'package.searchers', -- 5.2 + 'utf8', 'utf8.charpattern', -- 5.3 + 'string', -- + 'table', -- + 'math', 'math.huge', 'math.pi', -- + 'math.maxinteger', 'math.mininteger', -- 5.3 + 'io', 'io.stderr', 'io.stdin', 'io.stdout', -- + 'os' +}) + +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/makefile.lua b/lua/lexers/makefile.lua index 9c5f332..fd90ba2 100644 --- a/lua/lexers/makefile.lua +++ b/lua/lexers/makefile.lua @@ -1,88 +1,121 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Makefile LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('makefile', {lex_by_line = true}) +local lex = lexer.new(..., {lex_by_line = true}) --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +-- Function definition. +local word = (lexer.any - lexer.space - S('$:,#=(){}'))^1 +local func_name = lex:tag(lexer.FUNCTION, word) +local ws = lex:get_rule('whitespace') +local eq = lex:tag(lexer.OPERATOR, '=') +lex:add_rule('function_def', + lex:tag(lexer.KEYWORD, lexer.word_match('define')) * ws * func_name * ws^-1 * eq) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, P('!')^-1 * word_match({ - -- GNU Make conditionals. - 'ifeq', 'ifneq', 'ifdef', 'ifndef', 'else', 'endif', - -- Other conditionals. - 'if', 'elseif', 'elseifdef', 'elseifndef', - -- Directives and other keywords. - 'define', 'endef', 'export', 'include', 'override', 'private', 'undefine', 'unexport', 'vpath' -}, true))) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, P('!')^-1 * lex:word_match(lexer.KEYWORD, true))) -- Targets. -local special_target = token(lexer.CONSTANT, word_match{ - '.PHONY', '.SUFFIXES', '.DEFAULT', '.PRECIOUS', '.INTERMEDIATE', '.SECONDARY', '.SECONDEXPANSION', - '.DELETE_ON_ERROR', '.IGNORE', '.LOW_RESOLUTION_TIME', '.SILENT', '.EXPORT_ALL_VARIABLES', - '.NOTPARALLEL', '.ONESHELL', '.POSIX' -}) -local normal_target = token('target', (lexer.any - lexer.space - S(':#='))^1) -local target_list = normal_target * (ws * normal_target)^0 -lex:add_rule('target', lexer.starts_line((special_target + target_list) * ws^0 * #(':' * -P('=')))) -lex:add_style('target', lexer.styles.label) +local special_target = lex:tag(lexer.CONSTANT_BUILTIN, '.' * lex:word_match('special_targets')) +-- local normal_target = lex:tag('target', (lexer.any - lexer.space - S(':+?!=#'))^1) +local target = special_target -- + normal_target * (ws * normal_target)^0 +lex:add_rule('target', lexer.starts_line(target * ws^-1 * #(':' * lexer.space))) --- Variables. -local word_char = lexer.any - lexer.space - S(':#=(){}') -local assign = S(':+?')^-1 * '=' -local expanded_var = '$' * ('(' * word_char^1 * ')' + '{' * word_char^1 * '}') -local auto_var = '$' * S('@%<?^+|*') -local special_var = word_match{ - 'MAKEFILE_LIST', '.DEFAULT_GOAL', 'MAKE_RESTARTS', '.RECIPEPREFIX', '.VARIABLES', '.FEATURES', - '.INCLUDE_DIRS', 'GPATH', 'MAKECMDGOALS', 'MAKESHELL', 'SHELL', 'VPATH' -} * #(ws^0 * assign) -local implicit_var = word_match{ - -- Some common variables. - 'AR', 'AS', 'CC', 'CXX', 'CPP', 'FC', 'M2C', 'PC', 'CO', 'GET', 'LEX', 'YACC', 'LINT', 'MAKEINFO', - 'TEX', 'TEXI2DVI', 'WEAVE', 'CWEAVE', 'TANGLE', 'CTANGLE', 'RM', - -- Some common flag variables. - 'ARFLAGS', 'ASFLAGS', 'CFLAGS', 'CXXFLAGS', 'COFLAGS', 'CPPFLAGS', 'FFLAGS', 'GFLAGS', 'LDFLAGS', - 'LDLIBS', 'LFLAGS', 'YFLAGS', 'PFLAGS', 'RFLAGS', 'LINTFLAGS', - -- Other. - 'DESTDIR', 'MAKE', 'MAKEFLAGS', 'MAKEOVERRIDES', 'MFLAGS' -} * #(ws^0 * assign) -local variable = token(lexer.VARIABLE, expanded_var + auto_var + special_var + implicit_var) -local computed_var = token(lexer.OPERATOR, '$' * S('({')) * token(lexer.FUNCTION, word_match{ - -- Functions for String Substitution and Analysis. - 'subst', 'patsubst', 'strip', 'findstring', 'filter', 'filter-out', 'sort', 'word', 'wordlist', - 'words', 'firstword', 'lastword', - -- Functions for File Names. - 'dir', 'notdir', 'suffix', 'basename', 'addsuffix', 'addprefix', 'join', 'wildcard', 'realpath', - 'abspath', - -- Functions for Conditionals. - 'if', 'or', 'and', - -- Miscellaneous Functions. - 'foreach', 'call', 'value', 'eval', 'origin', 'flavor', 'shell', - -- Functions That Control Make. - 'error', 'warning', 'info' -}) -lex:add_rule('variable', variable + computed_var) +-- Variable and function assignments. +local func_assign = func_name * ws^-1 * eq * + #P(function(input, index) return input:find('%$%(%d%)', index) end) +local builtin_var = lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN)) +local var_name = lex:tag(lexer.VARIABLE, word) +local var_assign = (builtin_var + var_name) * ws^-1 * + lex:tag(lexer.OPERATOR, S(':+?!')^-1 * '=' + '::=') +lex:add_rule('assign', lexer.starts_line(func_assign + var_assign, true) + B(': ') * var_assign) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, assign + S(':$(){}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S(':(){}|'))) + +-- Strings. +lex:add_rule('string', lexer.range("'", true) + lexer.range('"', true)) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, word_char^1)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, word)) + +-- Functions. +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local call_func = lex:tag(lexer.FUNCTION_BUILTIN, 'call') * ws * func_name +local func = lex:tag(lexer.OPERATOR, '$' * S('({')) * (call_func + builtin_func) +lex:add_rule('function', func) + +-- Variables. +local auto_var = lex:tag(lexer.OPERATOR, '$') * lex:tag(lexer.VARIABLE_BUILTIN, S('@%<?^+|*')) + + lex:tag(lexer.OPERATOR, '$(') * lex:tag(lexer.VARIABLE_BUILTIN, S('@%<?^+*') * S('DF')) +local var_ref = lex:tag(lexer.OPERATOR, P('$(') + '${') * (builtin_var + var_name) +local var = auto_var + var_ref +lex:add_rule('variable', var) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) --- Embedded Bash. +-- Embedded Bash in target rules. local bash = lexer.load('bash') bash:modify_rule('variable', - token(lexer.VARIABLE, '$$' * word_char^1) + bash:get_rule('variable') + variable) -local bash_start_rule = token(lexer.WHITESPACE, '\t') + token(lexer.OPERATOR, ';') -local bash_end_rule = token(lexer.WHITESPACE, '\n') + lex:tag(lexer.VARIABLE, '$$' * word) + func + var + bash:get_rule('variable')) +local bash_start_rule = lex:tag(lexer.WHITESPACE, '\t') + lex:tag(lexer.OPERATOR, ';') +local bash_end_rule = lex:tag(lexer.WHITESPACE, '\n') lex:embed(bash, bash_start_rule, bash_end_rule) +-- Embedded Bash in $(shell ...) calls. +local shell = lexer.load('bash', 'bash.shell') +bash_start_rule = #P('$(shell') * func +bash_end_rule = -B('\\') * lex:tag(lexer.OPERATOR, ')') +lex:embed(shell, bash_start_rule, bash_end_rule) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'define', 'endef', -- multi-line + 'else', 'endif', 'ifdef', 'ifeq', 'ifndef', 'ifneq', -- conditionals + 'export', 'include', 'load', 'override', 'undefine', 'unexport', 'vpath', -- directives + 'private', -- + 'if', 'elseif', 'elseifdef', 'elseifndef' -- non-Make conditionals +}) + +lex:set_word_list('special_targets', { + 'DEFAULT', 'DELETE_ON_ERROR', 'EXPORT_ALL_VARIABLES', 'IGNORE', 'INTERMEDIATE', + 'LOW_RESOLUTION_TIME', 'NOTPARALLEL', 'ONESHELL', 'PHONY', 'POSIX', 'PRECIOUS', 'SECONDARY', + 'SECONDEXPANSION', 'SILENT', 'SUFFIXES' +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + -- Special. + '.DEFAULT_GOAL', '.FEATURES', '.INCLUDE_DIRS', '.LIBPATTERNS', '.LOADED', '.RECIPEPREFIX', + '.SHELLFLAGS', '.SHELLSTATUS', '.VARIABLES', -- + 'COMSPEC', 'MAKESHELL', 'SHELL', -- choosing the shell + 'GPATH', 'VPATH', -- search + -- Make. + 'MAKE', 'MAKECMDGOALS', 'MAKEFILES', 'MAKEFILE_LIST', 'MAKEFLAGS', 'MAKELEVEL', 'MAKEOVERRIDES', + 'MAKE_RESTARTS', 'MAKE_TERMERR', 'MAKE_TERMOUT', 'MFLAGS', + -- Other. + 'CURDIR', 'OUTPUT_OPTION', 'SUFFIXES', + -- Implicit. + 'AR', 'ARFLAGS', 'AS', 'ASFLAGS', 'CC', 'CFLAGS', 'CO', 'COFLAGS', 'CPP', 'CPPFLAGS', 'CTANGLE', + 'CWEAVE', 'CXX', 'CXXFLAGS', 'FC', 'FFLAGS', 'GET', 'GFLAGS', 'LDFLAGS', 'LDLIBS', 'LEX', + 'LFLAGS', 'LINT', 'LINTFLAGS', 'M2C', 'MAKEINFO', 'PC', 'PFLAGS', 'RFLAGS', 'RM', 'TANGLE', 'TEX', + 'TEXI2DVI', 'WEAVE', 'YACC', 'YFLAGS', -- + 'bindir', 'DESTDIR', 'exec_prefix', 'libexecdir', 'prefix', 'sbindir' -- directory +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + -- Filename. + 'abspath', 'addprefix', 'addsuffix', 'basename', 'dir', 'join', 'notdir', 'realpath', 'suffix', + 'wildcard', -- + 'and', 'if', 'or', -- conditional + 'error', 'info', 'warning', -- control + 'filter', 'filter-out', 'findstring', 'firstword', 'lastword', 'patsubst', 'sort', 'strip', + -- Text. + 'subst', 'word', 'wordlist', 'words', -- + 'call', 'eval', 'file', 'flavor', 'foreach', 'origin', 'shell', 'value' -- other +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/markdown.lua b/lua/lexers/markdown.lua index cbd4ba2..3819d3f 100644 --- a/lua/lexers/markdown.lua +++ b/lua/lexers/markdown.lua @@ -1,75 +1,68 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Markdown LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('markdown') +local lex = lexer.new(..., {no_user_word_lists = true}) + +-- Distinguish between horizontal and vertical space so html start rule has a chance to match. +lex:modify_rule('whitespace', lex:tag(lexer.WHITESPACE, S(' \t')^1 + S('\r\n')^1)) -- Block elements. -local function h(n) return token('h' .. n, lexer.to_eol(lexer.starts_line(string.rep('#', n)))) end -lex:add_rule('header', h(6) + h(5) + h(4) + h(3) + h(2) + h(1)) -local font_size = tonumber(lexer.property_expanded['style.default']:match('size:(%d+)')) or 10 -local function add_header_style(n) - lex:add_style('h' .. n, {fore = lexer.colors.red, size = (font_size + (6 - n))}) +local function h(n) + return lex:tag(string.format('%s.h%s', lexer.HEADING, n), + lexer.to_eol(lexer.starts_line(string.rep('#', n)))) end -for i = 1, 6 do add_header_style(i) end +lex:add_rule('header', h(6) + h(5) + h(4) + h(3) + h(2) + h(1)) -lex:add_rule('blockquote', - token(lexer.STRING, lpeg.Cmt(lexer.starts_line(S(' \t')^0 * '>'), function(input, index) - local _, e = input:find('\n[ \t]*\r?\n', index) - return (e or #input) + 1 +lex:add_rule('hr', + lex:tag('hr', lpeg.Cmt(lexer.starts_line(lpeg.C(S('*-_')), true), function(input, index, c) + local line = input:match('[^\r\n]*', index):gsub('[ \t]', '') + if line:find('[^' .. c .. ']') or #line < 2 then return nil end + return (select(2, input:find('\r?\n', index)) or #input) + 1 -- include \n for eolfilled styles end))) -lex:add_rule('list', token('list', - lexer.starts_line(S(' \t')^0 * (S('*+-') + lexer.digit^1 * '.')) * S(' \t'))) -lex:add_style('list', lexer.styles.constant) +lex:add_rule('list', lex:tag(lexer.LIST, + lexer.starts_line(lexer.digit^1 * '.' + S('*+-'), true) * S(' \t'))) -local hspace = S('\t\v\f\r ') +local hspace = lexer.space - '\n' local blank_line = '\n' * hspace^0 * ('\n' + P(-1)) -local code_line = lexer.to_eol(lexer.starts_line(P(' ')^4 + '\t') * -P('<')) * lexer.newline^-1 -local code_block = lexer.range(lexer.starts_line('```'), '\n```' * hspace^0 * ('\n' + P(-1))) +local code_line = lexer.starts_line((B(' ') + B('\t')) * lexer.to_eol(), true) +local code_block = + lexer.range(lexer.starts_line('```', true), '\n```' * hspace^0 * ('\n' + P(-1))) + + lexer.range(lexer.starts_line('~~~', true), '\n~~~' * hspace^0 * ('\n' + P(-1))) local code_inline = lpeg.Cmt(lpeg.C(P('`')^1), function(input, index, bt) -- `foo`, ``foo``, ``foo`bar``, `foo``bar` are all allowed. local _, e = input:find('[^`]' .. bt .. '%f[^`]', index) return (e or #input) + 1 end) -lex:add_rule('block_code', token('code', code_line + code_block + code_inline)) -lex:add_style('code', lexer.styles.embedded .. {eolfilled = true}) +lex:add_rule('block_code', lex:tag(lexer.CODE, code_line + code_block + code_inline)) -lex:add_rule('hr', - token('hr', lpeg.Cmt(lexer.starts_line(S(' \t')^0 * lpeg.C(S('*-_'))), function(input, index, c) - local line = input:match('[^\r\n]*', index):gsub('[ \t]', '') - if line:find('[^' .. c .. ']') or #line < 2 then return nil end - return (select(2, input:find('\r?\n', index)) or #input) + 1 +lex:add_rule('blockquote', + lex:tag(lexer.STRING, lpeg.Cmt(lexer.starts_line('>', true), function(input, index) + local _, e = input:find('\n[ \t]*\r?\n', index) -- the next blank line (possibly with indentation) + return (e or #input) + 1 end))) -lex:add_style('hr', {back = lexer.colors.black, eolfilled = true}) - --- Whitespace. -local ws = token(lexer.WHITESPACE, S(' \t')^1 + S('\v\r\n')^1) -lex:add_rule('whitespace', ws) -- Span elements. -lex:add_rule('escape', token(lexer.DEFAULT, P('\\') * 1)) +lex:add_rule('escape', lex:tag(lexer.DEFAULT, P('\\') * 1)) -local ref_link_label = token('link_label', lexer.range('[', ']', true) * ':') -local ref_link_url = token('link_url', (lexer.any - lexer.space)^1) -local ref_link_title = token(lexer.STRING, lexer.range('"', true, false) + - lexer.range("'", true, false) + lexer.range('(', ')', true)) -lex:add_rule('link_label', ref_link_label * ws * ref_link_url * (ws * ref_link_title)^-1) -lex:add_style('link_label', lexer.styles.label) -lex:add_style('link_url', {underlined = true}) - -local link_label = P('!')^-1 * lexer.range('[', ']', true) +local link_text = lexer.range('[', ']', true) local link_target = '(' * (lexer.any - S(') \t'))^0 * (S(' \t')^1 * lexer.range('"', false, false))^-1 * ')' -local link_ref = S(' \t')^0 * lexer.range('[', ']', true) local link_url = 'http' * P('s')^-1 * '://' * (lexer.any - lexer.space)^1 + ('<' * lexer.alpha^2 * ':' * (lexer.any - lexer.space - '>')^1 * '>') -lex:add_rule('link', token('link', link_label * (link_target + link_ref) + link_url)) -lex:add_style('link', {underlined = true}) +lex:add_rule('link', lex:tag(lexer.LINK, P('!')^-1 * link_text * link_target + link_url)) + +local link_ref = lex:tag(lexer.REFERENCE, link_text * S(' \t')^0 * lexer.range('[', ']', true)) +local ref_link_label = lex:tag(lexer.REFERENCE, lexer.range('[', ']', true) * ':') +local ws = lex:get_rule('whitespace') +local ref_link_url = lex:tag(lexer.LINK, (lexer.any - lexer.space)^1) +local ref_link_title = lex:tag(lexer.STRING, lexer.range('"', true, false) + + lexer.range("'", true, false) + lexer.range('(', ')', true)) +lex:add_rule('link_ref', link_ref + ref_link_label * ws * ref_link_url * (ws * ref_link_title)^-1) local punct_space = lexer.punct + lexer.space @@ -78,27 +71,25 @@ local punct_space = lexer.punct + lexer.space -- delimited ranges are not sufficient. local function flanked_range(s, not_inword) local fl_char = lexer.any - s - lexer.space - local left_fl = lpeg.B(punct_space - s) * s * #fl_char + s * #(fl_char - lexer.punct) - local right_fl = lpeg.B(lexer.punct) * s * #(punct_space - s) + lpeg.B(fl_char) * s + local left_fl = B(punct_space - s) * s * #fl_char + s * #(fl_char - lexer.punct) + local right_fl = B(lexer.punct) * s * #(punct_space - s) + B(fl_char) * s return left_fl * (lexer.any - blank_line - (not_inword and s * #punct_space or s))^0 * right_fl end local asterisk_strong = flanked_range('**') -local underscore_strong = (lpeg.B(punct_space) + #lexer.starts_line('_')) * - flanked_range('__', true) * #(punct_space + -1) -lex:add_rule('strong', token('strong', asterisk_strong + underscore_strong)) -lex:add_style('strong', {bold = true}) +local underscore_strong = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('__', true) * + #(punct_space + -1) +lex:add_rule('strong', lex:tag(lexer.BOLD, asterisk_strong + underscore_strong)) local asterisk_em = flanked_range('*') -local underscore_em = (lpeg.B(punct_space) + #lexer.starts_line('_')) * flanked_range('_', true) * +local underscore_em = (B(punct_space) + #lexer.starts_line('_')) * flanked_range('_', true) * #(punct_space + -1) -lex:add_rule('em', token('em', asterisk_em + underscore_em)) -lex:add_style('em', {italics = true}) +lex:add_rule('em', lex:tag(lexer.ITALIC, asterisk_em + underscore_em)) -- Embedded HTML. local html = lexer.load('html') -local start_rule = lexer.starts_line(P(' ')^-3) * #P('<') * html:get_rule('element') -- P(' ')^4 starts code_line -local end_rule = token(lexer.DEFAULT, blank_line) -- TODO: lexer.WHITESPACE errors +local start_rule = lexer.starts_line(P(' ')^-3) * #P('<') * html:get_rule('tag') -- P(' ')^4 starts code_line +local end_rule = #blank_line * ws lex:embed(html, start_rule, end_rule) return lex diff --git a/lua/lexers/matlab.lua b/lua/lexers/matlab.lua index b4871b3..5a26d27 100644 --- a/lua/lexers/matlab.lua +++ b/lua/lexers/matlab.lua @@ -1,73 +1,45 @@ --- Copyright 2006-2022 Martin Morawetz. See LICENSE. +-- Copyright 2006-2024 Martin Morawetz. See LICENSE. -- Matlab LPeg lexer. -- Based off of lexer code by Mitchell. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local lexer = lexer +local P, B, S = lpeg.P, lpeg.B, lpeg.S -local lex = lexer.new('matlab') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match({ - 'break', 'case', 'catch', 'continue', 'do', 'else', 'elseif', 'end', 'end_try_catch', - 'end_unwind_protect', 'endfor', 'endif', 'endswitch', 'endwhile', 'for', 'function', - 'endfunction', 'global', 'if', 'otherwise', 'persistent', 'replot', 'return', 'static', 'switch', - 'try', 'until', 'unwind_protect', 'unwind_protect_cleanup', 'varargin', 'varargout', 'while' -}, true))) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'abs', 'any', 'argvatan2', 'axes', 'axis', 'ceil', 'cla', 'clear', 'clf', 'columns', 'cos', - 'delete', 'diff', 'disp', 'doc', 'double', 'drawnow', 'exp', 'figure', 'find', 'fix', 'floor', - 'fprintf', 'gca', 'gcf', 'get', 'grid', 'help', 'hist', 'hold', 'isempty', 'isnull', 'length', - 'load', 'log', 'log10', 'loglog', 'max', 'mean', 'median', 'min', 'mod', 'ndims', 'numel', - 'num2str', 'ones', 'pause', 'plot', 'printf', 'quit', 'rand', 'randn', 'rectangle', 'rem', - 'repmat', 'reshape', 'round', 'rows', 'save', 'semilogx', 'semilogy', 'set', 'sign', 'sin', - 'size', 'sizeof', 'size_equal', 'sort', 'sprintf', 'squeeze', 'sqrt', 'std', 'strcmp', 'subplot', - 'sum', 'tan', 'tic', 'title', 'toc', 'uicontrol', 'who', 'xlabel', 'ylabel', 'zeros' -})) - --- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match( - 'EDITOR I IMAGEPATH INFO_FILE J LOADPATH OCTAVE_VERSION PAGER PS1 PS2 PS4 PWD'))) +-- Note: cannot tag normal functions because indexing variables uses the same syntax. +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +lex:add_rule('function', builtin_func * #(lexer.space^0 * S('('))) -- Variable. -lex:add_rule('variable', token(lexer.VARIABLE, word_match{ - 'ans', 'automatic_replot', 'default_return_value', 'do_fortran_indexing', - 'define_all_return_values', 'empty_list_elements_ok', 'eps', 'false', 'gnuplot_binary', - 'ignore_function_time_stamp', 'implicit_str_to_num_ok', 'Inf', 'inf', 'NaN', 'nan', - 'ok_to_lose_imaginary_part', 'output_max_field_width', 'output_precision', 'page_screen_output', - 'pi', 'prefer_column_vectors', 'prefer_zero_one_indexing', 'print_answer_id_name', - 'print_empty_dimensions', 'realmax', 'realmin', 'resize_on_range_error', - 'return_last_computed_value', 'save_precision', 'silent_functions', 'split_long_rows', - 'suppress_verbose_help_message', 'treat_neg_dim_as_zero', 'true', 'warn_assign_as_truth_value', - 'warn_comma_in_global_decl', 'warn_divide_by_zero', 'warn_function_name_clash', - 'whitespace_in_literal_matrix' -})) +lex:add_rule('variable', lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local sq_str = lexer.range("'", true) local dq_str = lexer.range('"') local bq_str = lexer.range('`') -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str + bq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + bq_str)) -- Comments. local line_comment = lexer.to_eol(S('%#')) local block_comment = lexer.range('%{', '%}') -lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('!%^&*()[]{}-=+/\\|:;.,?<>~`´'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('!%^&*()[]{}-=+/\\|:;.,?<>~`´'))) -- Fold points. lex:add_fold_point(lexer.KEYWORD, 'if', 'end') @@ -77,7 +49,42 @@ lex:add_fold_point(lexer.KEYWORD, 'switch', 'end') lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.COMMENT, '%{', '%}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('%')) -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists +lex:set_word_list(lexer.KEYWORD, { + 'break', 'case', 'catch', 'continue', 'do', 'else', 'elseif', 'end', 'end_try_catch', + 'end_unwind_protect', 'endfor', 'endif', 'endswitch', 'endwhile', 'for', 'function', + 'endfunction', 'global', 'if', 'otherwise', 'persistent', 'replot', 'return', 'static', 'switch', + 'try', 'until', 'unwind_protect', 'unwind_protect_cleanup', 'varargin', 'varargout', 'while' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'abs', 'any', 'argvatan2', 'axes', 'axis', 'ceil', 'cla', 'clear', 'clf', 'columns', 'cos', + 'delete', 'diff', 'disp', 'doc', 'double', 'drawnow', 'exp', 'figure', 'find', 'fix', 'floor', + 'fprintf', 'gca', 'gcf', 'get', 'grid', 'help', 'hist', 'hold', 'isempty', 'isnull', 'length', + 'load', 'log', 'log10', 'loglog', 'max', 'mean', 'median', 'min', 'mod', 'ndims', 'numel', + 'num2str', 'ones', 'pause', 'plot', 'printf', 'quit', 'rand', 'randn', 'rectangle', 'rem', + 'repmat', 'reshape', 'round', 'rows', 'save', 'semilogx', 'semilogy', 'set', 'sign', 'sin', + 'size', 'sizeof', 'size_equal', 'sort', 'sprintf', 'squeeze', 'sqrt', 'std', 'strcmp', 'subplot', + 'sum', 'tan', 'tic', 'title', 'toc', 'uicontrol', 'who', 'xlabel', 'ylabel', 'zeros' +}) + +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'ans', 'automatic_replot', 'default_return_value', 'do_fortran_indexing', + 'define_all_return_values', 'empty_list_elements_ok', 'eps', 'gnuplot_binary', + 'ignore_function_time_stamp', 'implicit_str_to_num_ok', 'ok_to_lose_imaginary_part', + 'output_max_field_width', 'output_precision', 'page_screen_output', 'prefer_column_vectors', + 'prefer_zero_one_indexing', 'print_answer_id_name', 'print_empty_dimensions', + 'resize_on_range_error', 'return_last_computed_value', 'save_precision', 'silent_functions', + 'split_long_rows', 'suppress_verbose_help_message', 'treat_neg_dim_as_zero', + 'warn_assign_as_truth_value', 'warn_comma_in_global_decl', 'warn_divide_by_zero', + 'warn_function_name_clash', 'whitespace_in_literal_matrix' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'false', 'Inf', 'inf', 'NaN', 'nan', 'pi', 'realmax', 'realmin', 'true' +}) + +lexer.property['scintillua.comment'] = '%' return lex diff --git a/lua/lexers/mediawiki.lua b/lua/lexers/mediawiki.lua index f304a7a..8c005a5 100644 --- a/lua/lexers/mediawiki.lua +++ b/lua/lexers/mediawiki.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- MediaWiki LPeg lexer. -- Contributed by Alexander Misel. @@ -12,28 +12,22 @@ local lex = lexer.new('mediawiki') lex:add_rule('comment', token(lexer.COMMENT, lexer.range('<!--', '-->'))) -- HTML-like tags -local tag_start = token('tag_start', '<' * P('/')^-1 * lexer.alnum^1 * lexer.space^0) +local tag_start = token(lexer.TAG, '<' * P('/')^-1 * lexer.alnum^1 * lexer.space^0) local dq_str = '"' * ((lexer.any - S('>"\\')) + ('\\' * lexer.any))^0 * '"' -local tag_attr = token('tag_attr', lexer.alpha^1 * lexer.space^0 * +local tag_attr = token(lexer.ATTRIBUTE, lexer.alpha^1 * lexer.space^0 * ('=' * lexer.space^0 * (dq_str + (lexer.any - lexer.space - '>')^0)^-1)^0 * lexer.space^0) -local tag_end = token('tag_end', P('/')^-1 * '>') +local tag_end = token(lexer.TAG, P('/')^-1 * '>') lex:add_rule('tag', tag_start * tag_attr^0 * tag_end) -lex:add_style('tag_start', lexer.styles.keyword) -lex:add_style('tag_attr', lexer.styles.type) -lex:add_style('tag_end', lexer.styles.keyword) -- Link lex:add_rule('link', token(lexer.STRING, S('[]'))) -lex:add_rule('internal_link', B('[[') * token('link_article', (lexer.any - '|' - ']]')^1)) -lex:add_style('link_article', lexer.styles.string .. {underlined = true}) +lex:add_rule('internal_link', B('[[') * token(lexer.LINK, (lexer.any - '|' - ']]')^1)) -- Templates and parser functions. lex:add_rule('template', token(lexer.OPERATOR, S('{}'))) lex:add_rule('parser_func', - B('{{') * token('parser_func', '#' * lexer.alpha^1 + lexer.upper^1 * ':')) -lex:add_rule('template_name', B('{{') * token('template_name', (lexer.any - S('{}|'))^1)) -lex:add_style('parser_func', lexer.styles['function']) -lex:add_style('template_name', lexer.styles.operator .. {underlined = true}) + B('{{') * token(lexer.FUNCTION, '#' * lexer.alpha^1 + lexer.upper^1 * ':')) +lex:add_rule('template_name', B('{{') * token(lexer.LINK, (lexer.any - S('{}|'))^1)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('-=|#~!'))) @@ -44,4 +38,7 @@ lex:add_rule('behavior_switch', (B(lexer.space) + start_pat) * token('behavior_s '__TOC__ __FORCETOC__ __NOTOC__ __NOEDITSECTION__ __NOCC__ __NOINDEX__')) * #lexer.space) lex:add_style('behavior_switch', lexer.styles.keyword) +lexer.property['scintillua.comment'] = '<!--|-->' +lexer.property['scintillua.angle.braces'] = '1' + return lex diff --git a/lua/lexers/meson.lua b/lua/lexers/meson.lua index c224f82..75fe32b 100644 --- a/lua/lexers/meson.lua +++ b/lua/lexers/meson.lua @@ -1,23 +1,58 @@ --- Copyright 2020-2022 Florian Fischer. See LICENSE. +-- Copyright 2020-2024 Florian Fischer. See LICENSE. -- Meson file LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match -local P, R, S = lpeg.P, lpeg.R, lpeg.S +local lexer = lexer +local S = lpeg.S -local lex = lexer.new('meson', {fold_by_indentation = true}) - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(..., {fold_by_indentation = true}) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match( - 'and or not if elif else endif foreach break continue endforeach'))) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) --- Methods. +-- Functions. -- https://mesonbuild.com/Reference-manual.html#builtin-objects -- https://mesonbuild.com/Reference-manual.html#returned-objects -local method_names = word_match{ +local method = lex:tag(lexer.FUNCTION_METHOD, lex:word_match(lexer.FUNCTION_METHOD)) +-- https://mesonbuild.com/Reference-manual.html#functions +local func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +-- A function call must be followed by an opening parenthesis. The matching of function calls +-- instead of just their names is needed to not falsely highlight function names which can also +-- be keyword arguments. For example 'include_directories' can be a function call itself or a +-- keyword argument of an 'executable' or 'library' function call. +lex:add_rule('function', (method + func) * #(lexer.space^0 * '(')) + +-- Builtin objects. +-- https://mesonbuild.com/Reference-manual.html#builtin-objects +lex:add_rule('object', lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN))) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Strings. +local str = lexer.range("'", true) +local multiline_str = lexer.range("'''") +lex:add_rule('string', lex:tag(lexer.STRING, multiline_str + str)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#', true))) + +-- Numbers. +local oct_num = '0o' * lpeg.R('07') +local integer = S('+-')^-1 * (lexer.hex_num + lexer.bin_num + oct_num + lexer.dec_num) +lex:add_rule('number', lex:tag(lexer.NUMBER, integer)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('()[]{}-=+/%:.,?<>'))) + +-- Word lists +lex:set_word_list(lexer.KEYWORD, { + 'and', 'or', 'not', 'if', 'elif', 'else', 'endif', 'foreach', 'break', 'continue', 'endforeach' +}) + +lex:set_word_list(lexer.FUNCTION_METHOD, { -- array -- 'contains', 'get', 'length', -- boolean -- @@ -69,14 +104,9 @@ local method_names = word_match{ 'found', 'get_variable', -- run result object -- 'compiled', 'returncode', 'stderr', 'stdout' -} --- A method call must be followed by an opening parenthesis. -lex:add_rule('method', token('method', method_names * #(lexer.space^0 * '('))) -lex:add_style('method', lexer.styles['function']) +}) --- Function. --- https://mesonbuild.com/Reference-manual.html#functions -local func_names = word_match{ +lex:set_word_list(lexer.FUNCTION_BUILTIN, { 'add_global_arguments', 'add_global_link_arguments', 'add_languages', 'add_project_arguments', 'add_project_link_arguments', 'add_test_setup', 'alias_targ', 'assert', 'benchmark', 'both_libraries', 'build_target', 'configuration_data', 'configure_file', 'custom_target', @@ -86,41 +116,14 @@ local func_names = word_match{ 'is_disabler', 'is_variable', 'jar', 'join_paths', 'library', 'message', 'warning', 'summary', 'project', 'run_command', 'run_targ', 'set_variable', 'shared_library', 'shared_module', 'static_library', 'subdir', 'subdir_done', 'subproject', 'test', 'vcs_tag' -} --- A function call must be followed by an opening parenthesis. The matching of function calls --- instead of just their names is needed to not falsely highlight function names which can also --- be keyword arguments. For example 'include_directories' can be a function call itself or a --- keyword argument of an 'executable' or 'library' function call. -lex:add_rule('function', token(lexer.FUNCTION, func_names * #(lexer.space^0 * '('))) - --- Builtin objects. --- https://mesonbuild.com/Reference-manual.html#builtin-objects -lex:add_rule('object', - token('object', word_match('meson build_machine host_machine target_machine'))) -lex:add_style('object', lexer.styles.type) +}) --- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match('false true'))) - --- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:set_word_list(lexer.VARIABLE_BUILTIN, { + 'meson', 'build_machine', 'host_machine', 'target_machine' +}) --- Strings. -local str = lexer.range("'", true) -local multiline_str = lexer.range("'''") -lex:add_rule('string', token(lexer.STRING, multiline_str + str)) +lex:set_word_list(lexer.CONSTANT_BUILTIN, {'false', 'true'}) --- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#', true))) - --- Numbers. -local dec = R('19')^1 * R('09')^0 -local bin = '0b' * S('01')^1 -local oct = '0o' * R('07')^1 -local integer = S('+-')^-1 * (bin + lexer.hex_num + oct + dec) -lex:add_rule('number', token(lexer.NUMBER, integer)) - --- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('()[]{}-=+/%:.,?<>'))) +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/moonscript.lua b/lua/lexers/moonscript.lua index f9bc4dd..1984e81 100644 --- a/lua/lexers/moonscript.lua +++ b/lua/lexers/moonscript.lua @@ -1,4 +1,4 @@ --- Copyright 2016-2022 Alejandro Baez (https://keybase.io/baez). See LICENSE. +-- Copyright 2016-2024 Alejandro Baez (https://keybase.io/baez). See LICENSE. -- Moonscript LPeg lexer. local lexer = require('lexer') @@ -12,7 +12,7 @@ lex:add_rule('whitspace', token(lexer.WHITESPACE, lexer.space^1)) -- Table keys. lex:add_rule('tbl_key', token('tbl_key', lexer.word * ':' + ':' * lexer.word)) -lex:add_style('tbl_key', lexer.STYLE_REGEX) +lex:add_style('tbl_key', lexer.styles.regex) -- Keywords. lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ @@ -48,7 +48,7 @@ lex:add_rule('constant', token(lexer.CONSTANT, word_match{ })) -- Libraries. -lex:add_rule('library', token('library', word_match{ +lex:add_rule('library', token(lexer.FUNCTION_BUILTIN, word_match{ -- Coroutine. 'coroutine', 'coroutine.create', 'coroutine.resume', 'coroutine.running', 'coroutine.status', 'coroutine.wrap', 'coroutine.yield', @@ -94,7 +94,7 @@ lex:add_rule('library', token('library', word_match{ -- Debug added in 5.2. 'debug.getuservalue', 'debug.setuservalue', 'debug.upvalueid', 'debug.upvaluejoin', - --- MoonScript 0.3.1 standard library. + -- MoonScript 0.3.1 standard library. -- Printing functions. 'p', -- Table functions. @@ -106,7 +106,6 @@ lex:add_rule('library', token('library', word_match{ -- Debug functions. 'debug.upvalue' })) -lex:add_style('library', lexer.styles.type) -- Identifiers. local identifier = token(lexer.IDENTIFIER, lexer.word) @@ -121,8 +120,7 @@ local longstring = lpeg.Cmt('[' * lpeg.C(P('=')^0) * '[', function(input, index, local _, e = input:find(']' .. eq .. ']', index, true) return (e or #input) + 1 end) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str) + token('longstring', longstring)) -lex:add_style('longstring', lexer.styles.string) +lex:add_rule('string', token(lexer.STRING, sq_str + dq_str) + token(lexer.STRING, longstring)) -- Comments. local line_comment = lexer.to_eol('--') @@ -141,4 +139,6 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-*!\\/%^#=<>;:,.'))) lex:add_rule('symbol', token('symbol', S('(){}[]'))) lex:add_style('symbol', lexer.styles.embedded) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/myrddin.lua b/lua/lexers/myrddin.lua index 4b7f559..c463aaf 100644 --- a/lua/lexers/myrddin.lua +++ b/lua/lexers/myrddin.lua @@ -1,4 +1,4 @@ --- Copyright 2017-2022 Michael Forney. See LICENSE +-- Copyright 2017-2024 Michael Forney. See LICENSE -- Myrddin LPeg lexer. local lexer = require('lexer') @@ -49,4 +49,6 @@ lex:add_rule('number', token(lexer.NUMBER, float + integer)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('`#_+-/*%<>~!=^&|~:;,.()[]{}'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/nemerle.lua b/lua/lexers/nemerle.lua index 96feb51..db77c98 100644 --- a/lua/lexers/nemerle.lua +++ b/lua/lexers/nemerle.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Nemerle LPeg lexer. local lexer = require('lexer') @@ -60,6 +60,7 @@ lex:add_fold_point(lexer.PREPROCESSOR, 'ifdef', 'endif') lex:add_fold_point(lexer.PREPROCESSOR, 'ifndef', 'endif') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/networkd.lua b/lua/lexers/networkd.lua index ff9af93..2bf3410 100644 --- a/lua/lexers/networkd.lua +++ b/lua/lexers/networkd.lua @@ -1,4 +1,4 @@ --- Copyright 2016-2022 Christian Hesse. See LICENSE. +-- Copyright 2016-2024 Christian Hesse. See LICENSE. -- systemd networkd file LPeg lexer. local lexer = require('lexer') @@ -90,12 +90,12 @@ lex:add_rule('section', token(lexer.LABEL, '[' * word_match{ lex:add_rule('comment', token(lexer.COMMENT, lexer.starts_line(lexer.to_eol(S(';#'))))) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local oct_num = '0' * S('01234567_')^1 -local integer = S('+-')^-1 * (lexer.hex_num + oct_num + dec) +local integer = S('+-')^-1 * (lexer.hex_num + lexer.oct_num_('_') + lexer.dec_num_('_')) lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, '=')) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/nim.lua b/lua/lexers/nim.lua index af333bb..a6f0335 100644 --- a/lua/lexers/nim.lua +++ b/lua/lexers/nim.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Nim LPeg lexer. local lexer = require('lexer') @@ -86,17 +86,12 @@ local block_comment = lexer.range('#[', ']#') lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local hex = '0' * S('xX') * lexer.xdigit^1 * ('_' * lexer.xdigit^1)^0 -local bin = '0' * S('bB') * S('01')^1 * ('_' * S('01')^1)^0 * -lexer.xdigit -local oct = '0o' * lpeg.R('07')^1 -local integer = S('+-')^-1 * (bin + hex + oct + dec) * - ("'" * S('iIuUfF') * (P('8') + '16' + '32' + '64'))^-1 -local float = lexer.digit^1 * ('_' * lexer.digit^1)^0 * ('.' * ('_' * lexer.digit)^0)^-1 * S('eE') * - S('+-')^-1 * lexer.digit^1 * ('_' * lexer.digit^1)^0 -lex:add_rule('number', token(lexer.NUMBER, float + integer)) +lex:add_rule('number', token(lexer.NUMBER, lexer.float_('_') + lexer.integer_('_') * + ("'" * S('iIuUfF') * (P('8') + '16' + '32' + '64'))^-1)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('=+-*/<>@$~&%|!?^.:\\`()[]{},;'))) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/nsis.lua b/lua/lexers/nsis.lua index 9f57012..c8bd3b8 100644 --- a/lua/lexers/nsis.lua +++ b/lua/lexers/nsis.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Robert Gieseke. See LICENSE. +-- Copyright 2006-2024 Robert Gieseke. See LICENSE. -- NSIS LPeg lexer -- Based on NSIS 2.46 docs: http://nsis.sourceforge.net/Docs/. @@ -145,4 +145,6 @@ lex:add_rule('label', token(lexer.LABEL, lexer.word * ':')) -- Identifiers. lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/null.lua b/lua/lexers/null.lua index ac61ab8..3fdee15 100644 --- a/lua/lexers/null.lua +++ b/lua/lexers/null.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Null LPeg lexer. return require('lexer').new('null') diff --git a/lua/lexers/objeck.lua b/lua/lexers/objeck.lua new file mode 100644 index 0000000..000d2de --- /dev/null +++ b/lua/lexers/objeck.lua @@ -0,0 +1,59 @@ +-- Copyright 2023-2024 Mitchell. See LICENSE. +-- Objeck LPeg lexer. + +local lexer = lexer +local P, S = lpeg.P, lpeg.S + +local lex = lexer.new(...) + +-- Keywords. +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Types. +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Class variables. +lex:add_rule('variable', lex:tag(lexer.VARIABLE, '@' * lexer.word)) + +-- Comments. +local line_comment = lexer.to_eol('#', true) +local block_comment = lexer.range('#~', '~#') +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) + +-- Strings. +local sq_str = lexer.range("'", true) +local dq_str = lexer.range('"', true) +local ml_str = lexer.range('"', false, false) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + ml_str + dq_str)) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('~!.,:;+-*/<>=\\^|&%?()[]{}'))) + +-- Fold points. +lex:add_fold_point(lexer.OPERATOR, '{', '}') +lex:add_fold_point(lexer.COMMENT, '#~', '~#') + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'class', 'method', 'function', 'public', 'abstract', 'private', 'static', 'native', 'virtual', + 'Parent', 'As', 'from', 'implements', 'interface', 'enum', 'alias', 'consts', 'bundle', 'use', + 'leaving', 'if', 'else', 'do', 'while', 'select', 'break', 'continue', 'other', 'for', 'each', + 'reverse', 'label', 'return', 'critical', 'New', 'and', 'or', 'xor', 'true', 'false' -- , 'Nil' +}) + +lex:set_word_list(lexer.TYPE, { + 'Nil', 'Byte', 'ByteHolder', 'Int', 'IntHolder', 'Float', 'FloatHolder', 'Char', 'CharHolder', + 'Bool', 'BoolHolder', 'String', 'BaseArrayHolder', 'BoolArrayHolder', 'ByteArrayHolder', + 'CharArrayHolder', 'FloatArrayHolder', 'IntArrayHolder', 'StringArrayHolder', 'Func2Holder', + 'Func3Holder', 'Func4Holder', 'FuncHolder' +}) + +lexer.property['scintillua.comment'] = '#' + +return lex diff --git a/lua/lexers/objective_c.lua b/lua/lexers/objective_c.lua index 101cb90..af665c1 100644 --- a/lua/lexers/objective_c.lua +++ b/lua/lexers/objective_c.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Objective C LPeg lexer. local lexer = require('lexer') @@ -63,6 +63,7 @@ lex:add_fold_point(lexer.PREPROCESSOR, 'ifdef', 'endif') lex:add_fold_point(lexer.PREPROCESSOR, 'ifndef', 'endif') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/output.lua b/lua/lexers/output.lua new file mode 100644 index 0000000..0ff9f1c --- /dev/null +++ b/lua/lexers/output.lua @@ -0,0 +1,97 @@ +-- Copyright 2022-2024 Mitchell. See LICENSE. +-- LPeg lexer for tool output. +-- If a warning or error is recognized, tags its filename, line, column (if available), +-- and message, and sets the line state to 1 for an error (first bit), and 2 for a warning +-- (second bit). +-- This is similar to Lexilla's errorlist lexer. + +local lexer = lexer +local starts_line = lexer.starts_line +local P, S = lpeg.P, lpeg.S + +local lex = lexer.new(..., {lex_by_line = true}) + +-- Tags a pattern as plain text. +local function text(patt) return lex:tag(lexer.DEFAULT, patt) end + +-- Tags a pattern as a filename. +local function filename(patt) return lex:tag('filename', patt) end + +-- Typical line and column number patterns. +local line = text('line ')^-1 * lex:tag('line', lexer.dec_num) +local column = lex:tag('column', lexer.dec_num) + +-- Tags a pattern as an error/warning/etc. message. +local function message(patt) return lex:tag('message', patt) end + +-- Immediately marks the current line as an error. +-- This should only be specified at the end of a rule, or else LPeg may backtrack and mistakenly +-- mark a non-error line. +local function mark_error(_, pos) + lexer.line_state[lexer.line_from_position(pos)] = 1 + return true +end + +-- Immediately marks the current line as a warning. +-- This should only be specified at the end of a rule, or else LPeg may backtrack and mistakenly +-- mark a non-warning line. +local function mark_warning(_, pos) + lexer.line_state[lexer.line_from_position(pos)] = 2 + return true +end + +-- filename:line: message (ruby) +-- filename:line:col: message (c, cpp, go, ...) +-- filename: line X: message (bash) +local c_filename = filename((lexer.nonnewline - ':')^1) +local colon = text(':' * P(' ')^-1) +local warning = message(lexer.to_eol('warning: ')) * mark_warning +local note = message(lexer.to_eol('note: ')) -- do not mark +local error = message(lexer.to_eol()) * mark_error +lex:add_rule('common', starts_line(c_filename) * colon * line * colon * (column * colon)^-1 * + (warning + note + error)) + +-- prog: filename:line: message (awk, lua) +lex:add_rule('prog', starts_line(text(lexer.word)) * colon * c_filename * colon * line * colon * + (warning + error)) + +-- File "filename", line X (python) +local py_filename = filename((lexer.nonnewline - '"')^1) +lex:add_rule('python', + starts_line(text('File "'), true) * py_filename * text('", ') * line * mark_error) + +-- filename(line): error: message (d, cuda) +local lparen, rparen = text('('), text(')') +local d_filename = filename((lexer.nonnewline - '(')^1) +local d_error = message(lexer.to_eol(S('Ee') * 'rror')) * mark_error +lex:add_rule('dmd', starts_line(d_filename) * lparen * line * rparen * colon * d_error) + +-- "filename" line X: message (gnuplot) +local gp_filename = filename((lexer.nonnewline - '"')^1) +lex:add_rule('gnuplot', starts_line(text('"')) * gp_filename * text('" ') * line * colon * error) + +-- at com.path(filename:line) (java) +lex:add_rule('java', + starts_line(text('at ' * (lexer.nonnewline - '(')^1), true) * lparen * c_filename * colon * line * + rparen * mark_error) + +-- message in filename on line X (php) +lex:add_rule('php', starts_line(message((lexer.nonnewline - ' in ')^1)) * text(' in ') * + filename((lexer.nonnewline - ' on ')^1) * text(' on ') * line * mark_error) + +-- filename(line, col): message (vb, csharp, fsharp, ...) +lex:add_rule('vb', + starts_line(filename((lexer.nonnewline - '(')^1)) * lparen * line * text(', ') * column * rparen * + colon * error) + +-- message at filename line X (perl) +lex:add_rule('perl', starts_line(message((lexer.nonnewline - ' at ')^1)) * text(' at ') * + filename((lexer.nonnewline - ' line ')^1) * text(' line ') * line * mark_error) + +-- CMake Error at filename:line: (cmake) +lex:add_rule('cmake', + starts_line(text('CMake Error at ')) * c_filename * colon * line * colon * mark_error) + +lex:add_rule('any_line', lex:tag(lexer.DEFAULT, lexer.to_eol())) + +return lex diff --git a/lua/lexers/pascal.lua b/lua/lexers/pascal.lua index 05e9f62..df46383 100644 --- a/lua/lexers/pascal.lua +++ b/lua/lexers/pascal.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Pascal LPeg lexer. local lexer = require('lexer') @@ -24,7 +24,7 @@ lex:add_rule('keyword', token(lexer.KEYWORD, word_match({ 'message', 'name', 'namespaces', 'near', 'nodefault', 'overload', 'override', 'package', 'pascal', 'platform', 'private', 'protected', 'public', 'published', 'read', 'readonly', 'register', 'reintroduce', 'requires', 'resident', 'safecall', 'stdcall', 'stored', 'varargs', 'virtual', - 'write', 'writeln', 'writeonly', -- + 'write', 'writeonly', -- 'false', 'nil', 'self', 'true' }, true))) @@ -59,4 +59,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number * S('LlDdFf')^-1)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('.,;^@:=<>+-/*()[]'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/perl.lua b/lua/lexers/perl.lua index 40727a4..085873e 100644 --- a/lua/lexers/perl.lua +++ b/lua/lexers/perl.lua @@ -1,52 +1,16 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Perl LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('perl') - --- Whitespace. -lex:add_rule('perl', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'STDIN', 'STDOUT', 'STDERR', 'BEGIN', 'END', 'CHECK', 'INIT', -- - 'require', 'use', -- - 'break', 'continue', 'do', 'each', 'else', 'elsif', 'foreach', 'for', 'if', 'last', 'local', 'my', - 'next', 'our', 'package', 'return', 'sub', 'unless', 'until', 'while', '__FILE__', '__LINE__', - '__PACKAGE__', -- - 'and', 'or', 'not', 'eq', 'ne', 'lt', 'gt', 'le', 'ge' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Markers. -lex:add_rule('marker', token(lexer.COMMENT, word_match('__DATA__ __END__') * lexer.any^0)) - --- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'abs', 'accept', 'alarm', 'atan2', 'bind', 'binmode', 'bless', 'caller', 'chdir', 'chmod', - 'chomp', 'chop', 'chown', 'chr', 'chroot', 'closedir', 'close', 'connect', 'cos', 'crypt', - 'dbmclose', 'dbmopen', 'defined', 'delete', 'die', 'dump', 'each', 'endgrent', 'endhostent', - 'endnetent', 'endprotoent', 'endpwent', 'endservent', 'eof', 'eval', 'exec', 'exists', 'exit', - 'exp', 'fcntl', 'fileno', 'flock', 'fork', 'format', 'formline', 'getc', 'getgrent', 'getgrgid', - 'getgrnam', 'gethostbyaddr', 'gethostbyname', 'gethostent', 'getlogin', 'getnetbyaddr', - 'getnetbyname', 'getnetent', 'getpeername', 'getpgrp', 'getppid', 'getpriority', 'getprotobyname', - 'getprotobynumber', 'getprotoent', 'getpwent', 'getpwnam', 'getpwuid', 'getservbyname', - 'getservbyport', 'getservent', 'getsockname', 'getsockopt', 'glob', 'gmtime', 'goto', 'grep', - 'hex', 'import', 'index', 'int', 'ioctl', 'join', 'keys', 'kill', 'lcfirst', 'lc', 'length', - 'link', 'listen', 'localtime', 'log', 'lstat', 'map', 'mkdir', 'msgctl', 'msgget', 'msgrcv', - 'msgsnd', 'new', 'oct', 'opendir', 'open', 'ord', 'pack', 'pipe', 'pop', 'pos', 'printf', 'print', - 'prototype', 'push', 'quotemeta', 'rand', 'readdir', 'read', 'readlink', 'recv', 'redo', 'ref', - 'rename', 'reset', 'reverse', 'rewinddir', 'rindex', 'rmdir', 'scalar', 'seekdir', 'seek', - 'select', 'semctl', 'semget', 'semop', 'send', 'setgrent', 'sethostent', 'setnetent', 'setpgrp', - 'setpriority', 'setprotoent', 'setpwent', 'setservent', 'setsockopt', 'shift', 'shmctl', 'shmget', - 'shmread', 'shmwrite', 'shutdown', 'sin', 'sleep', 'socket', 'socketpair', 'sort', 'splice', - 'split', 'sprintf', 'sqrt', 'srand', 'stat', 'study', 'substr', 'symlink', 'syscall', 'sysread', - 'sysseek', 'system', 'syswrite', 'telldir', 'tell', 'tied', 'tie', 'time', 'times', 'truncate', - 'ucfirst', 'uc', 'umask', 'undef', 'unlink', 'unpack', 'unshift', 'untie', 'utime', 'values', - 'vec', 'wait', 'waitpid', 'wantarray', 'warn', 'write' -})) +lex:add_rule('marker', lex:tag(lexer.COMMENT, lexer.word_match('__DATA__ __END__') * lexer.any^0)) -- Strings. local delimiter_matches = {['('] = ')', ['['] = ']', ['{'] = '}', ['<'] = '>'} @@ -83,6 +47,7 @@ local literal_delimited2 = P(function(input, index) -- for 2 delimiter sets if not final_match_pos then -- using (), [], {}, or <> notation final_match_pos = lpeg.match(lexer.space^0 * patt, input, first_match_pos) end + if final_match_pos and final_match_pos < index then final_match_pos = index end return final_match_pos or #input + 1 end end) @@ -101,50 +66,96 @@ end) local lit_str = 'q' * P('q')^-1 * literal_delimited local lit_array = 'qw' * literal_delimited local lit_cmd = 'qx' * literal_delimited -local lit_tr = (P('tr') + 'y') * literal_delimited2 * S('cds')^0 -local string = token(lexer.STRING, - sq_str + dq_str + cmd_str + heredoc + lit_str + lit_array + lit_cmd + lit_tr) -local regex_str = #P('/') * lexer.last_char_includes('-<>+*!~\\=%&|^?:;([{') * - lexer.range('/', true) * S('imosx')^0 +local string = lex:tag(lexer.STRING, + sq_str + dq_str + cmd_str + heredoc + lit_str + lit_array + lit_cmd) +local regex_str = lexer.after_set('-<>+*!~\\=%&|^?:;([{', lexer.range('/', true) * S('imosx')^0) local lit_regex = 'qr' * literal_delimited * S('imosx')^0 local lit_match = 'm' * literal_delimited * S('cgimosx')^0 local lit_sub = 's' * literal_delimited2 * S('ecgimosx')^0 -local regex = token(lexer.REGEX, regex_str + lit_regex + lit_match + lit_sub) +local lit_tr = (P('tr') + 'y') * literal_delimited2 * S('cds')^0 +local regex = lex:tag(lexer.REGEX, regex_str + lit_regex + lit_match + lit_sub + lit_tr) lex:add_rule('string', string + regex) +-- Functions. +lex:add_rule('function_builtin', + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) * + #(lexer.space^0 * P('(')^-1)) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = lpeg.B('->') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (method + func) * #(lexer.space^0 * '(')) + +-- Constants. +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) + -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments. local line_comment = lexer.to_eol('#', true) local block_comment = lexer.range(lexer.starts_line('=' * lexer.alpha), lexer.starts_line('=cut')) -lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local hex = '0' * S('xX') * lexer.xdigit^1 * ('_' * lexer.xdigit^1)^0 -local bin = '0' * S('bB') * S('01')^1 * ('_' * S('01')^1)^0 * -lexer.xdigit -local integer = S('+-')^-1 * (hex + bin + dec) -lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number_('_'))) -- Variables. --- LuaFormatter off -local special_var = '$' * ( - '^' * S('ADEFHILMOPSTWX')^-1 + - S('\\"[]\'&`+*.,;=%~?@<>(|/!-') + - ':' * (lexer.any - ':') + - P('$') * -lexer.word + - lexer.digit^1) --- LuaFormatter on +local builtin_var_s = '$' * + (lpeg.R('09') + S('!"$%&\'()+,-./:;<=>?@\\]_`|~') + '^' * S('ACDEFHILMNOPRSTVWX')^-1 + 'ARGV') +local builtin_var_a = '@' * (S('+-_F') + 'ARGV' + 'INC' + 'ISA') +local builtin_var_h = '%' * (S('+-!') + '^' * S('H')^-1 + 'ENV' + 'INC' + 'SIG') +lex:add_rule('variable_builtin', + lex:tag(lexer.VARIABLE_BUILTIN, builtin_var_s + builtin_var_a + builtin_var_h)) +local special_var = '$' * + ('^' * S('ADEFHILMOPSTWX')^-1 + S('\\"[]\'&`+*.,;=%~?@<>(|/!-') + ':' * (lexer.any - ':') + + (P('$') * -lexer.word) + lexer.digit^1) local plain_var = ('$#' + S('$@%')) * P('$')^0 * lexer.word + '$#' -lex:add_rule('variable', token(lexer.VARIABLE, special_var + plain_var)) +lex:add_rule('variable', lex:tag(lexer.VARIABLE, special_var + plain_var)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('-<>+*!~\\=/%&|^.,?:;()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('-<>+*!~\\=/%&|^.,?:;()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'STDIN', 'STDOUT', 'STDERR', 'BEGIN', 'END', 'CHECK', 'INIT', -- + 'require', 'use', -- + 'break', 'continue', 'do', 'each', 'else', 'elsif', 'foreach', 'for', 'if', 'last', 'local', 'my', + 'next', 'our', 'package', 'return', 'sub', 'unless', 'until', 'while', '__FILE__', '__LINE__', + '__PACKAGE__', -- + 'and', 'or', 'not', 'eq', 'ne', 'lt', 'gt', 'le', 'ge' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'abs', 'accept', 'alarm', 'atan2', 'bind', 'binmode', 'bless', 'caller', 'chdir', 'chmod', + 'chomp', 'chop', 'chown', 'chr', 'chroot', 'closedir', 'close', 'connect', 'cos', 'crypt', + 'dbmclose', 'dbmopen', 'defined', 'delete', 'die', 'dump', 'each', 'endgrent', 'endhostent', + 'endnetent', 'endprotoent', 'endpwent', 'endservent', 'eof', 'eval', 'exec', 'exists', 'exit', + 'exp', 'fcntl', 'fileno', 'flock', 'fork', 'format', 'formline', 'getc', 'getgrent', 'getgrgid', + 'getgrnam', 'gethostbyaddr', 'gethostbyname', 'gethostent', 'getlogin', 'getnetbyaddr', + 'getnetbyname', 'getnetent', 'getpeername', 'getpgrp', 'getppid', 'getpriority', 'getprotobyname', + 'getprotobynumber', 'getprotoent', 'getpwent', 'getpwnam', 'getpwuid', 'getservbyname', + 'getservbyport', 'getservent', 'getsockname', 'getsockopt', 'glob', 'gmtime', 'goto', 'grep', + 'hex', 'import', 'index', 'int', 'ioctl', 'join', 'keys', 'kill', 'lcfirst', 'lc', 'length', + 'link', 'listen', 'localtime', 'log', 'lstat', 'map', 'mkdir', 'msgctl', 'msgget', 'msgrcv', + 'msgsnd', 'new', 'oct', 'opendir', 'open', 'ord', 'pack', 'pipe', 'pop', 'pos', 'printf', 'print', + 'prototype', 'push', 'quotemeta', 'rand', 'readdir', 'read', 'readlink', 'recv', 'redo', 'ref', + 'rename', 'reset', 'reverse', 'rewinddir', 'rindex', 'rmdir', 'scalar', 'seekdir', 'seek', + 'select', 'semctl', 'semget', 'semop', 'send', 'setgrent', 'sethostent', 'setnetent', 'setpgrp', + 'setpriority', 'setprotoent', 'setpwent', 'setservent', 'setsockopt', 'shift', 'shmctl', 'shmget', + 'shmread', 'shmwrite', 'shutdown', 'sin', 'sleep', 'socket', 'socketpair', 'sort', 'splice', + 'split', 'sprintf', 'sqrt', 'srand', 'stat', 'study', 'substr', 'symlink', 'syscall', 'sysread', + 'sysseek', 'system', 'syswrite', 'telldir', 'tell', 'tied', 'tie', 'time', 'times', 'truncate', + 'ucfirst', 'uc', 'umask', 'undef', 'unlink', 'unpack', 'unshift', 'untie', 'utime', 'values', + 'vec', 'wait', 'waitpid', 'wantarray', 'warn', 'write' +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'ARGV', 'ARGVOUT', 'DATA', 'ENV', 'INC', 'SIG', 'STDERR', 'STDIN', 'STDOUT' +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/php.lua b/lua/lexers/php.lua index f1fbda7..c5a7e2e 100644 --- a/lua/lexers/php.lua +++ b/lua/lexers/php.lua @@ -1,60 +1,31 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- PHP LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('php') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - -- Reserved words (http://php.net/manual/en/reserved.keywords.php) - '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', - 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', - 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', - 'exit', 'extends', 'final', 'finally', 'fn', 'for', 'foreach', 'function', 'global', 'goto', 'if', - 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', - 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', - 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', - 'yield', 'from', - -- Reserved classes (http://php.net/manual/en/reserved.classes.php) - 'Directory', 'stdClass', '__PHP_Incomplete_Class', 'Exception', 'ErrorException', - 'php_user_filter', 'Closure', 'Generator', 'ArithmeticError', 'AssertionError', - 'DivisionByZeroError', 'Error', 'Throwable', 'ParseError', 'TypeError', 'self', 'static', 'parent' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Types. -lex:add_rule('type', token(lexer.TYPE, - word_match('int float bool string true false null void iterable object'))) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) + +-- Functions. +local word = (lexer.alpha + '_' + lpeg.R('\127\255')) * (lexer.alnum + '_' + lpeg.R('\127\255'))^0 +local func = lex:tag(lexer.FUNCTION, word) +local method = lpeg.B('->') * lex:tag(lexer.FUNCTION_METHOD, word) +lex:add_rule('function', (method + func) * #(lexer.space^0 * '(')) -- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match{ - -- Compile-time (https://www.php.net/manual/en/reserved.keywords.php) - '__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__', '__LINE__', '__METHOD__', '__NAMESPACE__', - '__TRAIT__', - -- Reserved (https://www.php.net/manual/en/reserved.constants.php) - 'PHP_VERSION', 'PHP_MAJOR_VERSION', 'PHP_MINOR_VERSION', 'PHP_RELEASE_VERSION', 'PHP_VERSION_ID', - 'PHP_EXTRA_VERSION', 'PHP_ZTS', 'PHP_DEBUG', 'PHP_MAXPATHLEN', 'PHP_OS', 'PHP_OS_FAMILY', - 'PHP_SAPI', 'PHP_EOL', 'PHP_INT_MAX', 'PHP_INT_MIN', 'PHP_INT_SIZE', 'PHP_FLOAT_DIG', - 'PHP_FLOAT_EPSILON', 'PHP_FLOAT_MIN', 'PHP_FLOAT_MAX', 'DEFAULT_INCLUDE_PATH', 'PEAR_INSTALL_DIR', - 'PEAR_EXTENSION_DIR', 'PHP_EXTENSION_DIR', 'PHP_PREFIX', 'PHP_BINDIR', 'PHP_BINARY', 'PHP_MANDIR', - 'PHP_LIBDIR', 'PHP_DATADIR', 'PHP_SYSCONFDIR', 'PHP_LOCALSTATEDIR', 'PHP_CONFIG_FILE_PATH', - 'PHP_CONFIG_FILE_SCAN_DIR', 'PHP_SHLIB_SUFFIX', 'PHP_FD_SETSIZE', 'E_ERROR', 'E_WARNING', - 'E_PARSE', 'E_NOTICE', 'E_CORE_ERROR', 'E_CORE_WARNING', 'E_COMPILE_ERROR', 'E_USER_ERROR', - 'E_USER_WARNING', 'E_USER_NOTICE', 'E_DEPRECATED', 'E_DEPRECATED', 'E_USER_DEPRECATED', 'E_ALL', - 'E_STRICT', '__COMPILER_HALT_OFFSET__' -})) +lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN))) -- Identifiers. -local word = (lexer.alpha + '_' + lpeg.R('\127\255')) * (lexer.alnum + '_' + lpeg.R('\127\255'))^0 -lex:add_rule('identifier', token(lexer.IDENTIFIER, word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, word)) -- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, '$' * word)) +lex:add_rule('variable', lex:tag(lexer.VARIABLE, '$' * word)) -- Strings. local sq_str = lexer.range("'") @@ -63,39 +34,74 @@ local bq_str = lexer.range('`') local heredoc = '<<<' * P(function(input, index) local _, e, delimiter = input:find('([%a_][%w_]*)[\n\r\f]+', index) if delimiter then - e = select(2, input:find('[\n\r\f]+' .. delimiter, e)) + _, e = input:find('[\n\r\f]+' .. delimiter, e) return e and e + 1 end end) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str + bq_str + heredoc)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + bq_str + heredoc)) -- TODO: interpolated code. -- Comments. local line_comment = lexer.to_eol(P('//') + '#') local block_comment = lexer.range('/*', '*/') -lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('!@%^*&()-+=|/?.,;:<>[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('!@%^*&()-+=|/?.,;:<>[]{}'))) -- Embedded in HTML. local html = lexer.load('html') -- Embedded PHP. -local php_start_rule = token('php_tag', '<?' * ('php' * lexer.space)^-1) -local php_end_rule = token('php_tag', '?>') +local php_start_rule = lex:tag(lexer.PREPROCESSOR, '<?' * ('php' * lexer.space)^-1) +local php_end_rule = lex:tag(lexer.PREPROCESSOR, '?>') html:embed(lex, php_start_rule, php_end_rule) -lex:add_style('php_tag', lexer.styles.embedded) -- Fold points. -lex:add_fold_point('php_tag', '<?', '?>') +lex:add_fold_point(lexer.PREPROCESSOR, '<?', '?>') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.OPERATOR, '(', ')') +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + -- Reserved words (http://php.net/manual/en/reserved.keywords.php) + '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', + 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', + 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', + 'exit', 'extends', 'final', 'finally', 'fn', 'for', 'foreach', 'function', 'global', 'goto', 'if', + 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', + 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', + 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', + 'yield', 'from', + -- Reserved classes (http://php.net/manual/en/reserved.classes.php) + 'Directory', 'stdClass', '__PHP_Incomplete_Class', 'Exception', 'ErrorException', + 'php_user_filter', 'Closure', 'Generator', 'ArithmeticError', 'AssertionError', + 'DivisionByZeroError', 'Error', 'Throwable', 'ParseError', 'TypeError', 'self', 'static', 'parent' +}) + +lex:set_word_list(lexer.TYPE, 'int float bool string true false null void iterable object') + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + -- Compile-time (https://www.php.net/manual/en/reserved.keywords.php) + '__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__', '__LINE__', '__METHOD__', '__NAMESPACE__', + '__TRAIT__', + -- Reserved (https://www.php.net/manual/en/reserved.constants.php) + 'PHP_VERSION', 'PHP_MAJOR_VERSION', 'PHP_MINOR_VERSION', 'PHP_RELEASE_VERSION', 'PHP_VERSION_ID', + 'PHP_EXTRA_VERSION', 'PHP_ZTS', 'PHP_DEBUG', 'PHP_MAXPATHLEN', 'PHP_OS', 'PHP_OS_FAMILY', + 'PHP_SAPI', 'PHP_EOL', 'PHP_INT_MAX', 'PHP_INT_MIN', 'PHP_INT_SIZE', 'PHP_FLOAT_DIG', + 'PHP_FLOAT_EPSILON', 'PHP_FLOAT_MIN', 'PHP_FLOAT_MAX', 'DEFAULT_INCLUDE_PATH', 'PEAR_INSTALL_DIR', + 'PEAR_EXTENSION_DIR', 'PHP_EXTENSION_DIR', 'PHP_PREFIX', 'PHP_BINDIR', 'PHP_BINARY', 'PHP_MANDIR', + 'PHP_LIBDIR', 'PHP_DATADIR', 'PHP_SYSCONFDIR', 'PHP_LOCALSTATEDIR', 'PHP_CONFIG_FILE_PATH', + 'PHP_CONFIG_FILE_SCAN_DIR', 'PHP_SHLIB_SUFFIX', 'PHP_FD_SETSIZE', 'E_ERROR', 'E_WARNING', + 'E_PARSE', 'E_NOTICE', 'E_CORE_ERROR', 'E_CORE_WARNING', 'E_COMPILE_ERROR', 'E_USER_ERROR', + 'E_USER_WARNING', 'E_USER_NOTICE', 'E_DEPRECATED', 'E_DEPRECATED', 'E_USER_DEPRECATED', 'E_ALL', + 'E_STRICT', '__COMPILER_HALT_OFFSET__' +}) + +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/pico8.lua b/lua/lexers/pico8.lua index 03f260e..760dc4f 100644 --- a/lua/lexers/pico8.lua +++ b/lua/lexers/pico8.lua @@ -1,37 +1,35 @@ --- Copyright 2016-2022 Alejandro Baez (https://keybase.io/baez). See LICENSE. +-- Copyright 2016-2024 Alejandro Baez (https://keybase.io/baez). See LICENSE. -- PICO-8 lexer. -- http://www.lexaloffle.com/pico-8.php -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local word_match = lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('pico8') - --- Whitespace -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords lex:add_rule('keyword', - token(lexer.KEYWORD, word_match('__lua__ __gfx__ __gff__ __map__ __sfx__ __music__'))) + lex:tag(lexer.KEYWORD, lexer.word_match('__gff__ __map__ __sfx__ __music__'))) -- Identifiers -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('//', true))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('//', true))) -- Numbers -lex:add_rule('number', token(lexer.NUMBER, lexer.integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.integer)) -- Operators -lex:add_rule('operator', token(lexer.OPERATOR, '_')) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, '_')) -- Embed Lua into PICO-8. local lua = lexer.load('lua') -local lua_start_rule = token('pico8_tag', '__lua__') -local lua_end_rule = token('pico8_tag', '__gfx__') +local lua_start_rule = lex:tag(lexer.KEYWORD, word_match('__lua__')) +local lua_end_rule = lex:tag(lexer.KEYWORD, word_match('__gfx__')) lex:embed(lua, lua_start_rule, lua_end_rule) -lex:add_style('pico8_tag', lexer.styles.embedded) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/pike.lua b/lua/lexers/pike.lua index 79efd53..7d48f2f 100644 --- a/lua/lexers/pike.lua +++ b/lua/lexers/pike.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Pike LPeg lexer. local lexer = require('lexer') @@ -48,6 +48,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('<>=!+-/*%&|^~@`.,:;()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/pkgbuild.lua b/lua/lexers/pkgbuild.lua index eae3703..ac4a17a 100644 --- a/lua/lexers/pkgbuild.lua +++ b/lua/lexers/pkgbuild.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 gwash. See LICENSE. +-- Copyright 2006-2024 gwash. See LICENSE. -- Archlinux PKGBUILD LPeg lexer. local lexer = require('lexer') @@ -73,6 +73,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=!<>+-/*^~.,:;?()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/pony.lua b/lua/lexers/pony.lua index b02f2a7..9782db1 100644 --- a/lua/lexers/pony.lua +++ b/lua/lexers/pony.lua @@ -1,4 +1,4 @@ --- Copyright 2017-2022 Murray Calavera. See LICENSE. +-- Copyright 2017-2024 Murray Calavera. See LICENSE. -- Pony LPeg lexer. local lexer = require('lexer') @@ -91,4 +91,6 @@ lex:add_rule('punctuation', -- Qualifiers. lex:add_rule('qualifier', token(lexer.LABEL, '#' * word_match('read send share any alias'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/powershell.lua b/lua/lexers/powershell.lua index f499cdb..5ed99bd 100644 --- a/lua/lexers/powershell.lua +++ b/lua/lexers/powershell.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 Mitchell. See LICENSE. +-- Copyright 2015-2024 Mitchell. See LICENSE. -- PowerShell LPeg lexer. -- Contributed by Jeff Stone. @@ -57,4 +57,6 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=!<>+-/*^&|~.,:;?()[]{}%`'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/prolog.lua b/lua/lexers/prolog.lua index 53e25c8..b595fcc 100644 --- a/lua/lexers/prolog.lua +++ b/lua/lexers/prolog.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Lexer enhanced to conform to the realities of Prologs on the ground by -- Michael T. Richter. Copyright is explicitly assigned back to Mitchell. -- Prolog LPeg lexer. @@ -30,9 +30,8 @@ local P, S = lpeg.P, lpeg.S local lex = lexer.new('prolog') -local dialects = setmetatable({gprolog = 'gprolog', swipl = 'swipl'}, - {__index = function(_, _) return 'iso' end}) -local dialect = dialects[lexer.property['prolog.dialect']] +local dialect = lexer.property['prolog.dialect'] +if dialect ~= 'gprolog' and dialog ~= 'swipl' then dialect = 'iso' end -- Directives. local directives = {} @@ -350,4 +349,6 @@ local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/props.lua b/lua/lexers/props.lua index a7b6723..b926f96 100644 --- a/lua/lexers/props.lua +++ b/lua/lexers/props.lua @@ -1,32 +1,36 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Props LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('props', {lex_by_line = true}) +local lex = lexer.new(..., {lex_by_line = true}) --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +-- Identifiers. +lex:add_rule('identifier', + lex:tag(lexer.IDENTIFIER, (lexer.alpha + S('.-_')) * (lexer.alnum + S('.-_')^0))) -- Colors. local xdigit = lexer.xdigit -lex:add_rule('color', token('color', '#' * xdigit * xdigit * xdigit * xdigit * xdigit * xdigit)) -lex:add_style('color', lexer.styles.number) +lex:add_rule('color', + lex:tag(lexer.NUMBER, '#' * xdigit * xdigit * xdigit * xdigit * xdigit * xdigit)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) -- Equals. -lex:add_rule('equals', token(lexer.OPERATOR, '=')) +lex:add_rule('equals', lex:tag(lexer.OPERATOR, '=')) -- Strings. local sq_str = lexer.range("'") local dq_str = lexer.range('"') -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, '$' * lexer.range('(', ')', true))) +lex:add_rule('variable', + lex:tag(lexer.OPERATOR, '$(') * lex:tag(lexer.VARIABLE, (lexer.nonnewline - lexer.space - ')')^0) * + lex:tag(lexer.OPERATOR, ')')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/protobuf.lua b/lua/lexers/protobuf.lua index df08d9f..c3c4368 100644 --- a/lua/lexers/protobuf.lua +++ b/lua/lexers/protobuf.lua @@ -1,4 +1,4 @@ --- Copyright 2016-2022 David B. Lamkins <david@lamkins.net>. See LICENSE. +-- Copyright 2016-2024 David B. Lamkins <david@lamkins.net>. See LICENSE. -- Protocol Buffer IDL LPeg lexer. -- <https://developers.google.com/protocol-buffers/> @@ -43,4 +43,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('<>=|;,.()[]{}'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/ps.lua b/lua/lexers/ps.lua index 4161539..1e2213f 100644 --- a/lua/lexers/ps.lua +++ b/lua/lexers/ps.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Postscript LPeg lexer. local lexer = require('lexer') @@ -44,4 +44,6 @@ lex:add_rule('label', token(lexer.LABEL, '/' * word)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('[]{}'))) +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/pure.lua b/lua/lexers/pure.lua index 35e8a48..69067aa 100644 --- a/lua/lexers/pure.lua +++ b/lua/lexers/pure.lua @@ -1,4 +1,4 @@ --- Copyright 2015-2022 David B. Lamkins <david@lamkins.net>. See LICENSE. +-- Copyright 2015-2024 David B. Lamkins <david@lamkins.net>. See LICENSE. -- pure LPeg lexer, see http://purelang.bitbucket.org/ local lexer = require('lexer') @@ -45,4 +45,6 @@ lex:add_rule('pragma', token(lexer.PREPROCESSOR, hashbang)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, '..' + S('+-/*%<>~!=^&|?~:;,.()[]{}@#$`\\\''))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/python.lua b/lua/lexers/python.lua index 901ccba..b870502 100644 --- a/lua/lexers/python.lua +++ b/lua/lexers/python.lua @@ -1,106 +1,128 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Python LPeg lexer. -local lexer = require('lexer') +local lexer = lexer local token, word_match = lexer.token, lexer.word_match -local P, S = lpeg.P, lpeg.S +local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('python', {fold_by_indentation = true}) - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(..., {fold_by_indentation = true}) -- Classes. -lex:add_rule('classdef', token(lexer.KEYWORD, 'class') * ws * token(lexer.CLASS, lexer.word)) +lex:add_rule('classdef', lex:tag(lexer.KEYWORD, 'class') * lex:get_rule('whitespace') * + lex:tag(lexer.CLASS, lexer.word)) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'and', 'as', 'assert', 'async', 'await', 'break', 'continue', 'def', 'del', 'elif', 'else', - 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', - 'nonlocal', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield', - -- Descriptors/attr access. - '__get__', '__set__', '__delete__', '__slots__', - -- Class. - '__new__', '__init__', '__del__', '__repr__', '__str__', '__cmp__', '__index__', '__lt__', - '__le__', '__gt__', '__ge__', '__eq__', '__ne__', '__hash__', '__nonzero__', '__getattr__', - '__getattribute__', '__setattr__', '__delattr__', '__call__', - -- Operator. - '__add__', '__sub__', '__mul__', '__div__', '__floordiv__', '__mod__', '__divmod__', '__pow__', - '__and__', '__xor__', '__or__', '__lshift__', '__rshift__', '__nonzero__', '__neg__', '__pos__', - '__abs__', '__invert__', '__iadd__', '__isub__', '__imul__', '__idiv__', '__ifloordiv__', - '__imod__', '__ipow__', '__iand__', '__ixor__', '__ior__', '__ilshift__', '__irshift__', - -- Conversions. - '__int__', '__long__', '__float__', '__complex__', '__oct__', '__hex__', '__coerce__', - -- Containers. - '__len__', '__getitem__', '__missing__', '__setitem__', '__delitem__', '__contains__', '__iter__', - '__getslice__', '__setslice__', '__delslice__', - -- Module and class attribs. - '__doc__', '__name__', '__dict__', '__file__', '__path__', '__module__', '__bases__', '__class__', - '__self__', - -- Stdlib/sys. - '__builtin__', '__future__', '__main__', '__import__', '__stdin__', '__stdout__', '__stderr__', - -- Other. - '__debug__', '__doc__', '__import__', '__name__' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD)) + + lex:tag(lexer.KEYWORD .. '.soft', lex:word_match(lexer.KEYWORD .. '.soft'))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'abs', 'all', 'any', 'apply', 'basestring', 'bool', 'buffer', 'callable', 'chr', 'classmethod', - 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', - 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'frozenset', 'getattr', - 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', - 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', - 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', - 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', - 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip' -})) +local builtin_func = -B('.') * + lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +local special_func = lex:tag(lexer.FUNCTION_BUILTIN .. '.special', + lex:word_match(lexer.FUNCTION_BUILTIN .. '.special')) +local func = lex:tag(lexer.FUNCTION, lexer.word) +local method = B('.') * lex:tag(lexer.FUNCTION_METHOD, lexer.word) +lex:add_rule('function', (builtin_func + special_func + method + func) * #(lexer.space^0 * '(')) -- Constants. -lex:add_rule('constant', token(lexer.CONSTANT, word_match{ - 'ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'DeprecationWarning', - 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', - 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', - 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', - 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', - 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', - 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', - 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', - 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', - 'ZeroDivisionError' -})) - --- Self. -lex:add_rule('self', token('self', 'self')) -lex:add_style('self', lexer.styles.type) +local builtin_const = lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN)) +local attr = lex:tag(lexer.ATTRIBUTE, B('.') * lex:word_match(lexer.ATTRIBUTE) + '__name__') +lex:add_rule('constant', builtin_const + attr) + +-- Strings. +local sq_str = lexer.range("'", true) +local dq_str = lexer.range('"', true) +local tq_str = lexer.range("'''") + lexer.range('"""') +lex:add_rule('string', lex:tag(lexer.STRING, (S('fFrRbBrR') * S('rRfFrRbB') + S('ruRUfFbB'))^-1 * + (tq_str + sq_str + dq_str))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#', true))) - --- Strings. -local sq_str = P('u')^-1 * lexer.range("'", true) -local dq_str = P('U')^-1 * lexer.range('"', true) -local tq_str = lexer.range("'''") + lexer.range('"""') --- TODO: raw_strs cannot end in single \. -local raw_sq_str = P('u')^-1 * 'r' * lexer.range("'", false, false) -local raw_dq_str = P('U')^-1 * 'R' * lexer.range('"', false, false) -lex:add_rule('string', token(lexer.STRING, tq_str + sq_str + dq_str + raw_sq_str + raw_dq_str)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#', true))) -- Numbers. -local dec = lexer.dec_num * S('Ll')^-1 -local bin = '0b' * S('01')^1 * ('_' * S('01')^1)^0 -local oct = lexer.oct_num * S('Ll')^-1 -local integer = S('+-')^-1 * (bin + lexer.hex_num + oct + dec) -lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number_('_') * S('jJ')^-1)) -- Decorators. -lex:add_rule('decorator', token('decorator', lexer.to_eol('@'))) -lex:add_style('decorator', lexer.styles.preprocessor) +lex:add_rule('decorator', lex:tag(lexer.ANNOTATION, '@' * lexer.word)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('!%^&*()[]{}-=+/|:;.,?<>~`'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('!@%^&*()[]{}-=+/|:;.,<>~'))) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', + 'else', 'except', 'False', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', + 'lambda', 'None', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'True', 'try', 'while', + 'with', 'yield' +}) + +lex:set_word_list(lexer.KEYWORD .. '.soft', '_ case match') + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'abs', 'aiter', 'all', 'any', 'anext', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', + 'callable', 'chr', 'classmethod', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', + 'enumerate', 'eval', 'exec', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', + 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', + 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', + 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', + 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip', '__import__' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN .. '.special', { + '__new__', '__init__', '__del__', '__repr__', '__str__', '__bytes', '__format__', '__lt__', + '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__hash__', '__bool__', -- + '__getattr__', '__getattribute__', '__setattr__', '__delattr__', '__dir__', -- + '__get__', '__set__', '__delete__', '__slots__', -- + '__init_subclass__', '__set_name__', -- + '__instancecheck__', '__subclasscheck__', -- + '__class_getitem__', -- + '__call__', -- + '__len__', '__length_hint', '__getitem__', '__setitem__', '__delitem__', '__missing__', + '__iter__', '__reversed__', '__contains__', -- + '__add__', '__sub__', '__mul__', '__matmul__', '__truediv__', '__floordiv__', '__mod__', + '__divmod__', '__pow__', '__lshift__', '__rshift__', '__and__', '__xor__', '__or__', -- + '__radd__', '__rsub__', '__rmul__', '__rmatmul__', '__rtruediv__', '__rfloordiv__', '__rmod__', + '__rdivmod__', '__rpow__', '__rlshift__', '__rrshift__', '__rand__', '__rxor__', '__ror__', -- + '__iadd__', '__isub__', '__imul__', '__imatmul__', '__itruediv__', '__ifloordiv__', '__imod__', + '__idivmod__', '__ipow__', '__ilshift__', '__irshift__', '__iand__', '__ixor__', '__ior__', -- + '__neg__', '__pos__', '__abs__', '__invert__', '__complex__', '__int__', '__float__', '__index__', + '__round__', '__trunc__', '__floor__', '__ceil__', -- + '__enter__', '__exit__', -- + '__match_args__', -- + '__await__', -- + '__aiter__', '__anext__', '__aenter__', '__aexit__' -- +}) + +lex:set_word_list(lexer.CONSTANT_BUILTIN, { + 'BaseException', 'Exception', 'Exception', 'ArithmeticError', 'BufferError', 'LookupError', -- + 'AssertionError', 'AttributeError', 'EOFError', 'FloatingPointError', 'GeneratorExit', + 'ImportError', 'ModuleNotFoundError', 'IndexError', 'KeyError', 'KeyboardInterrupt', + 'MemoryError', 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 'RecursionError', + 'ReferenceError', 'RuntimeError', 'StopIteration', 'StopAsyncIteration', 'SyntaxError', + 'IndentationError', 'TabError', 'SystemError', 'SystemExit', 'TypeError', 'UnboundLocalError', + 'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'ValueError', + 'ZeroDivisionError', -- + 'EnvironmentError', 'IOError', 'WindowsError', -- + 'BlockingIOError', 'ChildProcessError', 'ConnectionError', 'BrokenPipeError', + 'ConnectionAbortedError', 'ConnectionRefusedError', 'FileExistsError', 'FileNotFoundError', + 'InterruptedError', 'IsADirectoryError', 'NotADirectoryError', 'PermissionError', + 'ProcessLookupError', 'TimeoutError', -- + 'Warning', 'UserWarning', 'DeprecationWarning', 'PendingDeprecationWarning', 'SyntaxWarning', + 'RuntimeWarning', 'FutureWarning', 'ImportWarning', 'UnicodeWarning', 'BytesWarning', + 'ResourceWarning' +}) + +lex:set_word_list(lexer.ATTRIBUTE, { + '__doc__', '__name__', '__qualname__', '__module__', '__defaults__', '__code__', '__globals__', + '__dict__', '__closure__', '__annotations__', '__kwdefaults__', -- + '__file__', '__bases__', -- + '__class__', -- + '__self__', '__func__' -- +}) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/rails.lua b/lua/lexers/rails.lua index 9f750c6..7601127 100644 --- a/lua/lexers/rails.lua +++ b/lua/lexers/rails.lua @@ -1,17 +1,13 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Ruby on Rails LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('rails', {inherit = lexer.load('ruby')}) +local lex = lexer.new(..., {inherit = lexer.load('ruby')}) --- Whitespace -lex:modify_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) - --- Functions. -lex:modify_rule('function', token(lexer.FUNCTION, word_match{ +-- Word lists. +lex:set_word_list(lexer.FUNCTION_BUILTIN, { -- ActionPack. 'before_filter', 'skip_before_filter', 'skip_after_filter', 'after_filter', 'around_filter', 'filter', 'filter_parameter_logging', 'layout', 'require_dependency', 'render', 'render_action', @@ -38,6 +34,8 @@ lex:modify_rule('function', token(lexer.FUNCTION, word_match{ -- ActiveSupport. 'alias_method_chain', 'alias_attribute', 'delegate', 'cattr_accessor', 'mattr_accessor', 'returning', 'memoize' -}) + lex:get_rule('function')) +}, true) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/rc.lua b/lua/lexers/rc.lua index 7fb0ade..2e1cfd0 100644 --- a/lua/lexers/rc.lua +++ b/lua/lexers/rc.lua @@ -1,4 +1,4 @@ --- Copyright 2017-2022 Michael Forney. See LICENSE. +-- Copyright 2017-2024 Michael Forney. See LICENSE. -- rc LPeg lexer. local lexer = require('lexer') @@ -46,6 +46,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('@`=!<>*&^|;?()[]{}') + '\\\n') -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/reason.lua b/lua/lexers/reason.lua index 4607c25..a1eb3ac 100644 --- a/lua/lexers/reason.lua +++ b/lua/lexers/reason.lua @@ -1,4 +1,4 @@ --- Copyright 2018-2022 Hugo O. Rivera. See LICENSE. +-- Copyright 2018-2024 Hugo O. Rivera. See LICENSE. -- Reason (https://reasonml.github.io/) LPeg lexer. local lexer = require('lexer') @@ -62,4 +62,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('=<>+-*/.,:;~!#%^&|?[](){}'))) +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/rebol.lua b/lua/lexers/rebol.lua index 4ece279..e815758 100644 --- a/lua/lexers/rebol.lua +++ b/lua/lexers/rebol.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Rebol LPeg lexer. local lexer = require('lexer') @@ -96,7 +96,8 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=<>+/*:()[]'))) -- Fold points. lex:add_fold_point(lexer.COMMENT, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines(';')) lex:add_fold_point(lexer.OPERATOR, '[', ']') +lexer.property['scintillua.comment'] = ';' + return lex diff --git a/lua/lexers/rest.lua b/lua/lexers/rest.lua index e7bf467..9340fc2 100644 --- a/lua/lexers/rest.lua +++ b/lua/lexers/rest.lua @@ -1,54 +1,14 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- reStructuredText LPeg lexer. -local l = require('lexer') -local token, word_match, starts_line = l.token, l.word_match, l.starts_line +local lexer = require('lexer') +local token, word_match, starts_line = lexer.token, lexer.word_match, lexer.starts_line local P, S = lpeg.P, lpeg.S -local M = {_NAME = 'rest'} - --- Whitespace. -local ws = token(l.WHITESPACE, S(' \t')^1 + l.newline^1) -local any_indent = S(' \t')^0 - --- Section titles (2 or more characters). -local adornment_chars = lpeg.C(S('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~')) -local adornment = lpeg.C(adornment_chars^2 * any_indent) * (l.newline + -1) -local overline = lpeg.Cmt(starts_line(adornment), function(input, index, adm, c) - if not adm:find('^%' .. c .. '+%s*$') then return nil end - local rest = input:sub(index) - local lines = 1 - for line, e in rest:gmatch('([^\r\n]+)()') do - if lines > 1 and line:match('^(%' .. c .. '+)%s*$') == adm then - return index + e - 1 - end - if lines > 3 or #line > #adm then return nil end - lines = lines + 1 - end - return #input + 1 -end) -local underline = lpeg.Cmt(starts_line(adornment), function(_, index, adm, c) - local pos = adm:match('^%' .. c .. '+%s*()$') - return pos and index - #adm + pos - 1 or nil -end) --- Token needs to be a predefined one in order for folder to work. -local title = token(l.CONSTANT, overline + underline) - --- Lists. -local bullet_list = S('*+-') -- TODO: '•‣⁃', as lpeg does not support UTF-8 -local enum_list = P('(')^-1 * - (l.digit^1 + S('ivxlcmIVXLCM')^1 + l.alnum + '#') * S('.)') -local field_list = ':' * (l.any - ':')^1 * P(':')^-1 -local option_word = l.alnum * (l.alnum + '-')^0 -local option = S('-/') * option_word * (' ' * option_word)^-1 + - '--' * option_word * ('=' * option_word)^-1 -local option_list = option * (',' * l.space^1 * option)^-1 -local list = #(l.space^0 * (S('*+-:/') + enum_list)) * - starts_line(token('list', l.space^0 * - (option_list + bullet_list + enum_list + field_list) * l.space)) +local lex = lexer.new('rest') -- Literal block. -local block = P('::') * (l.newline + -1) * function(input, index) +local block = '::' * (lexer.newline + -1) * function(input, index) local rest = input:sub(index) local level, quote = #rest:match('^([ \t]*)') for pos, indent, line in rest:gmatch('()[ \t]*()([^\r\n]+)') do @@ -59,33 +19,61 @@ local block = P('::') * (l.newline + -1) * function(input, index) end return #input + 1 end -local literal_block = token('literal_block', block) +lex:add_rule('literal_block', token('literal_block', block)) +lex:add_style('literal_block', lexer.styles.embedded .. {eolfilled = true}) --- Line block. -local line_block_char = token(l.OPERATOR, starts_line(any_indent * '|')) +-- Lists. +local option_word = lexer.alnum * (lexer.alnum + '-')^0 +local option = S('-/') * option_word * (' ' * option_word)^-1 + + ('--' * option_word * ('=' * option_word)^-1) +local option_list = option * (',' * lexer.space^1 * option)^-1 +local bullet_list = S('*+-') -- TODO: '•‣⁃', as lpeg does not support UTF-8 +local enum_list = P('(')^-1 * (lexer.digit^1 + S('ivxlcmIVXLCM')^1 + lexer.alnum + '#') * S('.)') +local field_list = ':' * (lexer.any - ':')^1 * P(':')^-1 +lex:add_rule('list', #(lexer.space^0 * (S('*+-:/') + enum_list)) * + starts_line(token(lexer.LIST, + lexer.space^0 * (option_list + bullet_list + enum_list + field_list) * lexer.space))) -local word = l.alpha * (l.alnum + S('-.+'))^0 +local any_indent = S(' \t')^0 +local word = lexer.alpha * (lexer.alnum + S('-.+'))^0 +local prefix = any_indent * '.. ' -- Explicit markup blocks. -local prefix = any_indent * '.. ' -local footnote_label = '[' * (l.digit^1 + '#' * word^-1 + '*') * ']' -local footnote = token('footnote_block', prefix * footnote_label * l.space) +local footnote_label = '[' * (lexer.digit^1 + '#' * word^-1 + '*') * ']' +local footnote = token('footnote_block', prefix * footnote_label * lexer.space) local citation_label = '[' * word * ']' -local citation = token('citation_block', prefix * citation_label * l.space) +local citation = token('citation_block', prefix * citation_label * lexer.space) local link = token('link_block', prefix * '_' * - (l.range('`') + (P('\\') * 1 + l.nonnewline - ':')^1) * ':' * l.space) -local markup_block = #prefix * starts_line(footnote + citation + link) + (lexer.range('`') + (P('\\') * 1 + lexer.nonnewline - ':')^1) * ':' * lexer.space) +lex:add_rule('markup_block', #prefix * starts_line(footnote + citation + link)) +lex:add_style('footnote_block', lexer.styles.label) +lex:add_style('citation_block', lexer.styles.label) +lex:add_style('link_block', lexer.styles.label) + +-- Sphinx code block. +local indented_block = function(input, index) + local rest = input:sub(index) + local level = #rest:match('^([ \t]*)') + for pos, indent, line in rest:gmatch('()[ \t]*()([^\r\n]+)') do + if indent - pos < level and line ~= ' ' or level == 0 and pos > 1 then return index + pos - 1 end + end + return #input + 1 +end +local code_block = + prefix * 'code-block::' * S(' \t')^1 * lexer.nonnewline^0 * (lexer.newline + -1) * indented_block +lex:add_rule('code_block', #prefix * token('code_block', starts_line(code_block))) +lex:add_style('code_block', lexer.styles.embedded .. {eolfilled = true}) -- Directives. -local directive_type = word_match({ +local known_directive = token('directive', prefix * word_match{ -- Admonitions - 'attention', 'caution', 'danger', 'error', 'hint', 'important', 'note', 'tip', - 'warning', 'admonition', + 'attention', 'caution', 'danger', 'error', 'hint', 'important', 'note', 'tip', 'warning', + 'admonition', -- Images 'image', 'figure', -- Body elements - 'topic', 'sidebar', 'line-block', 'parsed-literal', 'code', 'math', 'rubric', - 'epigraph', 'highlights', 'pull-quote', 'compound', 'container', + 'topic', 'sidebar', 'line-block', 'parsed-literal', 'code', 'math', 'rubric', 'epigraph', + 'highlights', 'pull-quote', 'compound', 'container', -- Table 'table', 'csv-table', 'list-table', -- Document parts @@ -97,128 +85,101 @@ local directive_type = word_match({ -- Directives for substitution definitions 'replace', 'unicode', 'date', -- Miscellaneous - 'include', 'raw', 'class', 'role', 'default-role', 'title', - 'restructuredtext-test-directive', -}, '-') -local known_directive = token('directive', prefix * directive_type * '::' * - l.space) -local sphinx_directive_type = word_match({ + 'include', 'raw', 'class', 'role', 'default-role', 'title', 'restructuredtext-test-directive' +} * '::' * lexer.space) +local sphinx_directive = token('sphinx_directive', prefix * word_match{ -- The TOC tree. 'toctree', -- Paragraph-level markup. - 'note', 'warning', 'versionadded', 'versionchanged', 'deprecated', 'seealso', - 'rubric', 'centered', 'hlist', 'glossary', 'productionlist', + 'note', 'warning', 'versionadded', 'versionchanged', 'deprecated', 'seealso', 'rubric', + 'centered', 'hlist', 'glossary', 'productionlist', -- Showing code examples. 'highlight', 'literalinclude', -- Miscellaneous 'sectionauthor', 'index', 'only', 'tabularcolumns' -}, '-') -local sphinx_directive = token('sphinx_directive', prefix * - sphinx_directive_type * '::' * l.space) -local unknown_directive = token('unknown_directive', prefix * word * '::' * - l.space) -local directive = #prefix * starts_line(known_directive + sphinx_directive + - unknown_directive) +} * '::' * lexer.space) +local unknown_directive = token('unknown_directive', prefix * word * '::' * lexer.space) +lex:add_rule('directive', + #prefix * starts_line(known_directive + sphinx_directive + unknown_directive)) +lex:add_style('directive', lexer.styles.keyword) +lex:add_style('sphinx_directive', lexer.styles.keyword .. {bold = true}) +lex:add_style('unknown_directive', lexer.styles.keyword .. {italics = true}) --- Sphinx code block. -local indented_block = function(input, index) +-- Substitution definitions. +lex:add_rule('substitution', #prefix * token('substitution', starts_line(prefix * lexer.range('|') * + lexer.space^1 * word * '::' * lexer.space))) +lex:add_style('substitution', lexer.styles.variable) + +-- Comments. +local line_comment = lexer.to_eol(prefix) +local bprefix = any_indent * '..' +local block_comment = bprefix * lexer.newline * indented_block +lex:add_rule('comment', #bprefix * token(lexer.COMMENT, starts_line(line_comment + block_comment))) + +-- Section titles (2 or more characters). +local adornment_chars = lpeg.C(S('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~')) +local adornment = lpeg.C(adornment_chars^2 * any_indent) * (lexer.newline + -1) +local overline = lpeg.Cmt(starts_line(adornment), function(input, index, adm, c) + if not adm:find('^%' .. c .. '+%s*$') then return nil end local rest = input:sub(index) - local level = #rest:match('^([ \t]*)') - for pos, indent, line in rest:gmatch('()[ \t]*()([^\r\n]+)') do - if indent - pos < level and line ~= ' ' or level == 0 and pos > 1 then - return index + pos - 1 - end + local lines = 1 + for line, e in rest:gmatch('([^\r\n]+)()') do + if lines > 1 and line:match('^(%' .. c .. '+)%s*$') == adm then return index + e - 1 end + if lines > 3 or #line > #adm then return nil end + lines = lines + 1 end return #input + 1 -end -local code_block = prefix * 'code-block::' * S(' \t')^1 * l.nonnewline^0 * - (l.newline + -1) * indented_block -local sphinx_block = #prefix * token('code_block', starts_line(code_block)) +end) +local underline = lpeg.Cmt(starts_line(adornment), function(_, index, adm, c) + local pos = adm:match('^%' .. c .. '+%s*()$') + return pos and index - #adm + pos - 1 or nil +end) +-- Token needs to be a predefined one in order for folder to work. +lex:add_rule('title', token(lexer.HEADING, overline + underline)) --- Substitution definitions. -local substitution = #prefix * token('substitution', - starts_line(prefix * l.range('|') * l.space^1 * word * '::' * l.space)) +-- Line block. +lex:add_rule('line_block_char', token(lexer.OPERATOR, starts_line(any_indent * '|'))) --- Comments. -local line_comment = l.to_eol(prefix) -local bprefix = any_indent * '..' -local block_comment = bprefix * l.newline * indented_block -local comment = #bprefix * token(l.COMMENT, starts_line(line_comment + - block_comment)) +-- Whitespace. +lex:add_rule('whitespace', token(lexer.WHITESPACE, S(' \t')^1 + lexer.newline^1)) -- Inline markup. -local em = token('em', l.range('*')) -local strong = token('strong', l.range('**', '**')) +local strong = token(lexer.BOLD, lexer.range('**')) +local em = token(lexer.ITALIC, lexer.range('*')) +local inline_literal = token('inline_literal', lexer.range('``')) +local postfix_link = (word + lexer.range('`')) * '_' * P('_')^-1 +local prefix_link = '_' * lexer.range('`') +local link_ref = token(lexer.LINK, postfix_link + prefix_link) local role = token('role', ':' * word * ':' * (word * ':')^-1) -local interpreted = role^-1 * token('interpreted', l.range('`')) * role^-1 -local inline_literal = token('inline_literal', l.range('``', '``')) -local postfix_link = (word + l.range('`')) * '_' * P('_')^-1 -local prefix_link = '_' * l.range('`') -local link_ref = token('link', postfix_link + prefix_link) -local footnote_ref = token('footnote', footnote_label * '_') -local citation_ref = token('citation', citation_label * '_') -local substitution_ref = token('substitution', l.range('|', true) * - ('_' * P('_')^-1)^-1) -local link = token('link', l.alpha * (l.alnum + S('-.'))^1 * ':' * - (l.alnum + S('/.+-%@'))^1) -local inline_markup = (strong + em + inline_literal + link_ref + interpreted + - footnote_ref + citation_ref + substitution_ref + link) * -l.alnum +local interpreted = role^-1 * token('interpreted', lexer.range('`')) * role^-1 +local footnote_ref = token(lexer.REFERENCE, footnote_label * '_') +local citation_ref = token(lexer.REFERENCE, citation_label * '_') +local substitution_ref = token('substitution', lexer.range('|', true) * ('_' * P('_')^-1)^-1) +local link = token(lexer.LINK, + lexer.alpha * (lexer.alnum + S('-.'))^1 * ':' * (lexer.alnum + S('/.+-%@'))^1) +lex:add_rule('inline_markup', + (strong + em + inline_literal + link_ref + interpreted + footnote_ref + citation_ref + + substitution_ref + link) * -lexer.alnum) +lex:add_style('inline_literal', lexer.styles.embedded) +lex:add_style('role', lexer.styles.class) +lex:add_style('interpreted', lexer.styles.string) -- Other. -local non_space = token(l.DEFAULT, l.alnum * (l.any - l.space)^0) -local escape = token(l.DEFAULT, '\\' * l.any) - -M._rules = { - {'literal_block', literal_block}, - {'list', list}, - {'markup_block', markup_block}, - {'code_block', sphinx_block}, - {'directive', directive}, - {'substitution', substitution}, - {'comment', comment}, - {'title', title}, - {'line_block_char', line_block_char}, - {'whitespace', ws}, - {'inline_markup', inline_markup}, - {'non_space', non_space}, - {'escape', escape} -} - -M._tokenstyles = { - list = l.STYLE_TYPE, - literal_block = l.STYLE_EMBEDDED .. ',eolfilled', - footnote_block = l.STYLE_LABEL, - citation_block = l.STYLE_LABEL, - link_block = l.STYLE_LABEL, - directive = l.STYLE_KEYWORD, - sphinx_directive = l.STYLE_KEYWORD .. ',bold', - unknown_directive = l.STYLE_KEYWORD .. ',italics', - code_block = l.STYLE_EMBEDDED .. ',eolfilled', - substitution = l.STYLE_VARIABLE, - strong = 'bold', - em = 'italics', - role = l.STYLE_CLASS, - interpreted = l.STYLE_STRING, - inline_literal = l.STYLE_EMBEDDED, - link = 'underlined', - footnote = 'underlined', - citation = 'underlined', -} +lex:add_rule('non_space', token(lexer.DEFAULT, lexer.alnum * (lexer.any - lexer.space)^0)) +lex:add_rule('escape', token(lexer.DEFAULT, '\\' * lexer.any)) +-- Section-based folding. local sphinx_levels = { ['#'] = 0, ['*'] = 1, ['='] = 2, ['-'] = 3, ['^'] = 4, ['"'] = 5 } --- Section-based folding. -M._fold = function(text, start_pos, start_line, start_level) +function lex:fold(text, start_pos, start_line, start_level) local folds, line_starts = {}, {} - for pos in (text .. '\n'):gmatch('().-\r?\n') do - line_starts[#line_starts + 1] = pos - end - local style_at, CONSTANT, level = l.style_at, l.CONSTANT, start_level - local sphinx = l.property_int['fold.by.sphinx.convention'] > 0 - local FOLD_BASE = l.FOLD_BASE - local FOLD_HEADER, FOLD_BLANK = l.FOLD_HEADER, l.FOLD_BLANK + for pos in (text .. '\n'):gmatch('().-\r?\n') do line_starts[#line_starts + 1] = pos end + local style_at, CONSTANT, level = lexer.style_at, lexer.CONSTANT, start_level + local sphinx = lexer.property_int['fold.scintillua.rest.by.sphinx.convention'] > 0 + local FOLD_BASE = lexer.FOLD_BASE + local FOLD_HEADER, FOLD_BLANK = lexer.FOLD_HEADER, lexer.FOLD_BLANK for i = 1, #line_starts do local pos, next_pos = line_starts[i], line_starts[i + 1] local c = text:sub(pos, pos) @@ -237,16 +198,18 @@ M._fold = function(text, start_pos, start_line, start_level) return folds end -l.property['fold.by.sphinx.convention'] = '0' +-- lexer.property['fold.by.sphinx.convention'] = '0' --[[ Embedded languages. -local bash = l.load('bash') +local bash = lexer.load('bash') local bash_indent_level local start_rule = - #(prefix * 'code-block' * '::' * l.space^1 * 'bash' * (l.newline + -1)) * + #(prefix * 'code-block' * '::' * lexer.space^1 * 'bash' * (lexer.newline + -1)) * sphinx_directive * token('bash_begin', P(function(input, index) bash_indent_level = #input:match('^([ \t]*)', index) return index end))]] -return M +lexer.property['scintillua.comment'] = '.. ' + +return lex diff --git a/lua/lexers/rexx.lua b/lua/lexers/rexx.lua index 6766789..044a970 100644 --- a/lua/lexers/rexx.lua +++ b/lua/lexers/rexx.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Rexx LPeg lexer. local lexer = require('lexer') @@ -71,7 +71,8 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=!<>+-/\\*%&|^~.,:;(){}'))) lex:add_fold_point(lexer.KEYWORD, 'do', 'end') lex:add_fold_point(lexer.KEYWORD, 'select', 'return') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('--')) -- lex:add_fold_point(lexer.OPERATOR, ':', ?) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/rhtml.lua b/lua/lexers/rhtml.lua index 16c7706..c738dd9 100644 --- a/lua/lexers/rhtml.lua +++ b/lua/lexers/rhtml.lua @@ -1,20 +1,20 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- RHTML LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('rhtml', {inherit = lexer.load('html')}) +local lex = lexer.new(..., {inherit = lexer.load('html')}) -- Embedded Ruby. local ruby = lexer.load('rails') -local ruby_start_rule = token('rhtml_tag', '<%' * P('=')^-1) -local ruby_end_rule = token('rhtml_tag', '%>') +local ruby_start_rule = lex:tag(lexer.PREPROCESSOR, '<%' * P('=')^-1) +local ruby_end_rule = lex:tag(lexer.PREPROCESSOR, '%>') lex:embed(ruby, ruby_start_rule, ruby_end_rule) -lex:add_style('rhtml_tag', lexer.styles.embedded) -- Fold points. -lex:add_fold_point('rhtml_tag', '<%', '%>') +lex:add_fold_point(lexer.PREPROCESSOR, '<%', '%>') + +lexer.property['scintillua.comment'] = '<!--|-->' return lex diff --git a/lua/lexers/routeros.lua b/lua/lexers/routeros.lua index f301e8b..ceda578 100644 --- a/lua/lexers/routeros.lua +++ b/lua/lexers/routeros.lua @@ -1,4 +1,4 @@ --- Copyright 2020-2022 Christian Hesse. See LICENSE. +-- Copyright 2020-2024 Christian Hesse. See LICENSE. -- Mikrotik RouterOS script LPeg lexer. local lexer = require('lexer') @@ -54,6 +54,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('=!%<>+-/*&|~.,;()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/rpmspec.lua b/lua/lexers/rpmspec.lua new file mode 100644 index 0000000..18086ad --- /dev/null +++ b/lua/lexers/rpmspec.lua @@ -0,0 +1,33 @@ +-- Copyright 2022-2024 Matej Cepl mcepl.att.cepl.eu. See LICENSE. + +local lexer = require('lexer') +local token, word_match = lexer.token, lexer.word_match +local P, S = lpeg.P, lpeg.S + +local lex = lexer.new('rpmspec') + +-- Whitespace. +lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) + +-- Comments. +lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) + +-- Strings. +lex:add_rule('string', token(lexer.STRING, lexer.range('"'))) + +-- Keywords. +lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ + 'Prereq', 'Summary', 'Name', 'Version', 'Packager', 'Requires', 'Recommends', 'Suggests', + 'Supplements', 'Enhances', 'Icon', 'URL', 'Source', 'Patch', 'Prefix', 'Packager', 'Group', + 'License', 'Release', 'BuildRoot', 'Distribution', 'Vendor', 'Provides', 'ExclusiveArch', + 'ExcludeArch', 'ExclusiveOS', 'Obsoletes', 'BuildArch', 'BuildArchitectures', 'BuildRequires', + 'BuildConflicts', 'BuildPreReq', 'Conflicts', 'AutoRequires', 'AutoReq', 'AutoReqProv', + 'AutoProv', 'Epoch' +})) + +-- Macros +lex:add_rule('command', token(lexer.FUNCTION, '%' * lexer.word)) + +lexer.property['scintillua.comment'] = '#' + +return lex diff --git a/lua/lexers/rstats.lua b/lua/lexers/rstats.lua index 165b693..ae3e29e 100644 --- a/lua/lexers/rstats.lua +++ b/lua/lexers/rstats.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- R LPeg lexer. local lexer = require('lexer') @@ -46,6 +46,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('<->+*/^=.,:;|$()[]{}'))) lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/ruby.lua b/lua/lexers/ruby.lua index f6ba415..51334f2 100644 --- a/lua/lexers/ruby.lua +++ b/lua/lexers/ruby.lua @@ -1,42 +1,27 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Ruby LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('ruby') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'BEGIN', 'END', 'alias', 'and', 'begin', 'break', 'case', 'class', 'def', 'defined?', 'do', - 'else', 'elsif', 'end', 'ensure', 'false', 'for', 'if', 'in', 'module', 'next', 'nil', 'not', - 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super', 'then', 'true', 'undef', 'unless', - 'until', 'when', 'while', 'yield', '__FILE__', '__LINE__' -})) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) -- Functions. -lex:add_rule('function', token(lexer.FUNCTION, word_match{ - 'at_exit', 'autoload', 'binding', 'caller', 'catch', 'chop', 'chop!', 'chomp', 'chomp!', 'eval', - 'exec', 'exit', 'exit!', 'extend', 'fail', 'fork', 'format', 'gets', 'global_variables', 'gsub', - 'gsub!', 'include', 'iterator?', 'lambda', 'load', 'local_variables', 'loop', 'module_function', - 'open', 'p', 'print', 'printf', 'proc', 'putc', 'puts', 'raise', 'rand', 'readline', 'readlines', - 'require', 'require_relative', 'select', 'sleep', 'split', 'sprintf', 'srand', 'sub', 'sub!', - 'syscall', 'system', 'test', 'trace_var', 'trap', 'untrace_var' -}) * -S('.:|')) +local builtin_func = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN)) +lex:add_rule('function', -lpeg.B('.') * builtin_func * -S('.:|')) -- Identifiers. local word_char = lexer.alnum + S('_!?') local word = (lexer.alpha + '_') * word_char^0 -lex:add_rule('identifier', token(lexer.IDENTIFIER, word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, word)) -- Comments. local line_comment = lexer.to_eol('#', true) local block_comment = lexer.range(lexer.starts_line('=begin'), lexer.starts_line('=end')) -lex:add_rule('comment', token(lexer.COMMENT, block_comment + line_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, block_comment + line_comment)) -- Strings. local delimiter_matches = {['('] = ')', ['['] = ']', ['{'] = '}'} @@ -66,42 +51,36 @@ local heredoc = '<<' * P(function(input, index) local s, e, indented, _, delimiter = input:find('([%-~]?)(["`]?)([%a_][%w_]*)%2[\n\r\f;]+', index) if s == index and delimiter then local end_heredoc = (#indented > 0 and '[\n\r\f]+ *' or '[\n\r\f]+') - e = select(2, input:find(end_heredoc .. delimiter, e)) + s, e = input:find(end_heredoc .. delimiter, e) return e and e + 1 or #input + 1 end end) -local string = token(lexer.STRING, (sq_str + dq_str + lit_str + heredoc + cmd_str + lit_cmd + +local string = lex:tag(lexer.STRING, (sq_str + dq_str + lit_str + heredoc + cmd_str + lit_cmd + lit_array) * S('f')^-1) -- TODO: regex_str fails with `obj.method /patt/` syntax. -local regex_str = - #P('/') * lexer.last_char_includes('!%^&*([{-=+|:;,?<>~') * lexer.range('/', true) * S('iomx')^0 +local regex_str = lexer.after_set('!%^&*([{-=+|:;,?<>~', lexer.range('/', true) * S('iomx')^0) local lit_regex = '%r' * literal_delimited * S('iomx')^0 -local regex = token(lexer.REGEX, regex_str + lit_regex) +local regex = lex:tag(lexer.REGEX, regex_str + lit_regex) lex:add_rule('string', string + regex) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 * S('ri')^-1 -local bin = '0b' * S('01')^1 * ('_' * S('01')^1)^0 * -lexer.xdigit -local integer = S('+-')^-1 * (bin + lexer.hex_num + lexer.oct_num + dec) --- TODO: meta, control, etc. for numeric_literal. -local numeric_literal = '?' * (lexer.any - lexer.space) * -word_char -lex:add_rule('number', token(lexer.NUMBER, lexer.float * S('ri')^-1 + integer + numeric_literal)) +local numeric_literal = '?' * (lexer.any - lexer.space) * -word_char -- TODO: meta, control, etc. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number_('_') * S('ri')^-1 + numeric_literal)) -- Variables. local global_var = '$' * (word + S('!@L+`\'=~/\\,.;<>_*"$?:') + lexer.digit + '-' * S('0FadiIKlpvw')) local class_var = '@@' * word local inst_var = '@' * word -lex:add_rule('variable', token(lexer.VARIABLE, global_var + class_var + inst_var)) +lex:add_rule('variable', lex:tag(lexer.VARIABLE, global_var + class_var + inst_var)) -- Symbols. -lex:add_rule('symbol', token('symbol', ':' * P(function(input, index) - if input:sub(index - 2, index - 2) ~= ':' then return index end +lex:add_rule('symbol', lex:tag(lexer.STRING .. '.symbol', ':' * P(function(input, index) + if input:sub(index - 2, index - 2) ~= ':' then return true end end) * (word_char^1 + sq_str + dq_str))) -lex:add_style('symbol', lexer.styles.constant) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('!%^&*()[]{}-=+/|:;.,?<>~'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('!%^&*()[]{}-=+/|:;.,?<>~'))) -- Fold points. local function disambiguate(text, pos, line, s) @@ -123,6 +102,26 @@ lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '[', ']') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '=begin', '=end') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'BEGIN', 'END', 'alias', 'and', 'begin', 'break', 'case', 'class', 'def', 'defined?', 'do', + 'else', 'elsif', 'end', 'ensure', 'false', 'for', 'if', 'in', 'module', 'next', 'nil', 'not', + 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super', 'then', 'true', 'undef', 'unless', + 'until', 'when', 'while', 'yield', '__FILE__', '__LINE__' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'at_exit', 'autoload', 'binding', 'caller', 'catch', 'chop', 'chop!', 'chomp', 'chomp!', 'eval', + 'exec', 'exit', 'exit!', 'extend', 'fail', 'fork', 'format', 'gets', 'global_variables', 'gsub', + 'gsub!', 'include', 'iterator?', 'lambda', 'load', 'local_variables', 'loop', 'module_function', + 'open', 'p', 'print', 'printf', 'proc', 'putc', 'puts', 'raise', 'rand', 'readline', 'readlines', + 'require', 'require_relative', 'select', 'sleep', 'split', 'sprintf', 'srand', 'sub', 'sub!', + 'syscall', 'system', 'test', 'trace_var', 'trap', 'untrace_var' +}) + +lexer.property['scintillua.comment'] = '#' +lexer.property['scintillua.word.chars'] = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_?!' return lex diff --git a/lua/lexers/rust.lua b/lua/lexers/rust.lua index 25555d6..9b5b34a 100644 --- a/lua/lexers/rust.lua +++ b/lua/lexers/rust.lua @@ -1,54 +1,23 @@ --- Copyright 2015-2022 Alejandro Baez (https://keybase.io/baez). See LICENSE. +-- Copyright 2015-2024 Alejandro Baez (https://keybase.io/baez). See LICENSE. -- Rust LPeg lexer. -local lexer = require("lexer") -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S local C, Cmt = lpeg.C, lpeg.Cmt -local lex = lexer.new('rust') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Keywords. --- https://github.com/rust-lang/rust/blob/stable/src/libsyntax_pos/symbol.rs -lex:add_rule('keyword', token(lexer.KEYWORD, word_match{ - 'Self', 'abstract', 'as', 'async', 'auto', 'await', 'become', 'box', 'break', 'catch', 'const', - 'continue', 'crate', 'default', 'do', 'dyn', 'else', 'enum', 'extern', 'false', 'final', 'fn', - 'for', 'if', 'impl', 'in', 'let', 'loop', 'macro', 'match', 'mod', 'move', 'mut', 'override', - 'priv', 'pub', 'ref', 'return', 'self', 'static', 'struct', 'super', 'trait', 'true', 'try', - 'type', 'typeof', 'union', 'unsafe', 'unsized', 'use', 'virtual', 'where', 'while', 'yield' -})) - --- Macro names. -lex:add_rule('macro', token(lexer.FUNCTION, lexer.word * S("!"))) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) --- Library types -lex:add_rule('library', token(lexer.LABEL, lexer.upper * (lexer.lower + lexer.dec_num)^1)) - --- Numbers. -local identifier = P('r#')^-1 * lexer.word -local digit = lexer.digit -local decimal_literal = digit * (digit + '_')^0 -local function integer_suffix(digit) return P('_')^0 * digit * (digit + '_')^0 end -local function opt_cap(patt) return C(patt^-1) end -local float = decimal_literal * - (Cmt(opt_cap('.' * decimal_literal) * opt_cap(S('eE') * S('+-')^-1 * integer_suffix(digit)) * - opt_cap(P('f32') + 'f64'), function(input, index, decimals, exponent, type) - return decimals ~= "" or exponent ~= "" or type ~= "" - end) + '.' * -(S('._') + identifier)) -local function prefixed_integer(prefix, digit) return P(prefix) * integer_suffix(digit) end -local bin = prefixed_integer('0b', S('01')) -local oct = prefixed_integer('0o', lpeg.R('07')) -local hex = prefixed_integer('0x', lexer.xdigit) -local integer = (bin + oct + hex + decimal_literal) * - (S('iu') * (P('8') + '16' + '32' + '64' + '128' + 'size'))^-1 -lex:add_rule('number', token(lexer.NUMBER, float + integer)) +-- Library types. +lex:add_rule('library', lex:tag(lexer.TYPE, lexer.upper * (lexer.lower + lexer.dec_num)^1)) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match( - '() bool isize usize char str u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64'))) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE))) + +-- Lifetime annotation. +lex:add_rule('lifetime', lex:tag(lexer.OPERATOR, S('<&') * P("'"))) -- Strings. local sq_str = P('b')^-1 * lexer.range("'", true) @@ -57,26 +26,65 @@ local raw_str = Cmt(P('b')^-1 * P('r') * C(P('#')^0) * '"', function(input, inde local _, e = input:find('"' .. hashes, index, true) return (e or #input) + 1 end) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str + raw_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + raw_str)) + +-- Functions. +local builtin_macros = lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN) * '!') +local macros = lex:tag(lexer.FUNCTION, lexer.word * '!') +local func = lex:tag(lexer.FUNCTION, lexer.word) +lex:add_rule('function', (builtin_macros + macros + func) * #(lexer.space^0 * '(')) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, identifier)) +local identifier = P('r#')^-1 * lexer.word +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, identifier)) -- Comments. local line_comment = lexer.to_eol('//', true) local block_comment = lexer.range('/*', '*/', false, false, true) -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number_('_'))) -- Attributes. -lex:add_rule('preprocessor', token(lexer.PREPROCESSOR, '#' * lexer.range('[', ']', true))) +lex:add_rule('preprocessor', lex:tag(lexer.PREPROCESSOR, '#' * lexer.range('[', ']', true))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=`^~@&|?#~:;,.()[]{}'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>!=`^~@&|?#~:;,.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '{', '}') +-- https://doc.rust-lang.org/std/#keywords +lex:set_word_list(lexer.KEYWORD, { + 'SelfTy', 'as', 'async', 'await', 'break', 'const', 'continue', 'crate', 'dyn', 'else', 'enum', + 'extern', 'false', 'fn', 'for', 'if', 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', + 'pub', 'ref', 'return', 'self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'union', + 'unsafe', 'use', 'where', 'while' +}) + +-- https://doc.rust-lang.org/std/#primitives +lex:set_word_list(lexer.TYPE, { + 'never', 'array', 'bool', 'char', 'f32', 'f64', 'fn', 'i8', 'i16', 'i32', 'i64', 'i128', 'isize', + 'pointer', 'reference', 'slice', 'str', 'tuple', 'u8', 'u16', 'u32', 'u64', 'u128', 'unit', + 'usize' +}) + +lex:set_word_list(lexer.FUNCTION_BUILTIN, { + 'assert', 'assert_eq', 'assert_ne', 'cfg', 'column', 'compile_error', 'concat', 'dbg', + 'debug_assert', 'debug_assert_eq', 'debug_assert_ne', 'env', 'eprint', 'eprintln', 'file', + 'format', 'format_args', 'include', 'include_bytes', 'include_str', 'line', 'matches', + 'module_path', 'option_env', 'panic', 'print', 'println', 'stringify', 'thread_local', 'todo', + 'unimplemented', 'unreachable', 'vec', 'write', 'writeln', + -- Experimental + 'concat_bytes', 'concat_idents', 'const_format_args', 'format_args_nl', 'log_syntax', + 'trace_macros', + -- Deprecated + 'try' +}) + +lexer.property['scintillua.comment'] = '//' + return lex diff --git a/lua/lexers/sass.lua b/lua/lexers/sass.lua index bb14e1d..c281227 100644 --- a/lua/lexers/sass.lua +++ b/lua/lexers/sass.lua @@ -1,24 +1,21 @@ --- Copyright 2006-2022 Robert Gieseke. See LICENSE. +-- Copyright 2006-2024 Robert Gieseke. See LICENSE. -- Sass CSS preprocessor LPeg lexer. -- http://sass-lang.com -local lexer = require('lexer') -local token = lexer.token +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('sass', {inherit = lexer.load('css')}) +local lex = lexer.new(..., {inherit = lexer.load('css')}) -- Line comments. -lex:add_rule('line_comment', token(lexer.COMMENT, lexer.to_eol('//'))) +lex:add_rule('line_comment', lex:tag(lexer.COMMENT, lexer.to_eol('//'))) -- Variables. -lex:add_rule('variable', token(lexer.VARIABLE, '$' * (lexer.alnum + S('_-'))^1)) +lex:add_rule('variable', lex:tag(lexer.VARIABLE, '$' * (lexer.alnum + S('_-'))^1)) -- Mixins. -lex:add_rule('mixin', token('mixin', '@' * lexer.word)) -lex:add_style('mixin', lexer.styles['function']) +lex:add_rule('mixin', lex:tag(lexer.PREPROCESSOR, '@' * lexer.word)) --- Fold points. -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/scala.lua b/lua/lexers/scala.lua index 87c9095..5a7f271 100644 --- a/lua/lexers/scala.lua +++ b/lua/lexers/scala.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 JMS. See LICENSE. +-- Copyright 2006-2024 JMS. See LICENSE. -- Scala LPeg lexer. local lexer = require('lexer') @@ -55,6 +55,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/scheme.lua b/lua/lexers/scheme.lua index a19fa0f..264de3a 100644 --- a/lua/lexers/scheme.lua +++ b/lua/lexers/scheme.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Scheme LPeg lexer. -- Contributions by Murray Calavera. @@ -169,6 +169,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, P('#u8') + ',@' + S(".`'#(),"))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.COMMENT, '#|', '|#') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines(';')) + +lexer.property['scintillua.comment'] = ';' return lex diff --git a/lua/lexers/smalltalk.lua b/lua/lexers/smalltalk.lua index 982a514..f21d4b0 100644 --- a/lua/lexers/smalltalk.lua +++ b/lua/lexers/smalltalk.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Smalltalk LPeg lexer. local lexer = require('lexer') @@ -41,4 +41,6 @@ lex:add_rule('label', token(lexer.LABEL, '#' * lexer.word)) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '[', ']') +lexer.property['scintillua.comment'] = '"|"' + return lex diff --git a/lua/lexers/sml.lua b/lua/lexers/sml.lua index ba2015e..14bc97e 100644 --- a/lua/lexers/sml.lua +++ b/lua/lexers/sml.lua @@ -1,4 +1,4 @@ --- Copyright 2017-2022 Murray Calavera. See LICENSE. +-- Copyright 2017-2024 Murray Calavera. See LICENSE. -- Standard ML LPeg lexer. local lexer = require('lexer') @@ -88,4 +88,6 @@ lex:add_rule('typevar', token(lexer.VARIABLE, "'" * id)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('!*/+-^:@=<>()[]{},;._|#%&$?~`\\'))) +lexer.property['scintillua.comment'] = '(*)' + return lex diff --git a/lua/lexers/snobol4.lua b/lua/lexers/snobol4.lua index 0293370..387c47a 100644 --- a/lua/lexers/snobol4.lua +++ b/lua/lexers/snobol4.lua @@ -1,4 +1,4 @@ --- Copyright 2013-2022 Michael T. Richter. See LICENSE. +-- Copyright 2013-2024 Michael T. Richter. See LICENSE. -- SNOBOL4 lexer. -- This lexer works with classic SNOBOL4 as well as the CSNOBOL4 extensions. @@ -66,4 +66,6 @@ lex:add_rule('control', token(lexer.PREPROCESSOR, lexer.starts_line('-' * lexer. -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S'¬?$.!%*/#+-@⊥&^~\\=')) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/spin.lua b/lua/lexers/spin.lua index c906289..51ed0f2 100644 --- a/lua/lexers/spin.lua +++ b/lua/lexers/spin.lua @@ -1,4 +1,4 @@ --- Copyright 2017-2022 David B. Lamkins <david@lamkins.net>. See LICENSE. +-- Copyright 2017-2024 David B. Lamkins <david@lamkins.net>. See LICENSE. -- Spin LPeg lexer, see https://www.parallax.com/microcontrollers/propeller. local lexer = require('lexer') @@ -65,4 +65,6 @@ lex:add_rule('operator', token(lexer.OPERATOR, '<-' + '<-=' + '->' + '->=' + '><' + '><=' + '&=' + '|=' + 'and=' + 'or=' + '==' + '===' + '<>' + '<>=' + '<=' + '>=' + '=<' + '=<=' + '=>' + '=>=' + '..' + S('+-/*<>~!&=^|?:.()[]@#\\'))) +lexer.property['scintillua.comment'] = "'" + return lex diff --git a/lua/lexers/sql.lua b/lua/lexers/sql.lua index 0789210..63c41be 100644 --- a/lua/lexers/sql.lua +++ b/lua/lexers/sql.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- SQL LPeg lexer. local lexer = require('lexer') @@ -59,4 +59,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S(',()'))) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/strace.lua b/lua/lexers/strace.lua index 846c4fc..c2d2df1 100644 --- a/lua/lexers/strace.lua +++ b/lua/lexers/strace.lua @@ -1,35 +1,31 @@ --- Copyright 2017-2021 Marc André Tanner. See LICENSE. +-- Copyright 2017-2024 Marc André Tanner. See LICENSE. -- strace(1) output lexer -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local S, B = lpeg.S, lpeg.B -local lex = lexer.new('strace', {lex_by_line = true}) - --- Whitespace -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(..., {lex_by_line = true}) -- Syscall -lex:add_rule('syscall', token(lexer.KEYWORD, lexer.starts_line(lexer.word))) +lex:add_rule('syscall', lex:tag(lexer.FUNCTION, lexer.starts_line(lexer.word))) -- Upper case constants -lex:add_rule('constant', token(lexer.CONSTANT, - (lexer.upper + '_') * (lexer.upper + lexer.digit + '_')^0)) +lex:add_rule('constant', + lex:tag(lexer.CONSTANT, (lexer.upper + '_') * (lexer.upper + lexer.digit + '_')^0)) -- Single and double quoted strings local sq_str = lexer.range("'", true) local dq_str = lexer.range('"', true) -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Comments and text in parentheses at the line end local comment = lexer.range('/*', '*/') local description = lexer.range('(', ')') * lexer.newline -lex:add_rule('comment', token(lexer.COMMENT, comment + description)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, comment + description)) -lex:add_rule('result', token(lexer.TYPE, B(' = ') * lexer.integer)) -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) -lex:add_rule('number', token(lexer.NUMBER, lexer.float + lexer.integer)) -lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>~!=^&|?~:;,.()[]{}'))) +lex:add_rule('result', lex:tag(lexer.TYPE, B(' = ') * lexer.integer)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.float + lexer.integer)) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-/*%<>~!=^&|?~:;,.()[]{}'))) return lex diff --git a/lua/lexers/systemd.lua b/lua/lexers/systemd.lua index 8557a70..00a644d 100644 --- a/lua/lexers/systemd.lua +++ b/lua/lexers/systemd.lua @@ -1,4 +1,4 @@ --- Copyright 2016-2022 Christian Hesse. See LICENSE. +-- Copyright 2016-2024 Christian Hesse. See LICENSE. -- systemd unit file LPeg lexer. local lexer = require('lexer') @@ -122,12 +122,12 @@ lex:add_rule('section', token(lexer.LABEL, '[' * lex:add_rule('comment', token(lexer.COMMENT, lexer.starts_line(lexer.to_eol(S(';#'))))) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local oct_num = '0' * S('01234567_')^1 -local integer = S('+-')^-1 * (lexer.hex_num + oct_num + dec) +local integer = S('+-')^-1 * (lexer.hex_num + lexer.oct_num_('_') + lexer.dec_num_('_')) lex:add_rule('number', token(lexer.NUMBER, lexer.float + integer)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, '=')) +lexer.property['scintillua.comment'] = '#' + return lex diff --git a/lua/lexers/taskpaper.lua b/lua/lexers/taskpaper.lua index 4d77285..c314d44 100644 --- a/lua/lexers/taskpaper.lua +++ b/lua/lexers/taskpaper.lua @@ -1,39 +1,27 @@ --- Copyright (c) 2016-2022 Larry Hynes. See LICENSE. +-- Copyright (c) 2016-2024 Larry Hynes. See LICENSE. -- Taskpaper LPeg lexer -local lexer = require('lexer') -local token = lexer.token +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('taskpaper', {lex_by_line = true}) - -local delimiter = P(' ') + P('\t') +local lex = lexer.new(..., {lex_by_line = true}) -- Notes. -lex:add_rule('note', token('note', delimiter^1 * lexer.to_eol(lexer.alnum))) -lex:add_style('note', lexer.styles.constant) +local delimiter = lpeg.B(' ') + lpeg.B('\t') +lex:add_rule('note', delimiter * lex:tag('note', lexer.to_eol(lexer.alnum))) -- Tasks. -lex:add_rule('task', token('task', delimiter^1 * '-' + lexer.newline)) -lex:add_style('task', lexer.styles['function']) - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +lex:add_rule('task', delimiter * lex:tag(lexer.LIST, '-')) -- Projects. -lex:add_rule('project', - token('project', lexer.range(lexer.starts_line(lexer.alnum), ':') * lexer.newline)) -lex:add_style('project', lexer.styles.label) +lex:add_rule('project', lex:tag(lexer.HEADING, + lexer.range(lexer.starts_line(lexer.alnum), ':') * lexer.newline)) -- Tags. -lex:add_rule('extended_tag', token('extended_tag', '@' * lexer.word * '(' * +lex:add_rule('extended_tag', lex:tag(lexer.TAG .. '.extended', '@' * lexer.word * '(' * (lexer.word + lexer.digit + '-')^1 * ')')) -lex:add_style('extended_tag', lexer.styles.comment) -lex:add_rule('day_tag', token('day_tag', (P('@today') + '@tomorrow'))) -lex:add_style('day_tag', lexer.styles.class) -lex:add_rule('overdue_tag', token('overdue_tag', '@overdue')) -lex:add_style('overdue_tag', lexer.styles.preprocessor) -lex:add_rule('plain_tag', token('plain_tag', '@' * lexer.word)) -lex:add_style('plain_tag', lexer.styles.comment) +lex:add_rule('day_tag', lex:tag(lexer.TAG .. '.day', (P('@today') + '@tomorrow'))) +lex:add_rule('overdue_tag', lex:tag(lexer.TAG .. '.overdue', '@overdue')) +lex:add_rule('plain_tag', lex:tag(lexer.TAG .. '.plain', '@' * lexer.word)) return lex diff --git a/lua/lexers/tcl.lua b/lua/lexers/tcl.lua index 8686795..5552ed8 100644 --- a/lua/lexers/tcl.lua +++ b/lua/lexers/tcl.lua @@ -1,4 +1,4 @@ --- Copyright 2014-2022 Joshua Krämer. See LICENSE. +-- Copyright 2014-2024 Joshua Krämer. See LICENSE. -- Tcl LPeg lexer. -- This lexer follows the TCL dodekalogue (http://wiki.tcl.tk/10259). -- It is based on the previous lexer by Mitchell. @@ -16,7 +16,7 @@ lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#' * P(function(input, index) local i = index - 2 while i > 0 and input:find('^[ \t]', i) do i = i - 1 end - if i < 1 or input:find('^[\r\n;]', i) then return index end + if i < 1 or input:find('^[\r\n;]', i) then return true end end)))) -- Separator (semicolon). @@ -41,6 +41,7 @@ lex:add_rule('backslash', token(lexer.TYPE, '\\' * (oct + hex + unicode + 1))) -- Fold points. lex:add_fold_point(lexer.KEYWORD, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/template.txt b/lua/lexers/template.txt new file mode 100644 index 0000000..4046312 --- /dev/null +++ b/lua/lexers/template.txt @@ -0,0 +1,40 @@ +-- Copyright 2022-2024 Mitchell. See LICENSE. +-- ? LPeg lexer. + +local lexer = lexer +local P, S = lpeg.P, lpeg.S + +local lex = lexer.new(...) + +-- Keywords. +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD))) + +-- Identifiers. +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Strings. +local sq_str = lexer.range("'") +local dq_str = lexer.range('"') +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Operators. +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('+-*/%^=<>,.{}[]()'))) + +-- Fold points. +lex:add_fold_point(lexer.KEYWORD, 'start', 'end') +lex:add_fold_point(lexer.OPERATOR, '{', '}') + +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + 'keyword1', 'keyword2', 'keyword3' +}) + +lexer.property['scintillua.comment'] = '#' + +return lex diff --git a/lua/lexers/tex.lua b/lua/lexers/tex.lua index 8388c22..275bb1a 100644 --- a/lua/lexers/tex.lua +++ b/lua/lexers/tex.lua @@ -1,32 +1,28 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Plain TeX LPeg lexer. -- Modified by Robert Gieseke. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('tex') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('%'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('%'))) -- TeX environments. -lex:add_rule('environment', token('environment', '\\' * (P('begin') + 'end') * lexer.word)) -lex:add_style('environment', lexer.styles.keyword) +lex:add_rule('environment', lex:tag('environment', '\\' * (P('begin') + 'end') * lexer.word)) -- Commands. -lex:add_rule('command', token(lexer.KEYWORD, '\\' * (lexer.alpha^1 + S('#$&~_^%{}')))) +lex:add_rule('command', lex:tag('command', '\\' * (lexer.alpha^1 + S('#$&~_^%{}')))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('$&#{}[]'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('$&#{}[]'))) -- Fold points. -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('%')) lex:add_fold_point('environment', '\\begin', '\\end') lex:add_fold_point(lexer.OPERATOR, '{', '}') +lexer.property['scintillua.comment'] = '%' + return lex diff --git a/lua/lexers/texinfo.lua b/lua/lexers/texinfo.lua index 68ddb69..08fc948 100644 --- a/lua/lexers/texinfo.lua +++ b/lua/lexers/texinfo.lua @@ -1,4 +1,4 @@ --- Copyright 2014-2022 stef@ailleurs.land. See LICENSE. +-- Copyright 2014-2024 stef@ailleurs.land. See LICENSE. -- Plain Texinfo version 5.2 LPeg lexer -- Freely inspired from Mitchell work and valuable help from him too ! @@ -24,17 +24,61 @@ With the use of Scintilla's `SCI_FOLDALL(SC_FOLDACTION_TOGGLE)` or Textadept's large documents. ]] -local lexer = require('lexer') +local lexer = lexer local token, word_match = lexer.token, lexer.word_match local P, S = lpeg.P, lpeg.S -local lex = lexer.new('texinfo') - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(...) -- Directives. -local directives_base = word_match({ +lex:add_rule('directive', + lex:tag('command', ('@end' * lexer.space^1 + '@') * lex:word_match('directive', true))) + +-- Chapters. +lex:add_rule('chapter', lex:tag('command.section', + ('@end' * lexer.space^1 + '@') * lex:word_match('chapter', true))) + +-- Common keywords. +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, ('@end' * lexer.space^1 + '@') * + lex:word_match(lexer.KEYWORD, true))) + +-- Italics +local nested_braces = lexer.range('{', '}', false, false, true) +lex:add_rule('emph', lex:tag(lexer.ITALIC, '@emph' * nested_braces)) + +-- Bold +lex:add_rule('strong', lex:tag(lexer.BOLD, '@strong' * nested_braces)) + +-- Identifiers +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) + +-- Strings. +lex:add_rule('string', lex:tag(lexer.STRING, nested_braces)) + +-- Numbers. +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +-- Comments. +local line_comment = lexer.to_eol('@c', true) +-- local line_comment_long = lexer.to_eol('@comment', true) +local block_comment = lexer.range('@ignore', '@end ignore') +lex:add_rule('comment', lex:tag(lexer.COMMENT, line_comment + block_comment)) + +-- Fold points. +lex:add_fold_point('command', '@titlepage', '@end titlepage') +lex:add_fold_point('command', '@copying', '@end copying') +lex:add_fold_point('command', '@ifset', '@end ifset') +lex:add_fold_point('command', '@tex', '@end tex') +lex:add_fold_point('command', '@itemize', '@end itemize') +lex:add_fold_point('command', '@enumerate', '@end enumerate') +lex:add_fold_point('command', '@multitable', '@end multitable') +lex:add_fold_point('command', '@example', '@end example') +lex:add_fold_point('command', '@smallexample', '@end smallexample') +lex:add_fold_point('command', '@cartouche', '@end cartouche') +lex:add_fold_point('command', '@startchapter', '@end startchapter') + +-- Word lists. +lex:set_word_list('directive', { 'end', -- Custom keywords for chapter folding 'startchapter', 'endchapter', @@ -75,12 +119,9 @@ local directives_base = word_match({ -- not implemented -- Ending a Texinfo document (page 4, column 2) 'bye' -}, true) -lex:add_rule('directive', token('directives', ('@end' * lexer.space^1 + '@') * directives_base)) -lex:add_style('directives', lexer.styles['function']) +}) --- Chapters. -local chapters_base = word_match({ +lex:set_word_list('chapter', { -- Chapter structuring (page 1, column 2) 'lowersections', 'raisesections', 'part', -- Chapter structuring > Numbered, included in contents (page 1, column 2) @@ -95,12 +136,9 @@ local chapters_base = word_match({ 'appendixsubsubsec', 'appendixsubsubsection', -- Chapter structuring > Unumbered, not included in contents, no new page (page 1, column 3) 'chapheading', 'majorheading', 'heading', 'subheading', 'subsubheading' -}, true) -lex:add_rule('chapter', token('chapters', ('@end' * lexer.space^1 + '@') * chapters_base)) -lex:add_style('chapters', lexer.styles.class) +}) --- Common keywords. -local keyword_base = word_match({ +lex:set_word_list(lexer.KEYWORD, { 'end', -- Beginning a Texinfo document (page 1, column 1) 'setfilename', 'settitle', 'insertcopying', @@ -162,44 +200,8 @@ local keyword_base = word_match({ 'sp', 'page', 'need', 'group', 'vskip' -- Definition commands (page 3, column 2) -- not implemented -}, true) -lex:add_rule('keyword', token(lexer.KEYWORD, ('@end' * lexer.space^1 + '@') * keyword_base)) - --- Italics -local nested_braces = lexer.range('{', '}', false, false, true) -lex:add_rule('emph', token('emph', '@emph' * nested_braces)) -lex:add_style('emph', lexer.styles.string .. {italics = true}) - --- Bold -lex:add_rule('strong', token('strong', '@strong' * nested_braces)) -lex:add_style('strong', lexer.styles.string .. {bold = true}) - --- Identifiers -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +}) --- Strings. -lex:add_rule('string', token(lexer.STRING, nested_braces)) - --- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) - --- Comments. -local line_comment = lexer.to_eol('@c', true) --- local line_comment_long = lexer.to_eol('@comment', true) -local block_comment = lexer.range('@ignore', '@end ignore') -lex:add_rule('comment', token(lexer.COMMENT, line_comment + block_comment)) - --- Fold points. -lex:add_fold_point('directives', '@titlepage', '@end titlepage') -lex:add_fold_point('directives', '@copying', '@end copying') -lex:add_fold_point('directives', '@ifset', '@end ifset') -lex:add_fold_point('directives', '@tex', '@end tex') -lex:add_fold_point('directives', '@itemize', '@end itemize') -lex:add_fold_point('directives', '@enumerate', '@end enumerate') -lex:add_fold_point('directives', '@multitable', '@end multitable') -lex:add_fold_point('directives', '@example', '@end example') -lex:add_fold_point('directives', '@smallexample', '@end smallexample') -lex:add_fold_point('directives', '@cartouche', '@end cartouche') -lex:add_fold_point('directives', '@startchapter', '@end startchapter') +lexer.property['scintillua.comment'] = '@c' return lex diff --git a/lua/lexers/text.lua b/lua/lexers/text.lua index cdf7c3d..1e585ae 100644 --- a/lua/lexers/text.lua +++ b/lua/lexers/text.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Text LPeg lexer. local lexer = require('lexer') diff --git a/lua/lexers/toml.lua b/lua/lexers/toml.lua index ed61958..29cd79d 100644 --- a/lua/lexers/toml.lua +++ b/lua/lexers/toml.lua @@ -1,31 +1,27 @@ --- Copyright 2015-2022 Alejandro Baez (https://keybase.io/baez). See LICENSE. +-- Copyright 2015-2024 Alejandro Baez (https://keybase.io/baez). See LICENSE. -- TOML LPeg lexer. -local lexer = require("lexer") -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('toml', {fold_by_indentation = true}) +local lex = lexer.new(..., {fold_by_indentation = true}) --- Whitespace -lex:add_rule('whitespace', token(lexer.WHITESPACE, S(' \t')^1 + lexer.newline^1)) - --- kewwords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match('true false'))) +-- Keywords. +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lexer.word_match('true false'))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. local sq_str = lexer.range("'") local dq_str = lexer.range('"') -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str)) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('=+-,.{}[]()'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('=+-,.{}[]()'))) -- Datetime. local year = lexer.digit * lexer.digit * lexer.digit * lexer.digit @@ -37,12 +33,12 @@ local minutes = lexer.digit * lexer.digit local seconds = lexer.digit * lexer.digit local fraction = '.' * lexer.digit^0 local time = hours * ':' * minutes * ':' * seconds * fraction^-1 -local T = S(' \t')^1 + S('tT') local zone = 'Z' + S(' \t')^0 * S('-+') * hours * (':' * minutes)^-1 -lex:add_rule('datetime', token('timestamp', date * (T * time * zone^-1))) -lex:add_style('timestamp', lexer.styles.number) +lex:add_rule('datetime', lex:tag(lexer.NUMBER .. '.timestamp', date * (S('tT \t') * time * zone^-1))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number)) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/troff.lua b/lua/lexers/troff.lua new file mode 100644 index 0000000..b2d8e63 --- /dev/null +++ b/lua/lexers/troff.lua @@ -0,0 +1,42 @@ +-- Copyright 2023-2024 Mitchell. See LICENSE. +-- troff/man LPeg lexer. +-- Based on original Man lexer by David B. Lamkins and modified by Eolien55. + +local lexer = lexer +local P, R, S = lpeg.P, lpeg.R, lpeg.S + +local lex = lexer.new(...) + +-- Registers and groff's structured programming. +lex:add_rule('keywords', lex:tag(lexer.KEYWORD, (lexer.starts_line('.') * (lexer.space - '\n')^0 * + (P('while') + 'break' + 'continue' + 'nr' + 'rr' + 'rnn' + 'aln' + '\\}')) + '\\{')) + +-- Markup. +lex:add_rule('escape_sequences', lex:tag(lexer.VARIABLE, + '\\' * (('s' * S('+-')^-1) + S('*fgmnYV'))^-1 * (P('(') * 2 + lexer.range('[', ']') + 1))) + +lex:add_rule('headings', lex:tag(lexer.NUMBER, + lexer.starts_line('.') * (lexer.space - '\n')^0 * (S('STN') * 'H') * (lexer.space - '\n') * + lexer.nonnewline^0)) +lex:add_rule('man_alignment', lex:tag(lexer.KEYWORD, + lexer.starts_line('.') * (lexer.space - '\n')^0 * (P('br') + 'DS' + 'RS' + 'RE' + 'PD' + 'PP') * + lexer.space)) +lex:add_rule('font', lex:tag(lexer.VARIABLE, + lexer.starts_line('.') * (lexer.space - '\n')^0 * ('B' * P('R')^-1 + 'I' * S('PR')^-1) * + lexer.space)) + +-- Lowercase troff macros are plain macros (like .so or .nr). +lex:add_rule('troff_plain_macros', lex:tag(lexer.VARIABLE, lexer.starts_line('.') * + (lexer.space - '\n')^0 * lexer.lower^1)) +lex:add_rule('any_macro', lex:tag(lexer.PREPROCESSOR, + lexer.starts_line('.') * (lexer.space - '\n')^0 * (lexer.any - lexer.space)^0)) +lex:add_rule('comment', lex:tag(lexer.COMMENT, + (lexer.starts_line('.\\"') + '\\"' + '\\#') * lexer.nonnewline^0)) +lex:add_rule('string', lex:tag(lexer.STRING, lexer.range('"', true))) + +-- Usually used by eqn, and mandoc in some way. +lex:add_rule('in_dollars', lex:tag(lexer.EMBEDDED, lexer.range('$', false, false))) + +-- TODO: a lexer for each preprocessor? + +return lex diff --git a/lua/lexers/txt2tags.lua b/lua/lexers/txt2tags.lua index 9db5b80..fd9a469 100644 --- a/lua/lexers/txt2tags.lua +++ b/lua/lexers/txt2tags.lua @@ -1,4 +1,4 @@ --- Copyright 2019-2022 Julien L. See LICENSE. +-- Copyright 2019-2024 Julien L. See LICENSE. -- txt2tags LPeg lexer. -- (developed and tested with Txt2tags Markup Rules -- [https://txt2tags.org/doc/english/rules.t2t]) @@ -16,7 +16,7 @@ local ws = token(lexer.WHITESPACE, (lexer.space - lexer.newline)^1) -- Titles local alphanumeric = lexer.alnum + S('_-') -local header_label = token('header_label_start', '[') * token('header_label', alphanumeric^1) * +local header_label = token('header_label_start', '[') * token(lexer.LABEL, alphanumeric^1) * token('header_label_end', ']') local function h(level) local equal = string.rep('=', level) * (lexer.nonnewline - '=')^1 * string.rep('=', level) @@ -36,29 +36,29 @@ local function span(name, delimiter) (delimiter * nonspace * (lexer.nonnewline - nonspace * delimiter)^0 * nonspace * delimiter * S(delimiter)^0)) end -local bold = span('bold', '**') -local italic = span('italic', '//') -local underline = span('underline', '__') +local bold = span(lexer.BOLD, '**') +local italic = span(lexer.ITALIC, '//') +local underline = span(lexer.UNDERLINE, '__') local strike = span('strike', '--') -local mono = span('mono', '``') -local raw = span('raw', '""') +local mono = span(lexer.CODE, '``') +local raw = span(lexer.DEFAULT, '""') local tagged = span('tagged', "''") local inline = bold + italic + underline + strike + mono + raw + tagged -- Link. -local email = token('email', +local email = token(lexer.LINK, (nonspace - '@')^1 * '@' * (nonspace - '.')^1 * ('.' * (nonspace - S('.?'))^1)^1 * ('?' * nonspace^1)^-1) -local host = token('host', +local host = token(lexer.LINK, word_match('www ftp', true) * (nonspace - '.')^0 * '.' * (nonspace - '.')^1 * '.' * (nonspace - S(',.'))^1) -local url = token('url', +local url = token(lexer.LINK, (nonspace - '://')^1 * '://' * (nonspace - ',' - '.')^1 * ('.' * (nonspace - S(',./?#'))^1)^1 * ('/' * (nonspace - S('./?#'))^0 * ('.' * (nonspace - S(',.?#'))^1)^0)^0 * ('?' * (nonspace - '#')^1)^-1 * ('#' * nonspace^0)^-1) -local label_with_address = token('label_start', '[') * lexer.space^0 * - token('address_label', ((nonspace - ']')^1 * lexer.space^1)^1) * - token('address', (nonspace - ']')^1) * token('label_end', ']') +local label_with_address = token(lexer.LABEL, '[') * lexer.space^0 * + token(lexer.LABEL, ((nonspace - ']')^1 * lexer.space^1)^1) * token(lexer.LINK, (nonspace - ']')^1) * + token(lexer.LABEL, ']') local link = label_with_address + url + host + email -- Line. @@ -68,22 +68,22 @@ local line = token('line', S('-=_')^20) local image_only = token('image_start', '[') * token('image', (nonspace - ']')^1) * token('image_end', ']') local image_link = token('image_link_start', '[') * image_only * - token('image_link_sep', lexer.space^1) * token('image_link', (nonspace - ']')^1) * + token('image_link_sep', lexer.space^1) * token(lexer.LINK, (nonspace - ']')^1) * token('image_link_end', ']') local image = image_link + image_only -- Macro. -local macro = token('macro', '%%' * (nonspace - '(')^1 * lexer.range('(', ')', true)^-1) +local macro = token(lexer.PREPROCESSOR, '%%' * (nonspace - '(')^1 * lexer.range('(', ')', true)^-1) -- Verbatim. local verbatim_line = lexer.to_eol(lexer.starts_line('```') * S(' \t')) local verbatim_block = lexer.range(lexer.starts_line('```')) -local verbatim_area = token('verbatim_area', verbatim_block + verbatim_line) +local verbatim_area = token(lexer.CODE, verbatim_block + verbatim_line) -- Raw. local raw_line = lexer.to_eol(lexer.starts_line('"""') * S(' \t')) local raw_block = lexer.range(lexer.starts_line('"""')) -local raw_area = token('raw_area', raw_block + raw_line) +local raw_area = token(lexer.DEFAULT, raw_block + raw_line) -- Tagged. local tagged_line = lexer.to_eol(lexer.starts_line('\'\'\'') * S(' \t')) @@ -121,24 +121,9 @@ local font_size = tonumber(lexer.property_expanded['style.default']:match('size: for n = 5, 1, -1 do lex:add_style('h' .. n, {fore = lexer.colors.red, size = font_size + (6 - n)}) end -lex:add_style('header_label', lexer.styles.label) -lex:add_style('email', {underlined = true}) -lex:add_style('host', {underlined = true}) -lex:add_style('url', {underlined = true}) -lex:add_style('address_label', lexer.styles.label) -lex:add_style('address', {underlined = true}) lex:add_style('image', {fore = lexer.colors.green}) -lex:add_style('image_link', {underlined = true}) -lex:add_style('macro', lexer.styles.preprocessor) -lex:add_style('bold', {bold = true}) -lex:add_style('italic', {italics = true}) -lex:add_style('underline', {underlined = true}) lex:add_style('strike', {italics = true}) -- a strike style is not available -lex:add_style('mono', {font = 'mono'}) -lex:add_style('raw', {back = lexer.colors.grey}) lex:add_style('tagged', lexer.styles.embedded) -lex:add_style('verbatim_area', {font = 'mono'}) -- in consistency with mono -lex:add_style('raw_area', {back = lexer.colors.grey}) -- in consistency with raw lex:add_style('tagged_area', lexer.styles.embedded) -- in consistency with tagged lex:add_style('table_sep', {fore = lexer.colors.green}) lex:add_style('header_cell_content', {fore = lexer.colors.green}) diff --git a/lua/lexers/typescript.lua b/lua/lexers/typescript.lua index 414acf1..b1ee619 100644 --- a/lua/lexers/typescript.lua +++ b/lua/lexers/typescript.lua @@ -1,23 +1,18 @@ --- Copyright 2021-2022 Mitchell. See LICENSE. +-- Copyright 2021-2024 Mitchell. See LICENSE. -- TypeScript LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('typescript', {inherit = lexer.load('javascript')}) +local lex = lexer.new(..., {inherit = lexer.load('javascript')}) --- Whitespace -lex:modify_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +-- Word lists. +lex:set_word_list(lexer.KEYWORD, 'abstract as constructor declare is module namespace require type', + true) --- Keywords. -lex:modify_rule('keyword', token(lexer.KEYWORD, word_match[[ - abstract as constructor declare is module namespace require type -]]) + lex:get_rule('keyword')) +lex:set_word_list(lexer.TYPE, 'boolean number bigint string unknown any void never symbol object', + true) --- Types. -lex:modify_rule('type', token(lexer.TYPE, word_match[[ - boolean number bigint string unknown any void never symbol object -]]) + lex:get_rule('type')) +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/vala.lua b/lua/lexers/vala.lua index 4ff50b0..1c11f76 100644 --- a/lua/lexers/vala.lua +++ b/lua/lexers/vala.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Vala LPeg lexer. local lexer = require('lexer') @@ -56,6 +56,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/vb.lua b/lua/lexers/vb.lua index 5289558..bc9d704 100644 --- a/lua/lexers/vb.lua +++ b/lua/lexers/vb.lua @@ -1,49 +1,31 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- VisualBasic LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('vb', {case_insensitive_fold_points = true}) - --- Whitespace. -lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1)) +local lex = lexer.new(..., {case_insensitive_fold_points = true}) -- Keywords. -lex:add_rule('keyword', token(lexer.KEYWORD, word_match({ - -- Control. - 'If', 'Then', 'Else', 'ElseIf', 'While', 'Wend', 'For', 'To', 'Each', 'In', 'Step', 'Case', - 'Select', 'Return', 'Continue', 'Do', 'Until', 'Loop', 'Next', 'With', 'Exit', - -- Operators. - 'Mod', 'And', 'Not', 'Or', 'Xor', 'Is', - -- Storage types. - 'Call', 'Class', 'Const', 'Dim', 'ReDim', 'Preserve', 'Function', 'Sub', 'Property', 'End', 'Set', - 'Let', 'Get', 'New', 'Randomize', 'Option', 'Explicit', 'On', 'Error', 'Execute', 'Module', - -- Storage modifiers. - 'Private', 'Public', 'Default', - -- Constants. - 'Empty', 'False', 'Nothing', 'Null', 'True' -}, true))) +lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD, true))) -- Types. -lex:add_rule('type', token(lexer.TYPE, word_match( - 'Boolean Byte Char Date Decimal Double Long Object Short Single String', true))) +lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE, true))) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol("'" + word_match('rem', true)))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol("'" + lexer.word_match('rem', true)))) -- Identifiers. -lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) +lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word)) -- Strings. -lex:add_rule('string', token(lexer.STRING, lexer.range('"', true, false))) +lex:add_rule('string', lex:tag(lexer.STRING, lexer.range('"', true, false))) -- Numbers. -lex:add_rule('number', token(lexer.NUMBER, lexer.number * S('LlUuFf')^-2)) +lex:add_rule('number', lex:tag(lexer.NUMBER, lexer.number * S('LlUuFf')^-2)) -- Operators. -lex:add_rule('operator', token(lexer.OPERATOR, S('=><+-*^&:.,_()'))) +lex:add_rule('operator', lex:tag(lexer.OPERATOR, S('=><+-*^&:.,_()'))) -- Fold points. lex:add_fold_point(lexer.KEYWORD, 'If', 'End If') @@ -60,4 +42,27 @@ lex:add_fold_point(lexer.KEYWORD, 'Module', 'End Module') lex:add_fold_point(lexer.KEYWORD, 'Class', 'End Class') lex:add_fold_point(lexer.KEYWORD, 'Try', 'End Try') +-- Word lists. +lex:set_word_list(lexer.KEYWORD, { + -- Control. + 'If', 'Then', 'Else', 'ElseIf', 'While', 'Wend', 'For', 'To', 'Each', 'In', 'Step', 'Case', + 'Select', 'Return', 'Continue', 'Do', 'Until', 'Loop', 'Next', 'With', 'Exit', + -- Operators. + 'Mod', 'And', 'Not', 'Or', 'Xor', 'Is', + -- Storage types. + 'Call', 'Class', 'Const', 'Dim', 'ReDim', 'Preserve', 'Function', 'Sub', 'Property', 'End', 'Set', + 'Let', 'Get', 'New', 'Randomize', 'Option', 'Explicit', 'On', 'Error', 'Execute', 'Module', + -- Storage modifiers. + 'Private', 'Public', 'Default', + -- Constants. + 'Empty', 'False', 'Nothing', 'Null', 'True' +}) + +lex:set_word_list(lexer.TYPE, { + 'Boolean', 'Byte', 'Char', 'Date', 'Decimal', 'Double', 'Long', 'Object', 'Short', 'Single', + 'String' +}) + +lexer.property['scintillua.comment'] = "'" + return lex diff --git a/lua/lexers/vcard.lua b/lua/lexers/vcard.lua index 2ee82ba..737bdef 100644 --- a/lua/lexers/vcard.lua +++ b/lua/lexers/vcard.lua @@ -1,4 +1,4 @@ --- Copyright (c) 2015-2022 Piotr Orzechowski [drzewo.org]. See LICENSE. +-- Copyright (c) 2015-2024 Piotr Orzechowski [drzewo.org]. See LICENSE. -- vCard 2.1, 3.0 and 4.0 LPeg lexer. local lexer = require('lexer') diff --git a/lua/lexers/verilog.lua b/lua/lexers/verilog.lua index d7cb74b..8a111dc 100644 --- a/lua/lexers/verilog.lua +++ b/lua/lexers/verilog.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- Verilog LPeg lexer. local lexer = require('lexer') @@ -83,6 +83,7 @@ lex:add_fold_point(lexer.KEYWORD, 'begin', 'end') lex:add_fold_point(lexer.OPERATOR, '(', ')') lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/vhdl.lua b/lua/lexers/vhdl.lua index 5cb0bad..58efbd5 100644 --- a/lua/lexers/vhdl.lua +++ b/lua/lexers/vhdl.lua @@ -1,4 +1,4 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- VHDL LPeg lexer. local lexer = require('lexer') @@ -67,4 +67,6 @@ lex:add_rule('number', token(lexer.NUMBER, lexer.number)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('=/!:;<>+-/*%&|^~()'))) +lexer.property['scintillua.comment'] = '--' + return lex diff --git a/lua/lexers/wsf.lua b/lua/lexers/wsf.lua index 6972cfe..cbfa221 100644 --- a/lua/lexers/wsf.lua +++ b/lua/lexers/wsf.lua @@ -1,90 +1,87 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- WSF LPeg lexer (based on XML). -- Contributed by Jeff Stone. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('wsf') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(...) -- Comments. -lex:add_rule('comment', token(lexer.COMMENT, lexer.range('<!--', '-->'))) +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.range('<!--', '-->'))) -- Elements. -local alpha = lpeg.R('az', 'AZ', '\127\255') -local word_char = lexer.alnum + S('_-:.?') -local identifier = (alpha + S('_-:.?')) * word_char^0 -local element = token('element', '<' * P('/')^-1 * identifier) -lex:add_rule('element', element) -lex:add_style('element', lexer.styles.keyword) +local identifier = (lexer.alpha + S('_-')) * (lexer.alnum + S('_-'))^0 +local tag = lex:tag(lexer.TAG, '<' * P('/')^-1 * identifier) +lex:add_rule('tag', tag) -- Closing tags. -local tag_close = token('element', P('/')^-1 * '>') +local tag_close = lex:tag(lexer.TAG, P('/')^-1 * '>') lex:add_rule('tag_close', tag_close) --- Attributes. -local attribute = token('attribute', identifier) * #(lexer.space^0 * '=') -lex:add_rule('attribute', attribute) -lex:add_style('attribute', lexer.styles.type) - -- Equals. +-- TODO: performance is terrible on large files. local in_tag = P(function(input, index) local before = input:sub(1, index - 1) local s, e = before:find('<[^>]-$'), before:find('>[^<]-$') - if s and e then return s > e and index or nil end - if s then return index end - return input:find('^[^<]->', index) and index or nil + if s and e then return s > e end + if s then return true end + return input:find('^[^<]->', index) ~= nil end) -local equals = token(lexer.OPERATOR, '=') * in_tag -lex:add_rule('equals', equals) +local equals = lex:tag(lexer.OPERATOR, '=') -- * in_tag +-- lex:add_rule('equals', equals) + +-- Attributes. +local ws = lex:get_rule('whitespace') +local attribute_eq = lex:tag(lexer.ATTRIBUTE, identifier) * ws^-1 * equals +lex:add_rule('attribute', attribute_eq) -- Strings. local sq_str = lexer.range("'", false, false) local dq_str = lexer.range('"', false, false) -local string = #S('\'"') * lexer.last_char_includes('=') * token(lexer.STRING, sq_str + dq_str) +local string = lex:tag(lexer.STRING, lexer.after_set('=', sq_str + dq_str)) lex:add_rule('string', string) -- Numbers. -local number = token(lexer.NUMBER, lexer.dec_num * P('%')^-1) -lex:add_rule('number', #lexer.digit * lexer.last_char_includes('=') * number * in_tag) +local number = lex:tag(lexer.NUMBER, lexer.dec_num * P('%')^-1) +lex:add_rule('number', lexer.after_set('=', number)) -- * in_tag) -- Entities. -lex:add_rule('entity', token('entity', '&' * word_match('lt gt amp apos quot') * ';')) -lex:add_style('entity', lexer.styles.operator) +local predefined = lex:tag(lexer.CONSTANT_BUILTIN .. '.entity', + '&' * lexer.word_match('lt gt amp apos quot') * ';') +local general = lex:tag(lexer.CONSTANT .. '.entity', '&' * identifier * ';') +lex:add_rule('entity', predefined + general) -- Fold points. local function disambiguate_lt(text, pos, line, s) return not line:find('^</', s) and 1 or -1 end -lex:add_fold_point('element', '<', disambiguate_lt) -lex:add_fold_point('element', '/>', -1) +lex:add_fold_point(lexer.TAG, '<', disambiguate_lt) +lex:add_fold_point(lexer.TAG, '/>', -1) lex:add_fold_point(lexer.COMMENT, '<!--', '-->') -- Finally, add JavaScript and VBScript as embedded languages -- Tags that start embedded languages. -local embed_start_tag = element * (ws^1 * attribute * ws^0 * equals * ws^0 * string)^0 * ws^0 * - tag_close -local embed_end_tag = element * tag_close +local embed_start_tag = tag * (ws * attribute_eq * ws^-1 * string)^0 * ws^-1 * tag_close +local embed_end_tag = tag * tag_close -- Embedded JavaScript. local js = lexer.load('javascript') local js_start_rule = #(P('<script') * (P(function(input, index) - if input:find('^%s+language%s*=%s*(["\'])[jJ][ava]*[sS]cript%1', index) then return index end + if input:find('^%s+language%s*=%s*(["\'])[jJ][ava]*[sS]cript%1', index) then return true end end) + '>')) * embed_start_tag -- <script language="javascript"> -local js_end_rule = #('</script' * ws^0 * '>') * embed_end_tag -- </script> +local js_end_rule = #P('</script>') * embed_end_tag -- </script> lex:embed(js, js_start_rule, js_end_rule) -- Embedded VBScript. local vbs = lexer.load('vb', 'vbscript') local vbs_start_rule = #(P('<script') * (P(function(input, index) - if input:find('^%s+language%s*=%s*(["\'])[vV][bB][sS]cript%1', index) then return index end + if input:find('^%s+language%s*=%s*(["\'])[vV][bB][sS]cript%1', index) then return true end end) + '>')) * embed_start_tag -- <script language="vbscript"> -local vbs_end_rule = #('</script' * ws^0 * '>') * embed_end_tag -- </script> +local vbs_end_rule = #P('</script>') * embed_end_tag -- </script> lex:embed(vbs, vbs_start_rule, vbs_end_rule) +lexer.property['scintillua.comment'] = '<!--|-->' +lexer.property['scintillua.angle.braces'] = '1' + return lex diff --git a/lua/lexers/xml.lua b/lua/lexers/xml.lua index 640e924..0e33f6a 100644 --- a/lua/lexers/xml.lua +++ b/lua/lexers/xml.lua @@ -1,78 +1,75 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- XML LPeg lexer. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer local P, S = lpeg.P, lpeg.S -local lex = lexer.new('xml') - --- Whitespace. -local ws = token(lexer.WHITESPACE, lexer.space^1) -lex:add_rule('whitespace', ws) +local lex = lexer.new(...) -- Comments and CDATA. -lex:add_rule('comment', token(lexer.COMMENT, lexer.range('<!--', '-->'))) -lex:add_rule('cdata', token('cdata', lexer.range('<![CDATA[', ']]>'))) -lex:add_style('cdata', lexer.styles.comment) - --- Doctypes and other markup tags. -local alpha = lpeg.R('az', 'AZ', '\127\255') -local word_char = lexer.alnum + S('_-:.??') -local identifier = (alpha + S('_-:.?')) * word_char^0 -local doctype = token('doctype', '<!DOCTYPE') * ws * token('doctype', identifier) * - (ws * identifier)^-1 * (1 - P('>'))^0 * token('doctype', '>') +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.range('<!--', '-->'))) +lex:add_rule('cdata', lex:tag('cdata', lexer.range('<![CDATA[', ']]>'))) + +-- Doctype. +local ws = lex:get_rule('whitespace') +local identifier = (lexer.alpha + S('_-')) * (lexer.alnum + S('_-'))^0 +local doctype = lex:tag(lexer.TAG .. '.doctype', '<!DOCTYPE') * ws * + lex:tag(lexer.TAG .. '.doctype', identifier) * (ws * identifier)^-1 * (1 - P('>'))^0 * + lex:tag(lexer.TAG .. '.doctype', '>') lex:add_rule('doctype', doctype) -lex:add_style('doctype', lexer.styles.comment) -- Processing instructions. -lex:add_rule('proc_insn', token('proc_insn', '<?' * (1 - P('?>'))^0 * P('?>')^-1)) -lex:add_style('proc_insn', lexer.styles.comment) +lex:add_rule('proc_insn', lex:tag(lexer.TAG .. '.pi', '<?' * identifier + '?>')) --- Elements. -local namespace = token(lexer.OPERATOR, ':') * token('namespace', identifier) -lex:add_rule('element', token('element', '<' * P('/')^-1 * identifier) * namespace^-1) -lex:add_style('element', lexer.styles.keyword) -lex:add_style('namespace', lexer.styles.class) +-- Tags. +local namespace = lex:tag(lexer.OPERATOR, ':') * lex:tag(lexer.LABEL, identifier) +lex:add_rule('element', lex:tag(lexer.TAG, '<' * P('/')^-1 * identifier) * namespace^-1) -- Closing tags. -lex:add_rule('close_tag', token('element', P('/')^-1 * '>')) - --- Attributes. -lex:add_rule('attribute', token('attribute', identifier) * namespace^-1 * #(lexer.space^0 * '=')) -lex:add_style('attribute', lexer.styles.type) +lex:add_rule('close_tag', lex:tag(lexer.TAG, P('/')^-1 * '>')) -- Equals. -- TODO: performance is terrible on large files. local in_tag = P(function(input, index) local before = input:sub(1, index - 1) local s, e = before:find('<[^>]-$'), before:find('>[^<]-$') - if s and e then return s > e and index or nil end - if s then return index end - return input:find('^[^<]->', index) and index or nil + if s and e then return s > e end + if s then return true end + return input:find('^[^<]->', index) ~= nil end) --- lex:add_rule('equal', token(lexer.OPERATOR, '=')) -- * in_tag +local equals = lex:tag(lexer.OPERATOR, '=') -- * in_tag +-- lex:add_rule('equal', equals) + +-- Attributes. +local attribute_eq = lex:tag(lexer.ATTRIBUTE, identifier) * namespace^-1 * ws^-1 * equals +lex:add_rule('attribute', attribute_eq) -- Strings. local sq_str = lexer.range("'", false, false) local dq_str = lexer.range('"', false, false) -lex:add_rule('string', - #S('\'"') * lexer.last_char_includes('=') * token(lexer.STRING, sq_str + dq_str)) +lex:add_rule('string', lex:tag(lexer.STRING, lexer.after_set('=', sq_str + dq_str))) -- Numbers. -local number = token(lexer.NUMBER, lexer.dec_num * P('%')^-1) -lex:add_rule('number', #lexer.digit * lexer.last_char_includes('=') * number) -- *in_tag) +local number = lex:tag(lexer.NUMBER, lexer.dec_num * P('%')^-1) +lex:add_rule('number', lexer.after_set('=', number)) -- *in_tag) -- Entities. -lex:add_rule('entity', token('entity', '&' * word_match('lt gt amp apos quot') * ';')) -lex:add_style('entity', lexer.styles.operator) +local predefined = lex:tag(lexer.CONSTANT_BUILTIN .. '.entity', + '&' * lexer.word_match('lt gt amp apos quot') * ';') +local general = lex:tag(lexer.CONSTANT .. '.entity', '&' * identifier * ';') +lex:add_rule('entity', predefined + general) -- Fold Points. local function disambiguate_lt(text, pos, line, s) return not line:find('^</', s) and 1 or -1 end -lex:add_fold_point('element', '<', disambiguate_lt) -lex:add_fold_point('element', '/>', -1) +lex:add_fold_point(lexer.TAG, '<', disambiguate_lt) +lex:add_fold_point(lexer.TAG, '/>', -1) lex:add_fold_point(lexer.COMMENT, '<!--', '-->') lex:add_fold_point('cdata', '<![CDATA[', ']]>') +lexer.property['scintillua.comment'] = '<!--|-->' +lexer.property['scintillua.angle.braces'] = '1' +lexer.property['scintillua.word.chars'] = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' + return lex diff --git a/lua/lexers/xs.lua b/lua/lexers/xs.lua index 492590a..7527ecb 100644 --- a/lua/lexers/xs.lua +++ b/lua/lexers/xs.lua @@ -1,4 +1,4 @@ --- Copyright 2017-2022 David B. Lamkins. See LICENSE. +-- Copyright 2017-2024 David B. Lamkins. See LICENSE. -- xs LPeg lexer. -- Adapted from rc lexer by Michael Forney. @@ -54,6 +54,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('@`=!<>*&^|;?()[]{}') + '\\\n') -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('#')) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/xtend.lua b/lua/lexers/xtend.lua index c22d079..811e541 100644 --- a/lua/lexers/xtend.lua +++ b/lua/lexers/xtend.lua @@ -1,4 +1,4 @@ --- Copyright (c) 2014-2022 Piotr Orzechowski [drzewo.org]. See LICENSE. +-- Copyright (c) 2014-2024 Piotr Orzechowski [drzewo.org]. See LICENSE. -- Xtend LPeg lexer. local lexer = require('lexer') @@ -41,8 +41,7 @@ lex:add_rule('function', token(lexer.FUNCTION, lexer.word) * #P('(')) lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) -- Templates. -lex:add_rule('template', token('template', lexer.range("'''"))) -lex:add_style('template', lexer.styles.embedded) +lex:add_rule('template', token(lexer.EMBEDDED, lexer.range("'''"))) -- Strings. local sq_str = lexer.range("'", true) @@ -72,8 +71,7 @@ local float = float_pref * dec_inf * float_suff lex:add_rule('number', token(lexer.NUMBER, float + hex + dec)) -- Annotations. -lex:add_rule('annotation', token('annotation', '@' * lexer.word)) -lex:add_style('annotation', lexer.styles.preprocessor) +lex:add_rule('annotation', token(lexer.ANNOTATION, '@' * lexer.word)) -- Operators. lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;.()[]{}#'))) @@ -84,7 +82,7 @@ lex:add_rule('error', token(lexer.ERROR, lexer.any)) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') lex:add_fold_point(lexer.COMMENT, '/*', '*/') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) -lex:add_fold_point(lexer.KEYWORD, lexer.fold_consecutive_lines('import')) + +lexer.property['scintillua.comment'] = '//' return lex diff --git a/lua/lexers/yaml.lua b/lua/lexers/yaml.lua index ebf90cf..75705f4 100644 --- a/lua/lexers/yaml.lua +++ b/lua/lexers/yaml.lua @@ -1,34 +1,60 @@ --- Copyright 2006-2022 Mitchell. See LICENSE. +-- Copyright 2006-2024 Mitchell. See LICENSE. -- YAML LPeg lexer. -- It does not keep track of indentation perfectly. -local lexer = require('lexer') -local token, word_match = lexer.token, lexer.word_match +local lexer = lexer +local word_match = lexer.word_match local P, S, B = lpeg.P, lpeg.S, lpeg.B -local lex = lexer.new('yaml', {fold_by_indentation = true}) +local lex = lexer.new(..., {fold_by_indentation = true}) --- Whitespace. -local indent = #lexer.starts_line(S(' \t')) * - (token(lexer.WHITESPACE, ' ') + token('indent_error', '\t'))^1 -lex:add_rule('indent', indent) -lex:add_style('indent_error', {back = lexer.colors.red}) -lex:add_rule('whitespace', token(lexer.WHITESPACE, S(' \t')^1 + lexer.newline^1)) +-- Distinguish between horizontal and vertical space so indenting tabs can be marked as errors. +local tab_indent = lex:tag(lexer.ERROR .. '.indent', lexer.starts_line('\t', true)) +lex:modify_rule('whitespace', tab_indent + lex:tag(lexer.WHITESPACE, S(' \r\n')^1 + P('\t')^1)) + +-- Document boundaries. +lex:add_rule('doc_bounds', lex:tag(lexer.OPERATOR, lexer.starts_line(P('---') + '...'))) -- Keys. -local word = (lexer.alpha + '-' * -lexer.space) * (lexer.alnum + '-')^0 -lex:add_rule('key', token(lexer.KEYWORD, word * (S(' \t_')^1 * word^-1)^0) * #(':' * lexer.space)) +local word = (lexer.alnum + '-')^1 +lex:add_rule('key', -P('- ') * lex:tag(lexer.STRING, word * (S(' \t_')^1 * word^-1)^0) * + #P(':' * lexer.space)) + +-- Collections. +lex:add_rule('collection', lex:tag(lexer.OPERATOR, + lexer.after_set('?-:\n', S('?-') * #P(' '), ' \t') + ':' * #P(lexer.space) + S('[]{}') + ',' * + #P(' '))) + +-- Alias indicators. +local anchor = lex:tag(lexer.OPERATOR, '&') * lex:tag(lexer.LABEL, word) +local alias = lex:tag(lexer.OPERATOR, '*') * lex:tag(lexer.LABEL, word) +lex:add_rule('alias', anchor + alias) + +-- Tags. +local explicit_tag = '!!' * word_match{ + 'map', 'omap', 'pairs', 'set', 'seq', -- collection + 'binary', 'bool', 'float', 'int', 'merge', 'null', 'str', 'timestamp', 'value', 'yaml' -- scalar +} +local verbatim_tag = '!' * lexer.range('<', '>', true) +local short_tag = '!' * word * ('!' * (1 - lexer.space)^1)^-1 +lex:add_rule('tag', lex:tag(lexer.TYPE, explicit_tag + verbatim_tag + short_tag)) + +-- Comments. +lex:add_rule('comment', lex:tag(lexer.COMMENT, lexer.to_eol('#'))) + +-- Reserved. +lex:add_rule('reserved', + B(S(':,') * ' ') * lex:tag(lexer.ERROR, S('@`') + lexer.starts_line(S('@`')))) -- Constants. -lex:add_rule('constant', B(lexer.space) * token(lexer.CONSTANT, word_match('null true false', true))) +local scalar_end = #(S(' \t')^0 * lexer.newline + S(',]}') + -1) +lex:add_rule('constant', + lex:tag(lexer.CONSTANT_BUILTIN, word_match('null true false', true)) * scalar_end) -- Strings. local sq_str = lexer.range("'") local dq_str = lexer.range('"') -lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) - --- Comments. -lex:add_rule('comment', B(lexer.space) * token(lexer.COMMENT, lexer.to_eol('#'))) +lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str) * (scalar_end + #P(':' * lexer.space))) -- Timestamps. local year = lexer.digit * lexer.digit * lexer.digit * lexer.digit @@ -40,45 +66,41 @@ local minutes = lexer.digit * lexer.digit local seconds = lexer.digit * lexer.digit local fraction = '.' * lexer.digit^0 local time = hours * ':' * minutes * ':' * seconds * fraction^-1 -local T = S(' \t')^1 + S('tT') -local zone = 'Z' + S(' \t')^0 * S('-+') * hours * (':' * minutes)^-1 -lex:add_rule('timestamp', token('timestamp', date * (T * time * zone^-1)^-1)) -lex:add_style('timestamp', lexer.styles.number) +local zone = 'Z' + S(' \t')^-1 * S('-+') * hours * (':' * minutes)^-1 +lex:add_rule('timestamp', lex:tag(lexer.NUMBER .. '.timestamp', + date * (S('tT \t') * time * zone^-1)^-1) * scalar_end) -- Numbers. -local dec = lexer.digit^1 * ('_' * lexer.digit^1)^0 -local hex = '0' * S('xX') * ('_' * lexer.xdigit^1)^1 -local bin = '0' * S('bB') * S('01')^1 * ('_' * S('01')^1)^0 -local integer = S('+-')^-1 * (hex + bin + dec) -local float = S('+-')^-1 * - ((dec^-1 * '.' * dec + dec * '.' * dec^-1 * -P('.')) * (S('eE') * S('+-')^-1 * dec)^-1 + - (dec * S('eE') * S('+-')^-1 * dec)) local special_num = S('+-')^-1 * '.' * word_match('inf nan', true) -lex:add_rule('number', B(lexer.space) * token(lexer.NUMBER, special_num + float + integer)) +local number = lexer.number + special_num +lex:add_rule('number', (B(lexer.alnum) * lex:tag(lexer.DEFAULT, number) + + lex:tag(lexer.NUMBER, number)) * scalar_end) --- Types. -lex:add_rule('type', token(lexer.TYPE, '!!' * word_match({ - -- Collection types. - 'map', 'omap', 'pairs', 'set', 'seq', - -- Scalar types. - 'binary', 'bool', 'float', 'int', 'merge', 'null', 'str', 'timestamp', 'value', 'yaml' -}, true) + '!' * lexer.range('<', '>', true))) - --- Document boundaries. -lex:add_rule('doc_bounds', token('document', lexer.starts_line(P('---') + '...'))) -lex:add_style('document', lexer.styles.constant) +-- Scalars. +local block_indicator = S('|>') * (S('-+') * lexer.digit^-1 + lexer.digit * S('-+')^-1)^-1 +local block = lpeg.Cmt(lpeg.C(block_indicator * lexer.newline), function(input, index, indicator) + local indent = lexer.indent_amount[lexer.line_from_position(index - #indicator)] + for s, i, j in input:gmatch('()\n()[ \t]*()[^ \t\r\n]', index) do -- ignore blank lines + if s >= index then -- compatibility for Lua < 5.4, which doesn't have init for string.gmatch() + if j - i <= indent then return s end + end + end + return #input + 1 +end) +local seq = B('- ') * lexer.nonnewline^1 +local csv = B(', ') * (lexer.nonnewline - S(',]}'))^1 +local stop_chars, LF = {[string.byte('{')] = true, [string.byte('\n')] = true}, string.byte('\n') +local map = B(': ') * lexer.nonnewline * P(function(input, index) + local pos = index + while pos > 1 and not stop_chars[input:byte(pos)] do pos = pos - 1 end + local s = input:find(input:byte(pos) ~= LF and '[\n,}]' or '\n', index) + return s or #input + 1 +end) +lex:add_rule('scalar', lex:tag(lexer.DEFAULT, block + seq + csv + map)) -- Directives -lex:add_rule('directive', token('directive', lexer.starts_line(lexer.to_eol('%')))) -lex:add_style('directive', lexer.styles.preprocessor) - --- Indicators. -local anchor = B(lexer.space) * token(lexer.LABEL, '&' * word) -local alias = token(lexer.VARIABLE, '*' * word) -local tag = token('tag', '!' * word * P('!')^-1) -local reserved = token(lexer.ERROR, S('@`') * word) -local indicator_chars = token(lexer.OPERATOR, S('-?:,>|[]{}!')) -lex:add_rule('indicator', tag + indicator_chars + alias + anchor + reserved) -lex:add_style('tag', lexer.styles.class) +lex:add_rule('directive', lex:tag(lexer.PREPROCESSOR, lexer.starts_line(lexer.to_eol('%')))) + +lexer.property['scintillua.comment'] = '#' return lex diff --git a/lua/lexers/zig.lua b/lua/lexers/zig.lua index efc3a1f..910feb1 100644 --- a/lua/lexers/zig.lua +++ b/lua/lexers/zig.lua @@ -1,4 +1,4 @@ --- Copyright 2020-2022 Karchnu karchnu@karchnu.fr. See LICENSE. +-- Copyright 2020-2024 Karchnu karchnu@karchnu.fr. See LICENSE. -- Zig LPeg lexer. -- (Based on the C++ LPeg lexer from Mitchell.) @@ -75,9 +75,9 @@ lex:add_rule('string', token(lexer.STRING, sq_str + dq_str)) lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word)) -- Comments. -lex:add_rule('doc_comment', token('doc_comment', lexer.to_eol('///', true))) -lex:add_style('doc_comment', lexer.styles.comment) -lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('//', true))) +local doc_comment = lexer.to_eol('///', true) +local comment = lexer.to_eol('//', true) +lex:add_rule('comment', token(lexer.COMMENT, doc_comment + comment)) -- Numbers. lex:add_rule('number', token(lexer.NUMBER, lexer.number)) @@ -87,7 +87,7 @@ lex:add_rule('operator', token(lexer.OPERATOR, S('+-/*%<>!=^&|?~:;,.()[]{}'))) -- Fold points. lex:add_fold_point(lexer.OPERATOR, '{', '}') -lex:add_fold_point(lexer.COMMENT, lexer.fold_consecutive_lines('//')) -lex:add_fold_point(lexer.PREPROCESSOR, lexer.fold_consecutive_lines('///')) + +lexer.property['scintillua.comment'] = '//' return lex |
