diff --git a/README.md b/README.md index a3ca0a9..d4622dc 100644 --- a/README.md +++ b/README.md @@ -199,13 +199,8 @@ require'Trans'.setup { width = 37, height = 27, border = 'rounded', - title = { - { '', 'TransTitleRound' }, - { ' Trans', 'TransTitle' }, - { '', 'TransTitleRound' }, - }, + title = title, keymap = { - -- TODO : pageup = '[[', pagedown = ']]', pin = '[', @@ -226,16 +221,13 @@ require'Trans'.setup { 'BufLeave', }, auto_play = true, + timeout = 3000, }, float = { width = 0.8, height = 0.8, border = 'rounded', - title = { - { '', 'TransTitleRound' }, - { ' Trans', 'TransTitle' }, - { '', 'TransTitleRound' }, - }, + title = title, keymap = { quit = 'q', }, @@ -271,17 +263,28 @@ require'Trans'.setup { -- yes = '✔️', -- no = '❌' }, - theme = 'default', -- 目前可选的: default, tokyonight, dracula + theme = 'default', + -- theme = 'dracula', + -- theme = 'tokyonight', + db_path = '$HOME/.vim/dict/ultimate.db', - -- TODO add online translate engine - -- online_search = { - -- enable = false, - -- engine = {}, + engine = { + -- 目前支持hover窗口支持百度, 默认不开启 + -- baidu = { + -- appid = '', + -- appPasswd = '', + -- }, + }, + + -- TODO : + -- register word + -- history = { + -- -- TOOD -- } - -- TODO register word -} + -- TODO :add online translate engine +} ``` ## 快捷键绑定 @@ -342,6 +345,7 @@ vim.keymap.set('n', 'mi', 'TranslateInput') }, } ``` + ## 声明 - 本插件词典基于[ECDICT](https://github.com/skywind3000/ECDICT) diff --git a/lua/Trans/content.lua b/lua/Trans/content.lua index 8852511..912589a 100644 --- a/lua/Trans/content.lua +++ b/lua/Trans/content.lua @@ -15,6 +15,7 @@ local content = { if not self.modifiable then error('content can not add newline now') end + self.size = self.size + 1 self.lines[self.size] = value end, @@ -23,6 +24,7 @@ local content = { if not self.modifiable then error('content can not add newline now') end + self.hl_size = self.hl_size + 1 self.highlights[self.hl_size] = opt end, @@ -32,6 +34,7 @@ local content = { clear(self.lines) clear(self.highlights) self.size = 0 + self.hl_size = 0 end, ---将内容连接上对应的窗口 @@ -42,6 +45,7 @@ local content = { return end + offset = offset or 0 self.window:bufset('modifiable', true) local window = self.window --- NOTE : 使用-1 则需要按顺序设置 @@ -71,7 +75,6 @@ local content = { end end, - format = function(self, ...) local nodes = { ... } local size = #nodes @@ -85,10 +88,7 @@ local content = { end local space = math.floor(((self.window.width - width) / (size - 1))) - if space > 0 then - return - end - + assert(space > 0, 'try to expand the window') local interval = (' '):rep(space) return setmetatable({ text = table.concat(strs, interval), diff --git a/lua/Trans/init.lua b/lua/Trans/init.lua index 37d4828..03f582f 100644 --- a/lua/Trans/init.lua +++ b/lua/Trans/init.lua @@ -88,11 +88,11 @@ M.conf = { db_path = '$HOME/.vim/dict/ultimate.db', engine = { - baidu = { - appid = '', - appPasswd = '', - }, - -- youdao = { + -- baidu = { + -- appid = '', + -- appPasswd = '', + -- }, + -- -- youdao = { -- appkey = '', -- appPasswd = '', -- }, diff --git a/lua/Trans/query/baidu.lua b/lua/Trans/query/baidu.lua index 0746ca0..61c5c8f 100644 --- a/lua/Trans/query/baidu.lua +++ b/lua/Trans/query/baidu.lua @@ -1,17 +1,14 @@ local baidu = require('Trans').conf.engine.baidu local appid = baidu.appid 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' if appid == '' or appPasswd == '' then error('请查看README, 实现在线翻译或者设置将在线翻译设置为false') end -local ok, curl = pcall(require, 'plenary.curl') -if not ok then - error('plenary not found') -end +local post = require('Trans.util.curl').POST local function get_field(word) local to = 'zh' @@ -28,22 +25,40 @@ local function get_field(word) } end +--- this is a nice plugin +---返回一个channel +---@param word string +---@return function return function(word) local query = get_field(word) - local output = curl.post(uri, { + local result + + post(uri, { data = query, headers = { 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 - local res = vim.fn.json_decode(output.body) - if res and res.trans_result then - return { - word = word, - translation = res.trans_result[1].dst, - } - end + return function() + return result end end diff --git a/lua/Trans/translate.lua b/lua/Trans/translate.lua index 94e5ee5..d37ba38 100644 --- a/lua/Trans/translate.lua +++ b/lua/Trans/translate.lua @@ -42,7 +42,7 @@ local function translate(mode, view) mode = mode or vim.api.nvim_get_mode().mode view = view or require('Trans').conf.view[mode] assert(mode and view) - local word = get_word(mode) + local word = get_word(mode):gsub('^%s+', '', 1) require('Trans.view.' .. view)(word) end diff --git a/lua/Trans/util/curl.lua b/lua/Trans/util/curl.lua new file mode 100644 index 0000000..a136328 --- /dev/null +++ b/lua/Trans/util/curl.lua @@ -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 diff --git a/lua/Trans/view/float.lua b/lua/Trans/view/float.lua index 1a03212..d687dd0 100644 --- a/lua/Trans/view/float.lua +++ b/lua/Trans/view/float.lua @@ -72,18 +72,18 @@ return function(word) m_result = require('Trans.query.' .. engine_us)(word) local opt = { - relative = 'editor', - width = float.width, - height = float.height, - border = float.border, - title = float.title, - row = bit.rshift((vim.o.lines - float.height), 1), - col = bit.rshift((vim.o.columns - float.width), 1), - zindex = 50, + relative = 'editor', + width = float.width, + height = float.height, + border = float.border, + title = float.title, + animation = float.animation, + row = bit.rshift((vim.o.lines - float.height), 1), + col = bit.rshift((vim.o.columns - float.width), 1), + zindex = 50, } m_window = require('Trans.window')(true, opt) - m_window.animation = float.animation set_title() m_content = m_window.contents[2] @@ -95,7 +95,6 @@ return function(word) set_tag_hl(engine_us, 'fail') end - m_window:draw() m_window:open() m_window:bufset('bufhidden', 'wipe') diff --git a/lua/Trans/view/hover.lua b/lua/Trans/view/hover.lua index 1d21e85..601b377 100644 --- a/lua/Trans/view/hover.lua +++ b/lua/Trans/view/hover.lua @@ -1,5 +1,6 @@ local api = vim.api local conf = require('Trans').conf +local new_window = require('Trans.window') local m_window 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.oxford == 1 and icon.yes or icon.no) ) - end m_content:addline(line) end, @@ -166,14 +166,6 @@ local process = { m_content:newline('') 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, } +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) vim.validate { word = { word, 's' }, } - m_result = require('Trans.query.offline')(word) local hover = conf.hover - - m_window = require("Trans.window")(false, { - relative = 'cursor', - width = hover.width, - height = hover.height, - title = hover.title, - border = hover.border, - col = 1, - row = 1, + m_window = new_window(false, { + relative = 'cursor', + width = hover.width, + height = hover.height, + title = hover.title, + border = hover.border, + animation = hover.animation, + col = 1, + row = 1, }) - m_window.animation = hover.animation 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 - if not m_result then - m_result = require('Trans.query.baidu')(word) - 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 }) + local height = m_content:actual_height(true) + if height < m_window.height then + m_window:set_height(height) end else - process.failed() + online_query(word) 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 if hover.auto_close_events then cmd_id = api.nvim_create_autocmd( diff --git a/lua/Trans/window.lua b/lua/Trans/window.lua index 31fd894..c95a735 100644 --- a/lua/Trans/window.lua +++ b/lua/Trans/window.lua @@ -60,10 +60,7 @@ local window = { end) end, - ---**第一次**绘制窗口的内容 - ---@param self table 窗口的对象 draw = function(self) - -- TODO : local offset = 0 for _, content in ipairs(self.contents) do content:attach(offset) @@ -71,9 +68,13 @@ local window = { end end, - open = function(self, callback) - local animation = self.animation - if animation.open then + open = function(self, opts) + self:draw() + opts = opts or {} + local interval = self.animation.interval + local animation = opts.animation or self.animation.open + + if animation then check_busy() local handler @@ -85,12 +86,12 @@ local window = { busy = true count = count + 1 api[action](self.winid, count) - vim.defer_fn(handler[name], animation.interval) + vim.defer_fn(handler[name], interval) else busy = false - if callback then - callback() + if opts.callback then + opts.callback() end end end @@ -101,7 +102,7 @@ local window = { fold = wrap('fold', 'height'), } - handler[animation.open]() + handler[animation]() end end, @@ -191,22 +192,19 @@ return function(entry, option) } local opt = { - relative = nil, - width = nil, - height = nil, - border = nil, - title = nil, - col = nil, - row = nil, + relative = option.relative, + width = option.width, + height = option.height, + border = option.border, + title = option.title, + col = option.col, + row = option.row, title_pos = nil, focusable = false, - zindex = 100, + zindex = option.zindex or 100, style = 'minimal', } - for k, v in pairs(option) do - opt[k] = v - end if opt.title then opt.title_pos = 'center' end @@ -219,12 +217,13 @@ return function(entry, option) local win win = { - winid = winid, - bufnr = bufnr, - width = opt.width, - height = opt.height, - hl = api.nvim_create_namespace('TransWinHl'), - contents = setmetatable({}, { + winid = winid, + bufnr = bufnr, + width = opt.width, + height = opt.height, + animation = option.animation, + hl = api.nvim_create_namespace('TransWinHl'), + contents = setmetatable({}, { __index = function(self, key) assert(type(key) == 'number') self[key] = require('Trans.content')(win) @@ -234,7 +233,6 @@ return function(entry, option) } setmetatable(win, { __index = window }) - -- FIXME :config this win:bufset('filetype', 'Trans') win:bufset('buftype', 'nofile')