Compare commits

..

36 Commits

Author SHA1 Message Date
tqcq
e2fe39eeff fix fetch word 2025-03-20 17:00:31 +08:00
tqcq
35c6c48b2e feat support db_url 2025-03-18 03:34:15 +00:00
Zoran
3ebcd7b785
Merge pull request #56 from TD-Sky/v2
fix(font): replace removed characters with valids
2024-04-21 14:58:21 +08:00
TD-Sky
dd1fe3caae fix(font): replace removed characters with valids 2024-04-21 14:52:58 +08:00
Zoran
98507facfe
Merge pull request #53 from MoetaYuko/v2
Windows fixes
2024-02-13 15:06:38 +08:00
Moeta Yuko
a13c09e95f feat: use powershell to unzip on windows 2024-02-13 14:50:55 +08:00
Moeta Yuko
863213bbc8 fix: fix plugin_dir and use left slash as path separator for windows
Fixes #46
2024-02-13 14:50:55 +08:00
Moeta Yuko
4b9e8a7ab9 chore: remove useless relative_path 2024-02-13 13:29:16 +08:00
Zoran
50c2f003f4
Merge pull request #38 from Xiao-M0/v2
feat: get visual-block text
2023-05-16 16:07:03 +08:00
xiaomo
2de2a713af refactor: add get_range() 2023-05-15 12:43:42 +08:00
xiaomo
cb47a09cbc feat: get visual-block text 2023-05-15 09:04:40 +08:00
JuanZoran
5504ab5757 chore: remove dependency of node and fix up termux tts name 2023-05-14 12:58:06 +08:00
JuanZoran
edc6b89086 chore: add MIT License 2023-05-14 10:55:10 +08:00
JuanZoran
586cd6ff08 fix: system checker mistake 2023-05-13 19:36:40 +08:00
Zoran
b5e626a88c
Merge pull request #34 from Xiao-M0/v2
feat: add win wsl, remove python and nodejs dependencies
2023-05-13 19:07:36 +08:00
xiaomo
e993dc12be fix: get_select get_lines 2023-05-13 18:16:28 +08:00
xiaomo
871cc1e48a refactor: wsl 2023-05-13 18:13:32 +08:00
小尛
6e02f3b1ba fix: update exchange_map 2023-05-12 20:39:46 +08:00
小尛
377c84c146 fix: double quote error 2023-05-12 20:27:39 +08:00
小尛
ba08913b1e feat: get_lines 2023-04-25 13:48:52 +08:00
JuanZoran
854bce7b5a chore: remove useless code 2023-04-24 20:58:56 +08:00
小尛
a48caf51e1 remove: node 2023-04-24 19:57:33 +08:00
小尛
24a838646d feat: add win wsl 2023-04-24 18:48:36 +08:00
Zoran
156e03306a
Merge pull request #33 from Xiao-M0/v2
fix: win separator
2023-04-24 13:04:43 +08:00
小尛
0a1b6cf742 feat: say.py 2023-04-24 13:02:48 +08:00
小尛
758b9c0b33 fix: win separator 2023-04-24 12:42:59 +08:00
JuanZoran
3faab735fb chore: make hover window focusable and update docs 2023-04-16 16:38:39 +08:00
JuanZoran
335c5079cc docs: update README 2023-04-08 21:46:29 +08:00
JuanZoran
38b8e20729 chore: close window and buffer more safely 2023-04-07 19:05:24 +08:00
JuanZoran
fcde85544a feat: use a non-invasive keymap define method 2023-04-06 13:38:19 +08:00
JuanZoran
2ae2effecc chore: more spinner style support 2023-04-04 16:19:06 +08:00
JuanZoran
da6e717f2c feat: use a non-invasive keymap define method 2023-04-02 18:02:21 +08:00
JuanZoran
576f1eb66a feat: add termux tts support 2023-04-02 13:34:54 +08:00
JuanZoran
9357574b5c chore: sync 2023-04-01 09:59:09 +08:00
JuanZoran
987e1a3341 test: add simple window unit test 2023-03-31 12:51:11 +08:00
JuanZoran
b27bc4ed26 fix: add util unit test and fix util.center mistake 2023-03-31 01:04:36 +08:00
26 changed files with 996 additions and 509 deletions

21
LICENCE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Zoran
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

263
README.md
View File

@ -2,51 +2,48 @@
<!--toc:start--> <!--toc:start-->
- [Trans.nvim](#transnvim) - [注意: 当前分支目前没有发布, README.md 的描述并不准确, 遇到问题请切换到 `main`分支或者联系我](#注意-当前分支目前没有发布-readmemd-的描述并不准确-遇到问题请切换到-main分支或者联系我) - [Trans.nvim](#transnvim)
- [特点](#特点) - [特点](#特点)
- [屏幕截图](#屏幕截图) - [屏幕截图](#屏幕截图)
- [演示](#演示) - [演示](#演示)
- [离线查询](#离线查询) - [离线查询](#离线查询)
- [\*在线查询\*\* (有道)](#在线查询-有道) - [在线查询演示 (有道)](#在线查询演示-有道)
- [主题](#主题) - [主题](#主题)
- [安装](#安装) - [安装](#安装)
- [配置](#配置) - [配置](#配置)
- [快捷键绑定](#快捷键绑定)
- [高亮组](#高亮组) - [高亮组](#高亮组)
- [声明](#声明) - [声明](#声明)
- [感谢](#感谢) - [感谢](#感谢)
- [贡献](#贡献) - [贡献](#贡献)
- [从 v1 (main)分支迁移](#从-v1-main分支迁移)
- [待办 (画大饼)](#待办-画大饼) - [待办 (画大饼)](#待办-画大饼)
- [项目情况](#项目情况) - [项目情况](#项目情况)
<!--toc:end--> <!--toc:end-->
### 注意: 当前分支目前没有发布, README.md 的描述并不准确, 遇到问题请切换到 `main`分支或者联系我 > **插件默认词库的路径为插件目录**
例如: `lazy` 用户应该在 `$HOME/.local/share/nvim/lazy/Trans.nvim`
## 特点 ## 特点
- 使用纯 lua 编写, 速度极快 - `使用纯 lua 编写`
> `Lazy.nvim`的记录: <font color="#0099FF">`➜  Trans.nvim 0.82ms`</font>
- **可以定义快捷键读英文单词**
> 见 wiki
- 大部分功能可以自定义: - 大部分功能可以自定义:
- 高亮 - 🔍 高亮
- 悬浮大小 - 👀 悬浮大小
- 排版顺序 - 📜 排版顺序
- 弹窗大小 - 💬 弹窗大小
- `舒服窗口动画` - 🎉 舒服窗口动画
- etc (更多可以查看[配置](#配置)) - 更多可以查看[配置](#配置)
- **完全离线** 的单词翻译体验 (可能后面会支持在线翻译) - `离线``在线`翻译的支持
- 支持显示: - 支持显示:
- 柯林斯星级 - 🌟 柯林斯星级
- 牛津 3000 词汇 - 📚 牛津 3000 词汇
- 中文翻译 - 🇨🇳 中文翻译
- 英文翻译 (不是英译中, 而是用英文解释) - 🇺🇸 英文翻译 (不是英译中, 而是用英文解释)
- 词根 - 🌱 词根
- etc - etc
- 舒服的排版和`动画` - 支持`平滑动画`
- 支持 `normal``visual`模式 - 支持 `normal``visual`模式
> <font color='#FF9900'>不支持 visual-block mode</font> > <font color='#FF9900'>不支持 visual-block mode</font>
- 本地词库单词量: `430w` - 本地词库单词量: `430w`
@ -61,13 +58,13 @@
https://user-images.githubusercontent.com/107862700/226175984-1a95bea7-8d66-450e-87e1-ba9c91c37ab8.mp4 https://user-images.githubusercontent.com/107862700/226175984-1a95bea7-8d66-450e-87e1-ba9c91c37ab8.mp4
### 在线查询 (有道) ### 在线查询演示 (有道)
https://user-images.githubusercontent.com/107862700/226176106-c2962dd3-d66c-499c-b44a-1f471b79fe38.mp4 https://user-images.githubusercontent.com/107862700/226176106-c2962dd3-d66c-499c-b44a-1f471b79fe38.mp4
**使用在线查询需要配置相应的 app_id 和 app_passwd** **使用在线查询需要配置相应的 app_id 和 app_passwd**
在线查询配置见: [wiki](https://github.com/JuanZoran/Trans.nvim/wiki/%E9%85%8D%E7%BD%AE#%E5%9C%A8%E7%BA%BF%E6%9F%A5%E8%AF%A2%E9%85%8D%E7%BD%AE) 配置说明见: [wiki](https://github.com/JuanZoran/Trans.nvim/wiki/%E9%85%8D%E7%BD%AE#%E5%9C%A8%E7%BA%BF%E6%9F%A5%E8%AF%A2%E9%85%8D%E7%BD%AE)
### 主题 ### 主题
@ -96,7 +93,7 @@ _安装之前, 首先需要明确本插件的依赖:_
```lua ```lua
use { use {
'JuanZoran/Trans.nvim' 'JuanZoran/Trans.nvim',
run = function() require('Trans').install() end, -- 自动下载使用的本地词库 run = function() require('Trans').install() end, -- 自动下载使用的本地词库
requires = 'kkharji/sqlite.lua', , requires = 'kkharji/sqlite.lua', ,
-- 如果你不需要任何配置的话, 可以直接按照下面的方式启动 -- 如果你不需要任何配置的话, 可以直接按照下面的方式启动
@ -139,12 +136,13 @@ use {
```lua ```lua
{ {
"JuanZoran/Trans.nvim", "JuanZoran/Trans.nvim",
build = function () require'Trans'.install() end,
keys = { keys = {
-- 可以换成其他你想映射的键 -- 可以换成其他你想映射的键
{ 'mm', mode = { 'n', 'x' }, '<Cmd>Translate<CR>', desc = ' Translate' }, { 'mm', mode = { 'n', 'x' }, '<Cmd>Translate<CR>', desc = '󰊿 Translate' },
{ 'mk', mode = { 'n', 'x' }, '<Cmd>TransPlay<CR>', desc = ' Auto Play' }, { 'mk', mode = { 'n', 'x' }, '<Cmd>TransPlay<CR>', desc = ' Auto Play' },
-- 目前这个功能的视窗还没有做好可以在配置里将view.i改成hover -- 目前这个功能的视窗还没有做好可以在配置里将view.i改成hover
{ 'mi', '<Cmd>TranslateInput<CR>', desc = ' Translate From Input' }, { 'mi', '<Cmd>TranslateInput<CR>', desc = '󰊿 Translate From Input' },
}, },
dependencies = { 'kkharji/sqlite.lua', }, dependencies = { 'kkharji/sqlite.lua', },
opts = { opts = {
@ -157,40 +155,29 @@ use {
<font color="#FF9900">**注意事项**: </font> <font color="#FF9900">**注意事项**: </font>
- `install.sh`
- 使用了 `wget`下载词库, 安装请确保你的环境变量中存在 wget
- install.sh 下载后会自动将词库解压, 并移动到 `$HOME/.vim/dict`文件夹下
- 目前仅在 `Ubuntu22.04`的环境下测试通过
> 如果上述条件不符合, 请删掉 `run = 'install.sh'`部分, 考虑手动安装词库
> 如果上述条件满足, 仍出现问题, 欢迎在 issue 里向我反馈,我会及时尝试解决
- 下载词典的过程中, 需要能够 `流畅的访问github下载` - 下载词典的过程中, 需要能够 `流畅的访问github下载`
> 词库文件压缩包大小为: **281M** 如果下载出现问题, 正常是会自动下载
> 解压缩后的大小大概为: 1.2G
- 安装后如果不能正常运行, 请尝试检查一下问题: > 词库文件压缩包大小为: **281M**
> 解压缩后的大小大概为: **1.2G**
- 本机是否已经安装了 `sqlite3` - 安装后如果不能正常运行, 清尝试运行 `checkhealth Trans`
> Linux 下安装:
> `sudo pacman -S sqlite # Arch`
> `sudo apt-get install sqlite3 libsqlite3-dev # Ubuntu`
> **尝试运行 `checkhealth Trans`** - **`auto_play`** 的使用:
- **`auto_play`** 使用步骤: - `Linux` 需要安装`festival`
> linux 只需要安装`festival` > `sudo apt-get install festival festvox-kallpc16k`
> sudo apt-get install festival festvox-kallpc16k
> **_如果你想要设置音色发音可以访问:_** [Festival 官方](https://www.cstr.ed.ac.uk/projects/festival/morevoices.html)
> 可以选择英音、美音、男声、女声
> 其他操作系统 **如果你想要设置音色,发音可以访问:** [Festival 官方](https://www.cstr.ed.ac.uk/projects/festival/morevoices.html)
可以选择英音、美音、男声、女声
- 需要确保安装了`nodejs` - `Termux` 需要安装`termux-api`
- 进入插件的`tts`目录运行`npm install`
> 如果`install`运行正常则自动安装,如果安装失败,请尝试手动安装 - `Mac` 使用系统的`say`命令
- `Windows` 使用原生的 Powershell 命令, 感谢[PR](https://github.com/JuanZoran/Trans.nvim/pull/34)
- `title`的配置,只对`neovim 0.9+`版本有效 - `title`的配置,只对`neovim 0.9+`版本有效
@ -244,12 +231,16 @@ use {
## 配置 ## 配置
详细见**wiki**: [配置说明](https://github.com/JuanZoran/Trans.nvim/wiki/%E9%85%8D%E7%BD%AE) 详细见**wiki**: [基本配置说明](https://github.com/JuanZoran/Trans.nvim/wiki/%E9%85%8D%E7%BD%AE)
<details>
<summary>默认配置</summary>
```lua ```lua
require 'Trans'.setup { default_conf = {
---@type string the directory for database file and password file ---@type string the directory for database file and password file
dir = require 'Trans'.plugin_dir, dir = require 'Trans'.plugin_dir,
debug = true,
---@type 'default' | 'dracula' | 'tokyonight' global Trans theme [see lua/Trans/style/theme.lua] ---@type 'default' | 'dracula' | 'tokyonight' global Trans theme [see lua/Trans/style/theme.lua]
theme = 'default', -- default | tokyonight | dracula theme = 'default', -- default | tokyonight | dracula
strategy = { strategy = {
@ -268,7 +259,7 @@ require 'Trans'.setup {
border = 'rounded', border = 'rounded',
title = vim.fn.has 'nvim-0.9' == 1 and { title = vim.fn.has 'nvim-0.9' == 1 and {
{ '', 'TransTitleRound' }, { '', 'TransTitleRound' },
{ ' Trans', 'TransTitle' }, { '󰊿 Trans', 'TransTitle' },
{ '', 'TransTitleRound' }, { '', 'TransTitleRound' },
} or nil, -- need nvim-0.9+ } or nil, -- need nvim-0.9+
auto_play = true, auto_play = true,
@ -294,11 +285,13 @@ require 'Trans'.setup {
split_width = 60, split_width = 60,
padding = 10, -- padding for hover window width padding = 10, -- padding for hover window width
keymaps = { keymaps = {
pageup = '[[', -- INFO : No default keymaps anymore, please set it yourself
pagedown = ']]', -- pageup = '<C-u>',
pin = '<leader>[', -- pagedown = '<C-d>',
close = '<leader>]', -- pin = '<leader>[',
toggle_entry = '<leader>;', -- close = '<leader>]',
-- toggle_entry = '<leader>;',
-- play = '_', -- Deprecated -- play = '_', -- Deprecated
}, },
---@type string[] auto close events ---@type string[] auto close events
@ -333,7 +326,7 @@ require 'Trans'.setup {
-- or use emoji -- or use emoji
list = '●', -- ● | ○ | ◉ | ◯ | ◇ | ◆ | ▪ | ▫ | ⬤ | 🟢 | 🟡 | 🟣 | 🟤 | 🟠| 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟦 list = '●', -- ● | ○ | ◉ | ◯ | ◇ | ◆ | ▪ | ▫ | ⬤ | 🟢 | 🟡 | 🟣 | 🟤 | 🟠| 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟦
star = '', -- ⭐ | ✴ | ✳ | ✲ | ✱ | ✰ | ★ | ☆ | 🌟 | 🌠 | 🌙 | 🌛 | 🌜 | 🌟 | 🌠 | 🌌 | 🌙 | star = '', -- ⭐ | ✴ | ✳ | ✲ | ✱ | ✰ | ★ | ☆ | 🌟 | 🌠 | 🌙 | 🌛 | 🌜 | 🌟 | 🌠 | 🌌 | 🌙 |
notfound = ' ', --❔ | ❓ | ❗ | ❕| notfound = '󰆆 ', --❔ | ❓ | ❗ | ❕|
yes = '✔', -- ✅ | ✔️ | ☑ yes = '✔', -- ✅ | ✔️ | ☑
no = '', -- ❌ | ❎ | ✖ | ✘ | ✗ | no = '', -- ❌ | ❎ | ✖ | ✘ | ✗ |
cell = '■', -- ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉ cell = '■', -- ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉
@ -349,6 +342,8 @@ require 'Trans'.setup {
} }
``` ```
</details>
## 快捷键绑定 ## 快捷键绑定
**示例:** **示例:**
@ -361,66 +356,90 @@ vim.keymap.set({'n', 'x'}, 'mm', '<Cmd>Translate<CR>')
vim.keymap.set({'n', 'x'}, 'mk', '<Cmd>TransPlay<CR>') -- 自动发音选中或者光标下的单词 vim.keymap.set({'n', 'x'}, 'mk', '<Cmd>TransPlay<CR>') -- 自动发音选中或者光标下的单词
``` ```
## 高亮组 **窗口快捷键**
> 默认定义
```lua ```lua
{ require 'Trans'.setup {
TransWord = { frontend = {
fg = '#7ee787', hover = {
bold = true, keymaps = {
-- pageup = 'whatever you want',
-- pagedown = 'whatever you want',
-- pin = 'whatever you want',
-- close = 'whatever you want',
-- toggle_entry = 'whatever you want',
},
}, },
TransPhonetic = { },
link = 'Linenr'
},
TransTitle = {
fg = '#0f0f15',
bg = '#75beff',
bold = true,
},
TransTitleRound = {
fg = '#75beff',
},
TransTag = {
-- fg = '#e5c07b',
link = '@tag'
},
TransExchange = {
link = 'TransTag',
},
TransPos = {
link = 'TransTag',
},
TransTranslation = {
link = 'TransWord',
},
TransDefinition = {
link = 'Moremsg',
},
TransWin = {
link = 'Normal',
},
TransBorder = {
fg = '#89B4FA',
},
TransCollins = {
fg = '#faf743',
bold = true,
},
TransFailed = {
fg = '#7aa89f',
},
TransWaitting = {
link = 'MoreMsg'
},
TransWeb = {
-- TODO :
link = 'MoreMsg',
}
} }
}
``` ```
> 当窗口没有打开的时候, key 会被使用`vim.api.nvim_feedkey`来执行
## 高亮组
所有主题可见 `lua/Trans/style/theme.lua`
<details>
<summary>默认主题</summary>
```lua
TransWord = {
fg = '#7ee787',
bold = true,
}
TransPhonetic = {
link = 'Linenr'
}
TransTitle = {
fg = '#0f0f15',
bg = '#75beff',
bold = true,
}
TransTitleRound = {
fg = '#75beff',
}
TransTag = {
-- fg = '#e5c07b',
link = '@tag'
}
TransExchange = {
link = 'TransTag',
}
TransPos = {
link = 'TransTag',
}
TransTranslation = {
link = 'TransWord',
}
TransDefinition = {
link = 'Moremsg',
}
TransWin = {
link = 'Normal',
}
TransBorder = {
fg = '#89B4FA',
}
TransCollins = {
fg = '#faf743',
bold = true,
}
TransFailed = {
fg = '#7aa89f',
}
TransWaitting = {
link = 'MoreMsg'
}
TransWeb = {
link = 'MoreMsg',
}
```
</details>
## 声明 ## 声明
- 本插件词典基于[ECDICT](https://github.com/skywind3000/ECDICT) - 本插件词典基于[ECDICT](https://github.com/skywind3000/ECDICT)
@ -433,20 +452,24 @@ vim.keymap.set({'n', 'x'}, 'mk', '<Cmd>TransPlay<CR>') -- 自动发音选中或
## 贡献 ## 贡献
> 更新比较频繁, 文档先鸽了 > 更新比较频繁, 文档先鸽了 (wiki 写了一小部分
> 如果你想要参加这个项目, 可以提 issue, 我会把文档补齐 > 如果你想要参加这个项目, 可以提 issue, 我会把文档补齐
## 从 v1 (main)分支迁移
见[wiki](<https://github.com/JuanZoran/Trans.nvim/wiki/%E4%BB%8E(v1)main%E5%88%86%E6%94%AF%E8%BF%81%E7%A7%BB>)
## 待办 (画大饼) ## 待办 (画大饼)
- [x] 快捷键定义 - [x] 快捷键定义
- [x] 自动读音 - [x] 自动读音
- [x] 在线多引擎异步查询 - [x] 在线多引擎异步查询
- [x] `句子翻译` | `中翻英` 的支持 - [x] `句子翻译` | `中翻英` 的支持
- [ ] 迁移文档 - [x] 迁移文档
- [ ] 多风格样式查询 - [ ] 多风格样式查询
- [ ] 重新录制屏幕截图示例
- [ ] 变量命名的支持 - [ ] 变量命名的支持
- [ ] 历史查询结果保存 - [ ] 历史查询结果保存
- [ ] 翻译结果替换
## 项目情况 ## 项目情况

View File

@ -129,7 +129,7 @@ Packer.nvim ~
requires = { 'kkharji/sqlite.lua', }, requires = { 'kkharji/sqlite.lua', },
config = function() config = function()
require("Trans").setup {} -- 启动Trans require("Trans").setup {} -- 启动Trans
vim.keymap.set({"n", 'x'}, "mm", '<Cmd>Translate<CR>', { desc = ' Translate' }) -- 自动判断virtual 还是 normal 模式 vim.keymap.set({"n", 'x'}, "mm", '<Cmd>Translate<CR>', { desc = '󰊿 Translate' }) -- 自动判断virtual 还是 normal 模式
vim.keymap.set({'n', 'x'}, 'mk', '<Cmd>TransPlay<CR>', {desc = ' 自动发音'}) -- 自动发音选中或者光标下的单词 vim.keymap.set({'n', 'x'}, 'mk', '<Cmd>TransPlay<CR>', {desc = ' 自动发音'}) -- 自动发音选中或者光标下的单词
end end
} }
@ -142,11 +142,11 @@ Lazy.nvim ~
"JuanZoran/Trans.nvim", "JuanZoran/Trans.nvim",
keys = { keys = {
-- 可以换成其他你想映射的键 -- 可以换成其他你想映射的键
{ 'mm', mode = { 'n', 'x' }, '<Cmd>Translate<CR>', desc = ' Translate' }, { 'mm', mode = { 'n', 'x' }, '<Cmd>Translate<CR>', desc = '󰊿 Translate' },
{ 'mk', mode = { 'n', 'x' }, '<Cmd>TransPlay<CR>', desc = ' 自动发音' }, { 'mk', mode = { 'n', 'x' }, '<Cmd>TransPlay<CR>', desc = ' 自动发音' },
-- 目前这个功能的视窗还没有做好可以在配置里将view.i改成hover -- 目前这个功能的视窗还没有做好可以在配置里将view.i改成hover
{ 'mi', '<Cmd>TranslateInput<CR>', desc = ' Translate From Input' }, { 'mi', '<Cmd>TranslateInput<CR>', desc = '󰊿 Translate From Input' },
}, },
dependencies = { 'kkharji/sqlite.lua', }, dependencies = { 'kkharji/sqlite.lua', },
opts = { opts = {
@ -301,7 +301,7 @@ Festival配置(仅针对linux用户) ~
-- or use emoji -- or use emoji
list = '●', -- ● | ○ | ◉ | ◯ | ◇ | ◆ | ▪ | ▫ | ⬤ | 🟢 | 🟡 | 🟣 | 🟤 | 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟠 | 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟠 list = '●', -- ● | ○ | ◉ | ◯ | ◇ | ◆ | ▪ | ▫ | ⬤ | 🟢 | 🟡 | 🟣 | 🟤 | 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟠 | 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟠
star = '', -- ⭐ | ✴ | ✳ | ✲ | ✱ | ✰ | ★ | ☆ | 🌟 | 🌠 | 🌙 | 🌛 | 🌜 | 🌟 | 🌠 | 🌌 | 🌙 | star = '', -- ⭐ | ✴ | ✳ | ✲ | ✱ | ✰ | ★ | ☆ | 🌟 | 🌠 | 🌙 | 🌛 | 🌜 | 🌟 | 🌠 | 🌌 | 🌙 |
notfound = ' ', --❔ | ❓ | ❗ | ❕| notfound = '󰆆 ', --❔ | ❓ | ❗ | ❕|
yes = '✔', -- ✅ | ✔️ | ☑ yes = '✔', -- ✅ | ✔️ | ☑
no = '', -- ❌ | ❎ | ✖ | ✘ | ✗ | no = '', -- ❌ | ❎ | ✖ | ✘ | ✗ |
cell = '■', -- ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉ █ cell = '■', -- ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉ █

View File

@ -15,4 +15,9 @@
- [ ] Unlimit width for sentence - [ ] Unlimit width for sentence
已知问题:
1. 缓存了的单词, 无法使用toggle_entry 进入页面
2. 加载配置需要输入所有表的key
- default_strategy can't deal with table correctly - default_strategy can't deal with table correctly

View File

@ -1,7 +1,7 @@
local Trans = require 'Trans' local Trans = require 'Trans'
local db = require 'sqlite.db' local db = require 'sqlite.db'
local path = Trans.conf.dir .. Trans.separator .. 'ultimate.db' local path = Trans.conf.dir .. '/ultimate.db'
local dict = db:open(path) local dict = db:open(path)
local db_name = 'stardict' local db_name = 'stardict'
vim.api.nvim_create_autocmd('VimLeavePre', { vim.api.nvim_create_autocmd('VimLeavePre', {
@ -99,6 +99,8 @@ local formatter = {
['p'] = '过去式 ', ['p'] = '过去式 ',
['r'] = '比较级 ', ['r'] = '比较级 ',
['t'] = '最高级 ', ['t'] = '最高级 ',
['b'] = '比较级 ',
['z'] = '最高级 ',
['s'] = '复数 ', ['s'] = '复数 ',
['d'] = '过去分词 ', ['d'] = '过去分词 ',
['i'] = '现在分词 ', ['i'] = '现在分词 ',

View File

@ -2,7 +2,7 @@ local api, fn = vim.api, vim.fn
---@class TransBuffer ---@class TransBuffer
---@field bufnr integer buffer handle ---@field bufnr integer buffer handle
---@field [number] string|TransNode|TransNode[] buffer[line] content ---@field [integer] string|TransNode|TransNode[] buffer[line] content
local buffer = {} local buffer = {}
-- INFO : corountine can't invoke C function -- INFO : corountine can't invoke C function
@ -37,7 +37,7 @@ end
---Destory buffer ---Destory buffer
function buffer:destroy() function buffer:destroy()
api.nvim_buf_delete(self.bufnr, { force = true }) pcall(api.nvim_buf_delete, self.bufnr, { force = true })
end end
---Set buffer load keymap ---Set buffer load keymap
@ -91,11 +91,7 @@ end
---@return integer ---@return integer
function buffer:line_count() function buffer:line_count()
local line_count = api.nvim_buf_line_count(self.bufnr) local line_count = api.nvim_buf_line_count(self.bufnr)
if line_count == 1 and self[1] == '' then return line_count == 1 and self[1] == '' and 0 or line_count
return 0
end
return line_count
end end
---Set line content ---Set line content
@ -151,11 +147,11 @@ buffer.__index = function(self, key)
end end
end end
buffer.__newindex = function(self, key, nodes) buffer.__newindex = function(self, key, values)
if type(key) == 'number' then if type(key) == 'number' then
self:setline(nodes, key) self:setline(values, key)
else else
rawset(self, key, nodes) rawset(self, key, values)
end end
end end

View File

@ -6,7 +6,7 @@
return { return {
---@type string the directory for database file and password file ---@type string the directory for database file and password file
dir = require 'Trans'.plugin_dir, dir = require 'Trans'.plugin_dir,
warning = true, db_url = 'https://github.com/skywind3000/ECDICT-ultimate/releases/download/1.0.0/ecdict-ultimate-sqlite.zip',
debug = true, debug = true,
---@type 'default' | 'dracula' | 'tokyonight' global Trans theme [see lua/Trans/style/theme.lua] ---@type 'default' | 'dracula' | 'tokyonight' global Trans theme [see lua/Trans/style/theme.lua]
theme = 'default', -- default | tokyonight | dracula theme = 'default', -- default | tokyonight | dracula
@ -25,10 +25,10 @@ return {
query = 'fallback', query = 'fallback',
border = 'rounded', border = 'rounded',
title = vim.fn.has 'nvim-0.9' == 1 and { title = vim.fn.has 'nvim-0.9' == 1 and {
{ '', 'TransTitleRound' }, { '', 'TransTitleRound' },
{ ' Trans', 'TransTitle' }, { '󰊿 Trans', 'TransTitle' },
{ '', 'TransTitleRound' }, { '', 'TransTitleRound' },
} or nil, -- need nvim-0.9+ } or nil, -- need nvim-0.9+
auto_play = true, auto_play = true,
---@type {open: string | boolean, close: string | boolean, interval: integer} Hover Window Animation ---@type {open: string | boolean, close: string | boolean, interval: integer} Hover Window Animation
animation = { animation = {
@ -52,11 +52,12 @@ return {
split_width = 60, split_width = 60,
padding = 10, -- padding for hover window width padding = 10, -- padding for hover window width
keymaps = { keymaps = {
pageup = '[[', -- pageup = '<C-u>',
pagedown = ']]', -- pagedown = '<C-d>',
pin = '<leader>[', -- pin = '<leader>[',
close = '<leader>]', -- close = '<leader>]',
toggle_entry = '<leader>;', -- toggle_entry = '<leader>;',
-- play = '_', -- Deprecated -- play = '_', -- Deprecated
}, },
---@type string[] auto close events ---@type string[] auto close events
@ -91,7 +92,7 @@ return {
-- or use emoji -- or use emoji
list = '', -- ● | ○ | ◉ | ◯ | ◇ | ◆ | ▪ | ▫ | ⬤ | 🟢 | 🟡 | 🟣 | 🟤 | 🟠| 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟦 list = '', -- ● | ○ | ◉ | ◯ | ◇ | ◆ | ▪ | ▫ | ⬤ | 🟢 | 🟡 | 🟣 | 🟤 | 🟠| 🟦 | 🟨 | 🟧 | 🟥 | 🟪 | 🟫 | 🟩 | 🟦
star = '', -- ⭐ | ✴ | ✳ | ✲ | ✱ | ✰ | ★ | ☆ | 🌟 | 🌠 | 🌙 | 🌛 | 🌜 | 🌟 | 🌠 | 🌌 | 🌙 | star = '', -- ⭐ | ✴ | ✳ | ✲ | ✱ | ✰ | ★ | ☆ | 🌟 | 🌠 | 🌙 | 🌛 | 🌜 | 🌟 | 🌠 | 🌌 | 🌙 |
notfound = ' ', --❔ | ❓ | ❗ | ❕| notfound = '󰆆 ', --❔ | ❓ | ❗ | ❕|
yes = '', -- ✅ | ✔️ | ☑ yes = '', -- ✅ | ✔️ | ☑
no = '', -- ❌ | ❎ | ✖ | ✘ | ✗ | no = '', -- ❌ | ❎ | ✖ | ✘ | ✗ |
cell = '', -- ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉ cell = '', -- ■ | □ | ▇ | ▏ ▎ ▍ ▌ ▋ ▊ ▉

View File

@ -37,7 +37,7 @@ function M.new(opts)
data.backends[i] = Trans.backend[name] data.backends[i] = Trans.backend[name]
end end
if Trans.util.is_English(str) then if Trans.util.is_english(str) then
data.from = 'en' data.from = 'en'
data.to = 'zh' data.to = 'zh'
else else

View File

@ -5,7 +5,7 @@ return function()
local fn = vim.fn local fn = vim.fn
-- INFO :Check ultimate.db exists -- INFO :Check ultimate.db exists
local dir = Trans.conf.dir local dir = Trans.conf.dir
local path = dir .. 'ultimate.db' local path = dir .. '/ultimate.db'
if fn.isdirectory(dir) == 0 then if fn.isdirectory(dir) == 0 then
fn.mkdir(dir, 'p') fn.mkdir(dir, 'p')
@ -17,17 +17,16 @@ return function()
end end
-- INFO :Download ultimate.db -- INFO :Download ultimate.db
local uri = 'https://github.com/skywind3000/ECDICT-ultimate/releases/download/1.0.0/ecdict-ultimate-sqlite.zip' local uri = Trans.conf.db_url
local zip = dir .. 'ultimate.zip' local zip = dir .. '/ultimate.zip'
local continue = fn.filereadable(zip) == 1 local continue = fn.filereadable(zip) == 1
local handle = function(output) local handle = function(output)
if output.exit == 0 and fn.filereadable(zip) then if output.exit == 0 and fn.filereadable(zip) then
if fn.executable 'unzip' == 0 then local cmd =
vim.notify('unzip not found, Please unzip ' .. zip .. 'manually', vim.log.ERROR) Trans.system == 'win' and
return string.format('powershell.exe -Command "Expand-Archive -Force %s %s"', zip, dir) or
end fn.executable('unzip') == 1 and string.format('unzip %s -d %s', zip, dir) or
error('unzip not found, Please unzip ' .. zip .. ' manually')
local cmd = string.format('unzip %s -d %s', zip, dir)
local status = os.execute(cmd) local status = os.execute(cmd)
os.remove(zip) os.remove(zip)
if status == 0 then if status == 0 then
@ -40,18 +39,12 @@ return function()
vim.notify(debug_message, vim.log.ERROR) vim.notify(debug_message, vim.log.ERROR)
end end
Trans.curl.get(uri, { Trans.curl.get(uri, {
output = zip, output = zip,
callback = handle, callback = handle,
extra = continue and { '-C', '-' } or nil, extra = continue and { '-C', '-' } or nil,
}) })
local message = continue and 'Continue download database' or 'Begin to download database' local message = continue and 'Continue download database' or 'Begin to download database'
vim.notify(message, vim.log.levels.INFO) vim.notify(message, vim.log.levels.INFO)
-- INFO : Install tts dependencies
if fn.has 'linux' == 0 and fn.has 'mac' == 0 then
os.execute 'cd ./tts && npm install'
end
end end

View File

@ -52,12 +52,4 @@ return function(opts)
set_strategy_opts(conf) set_strategy_opts(conf)
define_highlights(conf) define_highlights(conf)
if Trans.conf.warning then
vim.notify([[
v2已经发布, :
https://github.com/JuanZoran/Trans.nvim
使(v2)
]], vim.log.levels.WARN)
end
end end

View File

@ -59,8 +59,7 @@ local strategy = {
local name = backend.name local name = backend.name
---@cast backend TransBackend ---@cast backend TransBackend
while result[name] == nil do while result[name] == nil and update(backend) do
if not update(backend) then break end
end end
if result[name] then return true end if result[name] then return true end
@ -72,12 +71,13 @@ local strategy = {
} }
-- HACK : Core process logic -- HACK : Core process logic
local function process(opts) local function process(opts)
opts = init_opts(opts) opts = init_opts(opts)
local str = opts.str
if not str or str == '' then return end
if not opts.str or opts.str == '' then return end
local str = opts.str:match("(%w+)")
-- Find in cache -- Find in cache

View File

@ -3,20 +3,23 @@ local fn, api = vim.fn, vim.api
---@class TransUtil ---@class TransUtil
local M = require 'Trans'.metatable 'util' local M = require 'Trans'.metatable 'util'
---Get the range of visual modes
---@return table
function M.get_range()
local _start = fn.getpos 'v'
local _end = fn.getpos '.'
local s_row, e_row = math.min(_start[2], _end[2]), math.max(_start[2], _end[2])
local s_col, e_col = math.min(_start[3], _end[3]), math.max(_start[3], _end[3])
return { s_row, e_row, s_col, e_col }
end
---Get selected text ---Get selected text
---@return string ---@return string
function M.get_select() function M.get_select()
local _start = fn.getpos 'v' local s_row, e_row, s_col, e_col = unpack(M.get_range())
local _end = fn.getpos '.'
if _start[2] > _end[2] or (_start[3] > _end[3] and _start[2] == _end[2]) then
_start, _end = _end, _start
end
local s_row, s_col = _start[2], _start[3]
local e_row, e_col = _end[2], _end[3]
-- print(s_row, e_row, s_col, e_col)
---@type string ---@type string
---@diagnostic disable-next-line: assign-type-mismatch ---@diagnostic disable-next-line: assign-type-mismatch
local line = fn.getline(e_row) local line = fn.getline(e_row)
@ -24,7 +27,6 @@ function M.get_select()
---@diagnostic disable-next-line: param-type-mismatch ---@diagnostic disable-next-line: param-type-mismatch
e_col = vim.str_byteindex(line, uidx) e_col = vim.str_byteindex(line, uidx)
if s_row == e_row then if s_row == e_row then
return line:sub(s_col, e_col) return line:sub(s_col, e_col)
else else
@ -32,7 +34,43 @@ function M.get_select()
local e = #lines local e = #lines
lines[1] = lines[1]:sub(s_col) lines[1] = lines[1]:sub(s_col)
lines[e] = line:sub(1, e_col) lines[e] = line:sub(1, e_col)
return table.concat(lines) return table.concat(lines, ' ')
end
end
---Get selected text
---@return string
function M.get_lines()
local s_row, e_row = unpack(M.get_range())
if s_row == e_row then
return fn.getline(s_row)
else
local lines = fn.getline(s_row, e_row)
return table.concat(lines, " ")
end
end
---Get selected text
---@return string
function M.get_block()
local s_row, e_row, s_col, e_col = unpack(M.get_range())
---@type string
---@diagnostic disable-next-line: assign-type-mismatch
local line = fn.getline(e_row)
local uidx = vim.str_utfindex(line, math.min(#line, e_col))
---@diagnostic disable-next-line: param-type-mismatch
e_col = vim.str_byteindex(line, uidx)
if s_row == e_row then
return line:sub(s_col, e_col)
else
local lines = fn.getline(s_row, e_row)
for col, l in pairs(lines) do
lines[col] = l:sub(s_col,e_col)
end
return table.concat(lines, " ")
end end
end end
@ -52,7 +90,12 @@ function M.get_str(mode)
return fn.input '需要翻译的字符串: ' return fn.input '需要翻译的字符串: '
end, end,
V = function() V = function()
print 'TODO' api.nvim_input '<Esc>'
return M.get_lines()
end,
[''] = function()
api.nvim_input '<Esc>'
return M.get_block()
end, end,
})[mode]():match '^%s*(.-)%s*$' })[mode]():match '^%s*(.-)%s*$'
end end
@ -60,6 +103,7 @@ end
---Puase coroutine for {ms} milliseconds ---Puase coroutine for {ms} milliseconds
---@param ms integer ---@param ms integer
function M.pause(ms) function M.pause(ms)
assert(ms)
local co = coroutine.running() local co = coroutine.running()
vim.defer_fn(function() vim.defer_fn(function()
coroutine.resume(co) coroutine.resume(co)
@ -70,7 +114,7 @@ end
---Detect whether the string is English ---Detect whether the string is English
---@param str string ---@param str string
---@return boolean ---@return boolean
function M.is_English(str) function M.is_english(str)
local char = { str:byte(1, -1) } local char = { str:byte(1, -1) }
for i = 1, #str do for i = 1, #str do
if char[i] > 128 then if char[i] > 128 then
@ -114,7 +158,6 @@ function M.center(node, win_width)
end end
local str = node[1] local str = node[1]
win_width = str:width()
local space = math.max(0, math.floor((win_width - str:width()) / 2)) local space = math.max(0, math.floor((win_width - str:width()) / 2))
node[1] = string.rep(' ', space) .. str node[1] = string.rep(' ', space) .. str
return node return node
@ -174,7 +217,7 @@ end
---@param str string ---@param str string
---@return boolean ---@return boolean
function M.is_word(str) function M.is_word(str)
return str:match '%w+' == str return str:find '%W' == nil
end end
---@param list any[] ---@param list any[]

View File

@ -124,7 +124,8 @@ function window:try_close()
} }
end end
api.nvim_win_close(self.winid, true)
pcall(api.nvim_win_close, self.winid, true)
end end
---Set window local highlight group ---Set window local highlight group
@ -161,21 +162,7 @@ window.__index = window
---@alias WindowOpts ---@alias WindowOpts
---|{style: string, border: string, focusable: boolean, noautocmd?: boolean, relative: string, width: integer, height: integer, col: integer, row: integer, zindex?: integer, title?: table | string} ---|{style: string, border: string, focusable: boolean, noautocmd?: boolean, relative: 'mouse'|'cursor'|'editor'|'win', width: integer, height: integer, col: integer, row: integer, zindex?: integer, title?: table | string}
---@class TransWindowOpts
local default_opts = {
enter = false,
winid = -1,
---@type WindowOpts
win_opts = {
style = 'minimal',
border = 'rounded',
focusable = false,
noautocmd = true,
},
}
---@class TransWindow ---@class TransWindow
@ -197,11 +184,37 @@ local default_opts = {
-- relative = relative, -- relative = relative,
-- } -- }
local default_opts = {
enter = false,
winid = -1,
---@type WindowOpts
win_opts = {
-- INFO : ensured options
-- col
-- row
-- width
-- height
-- relative
-- zindex
style = 'minimal',
border = 'rounded',
focusable = true,
noautocmd = true,
},
}
---@class TransWindowOpts
---@field buffer TransBuffer attached buffer object
---@field enter? boolean cursor should [enter] window when open,default: false
---@field win_opts WindowOpts window config [**When open**]
---@field animation? table? Hover Window Animation
---Create new window ---Create new window
---@param opts TransWindowOpts window config ---@param opts TransWindowOpts window config
---@return TransWindow ---@return TransWindow
function window.new(opts) function window.new(opts)
opts = vim.tbl_deep_extend('keep', opts, default_opts) opts = vim.tbl_deep_extend('keep', opts, default_opts)
opts.animation = opts.animation or { interval = 12 }
local win = setmetatable(opts, window) local win = setmetatable(opts, window)
---@cast win TransWindow ---@cast win TransWindow

View File

@ -1,5 +1,6 @@
---@type Trans ---@type Trans
local Trans = require 'Trans' local Trans = require 'Trans'
local util = Trans.util
-- FIXME :Adjust Window Size -- FIXME :Adjust Window Size
@ -18,16 +19,16 @@ local M = Trans.metatable('frontend.hover', {
M.__index = M M.__index = M
---Set up function which will be invoked when this module is loaded
function M.setup() function M.setup()
local set = vim.keymap.set local set = vim.keymap.set
for action, key in pairs(M.opts.keymaps) do for action, key in pairs(M.opts.keymaps) do
set('n', key, function() set('n', key, function()
local instance = M.get_active_instance() local instance = M.get_active_instance()
if instance then if instance then
coroutine.wrap(instance.execute)(instance, action) coroutine.wrap(instance.execute)(instance, action)
else else
return key vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), 'n', false)
end end
end) end)
end end
@ -92,13 +93,13 @@ function M:init_window(opts)
buffer = self.buffer, buffer = self.buffer,
animation = m_opts.animation, animation = m_opts.animation,
win_opts = { win_opts = {
relative = opts.relative or 'cursor',
col = opts.col or 1, col = opts.col or 1,
row = opts.row or 1, row = opts.row or 1,
width = opts.width or m_opts.width, width = opts.width or m_opts.width,
height = opts.height or m_opts.height, height = opts.height or m_opts.height,
relative = opts.relative or 'cursor',
title = m_opts.title, title = m_opts.title,
title_pos = m_opts.title and 'center' or nil, title_pos = m_opts.title and 'center',
zindex = 100, zindex = 100,
}, },
} }
@ -118,7 +119,6 @@ end
---Get Check function for waiting ---Get Check function for waiting
---@return fun(backend: TransBackend): boolean ---@return fun(backend: TransBackend): boolean
function M:wait() function M:wait()
local util = Trans.util
local opts = self.opts local opts = self.opts
local buffer = self.buffer local buffer = self.buffer
local pause = util.pause local pause = util.pause
@ -126,7 +126,7 @@ function M:wait()
local spinner = Trans.style.spinner[opts.spinner] local spinner = Trans.style.spinner[opts.spinner]
local times = opts.width - spinner[1]:width() local times = opts.width - spinner[1]:width()
local size = #spinner local size = #spinner
local interval = math.floor(opts.timeout / opts.width) local interval = math.floor(opts.timeout / times)
self:init_window { self:init_window {
height = 2, height = 2,
@ -154,7 +154,7 @@ function M:fallback()
local buffer = self.buffer local buffer = self.buffer
buffer:wipe() buffer:wipe()
buffer[1] = Trans.util.center(fallback_msg, opts.width) buffer[1] = util.center(fallback_msg, opts.width)
buffer:add_highlight(1, 'TransFailed') buffer:add_highlight(1, 'TransFailed')
if not self.window then if not self.window then
self:init_window { self:init_window {
@ -168,7 +168,7 @@ end
---Defer function when process done ---Defer function when process done
function M:defer() function M:defer()
Trans.util.main_loop(function() util.main_loop(function()
self.window:set('wrap', true) self.window:set('wrap', true)
self.buffer:set('modifiable', false) self.buffer:set('modifiable', false)
@ -204,7 +204,6 @@ function M:process(data)
return return
end end
local util = Trans.util
local opts = self.opts local opts = self.opts
local buffer = self.buffer local buffer = self.buffer
@ -227,11 +226,11 @@ function M:process(data)
local width = local width =
valid and valid and
(opts.auto_resize and (opts.auto_resize and
math.max( math.max(
math.min(opts.width, util.display_width(lines) + opts.padding), math.min(opts.width, util.display_width(lines) + opts.padding),
math.min(data.str:width(), opts.split_width) math.min(data.str:width(), opts.split_width)
) )
or opts.width) or opts.width)
or math.min(opts.width, util.display_width(lines) + opts.padding) or math.min(opts.width, util.display_width(lines) + opts.padding)
local height = math.min(opts.height, util.display_height(lines, width)) local height = math.min(opts.height, util.display_height(lines, width))

View File

@ -36,15 +36,16 @@ local function check_binary_dependencies()
local binary_dependencies = { local binary_dependencies = {
'curl', 'curl',
'sqlite3', 'sqlite3',
'unzip',
} }
if has 'linux' == 1 then binary_dependencies[3] = ({
binary_dependencies[3] = 'festival' win = nil,
elseif has 'mac' == 1 then mac = 'say',
binary_dependencies[3] = 'say' linux = 'festival',
else termux = 'termux-tts-speak',
binary_dependencies[3] = 'node' })[Trans.system]
end
for _, dep in ipairs(binary_dependencies) do for _, dep in ipairs(binary_dependencies) do
if executable(dep) == 1 then if executable(dep) == 1 then
@ -56,7 +57,7 @@ local function check_binary_dependencies()
end end
local function check_database() local function check_database()
local db_path = Trans.conf.dir .. Trans.separator .. 'ultimate.db' local db_path = Trans.conf.dir .. '/ultimate.db'
if fn.filereadable(db_path) == 1 then if fn.filereadable(db_path) == 1 then
ok [[ultimate database found ]] ok [[ultimate database found ]]
else else
@ -68,7 +69,7 @@ local function check_database()
end end
local function check_configure_file() local function check_configure_file()
local path = fn.expand(Trans.conf.dir .. Trans.separator .. 'Trans.json') local path = fn.expand(Trans.conf.dir .. '/Trans.json')
if not fn.filereadable(path) then if not fn.filereadable(path) then
warn 'Backend configuration file[%s] not found' warn 'Backend configuration file[%s] not found'
end end

View File

@ -18,28 +18,25 @@ end
---@field width function @Get string display width ---@field width function @Get string display width
---@field play function @Use tts to play string ---@field play function @Use tts to play string
local uname = vim.loop.os_uname().sysname
local system =
uname == 'Darwin' and 'mac' or
uname == 'Windows_NT' and 'win' or
uname == 'Linux' and (vim.fn.executable 'termux-api-start' == 1 and 'termux' or 'linux') or
error 'Unknown System, Please Report Issue'
local sep = vim.loop.os_uname().sysname == 'Windows' and '\\' or '/'
---@class Trans ---@class Trans
---@field style table @Style module ---@field style table @Style module
---@field cache table<string, TransData> @Cache for translated data object ---@field cache table<string, TransData> @Cache for translated data object
---@field plugin_dir string @Plugin directory ---@field plugin_dir string @Plugin directory
---@field separator string @Path separator ---@field system 'mac'|'win'|'termux'|'linux' @Operating system
local M = metatable('core', { local M = metatable('core', {
cache = {}, cache = {},
style = metatable 'style', style = metatable 'style',
plugin_dir = debug.getinfo(1, 'S').source:sub(2):match('(.-)lua' .. sep .. 'Trans'), system = system,
separator = sep, plugin_dir = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':p:h:h:h'),
}) })
M.metatable = metatable M.metatable = metatable
---Get abs_path of file
---@param path string[]
---@param is_dir boolean?
---@return string
function M.relative_path(path, is_dir)
return M.plugin_dir .. table.concat(path, sep) .. (is_dir and sep or '')
end
return M return M

View File

@ -29,248 +29,469 @@
-- SOFTWARE. -- SOFTWARE.
return { return {
dots = { dots = {
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
dots_negative = { -- dots2 dots_negative = { -- dots2
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
dots_snake = { -- dots3 dots_snake = { -- dots3
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
dots_footsteps = { -- dots10 dots_footsteps = { -- dots10
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
dots_hop = { -- dots11 dots_hop = { -- dots11
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
line = { line = {
"-", '-',
"\\", '\\',
"|", '|',
"/", '/',
}, },
pipe = { pipe = {
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
dots_ellipsis = { -- simpleDots dots_ellipsis = { -- simpleDots
". ", '. ',
".. ", '.. ',
"...", '...',
" ", ' ',
}, },
dots_scrolling = { -- simpleDotsScrolling dots_scrolling = { -- simpleDotsScrolling
". ", '. ',
".. ", '.. ',
"...", '...',
" ..", ' ..',
" .", ' .',
" ", ' ',
}, },
star = { star = {
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
flip = { flip = {
"_", '_',
"_", '_',
"_", '_',
"-", '-',
"`", '`',
"`", '`',
"'", "'",
"´", '´',
"-", '-',
"_", '_',
"_", '_',
"_", '_',
}, },
hamburger = { hamburger = {
"", '',
"", '',
"", '',
}, },
grow_vertical = { -- growVertical grow_vertical = { -- growVertical
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
grow_horizontal = { -- growHorizontal grow_horizontal = { -- growHorizontal
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
noise = { noise = {
"", '',
"", '',
"", '',
}, },
dots_bounce = { -- bounce dots_bounce = { -- bounce
"", '',
"", '',
"", '',
"", '',
}, },
triangle = { triangle = {
"", '',
"", '',
"", '',
"", '',
}, },
arc = { arc = {
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
circle = { circle = {
"", '',
"", '',
"", '',
}, },
square_corners = { -- squareCorners square_corners = { -- squareCorners
"", '',
"", '',
"", '',
"", '',
}, },
circle_quarters = { -- circleQuarters circle_quarters = { -- circleQuarters
"", '',
"", '',
"", '',
"", '',
}, },
circle_halves = { -- circleHalves circle_halves = { -- circleHalves
"", '',
"", '',
"", '',
"", '',
}, },
dots_toggle = { -- toggle dots_toggle = { -- toggle
"", '',
"", '',
}, },
box_toggle = { -- toggle2 box_toggle = { -- toggle2
"", '',
"", '',
}, },
arrow = { arrow = {
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
"", '',
}, },
clock = { clock = {
"🕛 ", '🕛 ',
"🕐 ", '🕐 ',
"🕑 ", '🕑 ',
"🕒 ", '🕒 ',
"🕓 ", '🕓 ',
"🕔 ", '🕔 ',
"🕕 ", '🕕 ',
"🕖 ", '🕖 ',
"🕗 ", '🕗 ',
"🕘 ", '🕘 ',
"🕙 ", '🕙 ',
"🕚 ", '🕚 ',
}, },
earth = { earth = {
"🌍 ", '🌍 ',
"🌎 ", '🌎 ',
"🌏 ", '🌏 ',
}, },
moon = { moon = {
"🌑 ", '🌑 ',
"🌒 ", '🌒 ',
"🌓 ", '🌓 ',
"🌔 ", '🌔 ',
"🌕 ", '🌕 ',
"🌖 ", '🌖 ',
"🌗 ", '🌗 ',
"🌘 ", '🌘 ',
}, },
dots_pulse = { -- point dots_pulse = { -- point
"∙∙∙", '∙∙∙',
"●∙∙", '●∙∙',
"∙●∙", '∙●∙',
"∙∙●", '∙∙●',
"∙∙∙", '∙∙∙',
},
fistBump = {
'🤜    🤛 ',
'🤜    🤛 ',
'🤜    🤛 ',
' 🤜  🤛  ',
'  🤜🤛   ',
' 🤜✨🤛   ',
'🤜 ✨ 🤛  ',
},
monkey = {
'🙈 ',
'🙈 ',
'🙉 ',
'🙊 '
},
soccerHeader = {
' 🧑⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
'🧑 ⚽️ 🧑 ',
},
weather = {
'☀️ ',
'☀️ ',
'☀️ ',
'🌤 ',
'⛅️ ',
'🌥 ',
'☁️ ',
'🌧 ',
'🌨 ',
'🌧 ',
'🌨 ',
'🌧 ',
'🌨 ',
'',
'🌨 ',
'🌧 ',
'🌨 ',
'☁️ ',
'🌥 ',
'⛅️ ',
'🌤 ',
'☀️ ',
'☀️ ',
},
speaker = {
'🔈 ',
'🔉 ',
'🔊 ',
'🔉 ',
},
smiley = {
'😄 ',
'😝 ',
},
toggle = {
'',
''
},
toggle10 = {
'',
'',
''
},
toggle11 = {
'',
''
},
toggle12 = {
'',
''
},
toggle13 = {
'=',
'*',
'-'
},
toggle2 = {
'',
''
},
toggle3 = {
'',
''
},
toggle4 = {
'',
'',
'',
''
},
toggle5 = {
'',
''
},
toggle6 = {
'',
''
},
toggle7 = {
'',
'⦿'
},
toggle8 = {
'',
''
},
toggle9 = {
'',
''
},
star = {
'',
'',
'',
'',
'',
''
},
star2 = {
'+',
'x',
'*'
},
orangeBluePulse = {
'🔸 ',
'🔶 ',
'🟠 ',
'🟠 ',
'🔶 ',
'🔹 ',
'🔷 ',
'🔵 ',
'🔵 ',
'🔷 ',
},
orangePulse = {
'🔸 ',
'🔶 ',
'🟠 ',
'🟠 ',
'🔶 '
},
mindblown = {
'😐 ',
'😐 ',
'😮 ',
'😮 ',
'😦 ',
'😦 ',
'😧 ',
'😧 ',
'🤯 ',
'💥 ',
'',
'  ',
'  ',
'  ',
},
hearts = {
'💛 ',
'💙 ',
'💜 ',
'💚 ',
'❤️ '
},
fingerDance = {
'🤘 ',
'🤟 ',
'🖖 ',
'',
'🤚 ',
'👆 '
},
christmas = {
'🌲',
'🎄'
},
circleHalves = {
'',
'',
'',
''
},
bouncingBall = {
'( ● )',
'( ● )',
'( ● )',
'( ● )',
'( ●)',
'( ● )',
'( ● )',
'( ● )',
'( ● )',
'(● )',
},
bluePulse = {
'🔹 ',
'🔷 ',
'🔵 ',
'🔵 ',
'🔷 '
},
betaWave = {
'ρββββββ',
'βρβββββ',
'ββρββββ',
'βββρβββ',
'ββββρββ',
'βββββρβ',
'ββββββρ',
}, },
} }

View File

@ -4,62 +4,63 @@ local util = require 'Trans'.util
---@field [1] string text to be rendered ---@field [1] string text to be rendered
---@field render fun(self: TransNode, buffer: TransBuffer, line: number, col: number) render the node ---@field render fun(self: TransNode, buffer: TransBuffer, line: number, col: number) render the node
local item = (function()
---@class TransItem : TransNode
local mt = {
---@param self TransItem
---@param buffer TransBuffer
---@param line integer
---@param col integer
render = function(self, buffer, line, col)
if self[2] then
buffer:add_highlight(line, self[2], col, col + #self[1])
end
end,
}
mt.__index = mt
---@class TransItem : TransNode ---Basic item node
local item_meta = { ---@param tuple {[1]: string, [2]: string?}
---@param self TransItem ---@return TransItem
---@param buffer TransBuffer return function(tuple)
---@param line integer return setmetatable(tuple, mt)
---@param col integer end
render = function(self, buffer, line, col) end)()
if self[2] then
buffer:add_highlight(line, self[2], col, col + #self[1])
end
end,
}
---@class TransText : TransNode local text = (function()
---@field step string ---@class TransText : TransNode
---@field nodes TransNode[] ---@field step string
local text_meta = { ---@field nodes TransNode[]
---@param self TransText
---@param buffer TransBuffer
---@param line integer
---@param col integer
render = function(self, buffer, line, col)
local nodes = self.nodes
local step = self.step
local len = step and #step or 0
for _, node in ipairs(nodes) do local mt = {
node:render(buffer, line, col) ---@param self TransText
col = col + #node[1] + len ---@param buffer TransBuffer
end ---@param line integer
end, ---@param col integer
} render = function(self, buffer, line, col)
local nodes = self.nodes
local step = self.step
local len = step and #step or 0
item_meta.__index = item_meta for _, node in ipairs(nodes) do
text_meta.__index = text_meta node:render(buffer, line, col)
col = col + #node[1] + len
end
end,
}
mt.__index = mt
---Basic item node ---@param nodes {[number]: TransNode, step: string?}
---@param tuple {[1]: string, [2]: string?} ---@return TransText
---@return TransItem return function(nodes)
local function item(tuple) return setmetatable({
return setmetatable(tuple, item_meta) [1] = table.concat(util.list_fields(nodes, 1), nodes.step),
end step = nodes.step,
nodes = nodes,
}, mt)
end
---@param nodes {[number]: TransNode, step: string?} end)()
---@return TransText
local function text(nodes)
return setmetatable({
[1] = table.concat(util.list_fields(nodes, 1), nodes.step),
step = nodes.step,
nodes = nodes,
}, text_meta)
end
---@param args {[number]: TransNode, width: integer, spin: string?} ---@param args {[number]: TransNode, width: integer, spin: string?}
@ -85,7 +86,6 @@ end
---@class TransUtil ---@class TransUtil
---@field node TransNodes ---@field node TransNodes
---@class TransNodes ---@class TransNodes
return { return {
item = item, item = item,

View File

@ -6,6 +6,7 @@ _G.api = vim.api
_G.fn = vim.fn _G.fn = vim.fn
_G.mock = require 'luassert.mock' _G.mock = require 'luassert.mock'
_G.stub = require 'luassert.stub' _G.stub = require 'luassert.stub'
string.width = api.nvim_strwidth
---@param func fun(buffer: TransBuffer) ---@param func fun(buffer: TransBuffer)
---@return fun() ---@return fun()

View File

@ -1,9 +1,92 @@
---@diagnostic disable: param-type-mismatch
require 'test.setup' require 'test.setup'
local util = Trans.util local util = Trans.util
describe('util.display_height', with_buffer(function(buffer) describe('util.display_height', function()
--- TODO :
it('can calculate the height of lines when window with wrap option', function() it('can calculate the height of lines when window with wrap option', function()
local lines = {
'1234567890',
'1234567890',
'1234567890',
'1234567890',
'1234567890',
'1234567890',
'1234567890',
'1234567890',
'1234567890',
}
assert.are.equal(#lines, util.display_height(lines, 10))
assert.are.equal(#lines, util.display_height(lines, 11))
assert.are.equal(2 * #lines, util.display_height(lines, 9))
-- Unicode width test
local u_lines = {
'12345678👍', -- 10
'あうえお', -- 8
'𠮷野い𠮷家野家家', -- 16
'👍👍👍お家', -- 10
}
assert.are.equal(4, util.display_height(u_lines, 20))
assert.are.equal(4, util.display_height(u_lines, 16))
assert.are.equal(5, util.display_height(u_lines, 10))
assert.are.equal(7, util.display_height(u_lines, 8))
assert.are.equal(9, util.display_height(u_lines, 7))
end) end)
end)) end)
describe('util.display_width', function()
it('can calculate the max width of lines', function()
local lines = {
'1234567890',
'123456789',
'12345678',
'1234567',
'123456',
'12345',
'1234',
'123',
'12',
'1',
}
assert.are.equal(10, util.display_width(lines))
-- Unicode width test
local u_lines = {
'12345678👍', -- 10
'あうえお', -- 8
'𠮷野い𠮷家野家家', -- 16
'👍👍👍お家', -- 10
}
assert.are.equal(16, util.display_width(u_lines))
end)
end)
describe('util.center', function()
it('will return the node if its width more than width', function()
local node = i { '1234567890' }
assert.are.same(node, util.center(node, 9))
end)
it('will auto padding space', function()
local node = i { '1234567890' }
assert.are.same(i { (' '):rep(2) .. '1234567890' }, util.center(node, 15))
end)
end)
describe('util.is_word', function()
it('can detect word', function()
for test, value in pairs {
['あうえお'] = false,
['hello'] = true,
[' hello'] = false,
['hello world'] = false,
['test_cool'] = false,
} do
assert.are.same(util.is_word(test), value)
end
end)
end)

100
lua/test/window_spec.lua Normal file
View File

@ -0,0 +1,100 @@
require 'test.setup'
describe('window', with_buffer(function(buffer)
local window
before_each(function()
buffer:wipe()
window = Trans.window.new {
buffer = buffer,
win_opts = {
col = 1,
row = 1,
width = 1,
height = 1,
relative = 'editor',
},
}
window:set('wrap', false)
end)
after_each(function()
window:try_close()
end)
it('can work well when no pass animation table', function()
window:open()
assert.is_true(api.nvim_win_is_valid(window.winid))
end)
describe('smooth_expand', function()
it('can work well when no pass animation table', function()
for field, values in pairs {
width = {
10,
6,
8,
5,
},
height = {
10,
6,
3,
},
} do
for _, value in ipairs(values) do
window:smooth_expand { field = field, to = value }
assert.are.same(value, window[field](window))
end
end
end)
it("don't change window wrap option", function()
window:smooth_expand { field = 'width', to = 10 }
assert.is_false(window:option 'wrap')
window:set('wrap', true)
window:smooth_expand { field = 'width', to = 10 }
assert.is_true(window:option 'wrap')
window:smooth_expand { field = 'height', to = 10 }
assert.is_true(window:option 'wrap')
end)
end)
it("resize() don't change window wrap option", function()
window:resize { width = 10, height = 10 }
assert.is_false(window:option 'wrap')
window:set('wrap', true)
window:resize { width = 5, height = 5 }
assert.is_true(window:option 'wrap')
end)
it('adjust_height() can auto adjust window height to buffer display height', function()
for idx, content in ipairs {
'cool',
'co10',
'家👍',
'👍ol',
'cあl',
'家野',
} do
buffer[idx] = content
end
local max_height = vim.o.lines - 2
for width, expect in ipairs {
[2] = 12,
[3] = 12,
[4] = 6,
[5] = 6,
} do
window:smooth_expand { field = 'width', to = width }
window:adjust_height()
assert.are.same(math.min(expect, max_height), window:height())
end
end)
end))

View File

@ -1,4 +1,4 @@
.PHONE: test .PHONE: test
test: test:
nvim --headless --noplugin -u script/minimal_init.vim -c "PlenaryBustedDirectory lua/test/ { minimal_init = './scripts/minimal_init.vim' }" -c 'qa!' nvim --headless --noplugin -u scripts/minimal_init.vim -c "PlenaryBustedDirectory lua/test/ { minimal_init = './scripts/minimal_init.vim' }" -c 'qa!'

View File

@ -1,34 +1,39 @@
local api, fn = vim.api, vim.fn local api, fn = vim.api, vim.fn
--- INFO :Define plugin command --- INFO :Define plugin command
local Trans = require("Trans") local Trans = require 'Trans'
local command = api.nvim_create_user_command local command = api.nvim_create_user_command
command("Translate", function() command('Translate', function() Trans.translate() end,
Trans.translate() { desc = '󰊿 Translate cursor word' })
end, { desc = " Translate cursor word" })
command("TranslateInput", function() command('TranslateInput', function() Trans.translate { mode = 'i' } end,
Trans.translate({ mode = 'i' }) { desc = '󰊿 Translate input word' })
end, { desc = " Translate input word" })
command("TransPlay", function() command('TransPlay', function()
local util = Trans.util local util = Trans.util
local str = util.get_str(vim.fn.mode()) local str = util.get_str(vim.fn.mode())
if str and str ~= "" and util.is_English(str) then if str and str ~= '' and util.is_english(str) then
str:play() str:play()
end end
end, { desc = " Auto play" }) end, { desc = ' Auto play' })
string.width = api.nvim_strwidth string.width = api.nvim_strwidth
local system = Trans.system
local f = local f =
fn.has('linux') == 1 and ([[echo %q | festival --tts]]) (vim.fn.has 'wsl' == 1 or system == 'win') and
or fn.has('mac') == 1 and ([[say %q]]) 'powershell.exe -Command "Add-Type -AssemblyName System.speech;(New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak(\\\"%s\\\")"' or
or 'node' .. Trans.relative_path { 'tts', 'say.js' } .. ' %q' system == 'mac' and 'say %q' or
system == 'termux' and 'termux-tts-speak %q' or
system == 'linux' and 'echo %q | festival --tts' or
error 'Unsupported system'
string.play = function(self) string.play = function(self)
fn.jobstart(f:format(self)) ---@diagnostic disable-next-line: param-type-mismatch
local s = string.gsub(self, '\"', ' ')
fn.jobstart(f:format(s))
end end

View File

@ -1,5 +0,0 @@
{
"dependencies": {
"say": "^0.16.0"
}
}

View File

@ -1,4 +0,0 @@
const say = require('say')
// console.log(word)
say.speak(process.argv.slice(2))