Dict lookups: have them always interruptible

They should be now interruptible when fuzzy search is disabled
and on Android.
pull/5178/head
poire-z 5 years ago committed by Frans de Jonge
parent 8749a3c5b6
commit a82d7832b9

@ -673,41 +673,29 @@ function ReaderDictionary:startSdcv(word, dict_names, fuzzy_search)
end end
end end
local results_str = nil local cmd = util.shell_escape(args)
if Device:isAndroid() then -- cmd = "sleep 7 ; " .. cmd -- uncomment to simulate long lookup time
local A = require("android")
results_str = A.stdout(unpack(args)) -- Some sdcv lookups, when using fuzzy search with many dictionaries
else -- and a really bad selected text, can take up to 10 seconds.
local cmd = util.shell_escape(args) -- It is nice to be able to cancel it when noticing wrong text was
-- cmd = "sleep 7 ; " .. cmd -- uncomment to simulate long lookup time -- selected.
-- Because sdcv starts outputing its output only at the end when it has
if self.lookup_progress_msg then -- done its work, we can use Trapper:dismissablePopen() to cancel it as
-- Some sdcv lookups, when using fuzzy search with many dictionaries -- long as we are waiting for output.
-- and a really bad selected text, can take up to 10 seconds. -- When fuzzy search is enabled, we have a lookup_progress_msg that can
-- It is nice to be able to cancel it when noticing wrong text was selected. -- be used to catch a tap and trigger cancellation.
-- As we have a lookup_progress_msg (that can be used to catch a tap -- When fuzzy search is disabled, we provide false instead so an
-- and trigger cancellation), and because sdcv starts outputing its -- invisible non-event-forwarding TrapWidget is used to catch a tap
-- output only at the end when it has done its work, we can -- and trigger cancellation (invisible so there's no need for repaint
-- use Trapper:dismissablePopen() to cancel it as long as we are waiting -- and refresh with the usually fast non-fuzzy search lookups).
-- for output. -- We must ensure we will have some output to be readable (if no
-- We must ensure we will have some output to be readable (if no -- definition found, sdcv will output some message on stderr, and
-- definition found, sdcv will output some message on stderr, and -- let stdout empty) by appending an "echo":
-- let stdout empty) by appending an "echo": cmd = cmd .. "; echo"
cmd = cmd .. "; echo" local completed, results_str = Trapper:dismissablePopen(cmd, self.lookup_progress_msg or false)
local completed lookup_cancelled = not completed
completed, results_str = Trapper:dismissablePopen(cmd, self.lookup_progress_msg)
lookup_cancelled = not completed
else
-- Fuzzy search disabled, usual option for people who don't want
-- a "Looking up..." InfoMessage and usually fast: do a classic
-- blocking io.popen()
local std_out = io.popen(cmd, "r")
if std_out then
results_str = std_out:read("*all")
std_out:close()
end
end
end
if results_str and results_str ~= "\n" then -- \n is when lookup was cancelled if results_str and results_str ~= "\n" then -- \n is when lookup was cancelled
local ok, results = pcall(JSON.decode, results_str) local ok, results = pcall(JSON.decode, results_str)
if ok and results then if ok and results then

@ -314,8 +314,9 @@ Notes and limitations:
3) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage}, 3) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage},
that, as a modal, will catch any @{ui.event|Tap event} happening during that, as a modal, will catch any @{ui.event|Tap event} happening during
`cmd` execution. This can be an existing already displayed widget, or `cmd` execution. This can be an existing already displayed widget, or
provided as a string (a new TrapWidget will be created). If nil, an invisible provided as a string (a new TrapWidget will be created). If nil, true or false,
TrapWidget will be used instead. an invisible TrapWidget will be used instead (if nil or true, the event will be
resent; if false, the event will not be resent).
If we really need to have more control, we would need to use `select()` via `ffi` If we really need to have more control, we would need to use `select()` via `ffi`
or do low level non-blocking reading on the file descriptor. or do low level non-blocking reading on the file descriptor.
@ -324,7 +325,7 @@ collect indefinitely, the best option would be to compile any `timeout.c`
and use it as a wrapper. and use it as a wrapper.
@string cmd shell `cmd` to execute and get output from @string cmd shell `cmd` to execute and get output from
@param trap_widget_or_string already shown widget, string or nil @param trap_widget_or_string already shown widget, string, or nil, true or false
@treturn boolean completed (`true` if not interrupted, `false` if dismissed) @treturn boolean completed (`true` if not interrupted, `false` if dismissed)
@treturn string output of command @treturn string output of command
]] ]]
@ -358,10 +359,15 @@ function Trapper:dismissablePopen(cmd, trap_widget_or_string)
UIManager:show(trap_widget) UIManager:show(trap_widget)
UIManager:forceRePaint() UIManager:forceRePaint()
else else
-- Use an invisible TrapWidget that resend event -- Use an invisible TrapWidget that resend event, but not if
-- trap_widget_or_string is false (rather than nil or true)
local resend_event = true
if trap_widget_or_string == false then
resend_event = false
end
trap_widget = TrapWidget:new{ trap_widget = TrapWidget:new{
text = nil, text = nil,
resend_event = true, resend_event = resend_event,
} }
UIManager:show(trap_widget) UIManager:show(trap_widget)
own_trap_widget_invisible = true own_trap_widget_invisible = true
@ -472,11 +478,12 @@ Notes and limitations:
4) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage}, 4) We need a @{ui.widget.trapwidget|TrapWidget} or @{ui.widget.infomessage|InfoMessage},
that, as a modal, will catch any @{ui.event|Tap event} happening during that, as a modal, will catch any @{ui.event|Tap event} happening during
`cmd` execution. This can be an existing already displayed widget, or `cmd` execution. This can be an existing already displayed widget, or
provided as a string (a new TrapWidget will be created). If nil, an invisible provided as a string (a new TrapWidget will be created). If nil, true or false,
TrapWidget will be used instead. an invisible TrapWidget will be used instead (if nil or true, the event will be
resent; if false, the event will not be resent).
@function task lua function to execute and get return values from @function task lua function to execute and get return values from
@param trap_widget_or_string already shown widget, string or nil @param trap_widget_or_string already shown widget, string, or nil, true or false
@boolean task_returns_simple_string[opt=false] true if task returns a single string @boolean task_returns_simple_string[opt=false] true if task returns a single string
@treturn boolean completed (`true` if not interrupted, `false` if dismissed) @treturn boolean completed (`true` if not interrupted, `false` if dismissed)
@return ... return values of task @return ... return values of task
@ -504,10 +511,15 @@ function Trapper:dismissableRunInSubprocess(task, trap_widget_or_string, task_re
UIManager:show(trap_widget) UIManager:show(trap_widget)
UIManager:forceRePaint() UIManager:forceRePaint()
else else
-- Use an invisible TrapWidget that resend event -- Use an invisible TrapWidget that resend event, but not if
-- trap_widget_or_string is false (rather than nil or true)
local resend_event = true
if trap_widget_or_string == false then
resend_event = false
end
trap_widget = TrapWidget:new{ trap_widget = TrapWidget:new{
text = nil, text = nil,
resend_event = true, resend_event = resend_event,
} }
UIManager:show(trap_widget) UIManager:show(trap_widget)
own_trap_widget_invisible = true own_trap_widget_invisible = true

@ -115,16 +115,13 @@ function TrapWidget:_dismissAndResent(evtype, ev)
self.dismiss_callback() self.dismiss_callback()
UIManager:close(self) UIManager:close(self)
if self.resend_event and evtype and ev then if self.resend_event and evtype and ev then
-- XXX There may be timing problems that could cause crashes, as we -- There may be some timing issues that could cause crashes, as we
-- use nextTick, if the dismiss_callback uses UIManager:scheduleIn() -- use nextTick, if the dismiss_callback uses UIManager:scheduleIn()
-- or has set up some widget that may catch that event while not being -- or has set up some widget that may catch that event while not being
-- yet fully initialiazed. -- yet fully initialiazed.
-- It happened mostly when I had some bug somewhere, and it was a quite -- (It happened mostly when I had some bug somewhere, and it was a quite
-- reliable sign of a bug somewhere, but the stacktrace was unrelated -- reliable sign of a bug somewhere, but the stacktrace was unrelated
-- to the bug location. -- to the bug location.)
-- Fix to avoid crashes: in GestureRange:match(), check that self.range()
-- does not return nil before using it:
-- if not range or not range:contains(gs.pos) then return false
UIManager:nextTick(function() UIManager:handleInputEvent(Event:new(evtype, ev)) end) UIManager:nextTick(function() UIManager:handleInputEvent(Event:new(evtype, ev)) end)
end end
return true return true

Loading…
Cancel
Save