From f71534e399c31f2518eedfaeea00155d26d80daf Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Fri, 3 Jun 2016 01:31:46 -0700 Subject: [PATCH 1/5] readerfooter(fix): only populate footer info after document is loaded --- frontend/apps/reader/modules/readerfooter.lua | 10 ++++++++++ frontend/apps/reader/readerui.lua | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index b707a661d..d5fdd2638 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -314,7 +314,13 @@ function ReaderFooter:updateFooterPos() self:updateFooterText() end +-- updateFooterText will start as a noop. After onReaderReady event is +-- received, it will initialized as _updateFooterText function ReaderFooter:updateFooterText() +end + +-- only call this function after document is fully loaded +function ReaderFooter:_updateFooterText() if self.settings.toc_markers and self.progress_bar.ticks == nil then local ticks_candidates = {} if self.ui.toc then @@ -410,6 +416,10 @@ function ReaderFooter:onUpdatePos() self:updateFooter() end +function ReaderFooter:onReaderReady() + self.updateFooterText = self._updateFooterText + self:updateFooter() +end function ReaderFooter:applyFooterMode(mode) -- three modes switcher for reader footer diff --git a/frontend/apps/reader/readerui.lua b/frontend/apps/reader/readerui.lua index 16eb91313..a35568920 100644 --- a/frontend/apps/reader/readerui.lua +++ b/frontend/apps/reader/readerui.lua @@ -321,7 +321,7 @@ function ReaderUI:init() v() end - -- After initialisation notify that document is loaded + -- After initialisation notify that document is loaded and rendered -- CREngine only reports correct page count after rendering is done -- Need the same event for PDF document self:handleEvent(Event:new("ReaderReady", self.doc_settings)) From 2e417cfbd8676bb6097d39f79159a39aed01d7dc Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Fri, 3 Jun 2016 22:05:14 -0700 Subject: [PATCH 2/5] filemanager(refactor): use purge method from docsettings --- .ci/script.sh | 2 +- frontend/apps/filemanager/filemanager.lua | 11 ++------ frontend/docsettings.lua | 34 ++++++++++------------- spec/unit/docsettings_spec.lua | 14 ++++++---- spec/unit/filemanager_spec.lua | 19 +++++++------ spec/unit/readerbookmark_spec.lua | 4 +-- spec/unit/readerui_spec.lua | 15 +++++----- 7 files changed, 46 insertions(+), 53 deletions(-) diff --git a/.ci/script.sh b/.ci/script.sh index 701ff6534..62ff30eda 100755 --- a/.ci/script.sh +++ b/.ci/script.sh @@ -8,4 +8,4 @@ make all retry_cmd 2 make testfront set +o pipefail luajit $(which luacheck) --no-color -q frontend | tee ./luacheck.out -test $(grep Total ./luacheck.out | awk '{print $2}') -le 54 +test $(grep Total ./luacheck.out | awk '{print $2}') -le 53 diff --git a/frontend/apps/filemanager/filemanager.lua b/frontend/apps/filemanager/filemanager.lua index 16e33ed29..9c1be469a 100644 --- a/frontend/apps/filemanager/filemanager.lua +++ b/frontend/apps/filemanager/filemanager.lua @@ -12,7 +12,7 @@ local FileChooser = require("ui/widget/filechooser") local TextWidget = require("ui/widget/textwidget") local Blitbuffer = require("ffi/blitbuffer") local lfs = require("libs/libkoreader-lfs") -local docsettings = require("docsettings") +local DocSettings = require("docsettings") local UIManager = require("ui/uimanager") local Screen = require("device").screen local Geom = require("ui/geometry") @@ -309,14 +309,7 @@ function FileManager:deleteFile(file) ok, err = os.remove(file_abs_path) if ok and err == nil then if is_doc ~= nil then - -- also delete history/settings for documents - local sidecar_dir = docsettings:getSidecarDir(file_abs_path) - if lfs.attributes(sidecar_dir, "mode") == "directory" then - util.purgeDir(sidecar_dir) - end - local legacy_history_file = docsettings:getHistoryPath(file) - -- @todo: check return from os.remove - os.remove(legacy_history_file) + DocSettings:open(file):purge() end UIManager:show(InfoMessage:new{ text = util.template(_("Deleted %1"), file), diff --git a/frontend/docsettings.lua b/frontend/docsettings.lua index 1827edbbd..1943d992e 100644 --- a/frontend/docsettings.lua +++ b/frontend/docsettings.lua @@ -5,7 +5,8 @@ local purgeDir = require("ffi/util").purgeDir local DocSettings = {} -local history_dir = DataStorage:getHistoryDir() +local HISTORY_DIR = DataStorage:getHistoryDir() +local READER_SETTING_FILE = DataStorage:getDataDir() .. "/settings.reader.lua" local function buildCandidate(file_path) if lfs.attributes(file_path, "mode") == "file" then @@ -21,7 +22,7 @@ function DocSettings:getSidecarDir(doc_path) end function DocSettings:getHistoryPath(fullpath) - return history_dir .. "/[" .. fullpath:gsub("(.*/)([^/]+)","%1] %2"):gsub("/","#") .. ".lua" + return HISTORY_DIR .. "/[" .. fullpath:gsub("(.*/)([^/]+)","%1] %2"):gsub("/","#") .. ".lua" end function DocSettings:getPathFromHistory(hist_name) @@ -39,24 +40,19 @@ function DocSettings:getNameFromHistory(hist_name) return string.sub(hist_name, s+2, -5) end -function DocSettings:purgeDocSettings(doc_path) - purgeDir(self:getSidecarDir(doc_path)) - os.remove(self:getHistoryPath(doc_path)) -end - function DocSettings:open(docfile) -- TODO(zijiehe): Remove history_path, use only sidecar. local new = { data = {} } local ok, stored if docfile == ".reader" then -- we handle reader setting as special case - new.history_file = DataStorage:getDataDir() .. "/settings.reader.lua" - + new.history_file = READER_SETTING_FILE ok, stored = pcall(dofile, new.history_file) else new.history_file = self:getHistoryPath(docfile) local sidecar = self:getSidecarDir(docfile) + new.sidecar = sidecar if lfs.attributes(sidecar, "mode") ~= "directory" then lfs.mkdir(sidecar) end @@ -67,19 +63,19 @@ function DocSettings:open(docfile) -- can handle two files with only different suffixes. new.sidecar_file = sidecar.."/metadata.".. docfile:match(".*%.(.*)")..".lua" + new.legacy_sidecar_file = sidecar.."/".. + docfile:match(".*%/(.*)")..".lua" end local candidates = {} -- New sidecar file - table.insert(candidates, buildCandidate(new.sidecar_file)); + table.insert(candidates, buildCandidate(new.sidecar_file)) -- Legacy sidecar file - table.insert(candidates, buildCandidate( - self:getSidecarDir(docfile).."/".. - docfile:match(".*%/(.*)")..".lua")) + table.insert(candidates, buildCandidate(new.legacy_sidecar_file)) -- Legacy history folder - table.insert(candidates, buildCandidate(new.history_file)); + table.insert(candidates, buildCandidate(new.history_file)) -- Legacy kpdfview setting - table.insert(candidates, buildCandidate(docfile..".kpdfview.lua")); + table.insert(candidates, buildCandidate(docfile..".kpdfview.lua")) table.sort(candidates, function(l, r) if l == nil then return false @@ -100,7 +96,7 @@ function DocSettings:open(docfile) new.data = stored end - return setmetatable(new, { __index = DocSettings}) + return setmetatable(new, {__index = DocSettings}) end function DocSettings:readSetting(key) @@ -144,12 +140,12 @@ function DocSettings:close() self:flush() end -function DocSettings:clear() +function DocSettings:purge() if self.history_file then os.remove(self.history_file) end - if self.sidecar_file then - os.remove(self.sidecar_file) + if lfs.attributes(self.sidecar, "mode") == "directory" then + purgeDir(self.sidecar) end end diff --git a/spec/unit/docsettings_spec.lua b/spec/unit/docsettings_spec.lua index a7a79edb4..743a8b802 100644 --- a/spec/unit/docsettings_spec.lua +++ b/spec/unit/docsettings_spec.lua @@ -1,10 +1,12 @@ -require("commonrequire") -local doc = require("docsettings") - describe("docsettings module", function() + local docsettings + setup(function() + require("commonrequire") + docsettings = require("docsettings") + end) it("should generate sidecar directory path", function() - assert.Equals("../../foo.sdr", doc:getSidecarDir("../../foo.pdf")) - assert.Equals("/foo/bar.sdr", doc:getSidecarDir("/foo/bar.pdf")) - assert.Equals("baz.sdr", doc:getSidecarDir("baz.pdf")) + assert.Equals("../../foo.sdr", docsettings:getSidecarDir("../../foo.pdf")) + assert.Equals("/foo/bar.sdr", docsettings:getSidecarDir("/foo/bar.pdf")) + assert.Equals("baz.sdr", docsettings:getSidecarDir("baz.pdf")) end) end) diff --git a/spec/unit/filemanager_spec.lua b/spec/unit/filemanager_spec.lua index e1af0fba3..e57303a3f 100644 --- a/spec/unit/filemanager_spec.lua +++ b/spec/unit/filemanager_spec.lua @@ -1,13 +1,14 @@ -require("commonrequire") -local FileManager = require("apps/filemanager/filemanager") -local lfs = require("libs/libkoreader-lfs") -local docsettings = require("docsettings") -local UIManager = require("ui/uimanager") -local Screen = require("device").screen -local util = require("ffi/util") -local DEBUG = require("dbg") - describe("FileManager module", function() + local FileManager, lfs, docsettings, UIManager, Screen, util + setup(function() + require("commonrequire") + FileManager = require("apps/filemanager/filemanager") + lfs = require("libs/libkoreader-lfs") + docsettings = require("docsettings") + UIManager = require("ui/uimanager") + Screen = require("device").screen + util = require("ffi/util") + end) it("should show file manager", function() UIManager:quit() local filemanager = FileManager:new{ diff --git a/spec/unit/readerbookmark_spec.lua b/spec/unit/readerbookmark_spec.lua index 3cc8e6a9a..057a82233 100644 --- a/spec/unit/readerbookmark_spec.lua +++ b/spec/unit/readerbookmark_spec.lua @@ -48,7 +48,7 @@ describe("ReaderBookmark module", function() local page = 10 local readerui setup(function() - DocSettings:purgeDocSettings(sample_epub) + DocSettings:open(sample_epub):purge() readerui = ReaderUI:new{ document = DocumentRegistry:openDocument(sample_epub), } @@ -130,7 +130,7 @@ describe("ReaderBookmark module", function() describe("bookmark for PDF document", function() local readerui setup(function() - DocSettings:purgeDocSettings(sample_pdf) + DocSettings:open(sample_pdf):purge() readerui = ReaderUI:new{ document = DocumentRegistry:openDocument(sample_pdf), } diff --git a/spec/unit/readerui_spec.lua b/spec/unit/readerui_spec.lua index da94bd58a..3ce53eec1 100644 --- a/spec/unit/readerui_spec.lua +++ b/spec/unit/readerui_spec.lua @@ -1,20 +1,21 @@ -require("commonrequire") -local DocumentRegistry = require("document/documentregistry") -local ReaderUI = require("apps/reader/readerui") -local DocSettings = require("docsettings") -local UIManager = require("ui/uimanager") - describe("Readerui module", function() + local DocumentRegistry, ReaderUI, DocSettings, UIManager local sample_epub = "spec/front/unit/data/leaves.epub" local readerui setup(function() + require("commonrequire") + DocumentRegistry = require("document/documentregistry") + ReaderUI = require("apps/reader/readerui") + DocSettings = require("docsettings") + UIManager = require("ui/uimanager") + readerui = ReaderUI:new{ document = DocumentRegistry:openDocument(sample_epub), } end) it("should save settings", function() -- remove history settings and sidecar settings - DocSettings:open(sample_epub):clear() + DocSettings:open(sample_epub):purge() local doc_settings = DocSettings:open(sample_epub) assert.are.same(doc_settings.data, {}) readerui:saveSettings() From cc425287da77133623e3e5fecd6bbed46fe5098d Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Sat, 4 Jun 2016 19:51:47 -0700 Subject: [PATCH 3/5] doc(fix): avoid module keyword --- frontend/dbg.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/dbg.lua b/frontend/dbg.lua index af437cc7b..8633510a1 100644 --- a/frontend/dbg.lua +++ b/frontend/dbg.lua @@ -30,9 +30,9 @@ function Dbg:turnOn() self.is_on = true Dbg_mt.__call = function(dbg, ...) LvDEBUG(math.huge, ...) end - Dbg.guard = function(_, module, method, pre_guard, post_guard) - local old_method = module[method] - module[method] = function(...) + Dbg.guard = function(_, mod, method, pre_guard, post_guard) + local old_method = mod[method] + mod[method] = function(...) if pre_guard then pre_guard(...) end From adf5ffdd26a06b27afd2b03ab59cd8ee3f1980e0 Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Sun, 5 Jun 2016 00:08:23 -0700 Subject: [PATCH 4/5] dictquicklookup(fix): use self.region for matching hold event --- frontend/ui/widget/dictquicklookup.lua | 9 ++++++--- spec/unit/textboxwidget_spec.lua | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 spec/unit/textboxwidget_spec.lua diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 0a66bb0db..57009c3e8 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -81,12 +81,14 @@ function DictQuickLookup:init() HoldWord = { GestureRange:new{ ges = "hold", - range = function() return self.dimen end, + range = function() + return self.region + end, }, -- callback function when HoldWord is handled as args args = function(word) - self.ui:handleEvent(Event:new("LookupWord", - word, self.word_box)) + self.ui:handleEvent( + Event:new("LookupWord", word, self.word_box)) end }, } @@ -293,6 +295,7 @@ function DictQuickLookup:onShow() end function DictQuickLookup:getHighlightedItem() + if not self.ui then return end return self.ui.highlight:getHighlightBookmarkItem() end diff --git a/spec/unit/textboxwidget_spec.lua b/spec/unit/textboxwidget_spec.lua new file mode 100644 index 000000000..d403ef979 --- /dev/null +++ b/spec/unit/textboxwidget_spec.lua @@ -0,0 +1,25 @@ +describe("TextBoxWidget module", function() + local TextBoxWidget, Font + setup(function() + require("commonrequire") + Font = require("ui/font") + TextBoxWidget = require("ui/widget/textboxwidget") + end) + + it("should select the correct word on HoldWord event", function() + local tw = TextBoxWidget:new{ + dimen = {x = 0, y = 0}, + face = Font:getFace("cfont", 25), + text = 'YOOOOOOOOOOOOOOOO\nFoo.\nBar.', + } + tw:onHoldWord(function(w) + assert.is.same(w, 'YOOOOOOOOOOOOOOOO') + end, {pos={x=110,y=4}}) + tw:onHoldWord(function(w) + assert.is.same(w, 'Foo') + end, {pos={x=0,y=50}}) + tw:onHoldWord(function(w) + assert.is.same(w, 'Bar') + end, {pos={x=20,y=80}}) + end) +end) From 301925e34a6bad634c0fefbed97e4d076029613b Mon Sep 17 00:00:00 2001 From: Qingping Hou Date: Sun, 5 Jun 2016 00:33:31 -0700 Subject: [PATCH 5/5] textboxwidget(fix): handle onHoldWord event --- frontend/ui/widget/textboxwidget.lua | 104 ++++++++++++++++++--------- frontend/util.lua | 39 +++++++--- spec/unit/util_spec.lua | 15 ++++ 3 files changed, 115 insertions(+), 43 deletions(-) diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index 0d10897df..d28bfba61 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -1,3 +1,17 @@ +--[[-- +A TextWidget that handles long text wrapping + +Example: + + local Foo = TextBoxWidget:new{ + face = Font:getFace("cfont", 25), + text = 'We can show multiple lines.\nFoo.\nBar.', + -- width = Screen:getWidth()*2/3, + } + UIManager:show(Foo) + +]] + local Blitbuffer = require("ffi/blitbuffer") local Widget = require("ui/widget/widget") local LineWidget = require("ui/widget/linewidget") @@ -5,11 +19,7 @@ local RenderText = require("ui/rendertext") local Screen = require("device").screen local Geom = require("ui/geometry") local util = require("util") -local DEBUG = require("dbg") ---[[ -A TextWidget that handles long text wrapping ---]] local TextBoxWidget = Widget:new{ text = nil, charlist = nil, @@ -29,11 +39,11 @@ local TextBoxWidget = Widget:new{ } function TextBoxWidget:init() - local line_height = (1 + self.line_height) * self.face.size + self.line_height_px = (1 + self.line_height) * self.face.size self.cursor_line = LineWidget:new{ dimen = Geom:new{ w = Screen:scaleBySize(1), - h = line_height, + h = self.line_height_px, } } self:_evalCharWidthList() @@ -51,7 +61,7 @@ function TextBoxWidget:init() self.dimen = Geom:new(self:getSize()) end --- Evaluate the width of each char in `self.charlist`. +-- Split `self.text` into `self.charlist` and evaluate the width of each char in it. function TextBoxWidget:_evalCharWidthList() if self.charlist == nil then self.charlist = util.splitToChars(self.text) @@ -66,8 +76,9 @@ end -- Split the text into logical lines to fit into the text box. function TextBoxWidget:_splitCharWidthList() - self.vertical_string_list = {} - self.vertical_string_list[1] = {text = "Demo hint", offset = 1, width = 0} -- hint for empty string + self.vertical_string_list = { + {text = "Demo hint", offset = 1, width = 0} -- hint for empty string + } local idx = 1 local size = #self.char_width_list @@ -113,10 +124,15 @@ function TextBoxWidget:_splitCharWidthList() end end -- endif util.isSplitable(c) end -- endif cur_line_width > self.width - self.vertical_string_list[ln] = {text = cur_line_text, offset = offset, width = cur_line_width} + self.vertical_string_list[ln] = { + text = cur_line_text, + offset = offset, + width = cur_line_width + } if hard_newline then idx = idx + 1 - self.vertical_string_list[ln + 1] = {text = "", offset = idx, width = 0} + -- FIXME: reuse newline entry + self.vertical_string_list[ln+1] = {text = "", offset = idx, width = 0} end ln = ln + 1 -- Make sure `idx` point to the next char to be processed in the next loop. @@ -125,11 +141,10 @@ end function TextBoxWidget:_renderText(start_row_idx, end_row_idx) local font_height = self.face.size - local line_height = (1 + self.line_height) * font_height if start_row_idx < 1 then start_row_idx = 1 end if end_row_idx > #self.vertical_string_list then end_row_idx = #self.vertical_string_list end local row_count = end_row_idx == 0 and 1 or end_row_idx - start_row_idx + 1 - local h = line_height * row_count + local h = self.line_height_px * row_count self._bb = Blitbuffer.new(self.width, h) self._bb:fill(Blitbuffer.COLOR_WHITE) local y = font_height @@ -139,7 +154,7 @@ function TextBoxWidget:_renderText(start_row_idx, end_row_idx) --@TODO Don't use kerning for monospaced fonts. (houqp) -- refert to cb25029dddc42693cc7aaefbe47e9bd3b7e1a750 in master tree RenderText:renderUtf8Text(self._bb, pen_x, y, self.face, line.text, true, self.bold, self.fgcolor) - y = y + line_height + y = y + self.line_height_px end -- -- if text is shorter than one line, shrink to text's width -- if #v_list == 1 then @@ -162,17 +177,15 @@ function TextBoxWidget:_findCharPos() x = x + self.char_width_list[offset].width offset = offset + 1 end - local line_height = (1 + self.line_height) * self.face.size - return x + 1, (ln - 1) * line_height -- offset `x` by 1 to avoid overlap + return x + 1, (ln - 1) * self.line_height_px -- offset `x` by 1 to avoid overlap end -- Click event: Move the cursor to a new location with (x, y), in pixels. -- Be aware of virtual line number of the scorllTextWidget. function TextBoxWidget:moveCursor(x, y) local w = 0 - local line_height = (1 + self.line_height) * self.face.size local ln = self.height == nil and 1 or self.virtual_line_num - ln = ln + math.ceil(y / line_height) - 1 + ln = ln + math.ceil(y / self.line_height_px) - 1 if ln > #self.vertical_string_list then ln = #self.vertical_string_list x = self.width @@ -191,13 +204,13 @@ function TextBoxWidget:moveCursor(x, y) end self:free() self:_renderText(1, #self.vertical_string_list) - self.cursor_line:paintTo(self._bb, w + 1, (ln - self.virtual_line_num) * line_height) + self.cursor_line:paintTo(self._bb, w + 1, + (ln - self.virtual_line_num) * self.line_height_px) return offset end function TextBoxWidget:getVisLineCount() - local line_height = (1 + self.line_height) * self.face.size - return math.floor(self.height / line_height) + return math.floor(self.height / self.line_height_px) end function TextBoxWidget:getAllLineCount() @@ -213,7 +226,7 @@ function TextBoxWidget:scrollDown() self.virtual_line_num = self.virtual_line_num + visible_line_count self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) end - return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list + return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list end -- TODO: modify `charpos` so that it can render the cursor @@ -228,7 +241,7 @@ function TextBoxWidget:scrollUp() end self:_renderText(self.virtual_line_num, self.virtual_line_num + visible_line_count - 1) end - return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list + return (self.virtual_line_num - 1) / #self.vertical_string_list, (self.virtual_line_num - 1 + visible_line_count) / #self.vertical_string_list end function TextBoxWidget:getSize() @@ -252,21 +265,48 @@ function TextBoxWidget:free() end function TextBoxWidget:onHoldWord(callback, ges) + if not callback then return end + local x, y = ges.pos.x - self.dimen.x, ges.pos.y - self.dimen.y - for _, l in ipairs(self.rendering_vlist) do - for _, w in ipairs(l) do - local box = w.box - if x > box.x and x < box.x + box.w and - y > box.y and y < box.y + box.h then - DEBUG("found word", w, "at", x, y) - if callback then - callback(w.word) + local line_num = math.ceil(y / self.line_height_px) + local line = self.vertical_string_list[line_num] + + if line then + local char_start = line.offset + local char_end -- char_end is non-inclusive + if line_num >= #self.vertical_string_list then + char_end = #self.char_width_list + 1 + else + char_end = self.vertical_string_list[line_num+1].offset + end + local char_probe_x = 0 + local idx = char_start + -- find which character the touch is holding + while idx < char_end do + local c = self.char_width_list[idx] + -- FIXME: this might break if kerning is enabled + char_probe_x = char_probe_x + c.width + if char_probe_x > x then + -- ignore spaces + if c.char == " " then break end + -- now find which word the character is in + local words = util.splitToWords(line.text) + local probe_idx = char_start + for _,w in ipairs(words) do + -- +1 for word separtor + probe_idx = probe_idx + string.len(w) + if idx <= probe_idx then + callback(w) + return + end end break end + idx = idx + 1 end end - return true + + return end return TextBoxWidget diff --git a/frontend/util.lua b/frontend/util.lua index 544863d7a..ac50a3824 100644 --- a/frontend/util.lua +++ b/frontend/util.lua @@ -1,16 +1,18 @@ -local BaseUtil = require("ffi/util") - --[[-- Miscellaneous helper functions for KOReader frontend. - ]] +]] +local BaseUtil = require("ffi/util") local util = {} -function util.stripePunctuations(word) - if not word then return end - -- strip ASCII punctuation characters around word - -- and strip any generic punctuation (U+2000 - U+206F) in the word - return word:gsub("\226[\128-\131][\128-\191]", ''):gsub("^%p+", ''):gsub("%p+$", '') +--- Strip all punctuations and spaces in a string. +---- @string text the string to be stripped +---- @treturn string stripped text +function util.stripePunctuations(text) + if not text then return end + -- strip ASCII punctuation characters around text + -- and strip any generic punctuation (U+2000 - U+206F) in the text + return text:gsub("\226[\128-\131][\128-\191]", ''):gsub("^%p+", ''):gsub("%p+$", '') end --[[ @@ -74,7 +76,7 @@ end --- Returns number of keys in a table. ---- @param T Lua table ----- @return number of keys in table T +---- @treturn int number of keys in table T function util.tableSize(T) local count = 0 for _ in pairs(T) do count = count + 1 end @@ -97,8 +99,9 @@ function util.lastIndexOf(string, ch) end --- Split string into a list of UTF-8 chars. --- @text: the string to be splitted. +--- Split string into a list of UTF-8 chars. +---- @string text the string to be splitted. +---- @treturn table list of UTF-8 chars function util.splitToChars(text) local tab = {} if text ~= nil then @@ -114,6 +117,20 @@ function util.splitToChars(text) return tab end +--- Split text into a list of words, spaces and punctuations. +---- @string text text to split +---- @treturn table list of words, spaces and punctuations +function util.splitToWords(text) + -- TODO: write test + local wlist = {} + for words in text:gmatch("[\32-\127\192-\255]+[\128-\191]*") do + for word in util.gsplit(words, "[%s%p]+", true) do + table.insert(wlist, word) + end + end + return wlist +end + -- Test whether a string could be separated by a char for multi-line rendering function util.isSplitable(c) return #c > 1 or c == " " or string.match(c, "%p") ~= nil diff --git a/spec/unit/util_spec.lua b/spec/unit/util_spec.lua index 9fc945aa4..ca1a2b7e4 100644 --- a/spec/unit/util_spec.lua +++ b/spec/unit/util_spec.lua @@ -37,4 +37,19 @@ describe("util module", function() end assert.are_same(argv, {"./sdcv", "-nj", "words", "a lot", "more or less", "--data-dir=dict"}) end) + + it("should split line into words", function() + local words = util.splitToWords("one two,three four . five") + assert.are_same(words, { + "one", + " ", + "two", + ",", + "three", + " ", + "four", + " . ", + "five", + }) + end) end)