feat: add online query support

This commit is contained in:
JuanZoran 2023-01-30 20:09:57 +08:00
parent d95bb8c8e5
commit 2377dbc0a7
9 changed files with 272 additions and 133 deletions

View File

@ -199,13 +199,8 @@ require'Trans'.setup {
width = 37, width = 37,
height = 27, height = 27,
border = 'rounded', border = 'rounded',
title = { title = title,
{ '', 'TransTitleRound' },
{ ' Trans', 'TransTitle' },
{ '', 'TransTitleRound' },
},
keymap = { keymap = {
-- TODO :
pageup = '[[', pageup = '[[',
pagedown = ']]', pagedown = ']]',
pin = '<leader>[', pin = '<leader>[',
@ -226,16 +221,13 @@ require'Trans'.setup {
'BufLeave', 'BufLeave',
}, },
auto_play = true, auto_play = true,
timeout = 3000,
}, },
float = { float = {
width = 0.8, width = 0.8,
height = 0.8, height = 0.8,
border = 'rounded', border = 'rounded',
title = { title = title,
{ '', 'TransTitleRound' },
{ ' Trans', 'TransTitle' },
{ '', 'TransTitleRound' },
},
keymap = { keymap = {
quit = 'q', quit = 'q',
}, },
@ -271,17 +263,28 @@ require'Trans'.setup {
-- yes = '✔️', -- yes = '✔️',
-- no = '❌' -- no = '❌'
}, },
theme = 'default', -- 目前可选的: default, tokyonight, dracula theme = 'default',
-- theme = 'dracula',
-- theme = 'tokyonight',
db_path = '$HOME/.vim/dict/ultimate.db', db_path = '$HOME/.vim/dict/ultimate.db',
-- TODO add online translate engine engine = {
-- online_search = { -- 目前支持hover窗口支持百度, 默认不开启
-- enable = false, -- baidu = {
-- engine = {}, -- appid = '',
-- appPasswd = '',
-- },
},
-- TODO :
-- register word
-- history = {
-- -- TOOD
-- } -- }
-- TODO register word -- TODO :add online translate engine
} }
``` ```
## 快捷键绑定 ## 快捷键绑定
@ -342,6 +345,7 @@ vim.keymap.set('n', 'mi', '<Cmd>TranslateInput<CR>')
}, },
} }
``` ```
## 声明 ## 声明
- 本插件词典基于[ECDICT](https://github.com/skywind3000/ECDICT) - 本插件词典基于[ECDICT](https://github.com/skywind3000/ECDICT)

View File

@ -15,6 +15,7 @@ local content = {
if not self.modifiable then if not self.modifiable then
error('content can not add newline now') error('content can not add newline now')
end end
self.size = self.size + 1 self.size = self.size + 1
self.lines[self.size] = value self.lines[self.size] = value
end, end,
@ -23,6 +24,7 @@ local content = {
if not self.modifiable then if not self.modifiable then
error('content can not add newline now') error('content can not add newline now')
end end
self.hl_size = self.hl_size + 1 self.hl_size = self.hl_size + 1
self.highlights[self.hl_size] = opt self.highlights[self.hl_size] = opt
end, end,
@ -32,6 +34,7 @@ local content = {
clear(self.lines) clear(self.lines)
clear(self.highlights) clear(self.highlights)
self.size = 0 self.size = 0
self.hl_size = 0
end, end,
---将内容连接上对应的窗口 ---将内容连接上对应的窗口
@ -42,6 +45,7 @@ local content = {
return return
end end
offset = offset or 0
self.window:bufset('modifiable', true) self.window:bufset('modifiable', true)
local window = self.window local window = self.window
--- NOTE : 使用-1 则需要按顺序设置 --- NOTE : 使用-1 则需要按顺序设置
@ -71,7 +75,6 @@ local content = {
end end
end, end,
format = function(self, ...) format = function(self, ...)
local nodes = { ... } local nodes = { ... }
local size = #nodes local size = #nodes
@ -85,10 +88,7 @@ local content = {
end end
local space = math.floor(((self.window.width - width) / (size - 1))) local space = math.floor(((self.window.width - width) / (size - 1)))
if space > 0 then assert(space > 0, 'try to expand the window')
return
end
local interval = (' '):rep(space) local interval = (' '):rep(space)
return setmetatable({ return setmetatable({
text = table.concat(strs, interval), text = table.concat(strs, interval),

View File

@ -88,11 +88,11 @@ M.conf = {
db_path = '$HOME/.vim/dict/ultimate.db', db_path = '$HOME/.vim/dict/ultimate.db',
engine = { engine = {
baidu = { -- baidu = {
appid = '', -- appid = '',
appPasswd = '', -- appPasswd = '',
}, -- },
-- youdao = { -- -- youdao = {
-- appkey = '', -- appkey = '',
-- appPasswd = '', -- appPasswd = '',
-- }, -- },

View File

@ -1,17 +1,14 @@
local baidu = require('Trans').conf.engine.baidu local baidu = require('Trans').conf.engine.baidu
local appid = baidu.appid local appid = baidu.appid
local appPasswd = baidu.appPasswd local appPasswd = baidu.appPasswd
local salt = tostring(math.random(bit.rshift(1, 5))) local salt = tostring(math.random(bit.lshift(1, 15)))
local uri = 'https://fanyi-api.baidu.com/api/trans/vip/translate' local uri = 'https://fanyi-api.baidu.com/api/trans/vip/translate'
if appid == '' or appPasswd == '' then if appid == '' or appPasswd == '' then
error('请查看README, 实现在线翻译或者设置将在线翻译设置为false') error('请查看README, 实现在线翻译或者设置将在线翻译设置为false')
end end
local ok, curl = pcall(require, 'plenary.curl') local post = require('Trans.util.curl').POST
if not ok then
error('plenary not found')
end
local function get_field(word) local function get_field(word)
local to = 'zh' local to = 'zh'
@ -28,22 +25,40 @@ local function get_field(word)
} }
end end
--- this is a nice plugin
---返回一个channel
---@param word string
---@return function
return function(word) return function(word)
local query = get_field(word) local query = get_field(word)
local output = curl.post(uri, { local result
post(uri, {
data = query, data = query,
headers = { headers = {
content_type = "application/x-www-form-urlencoded", content_type = "application/x-www-form-urlencoded",
} },
callback = function(str)
if result then
return
elseif str ~= '' then
local res = vim.fn.json_decode(str)
if res and res.trans_result then
result = {
word = word,
translation = res.trans_result[1].dst,
}
else
result = false
end
else
result = false
end
end,
}) })
if output.exit == 0 and output.status == 200 then return function()
local res = vim.fn.json_decode(output.body) return result
if res and res.trans_result then
return {
word = word,
translation = res.trans_result[1].dst,
}
end
end end
end end

View File

@ -42,7 +42,7 @@ local function translate(mode, view)
mode = mode or vim.api.nvim_get_mode().mode mode = mode or vim.api.nvim_get_mode().mode
view = view or require('Trans').conf.view[mode] view = view or require('Trans').conf.view[mode]
assert(mode and view) assert(mode and view)
local word = get_word(mode) local word = get_word(mode):gsub('^%s+', '', 1)
require('Trans.view.' .. view)(word) require('Trans.view.' .. view)(word)
end end

61
lua/Trans/util/curl.lua Normal file
View File

@ -0,0 +1,61 @@
--- TODO :wrapper for curl
local curl = {}
-- local example = {
-- data = {},
-- headers = {
-- k = 'v',
-- },
-- callback = function(output)
-- end,
-- }
curl.GET = function(uri, opts)
--- TODO :
end
curl.POST = function(uri, opts)
vim.validate {
uri = { uri, 's' },
opts = { opts, 't' }
}
local cmd = { 'curl', '-s', uri }
local size = 3
local function insert(...)
for _, v in ipairs { ... } do
size = size + 1
cmd[size] = v
end
end
local s = '"%s=%s"'
if opts.headers then
for k, v in pairs(opts.headers) do
insert('-H', s:format(k, v))
end
end
for k, v in pairs(opts.data) do
insert('-d', s:format(k, v))
end
local option
if opts.callback then
option = {
on_stdout = function (_, output)
opts.callback(table.concat(output))
end,
}
end
vim.fn.jobstart(table.concat(cmd, ' '), option)
end
return curl

View File

@ -72,18 +72,18 @@ return function(word)
m_result = require('Trans.query.' .. engine_us)(word) m_result = require('Trans.query.' .. engine_us)(word)
local opt = { local opt = {
relative = 'editor', relative = 'editor',
width = float.width, width = float.width,
height = float.height, height = float.height,
border = float.border, border = float.border,
title = float.title, title = float.title,
row = bit.rshift((vim.o.lines - float.height), 1), animation = float.animation,
col = bit.rshift((vim.o.columns - float.width), 1), row = bit.rshift((vim.o.lines - float.height), 1),
zindex = 50, col = bit.rshift((vim.o.columns - float.width), 1),
zindex = 50,
} }
m_window = require('Trans.window')(true, opt) m_window = require('Trans.window')(true, opt)
m_window.animation = float.animation
set_title() set_title()
m_content = m_window.contents[2] m_content = m_window.contents[2]
@ -95,7 +95,6 @@ return function(word)
set_tag_hl(engine_us, 'fail') set_tag_hl(engine_us, 'fail')
end end
m_window:draw()
m_window:open() m_window:open()
m_window:bufset('bufhidden', 'wipe') m_window:bufset('bufhidden', 'wipe')

View File

@ -1,5 +1,6 @@
local api = vim.api local api = vim.api
local conf = require('Trans').conf local conf = require('Trans').conf
local new_window = require('Trans.window')
local m_window local m_window
local m_result local m_result
@ -41,7 +42,6 @@ local process = {
it(m_result.collins and icon.star:rep(m_result.collins) or icon.notfound, 'TransCollins'), it(m_result.collins and icon.star:rep(m_result.collins) or icon.notfound, 'TransCollins'),
it(m_result.oxford == 1 and icon.yes or icon.no) it(m_result.oxford == 1 and icon.yes or icon.no)
) )
end end
m_content:addline(line) m_content:addline(line)
end, end,
@ -166,14 +166,6 @@ local process = {
m_content:newline('') m_content:newline('')
end end
end, end,
failed = function()
m_content:addline(
it(conf.icon.notfound .. m_indent .. '没有找到相关的翻译', 'TransFailed')
)
m_window:set_width(m_content.lines[1]:width())
end,
} }
@ -264,64 +256,134 @@ action = {
end, end,
} }
local function handle()
local hover = conf.hover
if hover.auto_play then
local ok = pcall(action.play)
if not ok then
vim.notify('自动发音失败, 请检查README发音部分', vim.log.WARN)
end
end
for _, field in ipairs(conf.order) do
process[field]()
end
for act, key in pairs(hover.keymap) do
vim.keymap.set('n', key, action[act], { buffer = true, silent = true })
end
end
local function online_query(word)
-- TODO :Progress Bar
local wait = {}
local size = 0
for k, _ in pairs(conf.engine) do
size = size + 1
wait[size] = require('Trans.query.' .. k)(word)
end
local error_msg = conf.icon.notfound .. ' 没有找到相关的翻译'
m_window:set_height(1)
local width = m_window.width
m_window:set_width(error_msg:width())
if size == 0 then
m_content:addline(it(error_msg, 'TransFailed'))
m_window:open()
return
end
m_window:open()
local timeout = conf.hover.timeout
local interval = math.floor(timeout / m_window.width)
-- --- char: ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉ █
-- --- ◖■■■■■■■◗▫◻ ▆ ▆ ▇⃞ ▉⃞
local cell = ''
local i = 1
local do_progress
do_progress = function()
m_content:wipe()
for j = 1, size do
local res = wait[j]()
if res then
m_result = res
m_window:set_width(width)
handle()
m_content:attach()
-- TODO :Animation
m_window.height = m_content:actual_height(true)
m_window:open {
animation = 'fold',
}
return
elseif res == false then
table.remove(wait, j)
size = size - 1
end
end
if i == m_window.width or size == 0 then
--- HACK : change framework
m_content:addline(
it(error_msg, 'TransFailed')
)
m_content:attach()
else
m_content:addline(
it(cell:rep(i), 'MoreMsg')
)
i = i + 1
m_content:attach()
vim.defer_fn(do_progress, interval)
end
end
do_progress()
end
return function(word) return function(word)
vim.validate { vim.validate {
word = { word, 's' }, word = { word, 's' },
} }
m_result = require('Trans.query.offline')(word)
local hover = conf.hover local hover = conf.hover
m_window = new_window(false, {
m_window = require("Trans.window")(false, { relative = 'cursor',
relative = 'cursor', width = hover.width,
width = hover.width, height = hover.height,
height = hover.height, title = hover.title,
title = hover.title, border = hover.border,
border = hover.border, animation = hover.animation,
col = 1, col = 1,
row = 1, row = 1,
}) })
m_window.animation = hover.animation
m_content = m_window.contents[1] m_content = m_window.contents[1]
m_result = require('Trans.query.offline')(word)
if m_result then
handle()
m_window:open({
callback = function()
m_window:set('wrap', true)
end,
})
-- TODO :Progress Bar local height = m_content:actual_height(true)
if not m_result then if height < m_window.height then
m_result = require('Trans.query.baidu')(word) m_window:set_height(height)
end
if m_result and m_result.translation then
if hover.auto_play then
local ok = pcall(action.play)
if not ok then
vim.notify('自动发音失败, 请检查README发音部分', vim.log.WARN)
end
end
for _, field in ipairs(conf.order) do
process[field]()
end
for act, key in pairs(hover.keymap) do
vim.keymap.set('n', key, action[act], { buffer = true, silent = true })
end end
else else
process.failed() online_query(word)
end end
m_window:draw()
local height = m_content:actual_height(true)
if height < m_window.height then
m_window:set_height(height)
end
m_window:open(function()
m_window:set('wrap', true)
end)
-- Auto Close -- Auto Close
if hover.auto_close_events then if hover.auto_close_events then
cmd_id = api.nvim_create_autocmd( cmd_id = api.nvim_create_autocmd(

View File

@ -60,10 +60,7 @@ local window = {
end) end)
end, end,
---**第一次**绘制窗口的内容
---@param self table 窗口的对象
draw = function(self) draw = function(self)
-- TODO :
local offset = 0 local offset = 0
for _, content in ipairs(self.contents) do for _, content in ipairs(self.contents) do
content:attach(offset) content:attach(offset)
@ -71,9 +68,13 @@ local window = {
end end
end, end,
open = function(self, callback) open = function(self, opts)
local animation = self.animation self:draw()
if animation.open then opts = opts or {}
local interval = self.animation.interval
local animation = opts.animation or self.animation.open
if animation then
check_busy() check_busy()
local handler local handler
@ -85,12 +86,12 @@ local window = {
busy = true busy = true
count = count + 1 count = count + 1
api[action](self.winid, count) api[action](self.winid, count)
vim.defer_fn(handler[name], animation.interval) vim.defer_fn(handler[name], interval)
else else
busy = false busy = false
if callback then if opts.callback then
callback() opts.callback()
end end
end end
end end
@ -101,7 +102,7 @@ local window = {
fold = wrap('fold', 'height'), fold = wrap('fold', 'height'),
} }
handler[animation.open]() handler[animation]()
end end
end, end,
@ -191,22 +192,19 @@ return function(entry, option)
} }
local opt = { local opt = {
relative = nil, relative = option.relative,
width = nil, width = option.width,
height = nil, height = option.height,
border = nil, border = option.border,
title = nil, title = option.title,
col = nil, col = option.col,
row = nil, row = option.row,
title_pos = nil, title_pos = nil,
focusable = false, focusable = false,
zindex = 100, zindex = option.zindex or 100,
style = 'minimal', style = 'minimal',
} }
for k, v in pairs(option) do
opt[k] = v
end
if opt.title then if opt.title then
opt.title_pos = 'center' opt.title_pos = 'center'
end end
@ -219,12 +217,13 @@ return function(entry, option)
local win local win
win = { win = {
winid = winid, winid = winid,
bufnr = bufnr, bufnr = bufnr,
width = opt.width, width = opt.width,
height = opt.height, height = opt.height,
hl = api.nvim_create_namespace('TransWinHl'), animation = option.animation,
contents = setmetatable({}, { hl = api.nvim_create_namespace('TransWinHl'),
contents = setmetatable({}, {
__index = function(self, key) __index = function(self, key)
assert(type(key) == 'number') assert(type(key) == 'number')
self[key] = require('Trans.content')(win) self[key] = require('Trans.content')(win)
@ -234,7 +233,6 @@ return function(entry, option)
} }
setmetatable(win, { __index = window }) setmetatable(win, { __index = window })
-- FIXME :config this -- FIXME :config this
win:bufset('filetype', 'Trans') win:bufset('filetype', 'Trans')
win:bufset('buftype', 'nofile') win:bufset('buftype', 'nofile')