diff options
Diffstat (limited to 'Master/texmf-dist/scripts/spelling/spelling-stage-2.lua')
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling-stage-2.lua | 208 |
1 files changed, 166 insertions, 42 deletions
diff --git a/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua b/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua index 5db44d41634..1a397217a16 100755 --- a/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua +++ b/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua @@ -21,7 +21,7 @@ -- -- @author Stephan Hennig -- @copyright 2012, 2013 Stephan Hennig --- @release version 0.3 +-- @release version 0.4 -- -- @trick Prevent LuaDoc from looking past here for module description. --[[ Trick LuaDoc into entering 'module' mode without using that command. @@ -33,20 +33,29 @@ module(...) local M = {} --- Import helper module. -local __recurse = require 'spelling-recurse' -local __recurse_node_list = __recurse.recurse_node_list +-- Import external modules. +local recurse = require('spelling-recurse') +local unicode = require('unicode') -- Function short-cuts. local tabconcat = table.concat local tabinsert = table.insert local tabremove = table.remove -local utf8char = unicode.utf8.char + local node_new = node.new local node_insert_after = node.insert_after local node_insert_before = node.insert_before +local recurse_node_list = recurse.recurse_node_list + +local Sfind = string.find +local Sgmatch = string.gmatch +local Smatch = string.match + +local Uchar = unicode.utf8.char +local Umatch = unicode.utf8.match + -- Short-cuts for constants. local DISC = node.id('disc') @@ -61,27 +70,17 @@ local PDF_COLORSTACK = node.subtype('pdf_colorstack') -- Declare local variables to store references to resources that are -- provided by external code. -- --- Table of known bad spellings. -local __is_bad +-- Table of bad rules. +local __rules_bad -- --- Table of known good spellings. -local __is_good +-- Table of good rules. +local __rules_good -- --- ID of user-defined whatsit nodes. -local __whatsit_uid - - ---- Set module resources. --- Make various resources, that are provided by external code, available --- to this module. +-- ID of user-defined whatsit nodes marking the start of a word. +local __uid_start_tag -- --- @param res Ressource table. -local function set_resources(res) - __is_bad = res.is_bad - __is_good = res.is_good - __whatsit_uid = res.whatsit_uid -end -M.set_resources = set_resources +-- ID of user-defined whatsit nodes marking the end of a word. +local __uid_end_tag --- Module options. @@ -98,18 +97,94 @@ local __opts = { --- Set colour used for highlighting. --- Set colourused for highlighting bad spellings in PDF output. +-- Set colour used for highlighting bad spellings in PDF output. The +-- argument is checked for a valid PDF colour statement. As an example, +-- the string `1 0 0 rg` represents a red colour in the RGB colour +-- space. A similar colour in the CMYK colour space would be +-- represented by the string '0 1 1 0 k'. -- --- @param col New colour. This must be a colour statement in PDF format --- given as string. As an example, the string `1 0 0 rg` represents a --- red colour in the RGB colour space. A similar colour in the CMYK --- colour space would be represented by the string '0 1 1 0 k'. +-- @param col New colour. local function set_highlight_color(col) - __opts.hl_color = col + -- Extract all colour components. + local components = Smatch(col, '^(%S+ %S+ %S+) rg$') or Smatch(col, '^(%S+ %S+ %S+ %S+) k$') + local is_valid_arg = components + if is_valid_arg then + -- Validate colour components. + for comp in Sgmatch(components, '%S+') do + -- Check number syntax. + local is_valid_comp = Sfind(comp, '^%d+%.?%d*$') or Sfind(comp, '^%d*%.?%d+$') + if is_valid_comp then + -- Check number range. + comp = tonumber(comp) + is_valid_comp = comp >= 0 and comp <= 1 + end + is_valid_arg = is_valid_arg and is_valid_comp + end + end + if is_valid_arg then + __opts.hl_color = col + else + error('package spelling: Error! Invalid PDF colour statement: ' .. tostring(col)) + end end M.set_highlight_color = set_highlight_color +--- Highlighting status cache table. +-- Determining the highlighting status of a string can be an expensive +-- operation. To reduce average run-time penalty per string, +-- highlighting status of all strings found in a document is cached in +-- this table, so that determining the highlighting status of a known +-- string requires only one table look-up.<br /> +-- +-- This table needs an `__index` meta method calculating the +-- highlighting status of unknown keys (strings). +-- +-- @class table +-- @name __is_highlighting_needed +local __is_highlighting_needed = {} + + +--- Calculate and cache the highlighting status of a string. +-- First, surrounding punctuation is stripped from the string argument. +-- Then, the given raw as well as the stripped string are checked +-- against all rules. Highlighting of the string is required, if any +-- bad rule matches, but no good rule matches. That is, good rules take +-- precedence over bad rules. +-- +-- @param t Original table. +-- @param raw Raw string to check. +-- @return True, if highlighting is required. False, otherwise. +local function __calc_is_highlighting_needed(t, raw) + -- Strip surrounding punctuation from string. + local stripped = Umatch(raw, '^%p*(.-)%p*$') + -- Check for a bad match. + local is_bad = false + for _,matches_bad in ipairs(__rules_bad) do + is_bad = is_bad or matches_bad(raw, stripped) + if is_bad then break end + end + -- Check for a good match. + local is_good = false + for _,matches_good in ipairs(__rules_good) do + is_good = is_good or matches_good(raw, stripped) + if is_good then break end + end + -- Calculate highlighting status. + local status = (is_bad and not is_good) or false + -- Store status in cache table. + rawset(t, raw, status) + -- Return status. + return status +end + + +-- Set-up meta table for highlighting status cache table. +setmetatable(__is_highlighting_needed, { + __index = __calc_is_highlighting_needed, +}) + + --- Convert a Unicode code point to a regular UTF-8 encoded string. -- This function can be used as an `__index` meta method. -- @@ -117,7 +192,7 @@ M.set_highlight_color = set_highlight_color -- @param cp originl key, a Unicode code point -- @return UTF-8 encoded string corresponding to the Unicode code point. local function __meta_cp2utf8(t, cp) - return utf8char(cp) + return Uchar(cp) end @@ -146,8 +221,6 @@ local __codepoint_map = { [0x0153] = 'oe',-- LATIN SMALL LIGATURE OE [0x017f] = 's',-- LATIN SMALL LETTER LONG S - [0x1e9e] = 'SS',-- LATIN CAPITAL LETTER SHARP S - [0xfb00] = 'ff',-- LATIN SMALL LIGATURE FF [0xfb01] = 'fi',-- LATIN SMALL LIGATURE FI [0xfb02] = 'fl',-- LATIN SMALL LIGATURE FL @@ -250,7 +323,7 @@ local function __tag_word(word) local start_tag = node_new(WHATSIT, USER_DEFINED) -- Mark whatsit node with module ID, so that we can recognize it -- later. - start_tag.user_id = __whatsit_uid + start_tag.user_id = __uid_start_tag -- Value is an empty string. start_tag.type = 115 start_tag.value = '' @@ -261,7 +334,7 @@ local function __tag_word(word) local end_tag = node_new(WHATSIT, USER_DEFINED) -- Mark whatsit node with module ID, so that we can recognize it -- later. - end_tag.user_id = __whatsit_uid + end_tag.user_id = __uid_end_tag -- Value of end tag is an index (a number). end_tag.type = 115 end_tag.value = word @@ -283,6 +356,29 @@ local function __highlight_by_color() local pop = node_new(WHATSIT, PDF_COLORSTACK) push.stack = 0 pop.stack = 0 + push.command = 1 + pop.command = 2 + push.data = __opts.hl_color + node_insert_before(__curr_word_start_head, __curr_word_start, push) + node_insert_after(__curr_word_end_head, __curr_word_end, pop) + end +end + + +--- Highlight bad spelling by colour (using node field `cmd`). +-- Insert colour whatsits before and after the first and last nodes +-- known to belong to the current word. +-- @see function __highlight_by_color +local function __highlight_by_color_cmd() + -- Check, if start node of current word is a head node. Inserting + -- before head nodes needs special attention. This is not yet + -- implemented. + if (__curr_word_start ~= __curr_word_start_head) then + -- Create pdf_colorstack whatsit nodes. + local push = node_new(WHATSIT, PDF_COLORSTACK) + local pop = node_new(WHATSIT, PDF_COLORSTACK) + push.stack = 0 + pop.stack = 0 push.cmd = 1 pop.cmd = 2 push.data = __opts.hl_color @@ -332,13 +428,10 @@ local function __finish_current_word() if start_prev and end_next and (start_prev.id == WHATSIT) and (start_prev.subtype == USER_DEFINED) - and (start_prev.user_id == __whatsit_uid) - and (start_prev.type == 115) - and (start_prev.value == '') + and (start_prev.user_id == __uid_start_tag) and (end_next.id == WHATSIT) and (end_next.subtype == USER_DEFINED) - and (end_next.user_id == __whatsit_uid) - and (end_next.type == 115) + and (end_next.user_id == __uid_end_tag) and (end_next.value == word) then __curr_word = nil __curr_word_start_head = nil @@ -351,8 +444,8 @@ local function __finish_current_word() if __is_active_tagging then __tag_word(word) end - -- Test for bad spelling. - if __is_active_highlighting and __is_bad[word] and not __is_good[word] then + -- Highlighting needed? + if __is_highlighting_needed[word] and __is_active_highlighting then __highlight_bad_word() end __curr_word = nil @@ -475,7 +568,7 @@ local function __process_node_list(head) __curr_word_start = nil __curr_word_end_head = nil __curr_word_end = nil - __recurse_node_list(head, __cb_tag_words) + recurse_node_list(head, __cb_tag_words) -- Clean-up left-over word and/or paragraph. __finish_current_paragraph() end @@ -526,9 +619,40 @@ end M.disable_word_highlighting = disable_word_highlighting +--- Try to maintain compatibility with older LuaTeX versions. +-- Between LuaTeX 0.70.2 and 0.76.0 node field `cmd` of `whatsits` nodes +-- of subtype `pdf_colorstack` has been renamed to `command`. This +-- function checks which node field is the correct one and activates a +-- fall-back function in case. Due to a bug in LuaTeX 0.76.0 (shipped +-- with TL2013) function `node.has_field()` doesn't return correct +-- results. It is therefore tested if an assignment to field `command` +-- raises an error or not. +local function __maintain_compatibility() + -- Create pdf_colorstack whatsit node. + local n = node.new(WHATSIT, PDF_COLORSTACK) + -- Function that assigns a value to node field 'command'. + local f = function() + n.command = 1 + end + -- If the assignment is not successful, fall-back to node field 'cmd'. + if not pcall(f) then + __highlight_by_color = __highlight_by_color_cmd + end + -- Delete test node. + node.free(n) +end + + --- Module initialisation. -- local function __init() + -- Try to maintain compatibility with older LuaTeX versions. + __maintain_compatibility() + -- Get local references to package ressources. + __rules_bad = PKG_spelling.res.rules_bad + __rules_good = PKG_spelling.res.rules_good + __uid_start_tag = PKG_spelling.res.whatsit_ids.start_tag + __uid_end_tag = PKG_spelling.res.whatsit_ids.end_tag -- Create empty paragraph management stack. __is_vlist_paragraph = {} -- Remember tagging status. |