feat: 自动发音和中翻英的支持
This commit is contained in:
parent
86f8b7f6e8
commit
b2b716de6b
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
lua/Trans/test/
|
lua/Trans/test/
|
||||||
note/
|
wiki/
|
||||||
demo.mp4
|
demo.mp4
|
||||||
screenshot.gif
|
screenshot.gif
|
||||||
tts/node_modules/
|
tts/node_modules/
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
- 使用纯lua编写, 速度极快
|
- 使用纯lua编写, 速度极快
|
||||||
> `Lazy.nvim`的记录: <font color="#0099FF">`➜ Trans.nvim 0.82ms`</font>
|
> `Lazy.nvim`的记录: <font color="#0099FF">`➜ Trans.nvim 0.82ms`</font>
|
||||||
|
|
||||||
|
- **可以定义快捷键读英文单词**
|
||||||
|
> 见wiki
|
||||||
|
|
||||||
- 大部分功能可以自定义:
|
- 大部分功能可以自定义:
|
||||||
- 高亮
|
- 高亮
|
||||||
- 悬浮大小
|
- 悬浮大小
|
||||||
@ -315,7 +318,8 @@ require'Trans'.setup {
|
|||||||
**示例:**
|
**示例:**
|
||||||
> 示例中展示, 将`mm`映射成快捷键
|
> 示例中展示, 将`mm`映射成快捷键
|
||||||
```lua
|
```lua
|
||||||
vim.keymap.set({'n', 'v'}, 'mm', '<Cmd>Translate<CR>')
|
vim.keymap.set({'n', 'x'}, 'mm', '<Cmd>Translate<CR>')
|
||||||
|
vim.keymap.set({'n', 'x'}, 'mk', '<Cmd>TransPlay<CR>') -- 自动发音选中或者光标下的单词
|
||||||
vim.keymap.set('n', 'mi', '<Cmd>TranslateInput<CR>')
|
vim.keymap.set('n', 'mi', '<Cmd>TranslateInput<CR>')
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -7,6 +7,28 @@ local title = vim.fn.has('nvim-0.9') == 1 and {
|
|||||||
} or nil
|
} or nil
|
||||||
|
|
||||||
|
|
||||||
|
string.width = vim.fn.strwidth
|
||||||
|
string.isEn = function(self)
|
||||||
|
local char = { self:byte(1, -1) }
|
||||||
|
for i = 1, #self do
|
||||||
|
if char[i] > 127 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
string.play = vim.fn.has('linux') == 1 and function(self)
|
||||||
|
local cmd = ([[echo "%s" | festival --tts]]):format(self)
|
||||||
|
vim.fn.jobstart(cmd)
|
||||||
|
end or function(self)
|
||||||
|
local seperator = vim.fn.has('unix') and '/' or '\\'
|
||||||
|
local file = debug.getinfo(1, "S").source:sub(2):match('(.*)lua') .. seperator .. 'tts' .. seperator .. 'say.js'
|
||||||
|
vim.fn.jobstart('node ' .. file .. ' ' .. self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
M.conf = {
|
M.conf = {
|
||||||
view = {
|
view = {
|
||||||
i = 'float',
|
i = 'float',
|
||||||
@ -132,28 +154,93 @@ M.setup = function(opts)
|
|||||||
|
|
||||||
times = times + 1
|
times = times + 1
|
||||||
if times == 1 then
|
if times == 1 then
|
||||||
M.translate = require('Trans.translate')
|
local api = vim.api
|
||||||
|
|
||||||
|
local get_mode = api.nvim_get_mode
|
||||||
|
local set_hl = api.nvim_set_hl
|
||||||
|
local new_command = api.nvim_create_user_command
|
||||||
|
|
||||||
if vim.fn.executable('sqlite3') ~= 1 then
|
if vim.fn.executable('sqlite3') ~= 1 then
|
||||||
error('Please check out sqlite3')
|
error('Please check out sqlite3')
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_create_user_command('Translate', function()
|
new_command('Translate', function()
|
||||||
M.translate()
|
M.translate()
|
||||||
end, { desc = ' 单词翻译', })
|
end, { desc = ' 单词翻译', })
|
||||||
|
|
||||||
vim.api.nvim_create_user_command('TranslateInput', function()
|
new_command('TranslateInput', function()
|
||||||
M.translate('i')
|
M.translate('i')
|
||||||
end, { desc = ' 搜索翻译' })
|
end, { desc = ' 搜索翻译' })
|
||||||
|
|
||||||
|
new_command('TransPlay', function()
|
||||||
|
local word = M.get_word(get_mode().mode)
|
||||||
|
if word ~= '' and word:isEn() then
|
||||||
|
word:play()
|
||||||
|
end
|
||||||
|
end, { desc = ' 自动发音' })
|
||||||
|
|
||||||
local hls = require('Trans.ui.theme')[conf.theme]
|
local hls = require('Trans.ui.theme')[conf.theme]
|
||||||
for hl, opt in pairs(hls) do
|
for hl, opt in pairs(hls) do
|
||||||
vim.api.nvim_set_hl(0, hl, opt)
|
set_hl(0, hl, opt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_select()
|
||||||
|
local s_start = vim.fn.getpos("v")
|
||||||
|
local s_end = vim.fn.getpos(".")
|
||||||
|
if s_start[2] > s_end[2] or s_start[3] > s_end[3] then
|
||||||
|
s_start, s_end = s_end, s_start
|
||||||
|
end
|
||||||
|
|
||||||
|
local n_lines = math.abs(s_end[2] - s_start[2]) + 1
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(0, s_start[2] - 1, s_end[2], false)
|
||||||
|
lines[1] = string.sub(lines[1], s_start[3], -1)
|
||||||
|
if n_lines == 1 then
|
||||||
|
lines[n_lines] = string.sub(lines[n_lines], 1, s_end[3] - s_start[3] + 1)
|
||||||
|
else
|
||||||
|
lines[n_lines] = string.sub(lines[n_lines], 1, s_end[3])
|
||||||
|
end
|
||||||
|
return table.concat(lines, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
M.get_word = function(mode)
|
||||||
|
local word
|
||||||
|
if mode == 'n' then
|
||||||
|
word = vim.fn.expand('<cword>')
|
||||||
|
elseif mode == 'v' then
|
||||||
|
vim.api.nvim_input('<ESC>')
|
||||||
|
word = get_select()
|
||||||
|
elseif mode == 'i' then
|
||||||
|
-- TODO Use Telescope with fuzzy finder
|
||||||
|
vim.ui.input({ prompt = '请输入需要查询的单词: ' }, function(input)
|
||||||
|
word = input
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
error('invalid mode: ' .. mode)
|
||||||
|
end
|
||||||
|
return word
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
M.translate = function(mode, view)
|
||||||
|
vim.validate {
|
||||||
|
mode = { mode, 's', true },
|
||||||
|
view = { view, 's', true }
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = mode or vim.api.nvim_get_mode().mode
|
||||||
|
view = view or M.conf.view[mode]
|
||||||
|
assert(mode and view)
|
||||||
|
local word = M.get_word(mode)
|
||||||
|
if word == nil or word == '' then
|
||||||
|
return
|
||||||
|
else
|
||||||
|
require('Trans.view.' .. view)(word:gsub('^%s+', '', 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
M.augroup = vim.api.nvim_create_augroup('Trans', { clear = true })
|
M.augroup = vim.api.nvim_create_augroup('Trans', { clear = true })
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@ -10,8 +10,8 @@ end
|
|||||||
|
|
||||||
local post = require('Trans.util.curl').POST
|
local post = require('Trans.util.curl').POST
|
||||||
|
|
||||||
local function get_field(word)
|
local function get_field(word, isEn)
|
||||||
local to = 'zh'
|
local to = isEn and 'zh' or 'en'
|
||||||
local tmp = appid .. word .. salt .. appPasswd
|
local tmp = appid .. word .. salt .. appPasswd
|
||||||
local sign = require('Trans.util.md5').sumhexa(tmp)
|
local sign = require('Trans.util.md5').sumhexa(tmp)
|
||||||
|
|
||||||
@ -25,12 +25,12 @@ local function get_field(word)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- this is a nice plugin
|
|
||||||
---返回一个channel
|
---返回一个channel
|
||||||
---@param word string
|
---@param word string
|
||||||
---@return table
|
---@return table
|
||||||
return function(word)
|
return function(word)
|
||||||
local query = get_field(word)
|
local isEn = word:isEn()
|
||||||
|
local query = get_field(word, isEn)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
post(uri, {
|
post(uri, {
|
||||||
@ -39,11 +39,11 @@ return function(word)
|
|||||||
content_type = "application/x-www-form-urlencoded",
|
content_type = "application/x-www-form-urlencoded",
|
||||||
},
|
},
|
||||||
callback = function(str)
|
callback = function(str)
|
||||||
local res = vim.json.decode(str)
|
local ok, res = pcall(vim.json.decode, str)
|
||||||
if res and res.trans_result then
|
if ok and res and res.trans_result then
|
||||||
result.value = {
|
result.value = {
|
||||||
word = word,
|
word = word,
|
||||||
translation = res.trans_result[1].dst,
|
[isEn and 'translation' or 'definition'] = res.trans_result[1].dst,
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.callback then
|
if result.callback then
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
local function get_select()
|
|
||||||
local s_start = vim.fn.getpos("v")
|
|
||||||
local s_end = vim.fn.getpos(".")
|
|
||||||
if s_start[2] > s_end[2] or s_start[3] > s_end[3] then
|
|
||||||
s_start, s_end = s_end, s_start
|
|
||||||
end
|
|
||||||
|
|
||||||
local n_lines = math.abs(s_end[2] - s_start[2]) + 1
|
|
||||||
local lines = vim.api.nvim_buf_get_lines(0, s_start[2] - 1, s_end[2], false)
|
|
||||||
lines[1] = string.sub(lines[1], s_start[3], -1)
|
|
||||||
if n_lines == 1 then
|
|
||||||
lines[n_lines] = string.sub(lines[n_lines], 1, s_end[3] - s_start[3] + 1)
|
|
||||||
else
|
|
||||||
lines[n_lines] = string.sub(lines[n_lines], 1, s_end[3])
|
|
||||||
end
|
|
||||||
return table.concat(lines, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_word(mode)
|
|
||||||
local word
|
|
||||||
if mode == 'n' then
|
|
||||||
word = vim.fn.expand('<cword>')
|
|
||||||
elseif mode == 'v' then
|
|
||||||
vim.api.nvim_input('<ESC>')
|
|
||||||
word = get_select()
|
|
||||||
elseif mode == 'i' then
|
|
||||||
-- TODO Use Telescope with fuzzy finder
|
|
||||||
vim.ui.input({ prompt = '请输入需要查询的单词: ' }, function(input)
|
|
||||||
word = input
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
error('invalid mode: ' .. mode)
|
|
||||||
end
|
|
||||||
|
|
||||||
return word
|
|
||||||
end
|
|
||||||
|
|
||||||
return function(mode, view)
|
|
||||||
vim.validate {
|
|
||||||
mode = { mode, 's', true },
|
|
||||||
view = { view, 's', true }
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if word == nil or word == '' then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
require('Trans.view.' .. view)(word:gsub('^%s+', '', 1))
|
|
||||||
end
|
|
||||||
end
|
|
@ -11,13 +11,14 @@ local display = function(self)
|
|||||||
self:frame(i)
|
self:frame(i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
while self.run do
|
while self.run do
|
||||||
self:frame()
|
self:frame()
|
||||||
end
|
end
|
||||||
callback()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
callback()
|
||||||
else
|
else
|
||||||
local frame
|
local frame
|
||||||
if self.times then
|
if self.times then
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
---判断单词是否含有中文
|
|
||||||
---@param word string 需要判断的单词
|
|
||||||
---@return boolean 判断结果
|
|
||||||
return function (word)
|
|
||||||
return false
|
|
||||||
end
|
|
@ -105,7 +105,7 @@ local process = {
|
|||||||
d = '限定词determiner ',
|
d = '限定词determiner ',
|
||||||
}
|
}
|
||||||
|
|
||||||
local f = '%s %s%%'
|
local f = '%s %2s%%'
|
||||||
for pos in vim.gsplit(m_result.pos, '/', true) do
|
for pos in vim.gsplit(m_result.pos, '/', true) do
|
||||||
m_content:addline(
|
m_content:addline(
|
||||||
it(m_indent .. f:format(pos_map[pos:sub(1, 1)], pos:sub(3)), 'TransPos')
|
it(m_indent .. f:format(pos_map[pos:sub(1, 1)], pos:sub(3)), 'TransPos')
|
||||||
@ -143,6 +143,7 @@ local process = {
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
translation = function()
|
translation = function()
|
||||||
|
if exist(m_result.translation) then
|
||||||
title('中文翻译')
|
title('中文翻译')
|
||||||
|
|
||||||
for trs in vim.gsplit(m_result.translation, '\n', true) do
|
for trs in vim.gsplit(m_result.translation, '\n', true) do
|
||||||
@ -150,6 +151,7 @@ local process = {
|
|||||||
it(m_indent .. trs, 'TransTranslation')
|
it(m_indent .. trs, 'TransTranslation')
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
m_content:newline('')
|
m_content:newline('')
|
||||||
end,
|
end,
|
||||||
@ -251,19 +253,15 @@ action = {
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
play = vim.fn.has('linux') == 1 and function()
|
play = function()
|
||||||
local cmd = ([[echo "%s" | festival --tts]]):format(m_result.word)
|
m_result.word:play()
|
||||||
vim.fn.jobstart(cmd)
|
|
||||||
end or function()
|
|
||||||
local seperator = vim.fn.has('unix') and '/' or '\\'
|
|
||||||
local file = debug.getinfo(1, "S").source:sub(2):match('(.*)lua') .. seperator .. 'tts' .. seperator .. 'say.js'
|
|
||||||
vim.fn.jobstart('node ' .. file .. ' ' .. m_result.word)
|
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
local function handle()
|
local function handle()
|
||||||
local hover = conf.hover
|
local hover = conf.hover
|
||||||
if hover.auto_play then
|
if m_result.translation and hover.auto_play then
|
||||||
local ok = pcall(action.play)
|
local ok = pcall(action.play)
|
||||||
if not ok then
|
if not ok then
|
||||||
vim.notify('自动发音失败, 请检查README发音部分', vim.log.WARN)
|
vim.notify('自动发音失败, 请检查README发音部分', vim.log.WARN)
|
||||||
@ -280,20 +278,21 @@ local function handle()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function online_query(word)
|
local function online_query(word)
|
||||||
-- TODO :Progress Bar
|
|
||||||
local lists = {}
|
local lists = {}
|
||||||
local engines = conf.engines
|
local engines = conf.engines
|
||||||
local size = #engines
|
local size = #engines
|
||||||
local icon = conf.icon
|
local icon = conf.icon
|
||||||
local error_msg = icon.notfound .. ' 没有找到相关的翻译'
|
local error_msg = icon.notfound .. ' 没有找到相关的翻译'
|
||||||
|
|
||||||
m_window:set_height(1)
|
m_window:set_height(1)
|
||||||
local width = m_window.width
|
local origin_width = m_window.width
|
||||||
m_window:set_width(error_msg:width())
|
m_window:set_width(error_msg:width())
|
||||||
|
|
||||||
if size == 0 then
|
if size == 0 then
|
||||||
m_content:addline(it(error_msg, 'TransFailed'))
|
m_content:addline(it(error_msg, 'TransFailed'))
|
||||||
m_window:open()
|
m_window:open()
|
||||||
return
|
return
|
||||||
|
|
||||||
else
|
else
|
||||||
m_window:open()
|
m_window:open()
|
||||||
for i = 1, size do
|
for i = 1, size do
|
||||||
@ -307,16 +306,19 @@ local function online_query(word)
|
|||||||
|
|
||||||
local timeout = conf.hover.timeout
|
local timeout = conf.hover.timeout
|
||||||
local interval = math.floor(timeout / (m_window.width - spinner[1]:width()))
|
local interval = math.floor(timeout / (m_window.width - spinner[1]:width()))
|
||||||
|
local width = m_window.width
|
||||||
|
|
||||||
local f = '%s %s'
|
local f = '%s %s'
|
||||||
require('Trans.util.animation')({
|
require('Trans.util.animation')({
|
||||||
times = m_window.width,
|
times = width,
|
||||||
|
interval = interval,
|
||||||
frame = function(self, times)
|
frame = function(self, times)
|
||||||
m_content:wipe()
|
m_content:wipe()
|
||||||
for i, v in ipairs(lists) do
|
for i, v in ipairs(lists) do
|
||||||
local res = v.value
|
local res = v.value
|
||||||
if res then
|
if res then
|
||||||
m_result = res
|
m_result = res
|
||||||
m_window:set_width(width)
|
m_window:set_width(origin_width)
|
||||||
handle()
|
handle()
|
||||||
m_content:attach()
|
m_content:attach()
|
||||||
|
|
||||||
@ -327,26 +329,24 @@ local function online_query(word)
|
|||||||
|
|
||||||
self.run = false
|
self.run = false
|
||||||
return
|
return
|
||||||
elseif res == 'false' then
|
|
||||||
|
elseif res == false then
|
||||||
table.remove(lists, i)
|
table.remove(lists, i)
|
||||||
size = size - 1
|
size = size - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if size == 0 then
|
local line
|
||||||
m_content:addline(
|
if size == 0 or times == width then
|
||||||
it(error_msg, 'TransFailed')
|
line = it(error_msg, 'TransFailed')
|
||||||
)
|
self.run = false
|
||||||
m_content:attach()
|
|
||||||
|
|
||||||
else
|
else
|
||||||
m_content:addline(
|
line = it(f:format(spinner[times % range + 1], cell:rep(times)), 'MoreMsg')
|
||||||
it(f:format(spinner[times % range + 1], cell:rep(times)), 'MoreMsg')
|
|
||||||
)
|
|
||||||
m_content:attach()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
m_content:addline(line)
|
||||||
|
m_content:attach()
|
||||||
end,
|
end,
|
||||||
interval = interval,
|
|
||||||
}):display()
|
}):display()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ local api = vim.api
|
|||||||
local new_content = require('Trans.content')
|
local new_content = require('Trans.content')
|
||||||
local new_animation = require('Trans.util.animation')
|
local new_animation = require('Trans.util.animation')
|
||||||
|
|
||||||
string.width = vim.fn.strwidth
|
|
||||||
|
|
||||||
local busy = false
|
local busy = false
|
||||||
local function lock()
|
local function lock()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user