aboutsummaryrefslogtreecommitdiffhomepage
path: root/lexlua/lexer.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lexlua/lexer.lua')
-rw-r--r--lexlua/lexer.lua314
1 files changed, 164 insertions, 150 deletions
diff --git a/lexlua/lexer.lua b/lexlua/lexer.lua
index 6f88d240f..0a92f6064 100644
--- a/lexlua/lexer.lua
+++ b/lexlua/lexer.lua
@@ -306,70 +306,39 @@ local M = {}
-- 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
--- strings of comma-separated property settings. 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 include [`lexer.STYLE_CLASS`](),
--- [`lexer.STYLE_COMMENT`](), [`lexer.STYLE_CONSTANT`](),
--- [`lexer.STYLE_ERROR`](), [`lexer.STYLE_EMBEDDED`](),
--- [`lexer.STYLE_FUNCTION`](), [`lexer.STYLE_IDENTIFIER`](),
--- [`lexer.STYLE_KEYWORD`](), [`lexer.STYLE_LABEL`](), [`lexer.STYLE_NUMBER`](),
--- [`lexer.STYLE_OPERATOR`](), [`lexer.STYLE_PREPROCESSOR`](),
--- [`lexer.STYLE_REGEX`](), [`lexer.STYLE_STRING`](), [`lexer.STYLE_TYPE`](),
--- [`lexer.STYLE_VARIABLE`](), and [`lexer.STYLE_WHITESPACE`](). Like with
--- predefined token names and LPeg patterns, you may define your own styles. At
--- their core, styles are just strings, so you may create new ones and/or modify
--- existing ones. Each style consists of the following comma-separated settings:
---
--- Setting | Description
--- ---------------|------------
--- font:_name_ | The name of the font the style uses.
--- size:_int_ | The size of the font the style uses.
--- [not]bold | Whether or not the font face is bold.
--- weight:_int_ | The weight or boldness of a font, between 1 and 999.
--- [not]italics | Whether or not the font face is italic.
--- [not]underlined| Whether or not the font face is underlined.
--- fore:_color_ | The foreground color of the font face.
--- back:_color_ | The background color of the font face.
--- [not]eolfilled | Does the background color extend to the end of the line?
--- case:_char_ | The case of the font ('u': upper, 'l': lower, 'm': normal).
--- [not]visible | Whether or not the text is visible.
--- [not]changeable| Whether the text is changeable or read-only.
---
--- Specify font colors in either "#RRGGBB" format, "0xBBGGRR" format, or the
--- decimal equivalent of the latter. 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.STYLE_STRING` style instead of defining a new one.
+-- 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 string:
+-- default theme settings is simply an empty table:
--
--- local style_nothing = ''
+-- local style_nothing = {}
--
-- A similar style but with a bold font face looks like this:
--
--- local style_bold = 'bold'
+-- local style_bold = {bold = true}
--
--- If you want the same style, but also with an italic font face, define the new
--- style in terms of the old one:
+-- 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_bold_italic = style_bold .. ',italics'
---
--- This allows you to derive new styles from predefined ones without having to
--- rewrite them. This operation leaves the old style unchanged. Thus if you
--- had a "static variable" token whose style you wanted to base off of
--- `lexer.STYLE_VARIABLE`, it would probably look like:
---
--- local style_static_var = lexer.STYLE_VARIABLE .. ',italics'
+-- local style_static_var = lexer.styles.variable .. {italics = true}
--
-- The color theme files in the *lexers/themes/* folder give more examples of
-- style definitions.
@@ -391,7 +360,7 @@ local M = {}
--
-- Assigning a style to this token looks like:
--
--- lex:add_style('custom_whitespace', lexer.STYLE_WHITESPACE)
+-- 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"
@@ -399,13 +368,11 @@ local M = {}
-- prefer to color the background of whitespace a shade of grey, it might look
-- like:
--
--- local custom_style = lexer.STYLE_WHITESPACE .. ',back:$(color.grey)'
--- lex:add_style('custom_whitespace', custom_style)
+-- lex:add_style('custom_whitespace',
+-- lexer.styles.whitespace .. {back = lexer.colors.grey})
--
--- Notice that the lexer peforms Scintilla-style "$()" property expansion. You
--- may also use "%()". Remember to refrain from assigning specific colors in
--- styles, but in this case, all user color themes probably define the
--- "color.grey" property.
+-- Remember to refrain from assigning specific colors in styles, but in this
+-- case, all user color themes probably define `colors.grey`.
--
-- #### Line Lexers
--
@@ -491,7 +458,7 @@ local M = {}
-- 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.STYLE_EMBEDDED)
+-- lex:add_style('php_tag', lexer.styles.embedded)
-- html:embed(lex, php_start_rule, php_end_rule)
--
-- #### Lexers with Complex State
@@ -702,7 +669,7 @@ local M = {}
-- lex:add_rule('whitespace', token(lexer.WHITESPACE, lexer.space^1))
-- lex:add_rule('keyword', token(lexer.KEYWORD, word_match[[foo bar baz]]))
-- lex:add_rule('custom', token('custom', P('quux')))
--- lex:add_style('custom', lexer.STYLE_KEYWORD .. ',bold')
+-- lex:add_style('custom', lexer.styles.keyword .. {bold = true})
-- lex:add_rule('identifier', token(lexer.IDENTIFIER, lexer.word))
-- lex:add_rule('string', token(lexer.STRING, lexer.range('"')))
-- lex:add_rule('comment', token(lexer.COMMENT, lexer.to_eol('#')))
@@ -796,58 +763,6 @@ local M = {}
-- The token name for label tokens.
-- @field REGEX (string)
-- The token name for regex tokens.
--- @field STYLE_CLASS (string)
--- The style typically used for class definitions.
--- @field STYLE_COMMENT (string)
--- The style typically used for code comments.
--- @field STYLE_CONSTANT (string)
--- The style typically used for constants.
--- @field STYLE_ERROR (string)
--- The style typically used for erroneous syntax.
--- @field STYLE_FUNCTION (string)
--- The style typically used for function definitions.
--- @field STYLE_KEYWORD (string)
--- The style typically used for language keywords.
--- @field STYLE_LABEL (string)
--- The style typically used for labels.
--- @field STYLE_NUMBER (string)
--- The style typically used for numbers.
--- @field STYLE_OPERATOR (string)
--- The style typically used for operators.
--- @field STYLE_REGEX (string)
--- The style typically used for regular expression strings.
--- @field STYLE_STRING (string)
--- The style typically used for strings.
--- @field STYLE_PREPROCESSOR (string)
--- The style typically used for preprocessor statements.
--- @field STYLE_TYPE (string)
--- The style typically used for static types.
--- @field STYLE_VARIABLE (string)
--- The style typically used for variables.
--- @field STYLE_WHITESPACE (string)
--- The style typically used for whitespace.
--- @field STYLE_EMBEDDED (string)
--- The style typically used for embedded code.
--- @field STYLE_IDENTIFIER (string)
--- The style typically used for identifier words.
--- @field STYLE_DEFAULT (string)
--- The style all styles are based off of.
--- @field STYLE_LINENUMBER (string)
--- The style used for all margins except fold margins.
--- @field STYLE_BRACELIGHT (string)
--- The style used for highlighted brace characters.
--- @field STYLE_BRACEBAD (string)
--- The style used for unmatched brace characters.
--- @field STYLE_CONTROLCHAR (string)
--- The style used for control characters.
--- Color attributes are ignored.
--- @field STYLE_INDENTGUIDE (string)
--- The style used for indentation guides.
--- @field STYLE_CALLTIP (string)
--- The style used by call tips if [`view.call_tip_use_style`]() is set.
--- Only the font name, size, and color attributes are used.
--- @field STYLE_FOLDDISPLAYTEXT (string)
--- The style used for fold display text.
-- @field any (pattern)
-- A pattern that matches any single character.
-- @field ascii (pattern)
@@ -965,6 +880,98 @@ local function searchpath(name, path)
return nil, table.concat(tried, '\n')
end
+---
+-- Map of color names strings to color values in `0xBBGGRR` or `"#RRGGBB"`
+-- format.
+-- @name colors
+-- @class table
+M.colors = setmetatable({}, {
+ __index = function(_, name) return M.property['color.' .. name] end,
+ __newindex = function(_, name, color) M.property['color.' .. name] = color end
+})
+
+-- 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)
+ end
+ prop_string = table.concat(settings, ',')
+ end
+ return setmetatable({prop_string = prop_string}, style_obj)
+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)
+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.
+--
+-- 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 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)
+ end
+})
+
-- Default styles.
local default = {
'nothing', 'whitespace', 'comment', 'string', 'number', 'keyword',
@@ -973,16 +980,16 @@ local default = {
}
for _, name in ipairs(default) do
M[name:upper()] = name
- M['STYLE_' .. name:upper()] = string.format('$(style.%s)', name)
+ M['STYLE_' .. name:upper()] = style_obj.new(name) -- backward compatibility
end
-- Predefined styles.
local predefined = {
- 'default', 'linenumber', 'bracelight', 'bracebad', 'controlchar',
- 'indentguide', 'calltip', 'folddisplaytext'
+ '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()] = string.format('$(style.%s)', name)
+ M['STYLE_' .. name:upper()] = style_obj.new(name) -- backward compatibility
end
---
@@ -1032,44 +1039,47 @@ function M.get_rule(lexer, id)
end
---
--- Associates string *token_name* in lexer *lexer* with Scintilla style string
--- *style*.
--- Style strings are comma-separated property settings. Available property
--- settings are:
---
--- * `font:name`: Font name.
--- * `size:int`: Font size.
--- * `bold` or `notbold`: Whether or not the font face is bold.
--- * `weight:int`: Font weight (between 1 and 999).
--- * `italics` or `notitalics`: Whether or not the font face is italic.
--- * `underlined` or `notunderlined`: Whether or not the font face is
--- underlined.
--- * `fore:color`: Font face foreground color in "#RRGGBB" or 0xBBGGRR format.
--- * `back:color`: Font face background color in "#RRGGBB" or 0xBBGGRR format.
--- * `eolfilled` or `noteolfilled`: Whether or not the background color
--- extends to the end of the line.
--- * `case:char`: Font case ('u' for uppercase, 'l' for lowercase, and 'm' for
--- mixed case).
--- * `visible` or `notvisible`: Whether or not the text is visible.
--- * `changeable` or `notchangeable`: Whether or not the text is changeable or
--- read-only.
---
--- Property settings may also contain "$(property.name)" expansions for
--- properties defined in Scintilla, theme files, etc.
+-- 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.STYLE_STRING)
--- @usage lex:add_style('deprecated_func', lexer.STYLE_FUNCTION .. ',italics')
--- @usage lex:add_style('visible_ws',
--- lexer.STYLE_WHITESPACE .. ',back:$(color.grey)')
+-- @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
- lexer._EXTRASTYLES[token_name] = style
+ 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
@@ -1529,7 +1539,11 @@ function M.load(name, alt_name, cache)
-- `property_int` tables do not exist (they are not useful). Create them in
-- order prevent errors from occurring.
if not M.property then
- M.property = {['lexer.lpeg.home'] = package.path:gsub('/%?%.lua', '')}
+ 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
+ })
M.property_int = setmetatable({}, {
__index = function(t, k) return tonumber(M.property[k]) or 0 end,
__newindex = function() error('read-only property') end
@@ -1555,7 +1569,7 @@ function M.load(name, alt_name, cache)
process_legacy_lexer(lexer._lexer) -- mainly for `_foldsymbols` edits
end
end
- lexer:add_style((alt_name or name) .. '_whitespace', M.STYLE_WHITESPACE)
+ 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