Merge pull request #1 from JuanZoran/experimental

重构了项目结构和逻辑, 优化了部分代码(可能
This commit is contained in:
Zoran 2023-01-10 23:17:29 +08:00 committed by GitHub
commit 343ec54223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1826 additions and 442 deletions

View File

@ -30,7 +30,7 @@
- 支持 `normal``visual`模式
> 不支持 visual-block mode
- 词库单词量: `43w`
- 本地词库单词量: `430w`
## 屏幕截图
![ScreenShot](./screenshot.gif)

3
go/go.mod Normal file
View File

@ -0,0 +1,3 @@
module query_online
go 1.19

50
go/query_online.go Normal file
View File

@ -0,0 +1,50 @@
package query_youcao
import (
"net/url"
"time"
)
const (
youdao = "https://openapi.youdao.com/api"
appKey = "1858465a8708c121"
appPasswd = "fG0sitfk16nJOlIlycnLPYZn1optxUxL"
)
type data struct {
q string
from string
to string
// appKey string
salt string
sign string
signType string
curtime string
}
func input(word string) string {
var input string
len := len(word)
if len > 20 {
input = word[:10] + string(rune(len)) + word[len-10:]
} else {
input = word
}
return input
}
func salt(_ string) string {
// TODO : hash salt
var salt string
return salt
}
func to_value(d data) url.Values {
// return value
}
func Query(word string) {
}

13
lua/.luarc.json Normal file
View File

@ -0,0 +1,13 @@
{
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
"Lua.diagnostics.disable": [
"empty-block",
"trailing-space"
],
"Lua.diagnostics.globals": [
"vim",
"user_conf",
"default_conf"
],
"Lua.workspace.checkThirdParty": false
}

7
lua/Trans/api/README.md Normal file
View File

@ -0,0 +1,7 @@
# API说明
## 概述
- 翻译查询
- ``
- 字段处理
- 窗口显示

8
lua/Trans/api/init.lua Normal file
View File

@ -0,0 +1,8 @@
local M = {}
local query_warpper = require 'Trans.api.query'
M.query = query_warpper.query
return M

50
lua/Trans/api/query.lua Normal file
View File

@ -0,0 +1,50 @@
local M = {}
local _, db = pcall(require, 'sqlite.db')
if not _ then
error('Please check out sqlite.lua')
end
local type_check = vim.validate
-- INFO : init database
local path = require("Trans.conf.loader").loaded_conf.base.db_path
local dict = db:open(path)
-- INFO :Auto Close
vim.api.nvim_create_autocmd('VimLeavePre', {
group = require("Trans").augroup,
callback = function()
if db:isopen() then
db:close()
end
end
})
local query_field = {
'word',
'phonetic',
'definition',
'translation',
'pos',
'collins',
'oxford',
'tag',
'exchange',
}
-- NOTE : local query
M.query = function(arg)
-- TODO : more opts
type_check {
arg = { arg, 'string' },
}
local res = dict:select('stardict', {
where = {
word = arg,
},
keys = query_field,
})
return res[1]
end
return M

View File

@ -0,0 +1,143 @@
local M = {}
local type_check = vim.validate
M.__index = M
M.lines = {}
M.highlight = {}
M.height = 0
M.width = 0
M.interval = ' '
M.opts = {}
function M:new(opts)
if opts then
self.opts = opts
end
local content = {}
setmetatable(content, self)
return content
end
-- NOTE :
-- local items = {
-- -- style1: string 不需要单独设置高亮的情况
-- 'text',
-- -- style2: string[] 需要设置高亮,第二个名称为高亮组
-- {'text2', 'highlight name'},
-- }
-- local opts = {
-- -- 可选的参数
-- highlight = 'highlight name' -- string 该行的高亮
-- indent = 4 -- integer 该行的应该在开头的缩进
-- interval = 4 -- integer 该行组件的间隔
-- }
function M:insert(items)
type_check {
items = { items, 'table' },
}
self.height = self.height + 1 -- line数加一
local line = {
indent = items.indent,
highlight = items.highlight,
}
local highlight = {}
for i, item in ipairs(items) do
if type(item) == 'string' then
item = { item }
end
line[i] = item[1]
if item[2] then
highlight[i] = item[2]
end
end
self.highlight[self.height] = highlight
self.lines[self.height] = line
end
---Usage:
--- local buffer_id
--- local lines, highlights = M:lines()
--- vim.api.nvim_buf_set_lines(buffer_id, 0, -1, false,lines)
--- for i, hl in ipairs(highlights) do
--- vim.api.nvim_buf_add_highlight(buffer_id, 0, hl.name, i, hl._start, hl._end)
--- end
---@return table line
---@return table highlight
function M:data()
-- NOTE 返回格式化的行,如果需要高亮,则第二个参数返回高亮
local lines = {}
local highlights = {}
for index = 1, #self.lines do
local line = ''
local highlight = {}
local l = self.lines[index]
local hl = self.highlight[index]
if l.indent then
line = (' '):rep(l.indent)
end
if l.highlight then
line = line .. table.concat(l, self.interval)
highlight[1] = { name = l.highlight, _start = 1, _end = -1 }
else
line = line .. l[1]
if hl[1] then
-- WARN :可能需要设置成字符串宽度!!!
table.insert(highlight, { name = hl[1], _start = #line - #l[1], _end = #line })
end
for i = 2, #l do
line = line .. self.interval .. l[i]
if hl[i] then
local _end = #line
table.insert(highlight, { name = hl[i], _start = _end - #l[i], _end = _end })
end
end
end
lines[index] = line
local len = #line
if self.width < len then
self.width = len
end
highlights[index] = highlight
end
return lines, highlights
end
function M:attach()
local height = self.opts.win.height
local width = self.opts.win.width
local lines, hls = self:data()
vim.api.nvim_buf_set_lines(self.opts.bufnr, 0, -1, false, lines)
for line, l_hl in ipairs(hls) do
for _, hl in ipairs(l_hl) do
vim.api.nvim_buf_add_highlight(self.opts.bufnr, -1, hl.name, line - 1, hl._start, hl._end)
end
end
if self.height < height then
vim.api.nvim_win_set_height(self.opts.winid, self.height)
end
if self.width < width then
vim.api.nvim_win_set_width(self.opts.winid, self.width)
end
end
return M

View File

@ -0,0 +1,20 @@
local M = {}
M.__index = M
M.len = 0
function M:new()
local items = {}
setmetatable(items, self)
return items
end
function M:insert(item, highlight)
table.insert(self, item)
self.len = self.len + #item
end
function M:format(win_width)
end
return M

View File

@ -0,0 +1,68 @@
local M = {}
M.component = function (field, max_size)
if field.definition and field.definition ~= '' then
local ref = {
{ '英文注释', 'TransRef' }
}
local definitions = {
highlight = 'TransDefinition',
needformat = true,
indent = 4,
}
local size = 0
for defin in vim.gsplit(field.definition, '\n', true) do
if defin ~= '' then
table.insert(definitions, defin)
size = size + 1
if size == max_size then
break
end
end
end
return { ref, definitions }
end
end
return M
--[[n a formation of people or things one beside another
n a mark that is long relative to its width
n a formation of people or things one behind another
n a length (straight or curved) without breadth or thickness; the trace of a moving point
n text consisting of a row of words written across a page or computer screen
n a single frequency (or very narrow band) of radiation in a spectrum
n a fortified position (especially one marking the most forward position of troops)
n a course of reasoning aimed at demonstrating a truth or falsehood; the methodical process of logical reasoning
n a conductor for transmitting electrical or optical signals or electric power
n a connected series of events or actions or developments
n a spatial location defined by a real or imaginary unidimensional extent
n a slight depression in the smoothness of a surface
n a pipe used to transport liquids or gases
n the road consisting of railroad track and roadbed
n a telephone connection
n acting in conformity
n the descendants of one individual
n something (as a cord or rope) that is long and thin and flexible
n the principal activity in your life that you do to earn money
n in games or sports; a mark indicating positions or bounds of the playing area
n (often plural) a means of communication or access
n a particular kind of product or merchandise
n a commercial organization serving as a common carrier
n space for one line of print (one column wide and 1/14 inch deep) used to measure advertising
n the maximum credit that a customer is allowed
n a succession of notes forming a distinctive sequence
n persuasive but insincere talk that is usually intended to deceive or impress
n a short personal letter
n a conceptual separation or distinction
n mechanical system in a factory whereby an article is conveyed through sites at which successive operations are performed on it
v be in line with; form a line along
v cover the interior of
v make a mark or lines on a surface
v mark with lines
v fill plentifully
v reinforce with fabric
--]]

View File

@ -0,0 +1,45 @@
local M = {}
local exchange_map = {
p = '过去式',
d = '过去分词',
i = '现在分词',
r = '形容词比较级',
t = '形容词最高级',
s = '名词复数形式',
f = '第三人称单数',
['0'] = '词根',
['1'] = '词根的变化形式',
['3'] = '第三人称单数',
}
M.component = function(field)
-- TODO
if field.exchange and field.exchange ~= '' then
local ref = {
{ '词型变化', 'TransRef' },
}
local exchanges = {
needformat = true,
highlight = 'TransExchange',
indent = 4,
emptyline = true,
}
for _exchange in vim.gsplit(field.exchange, '/', true) do
local prefix = exchange_map[_exchange:sub(1, 1)]
if prefix then
local exchange = prefix .. _exchange:sub(2)
-- local exchange = exchange_map[_exchange:sub(1, 1)] .. _exchange:sub(2)
table.insert(exchanges, exchange)
else
error('add exchange_map for [' .. _exchange .. ']')
end
end
return { ref, exchanges }
end
end
return M

View File

@ -0,0 +1,20 @@
local M = {}
M.component = function(field)
-- TODO
if field.pos and field.pos ~= '' then
local ref = {
{ '词性:', 'TransRef' },
}
local pos = {
{ field.pos },
highlight = 'TransPos',
indent = 4,
emptyline = true,
}
return { ref, pos }
end
end
return M

View File

@ -0,0 +1,46 @@
local M = {}
local tag_map = {
zk = '中考',
gk = '高考',
ky = '考研',
cet4 = '四级',
cet6 = '六级',
ielts = '雅思',
toefl = '托福',
gre = 'GRE',
}
---从查询结果中获取字符串
---@param field table 查询的结果
---@return component? component 提取的组件信息[包含多个组件]
M.component = function(field)
-- TODO
if field.tag and field.tag ~= '' then
local ref = {
{ '标签:', 'TransRef' },
}
local tags = {
needformat = true,
highlight = 'TransTag',
indent = 4,
emptyline = true,
}
for _tag in vim.gsplit(field.tag, ' ', true) do
local tag = tag_map[_tag]
if tag then
table.insert(tags, tag)
else
error('add tag_map for [' .. _tag .. ']')
end
end
return { ref, tags }
end
end
return M

View File

@ -0,0 +1,67 @@
local M = {}
local display = require("Trans.conf.loader").loaded_conf.ui.display
local icon = require("Trans.conf.loader").loaded_conf.ui.icon
-- {
-- collins = 3,
-- definition = "n. an expression of greeting",
-- exchange = "s:hellos",
-- oxford = 1,
-- phonetic = "hə'ləʊ",
-- pos = "u:97/n:3",
-- tag = "zk gk",
-- translation = "n. 表示问候, 惊奇或唤起注意时的用语\nint. 喂;哈罗\nn. (Hello)人名;(法)埃洛",
-- word = "hello"
-- }
-- local data = {
-- { word, 'TransWord' },
-- -- NOTE :如果平配置设置显示,并且数据库中存在则有以下字段
-- { phonetic, 'TransPhonetic' },
-- collins,
-- oxford
-- -- { phonetic, 'TransPhonetic' },
-- }
---@alias items
---| 'string[]' # 所有组件的信息
---| 'needformat?'# 是否需要格式化
---| 'highlight?' # 整个组件的高亮
---| 'indent?' # 每行整体的缩进
---@alias component items[]
---从查询结果中获取字符串
---@param field table 查询的结果
---@return component component 提取的组件信息[包含多个组件]
M.component = function(field)
local component = {}
local data = {
{ field.word, 'TransWord' },
}
if display.phnoetic and field.phonetic and field.phonetic ~= '' then
table.insert(
data,
{ '[' .. field.phonetic .. ']', 'TransPhonetic' }
)
end
if display.collins and field.collins then
table.insert(data, {
icon.star:rep(field.collins)
})
end
if display.oxford and field.oxford then
table.insert(data,
{ field.oxford == 1 and icon.isOxford or icon.notOxford, }
)
end
component[1] = data
return component
end
return M

View File

@ -0,0 +1,23 @@
local M = {}
M.component = function(field)
if field.translation then
local ref = {
{ '中文翻译', 'TransRef' }
}
local translations = {
highlight = 'TransTranslation',
indent = 4,
emptyline = true,
needformat = true,
}
for trans in vim.gsplit(field.translation, '\n', true) do
table.insert(translations, trans)
end
return { ref, translations }
end
end
return M

View File

@ -1,42 +0,0 @@
return {
display = {
style = 'minimal',
max_height = 50, -- 小于0代表无限制
max_width = 50,
-- phnoetic = true,
collins_star = true,
oxford = true,
-- history = false,
wrap = true,
border_style = 'rounded',
view = 'cursor',
offset_x = 2,
offset_y = 2,
},
order = {
'title',
'tag',
'pos',
'exchange',
'zh',
'en',
},
db_path = '$HOME/.vim/dict/ultimate.db', -- FIXME: change the path
icon = {
star = '',
isOxford = '',
notOxford = ''
},
auto_close = true,
buf = vim.api.nvim_create_buf(false, true)
-- TODO: add online translate engine
-- online_search = {
-- enable = false,
-- engine = {},
-- }
-- TODO: register word
}

123
lua/Trans/conf/default.lua Normal file
View File

@ -0,0 +1,123 @@
local M = {}
M.conf = {
style = {
ui = {
input = 'float',
normal = 'cursor',
select = 'cursor'
},
window = {
cursor = {
border = 'rounded',
width = 50,
height = 50,
},
float = {
border = 'rounded',
width = 0.9,
height = 0.8,
},
-- NOTE :如果你想限制某个组件的行数,可以设置 (名称与order相同)
-- Example:
-- limit = {
-- En = 1, -- 只显示第一行,(一般为最广泛的释义)
-- },
limit = nil,
},
},
order = {
offline = {
'Title',
'Tag',
'Pos',
'Exchange',
'Translation',
-- { 'Definition', max_size = 4 },
},
-- online = {
-- -- TODO
-- },
},
ui = {
highlight = {
TransWord = {
fg = '#7ee787',
bold = true,
},
TransPhonetic = {
link = 'Linenr'
},
TransRef = {
fg = '#75beff',
bold = true,
},
TransTag = {
fg = '#e5c07b',
},
TransExchange = {
link = 'TransTag',
},
TransPos = {
link = 'TransTag',
},
TransTranslation = {
link = 'TransWord',
},
TransDefinition = {
-- fg = '#bc8cff',
link = 'Moremsg',
},
TransCursorWin = {
link = 'Normal',
},
TransCursorBorder = {
link = 'FloatBorder',
}
},
icon = {
star = '',
isOxford = '',
notOxford = ''
},
display = {
phnoetic = true,
collins = true,
oxford = true,
-- TODO
-- history = false,
},
},
base = {
db_path = '$HOME/.vim/dict/ultimate.db',
auto_close = true,
engine = {
-- TODO
'offline',
}
},
-- map = {
-- -- TODO
-- },
-- history = {
-- -- TOOD
-- }
-- TODO add online translate engine
-- online_search = {
-- enable = false,
-- engine = {},
-- }
-- TODO register word
}
-- INFO :加载的规则 [LuaRule]
M.replace_rules = {
'order.*',
'ui.highlight.*',
}
return M

60
lua/Trans/conf/loader.lua Normal file
View File

@ -0,0 +1,60 @@
-- -@diagnostic disable: unused-local, unused-function, lowercase-global
local M = {}
local replace_rules = require("Trans.conf.default").replace_rules
local star_format = [[
local def, usr = default_conf.%s, user_conf.%s
if def and usr then
for k, v in pairs(usr) do
def[k] = v
usr[k] = nil
end
end
]]
local plain_format = [[
default_conf.%s = user_conf.%s or default_conf.%s
]]
local function pre_process()
if replace_rules then
for _, v in ipairs(replace_rules) do
local start = v:find('.*', 1, true)
local operation
if start then
-- 替换表内所有键
v = v:sub(1, start - 1)
-- print('v is :', v)
operation = string.format(star_format, v, v)
else
operation = plain_format:format(v, v, v)
end
-- print(operation)
pcall(loadstring(operation))
end
end
end
M.load_conf = function(conf)
user_conf = conf or {}
default_conf = require("Trans.conf.default").conf
if user_conf.style and user_conf.window then
end
pre_process()
M.loaded_conf = vim.tbl_deep_extend('force', default_conf, user_conf)
local win = M.loaded_conf.style.window
assert(win.float.height <= 1 and win.float.height > 0 and win.cursor.height > 1, win.cursor.width > 1)
win.float.width = math.floor(win.float.width * vim.o.columns)
win.float.height = math.floor(win.float.height * (vim.o.lines - vim.o.cmdheight))
user_conf = nil
default_conf = nil
end
M.loaded_conf = nil
return M

96
lua/Trans/core/README.md Normal file
View File

@ -0,0 +1,96 @@
# 命令说明
<!--toc:start-->
- [命令说明](#命令说明)
- [Translate](#translate)
- [TranslateInput](#translateinput)
- [TranslateHistory](#translatehistory)
- [自定义](#自定义)
- [可选项说明](#可选项说明)
- [示例](#示例)
<!--toc:end-->
## Translate
**窗口风格默认为:** `cursor`
- 动作(action):
- `vsplit` 水平分屏
- `split` 垂直分屏
- `float` 窗口样式又`cursor` 变为`float`
- `online_query` 使用在线引擎重新进行查询
- `history_insert` 将此次查询的单词记录到历史记录
- `next` 展示下一个引擎的查询结果(如果默认设置了多个引擎)
- `prev` 展示上一个查询结果
> 如果没有设置自动保存历史的话
- `history` 查看历史查询的记录
- `online_query`:
- `local_add` 将此次查询的结果添加到本地数据库
> **如果本地已经存在该单词,会询问是否需要覆盖掉相同的字段**
- `local_update` 和*local_add* 类似, 但是不会询问是否覆盖
- `diff` 对比本地查询结果和此次在线查询的区别
> **注意**: 动作是任何窗口通用的
## TranslateInput
**窗口风格默认为:** `float`
- 自行得到要查询的单词
- TODO:
- fuzzy match
## TranslateHistory
**窗口风格默认为:** `float`
- 查看历史查询
---
## 自定义
### 可选项说明
- 查询方式(method): `string`
- `input` 自行输入需要查询的单词
- `last` 显示上一次查询的结果
- `history`
- 查询引擎(engine): `string | table`
- `offline` 离线的数据库
- `youcao` 有道api
- `baidu` 百度api
- `google` 谷歌api
- `bing` 必应api
- `iciba` 金山词霸api
- `xunfei` 讯飞api
- 窗口风格(win): `string | table`
- 样式(style):
- `cursor` 在光标附近弹出
- `float` 悬浮窗口
- `split` 在上方或者下方分屏
- `vsplit` 在左边或者右边分屏
- 高度(height):
- `value > 1` 最大高度
- `0 <= value <= 1` 相对高度
- `0 < value` 无限制
- 宽度(width):
> 和`高度(height)`相同
### 示例
```lua
vim.keymap.set('n', 'mi', function ()
require('Trans').translate({
method = 'input', -- 不填则自动判断mode获取查询的单词
engine = { -- 异步查询所有的引擎, 按照列表
'offline',
'youdao',
'baidu'
},
-- win = 'cursor'
win = {
style = 'cursor',
height = 50,
width = 30,
}
})
end, { desc = '在光标旁弹出输入的单词释义'})
```

294
lua/Trans/core/backup.lua Normal file
View File

@ -0,0 +1,294 @@
local M = {}
local opt = {
method = 'select',
view = 'cursor',
}
M.Translate = function(opts)
local res = get_query_res(opts.method)
-- TODO <++>
end
-- M.Translate_online = function ()
-- -- TOOD
-- end
-- local win = 0
-- local line = 0
-- local pos_info = {}
--
-- local handler = {}
-- api.nvim_buf_set_option(buf, 'filetype', 'Trans')
--
-- local function show_win(width, height)
-- end
--
-- -- NOTE title
-- handler.title = function(text, query_res)
-- local title = ('%s [%s] %s %s'):format(
-- query_res.word,
-- query_res.phonetic,
-- (display.oxford and (query_res.oxford == 1 and icon.isOxford or icon.notOxford) or ''),
-- ((display.collins_star and query_res.collins) and string.rep(icon.star, query_res.collins) or '')
-- )
-- table.insert(text, title)
--
-- pos_info.title = {}
-- pos_info.title.word = #query_res.word
-- pos_info.title.phonetic = query_res.phonetic and #query_res.phonetic or 3
-- pos_info.title.line = line
-- line = line + 1
-- end
--
-- -- NOTE tag
-- handler.tag = function(text, query_res)
-- if query_res.tag and #query_res.tag > 0 then
-- local tag = query_res.tag:gsub('zk', '中考'):gsub('gk', '高考'):gsub('ky', '考研'):gsub('cet4', '四级'):
-- gsub('cet6', '六级'):gsub('ielts', '雅思'):gsub('toefl', '托福'):gsub('gre', 'GRE')
--
-- table.insert(text, '标签:')
-- table.insert(text, ' ' .. tag)
-- table.insert(text, '')
--
-- pos_info.tag = line
-- line = line + 3
-- end
-- end
--
-- -- NOTE pos 词性
-- handler.pos = function(text, query_res)
-- if query_res.pos and #query_res.pos > 0 then
-- table.insert(text, '词性:')
--
-- local content = 0
-- for v in vim.gsplit(query_res.pos, [[/]]) do
-- table.insert(text, string.format(' %s', v .. '%'))
-- content = content + 1
-- end
--
-- table.insert(text, '')
--
-- pos_info.pos = {}
-- pos_info.pos.line = line
-- pos_info.pos.content = content
-- line = line + content + 2
-- end
-- end
--
-- -- NOTE exchange
-- handler.exchange = function(text, query_res)
-- if query_res.exchange and #query_res.exchange > 0 then
-- table.insert(text, '词形变化:')
--
-- local exchange_map = {
-- p = '过去式',
-- d = '过去分词',
-- i = '现在分词',
-- r = '形容词比较级',
-- t = '形容词最高级',
-- s = '名词复数形式',
-- O = '词干',
-- ['3'] = '第三人称单数',
-- }
--
-- local content = 0
-- for v in vim.gsplit(query_res.exchange, [[/]]) do
-- table.insert(text, string.format(' %s: %s', exchange_map[v:sub(1, 1)], v:sub(3)))
-- content = content + 1
-- -- FIXME: 中文字符与字母位宽不一致, 暂时无法对齐
-- end
-- table.insert(text, '')
--
-- pos_info.exchange = {}
-- pos_info.exchange.line = line
-- pos_info.exchange.content = content
-- line = line + content + 2
-- end
-- end
--
-- -- NOTE 中文翻译
-- handler.zh = function(text, query_res)
-- if query_res.translation then
-- table.insert(text, '中文翻译:')
--
-- local content = 0
-- for v in vim.gsplit(query_res.translation, '\n') do
-- table.insert(text, ' ' .. v)
-- content = content + 1
-- end
-- table.insert(text, '')
--
-- pos_info.zh = {}
-- pos_info.zh.line = line
-- pos_info.zh.content = content
-- line = content + line + 2
-- end
-- end
--
-- -- NOTE 英文翻译
-- handler.en = function(text, query_res)
-- if query_res.definition and #query_res.definition > 0 then
-- table.insert(text, '英文翻译:')
--
-- local content = 0
-- for v in vim.gsplit(query_res.definition, '\n') do
-- table.insert(text, ' ' .. v)
-- content = content + 1
-- end
-- table.insert(text, '')
--
-- pos_info.en = {}
-- pos_info.en.line = line
-- pos_info.en.content = content
-- line = line + content + 2
-- end
-- end
--
-- -- @return string array
-- local function get_text(query_res)
-- local text = {}
-- for _, v in ipairs(order) do
-- handler[v](text, query_res)
-- end
-- return text
-- end
--
-- local function set_text(query_res)
-- local text = query_res and get_text(query_res) or { '没有找到相关定义' }
--
-- api.nvim_buf_set_lines(buf, 0, -1, false, text)
-- local width = 0
-- for _, v in ipairs(text) do
-- if #v > width then
-- width = v:len()
-- end
-- end
-- return width, #text
-- end
--
-- local hl_handler = {}
--
-- hl_handler.title = function()
-- api.nvim_buf_add_highlight(buf, -1, hl.word, pos_info.title.line, 0, pos_info.title.word)
-- api.nvim_buf_add_highlight(buf, -1, hl.phonetic, pos_info.title.line, pos_info.title.word + 5,
-- pos_info.title.word + 5 + pos_info.title.phonetic)
-- end
--
-- hl_handler.tag = function()
-- if pos_info.tag then
-- api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.tag, 0, -1)
-- api.nvim_buf_add_highlight(buf, -1, hl.tag, pos_info.tag + 1, 0, -1)
-- end
-- end
--
-- hl_handler.pos = function()
-- if pos_info.pos then
-- api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.pos.line, 0, -1)
-- for i = 1, pos_info.pos.content, 1 do
-- api.nvim_buf_add_highlight(buf, -1, hl.pos, pos_info.pos.line + i, 0, -1)
-- end
-- end
-- end
--
-- hl_handler.exchange = function()
-- if pos_info.exchange then
-- api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.exchange.line, 0, -1)
-- for i = 1, pos_info.exchange.content, 1 do
-- api.nvim_buf_add_highlight(buf, -1, hl.exchange, pos_info.exchange.line + i, 0, -1)
-- end
-- end
-- end
--
-- hl_handler.zh = function()
-- api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.zh.line, 0, -1)
-- for i = 1, pos_info.zh.content, 1 do
-- api.nvim_buf_add_highlight(buf, -1, hl.zh, pos_info.zh.line + i, 0, -1)
-- end
-- end
--
-- hl_handler.en = function()
-- if pos_info.en then
-- api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.en.line, 0, -1)
-- for i = 1, pos_info.en.content, 1 do
-- api.nvim_buf_add_highlight(buf, -1, hl.en, pos_info.en.line + i, 0, -1)
-- end
-- end
-- end
--
--
-- local function set_hl()
-- for _, v in ipairs(order) do
-- hl_handler[v]()
-- end
-- end
--
-- local function clear_tmp_info()
-- pos_info = {}
-- line = 0
-- end
--
--
-- function M.query(mode)
-- assert(buf > 0)
-- local word = ''
-- if mode == 'n' then
-- word = vim.fn.expand('<cword>')
-- elseif mode == 'v' then
-- word = get_visual_selection()
-- elseif mode == 'I' then
-- word = vim.fn.input('请输入您要查询的单词: ')
-- -- vim.ui.input({prompt = '请输入您要查询的单词: '}, function (input)
-- -- word = input
-- -- end)
-- else
-- error('mode argument is invalid')
-- end
--
-- local res = require("Trans.database").query(word)
-- local width, height = set_text(res)
-- show_win(width, height)
-- if res then
-- set_hl()
-- clear_tmp_info()
-- end
--
-- if auto_close then
-- api.nvim_create_autocmd(
-- { 'InsertEnter', 'CursorMoved', 'BufLeave', }, {
-- buffer = 0,
-- once = true,
-- callback = M.close_win,
-- })
-- end
-- end
--
-- function M.query_cursor()
-- M.query('n')
-- end
--
-- function M.query_select()
-- M.query('v')
-- end
--
-- function M.query_input()
-- M.query('I')
-- end
--
-- function M.close_win()
-- if win > 0 then
-- api.nvim_win_close(win, true)
-- win = 0
-- end
-- end
--
-- -- function M.enter_win()
-- -- if api.nvim_win_is_valid(win) then
-- -- else
-- -- error('current win is not valid')
-- -- end
-- -- end
--
-- return M

7
lua/Trans/core/init.lua Normal file
View File

@ -0,0 +1,7 @@
local M = {}
M.process = require('Trans.core.process')
M.query = require('Trans.core.query')
-- M.show_win = require('Trans.core.show_win')
return M

170
lua/Trans/core/process.lua Normal file
View File

@ -0,0 +1,170 @@
local type_check = vim.validate
-- NOTE :中文字符及占两个字节宽但是在lua里是3个字节长度
-- 为了解决中文字符在lua的长度和neovim显示不一致的问题
local get_width = vim.fn.strdisplaywidth
local function format(win_width, items)
local size = #items
local tot_width = 0
if items.indent then
win_width = win_width - items.indent
end
for i = 1, size do
if type(items[i]) == 'string' then
items[i] = { items[i] }
end
tot_width = tot_width + #items[i][1] + 4
end
-- 判断宽度是否超过最大宽度
if tot_width > win_width + 4 then
-- 放不下则需要分成多行
local lines = {}
-- 行内字符串按照宽度排序
table.sort(items, function(a, b)
return #a[1] > #b[1]
end)
local cols = 1
win_width = win_width - #items[1][1]
while win_width > 0 and cols < size do
cols = cols + 1
win_width = win_width - #items[cols][1] + 4
end
if cols > 1 then
cols = cols - 1
end
if cols == 1 then -- 只能放在一行时就对齐了
for i = size, 1, -1 do
lines[i] = {
items[i][1],
highlight = items.highlight,
indent = items.indent,
}
end
return lines, true
end
local rows = math.ceil(size / cols)
local rest = size % cols
if rest == 0 then
rest = cols
end
local max_width = get_width(items[1][1])
local index = 1 -- 当前操作的字符串下标
for i = rows, 1, -1 do -- 当前操作的行号
lines[i] = {
highlight = items.highlight,
indent = items.indent,
}
local item = items[index]
-- if not item then
-- error('item nil ' .. tostring(index) .. ' rows:' .. tostring(rows) .. vim.inspect(items) )
-- end
item[1] = item[1] .. (' '):rep(max_width - get_width(item[1]))
lines[i][1] = items[index]
index = index + 1
end
for col = 2, cols do
max_width = get_width(items[index][1])
local _end = col > rest and rows - 1 or rows
for i = _end, 1, -1 do
local item = items[index]
item[1] = item[1] .. (' '):rep(max_width - get_width(item[1]))
lines[i][col] = item
index = index + 1
end
end
return lines, true
end
return items
end
local function process(opts)
type_check {
opts = { opts, 'table' },
['opts.field'] = { opts.field, 'table', true },
['opts.order'] = { opts.order, 'table' },
['opts.win'] = { opts.win, 'table' },
['opts.engine'] = { opts.engine, 'table' },
}
if opts.field == nil then
local lines = { '⚠️ 本地没有找到相关释义' }
vim.api.nvim_buf_set_lines(opts.bufnr, 0, -1, false, lines)
vim.api.nvim_win_set_height(opts.winid, 1)
vim.api.nvim_win_set_width(opts.winid, get_width(lines[1]))
else
local content = require('Trans.component.content'):new(opts)
for _, v in ipairs(opts.order) do
local component
if type(v) == 'table' then
component = require("Trans.component." .. 'offline' --[[ opts.engine ]] .. '.' .. v[1]).component(opts.field
, v.max_size)
else
component = require("Trans.component." .. 'offline' --[[ opts.engine ]] .. '.' .. v).component(opts.field)
end
if component then
for _, items in ipairs(component) do
if items.needformat then
local formatted_items, split = format(opts.win.width, items)
if split then
for _, itms in ipairs(formatted_items) do
content:insert(itms)
end
else
content:insert(formatted_items)
end
else
content:insert(items)
end
if items.emptyline then
content:insert({ '' })
end
end
end
end
content:attach()
end
vim.api.nvim_buf_set_option(opts.bufnr, 'modifiable', false)
vim.api.nvim_buf_set_option(opts.bufnr, 'filetype', 'Trans')
vim.api.nvim_win_set_option(opts.winid, 'wrap', true)
vim.api.nvim_win_set_option(opts.winid, 'winhl', 'Normal:TransCursorWin,FloatBorder:TransCursorBorder')
if opts.win.style == 'cursor' then
vim.api.nvim_create_autocmd(
{ 'InsertEnter', 'CursorMoved', 'BufLeave', }, {
buffer = 0,
once = true,
callback = function()
if vim.api.nvim_win_is_valid(opts.winid) then
vim.api.nvim_win_close(opts.winid, true)
end
end,
})
end
end
return process

43
lua/Trans/core/query.lua Normal file
View File

@ -0,0 +1,43 @@
local type_check = vim.validate
local query = require("Trans.api").query
local function get_select()
local s_start = vim.fn.getpos("v")
local s_end = vim.fn.getpos(".")
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, '\n')
end
local query_wrapper = function(opts)
type_check {
opts = { opts, 'table' },
['opts.method'] = { opts.method, 'string' },
}
local word = ''
if opts.method == 'input' then
---@diagnostic disable-next-line: param-type-mismatch
word = vim.fn.input('请输入您要查询的单词:') -- TODO Use Telescope with fuzzy finder
elseif opts.method == 'n' then
word = vim.fn.expand('<cword>')
elseif opts.method == 'v' then
word = get_select()
-- TODO : other method
else
error('invalid method' .. opts.method)
end
return query(word)
end
return query_wrapper

View File

@ -0,0 +1,113 @@
-- Default conf
local conf = require("Trans.conf.loader").loaded_conf
local core = require("Trans.core")
local function get_opts(opts)
local default_conf = {
method = vim.api.nvim_get_mode().mode,
engine = {
'offline',
-- TODO : other engine
},
win = {
style = 'cursor',
width = conf.style.window.cursor.width,
height = conf.style.window.cursor.height
},
}
if type(opts.engine) == 'string' then
opts.engine = { opts.engine }
end
if opts.win then
local width, height = opts.win.width, opts.win.height
if width and width > 0 and width <= 1 then
opts.win.width = math.floor(vim.o.columns * width)
end
if height and height > 0 and height <= 1 then
opts.win.height = math.floor(vim.o.lines * opts.win.height)
end
end
return vim.tbl_extend('force', default_conf, opts)
end
-- EXAMPLE :
-- require('Trans').translate({
-- method = 'input', -- 不填则自动判断mode获取查询的单词
-- engine = { -- 异步查询所有的引擎, 按照列表
-- 'offline',
-- 'youdao',
-- 'baidu'
-- },
-- -- win = 'cursor'
-- win = {
-- style = 'cursor',
-- height = 50,
-- width = 30,
-- }
-- })
local function create_win(win)
local bufnr = vim.api.nvim_create_buf(false, true)
local is_float = win.style == 'float'
local win_opts = {
relative = is_float and 'editor' or 'cursor',
width = win.width,
height = win.height,
style = 'minimal',
border = conf.style.window[win.style].border,
title = 'Trans',
title_pos = 'center',
focusable = true,
zindex = 100,
}
if is_float then
win_opts.row = math.floor((vim.o.lines - win_opts.height - vim.o.cmdheight) / 2)
win_opts.col = math.floor((vim.o.columns - win_opts.width) / 2)
else
win_opts.row = 2
win_opts.col = 2
end
local winid = vim.api.nvim_open_win(bufnr, is_float, win_opts)
return bufnr, winid
end
local function translate(opts)
vim.validate {
opts = { opts, 'table', true }
}
--- TODO : 异步请求
-- NOTE : 这里只处理了本地数据库查询
opts = get_opts(opts or {})
local field = core.query(opts)
local bufnr, winid = create_win(opts.win)
local proc_opts = {
bufnr = bufnr,
winid = winid,
win = opts.win,
field = field,
order = conf.order['offline'],
engine = { 'offline' },
}
core.process(proc_opts)
end
return translate

View File

@ -1,20 +0,0 @@
local M = {}
local db_path = require("Trans").conf.db_path
local dict = require("Trans").db:open(db_path)
function M.query(arg)
-- TODO: return type: a result table:
local res = {}
if type(arg) == 'string' then
res = dict:select('stardict', {
where = { word = arg },
})
elseif type(arg) == 'table' then
res = dict:select('stardict', arg)
else
vim.notify('query argument error!')
end
return res[1]
end
return M

View File

@ -1,309 +0,0 @@
local M = {}
local api = vim.api
local display = require("Trans").conf.display
local icon = require("Trans").conf.icon
local order = require("Trans").conf.order
local auto_close = require("Trans").conf.auto_close
local hl = require("Trans.highlight").hlgroup
local buf = require("Trans.conf").buf
local win = 0
local line = 0
local pos_info = {}
local handler = {}
api.nvim_buf_set_option(buf, 'filetype', 'Trans')
local function show_win(width, height)
win = api.nvim_open_win(buf, false, {
relative = 'cursor',
col = display.offset_x,
row = display.offset_y,
title = 'Trans',
title_pos = 'center',
style = display.style,
width = (display.max_width > 0 and width > display.max_width) and display.max_width or width,
height = (display.max_width > 0 and height > display.max_height) and display.max_height or height,
border = display.border_style,
focusable = false,
zindex = 250, -- Top
})
api.nvim_win_set_option(win, 'wrap', display.wrap)
end
-- NOTE title
handler.title = function(text, query_res)
local title = ('%s [%s] %s %s'):format(
query_res.word,
query_res.phonetic,
(display.oxford and (query_res.oxford == 1 and icon.isOxford or icon.notOxford) or ''),
((display.collins_star and query_res.collins) and string.rep(icon.star, query_res.collins) or '')
)
table.insert(text, title)
pos_info.title = {}
pos_info.title.word = #query_res.word
pos_info.title.phonetic = query_res.phonetic and #query_res.phonetic or 3
pos_info.title.line = line
line = line + 1
end
-- NOTE tag
handler.tag = function(text, query_res)
if query_res.tag and #query_res.tag > 0 then
local tag = query_res.tag:gsub('zk', '中考'):gsub('gk', '高考'):gsub('ky', '考研'):gsub('cet4', '四级'):
gsub('cet6', '六级'):gsub('ielts', '雅思'):gsub('toefl', '托福'):gsub('gre', 'GRE')
table.insert(text, '标签:')
table.insert(text, ' ' .. tag)
table.insert(text, '')
pos_info.tag = line
line = line + 3
end
end
-- NOTE pos 词性
handler.pos = function(text, query_res)
if query_res.pos and #query_res.pos > 0 then
table.insert(text, '词性:')
local content = 0
for v in vim.gsplit(query_res.pos, [[/]]) do
table.insert(text, string.format(' %s', v .. '%'))
content = content + 1
end
table.insert(text, '')
pos_info.pos = {}
pos_info.pos.line = line
pos_info.pos.content = content
line = line + content + 2
end
end
-- NOTE exchange
handler.exchange = function(text, query_res)
if query_res.exchange and #query_res.exchange > 0 then
table.insert(text, '词形变化:')
local exchange_map = {
p = '过去式',
d = '过去分词',
i = '现在分词',
r = '形容词比较级',
t = '形容词最高级',
s = '名词复数形式',
O = '词干',
['3'] = '第三人称单数',
}
local content = 0
for v in vim.gsplit(query_res.exchange, [[/]]) do
table.insert(text, string.format(' %s: %s', exchange_map[v:sub(1, 1)], v:sub(3)))
content = content + 1
-- FIXME: 中文字符与字母位宽不一致, 暂时无法对齐
end
table.insert(text, '')
pos_info.exchange = {}
pos_info.exchange.line = line
pos_info.exchange.content = content
line = line + content + 2
end
end
-- NOTE 中文翻译
handler.zh = function(text, query_res)
if query_res.translation then
table.insert(text, '中文翻译:')
local content = 0
for v in vim.gsplit(query_res.translation, '\n') do
table.insert(text, ' ' .. v)
content = content + 1
end
table.insert(text, '')
pos_info.zh = {}
pos_info.zh.line = line
pos_info.zh.content = content
line = content + line + 2
end
end
-- NOTE 英文翻译
handler.en = function(text, query_res)
if query_res.definition and #query_res.definition > 0 then
table.insert(text, '英文翻译:')
local content = 0
for v in vim.gsplit(query_res.definition, '\n') do
table.insert(text, ' ' .. v)
content = content + 1
end
table.insert(text, '')
pos_info.en = {}
pos_info.en.line = line
pos_info.en.content = content
line = line + content + 2
end
end
-- @return string array
local function get_text(query_res)
local text = {}
for _, v in ipairs(order) do
handler[v](text, query_res)
end
return text
end
local function set_text(query_res)
local text = query_res and get_text(query_res) or { '没有找到相关定义' }
api.nvim_buf_set_lines(buf, 0, -1, false, text)
local width = 0
for _, v in ipairs(text) do
if #v > width then
width = v:len()
end
end
return width, #text
end
local hl_handler = {}
hl_handler.title = function()
api.nvim_buf_add_highlight(buf, -1, hl.word, pos_info.title.line, 0, pos_info.title.word)
api.nvim_buf_add_highlight(buf, -1, hl.phonetic, pos_info.title.line, pos_info.title.word + 5,
pos_info.title.word + 5 + pos_info.title.phonetic)
end
hl_handler.tag = function()
if pos_info.tag then
api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.tag, 0, -1)
api.nvim_buf_add_highlight(buf, -1, hl.tag, pos_info.tag + 1, 0, -1)
end
end
hl_handler.pos = function()
if pos_info.pos then
api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.pos.line, 0, -1)
for i = 1, pos_info.pos.content, 1 do
api.nvim_buf_add_highlight(buf, -1, hl.pos, pos_info.pos.line + i, 0, -1)
end
end
end
hl_handler.exchange = function()
if pos_info.exchange then
api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.exchange.line, 0, -1)
for i = 1, pos_info.exchange.content, 1 do
api.nvim_buf_add_highlight(buf, -1, hl.exchange, pos_info.exchange.line + i, 0, -1)
end
end
end
hl_handler.zh = function()
api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.zh.line, 0, -1)
for i = 1, pos_info.zh.content, 1 do
api.nvim_buf_add_highlight(buf, -1, hl.zh, pos_info.zh.line + i, 0, -1)
end
end
hl_handler.en = function()
if pos_info.en then
api.nvim_buf_add_highlight(buf, -1, hl.ref, pos_info.en.line, 0, -1)
for i = 1, pos_info.en.content, 1 do
api.nvim_buf_add_highlight(buf, -1, hl.en, pos_info.en.line + i, 0, -1)
end
end
end
local function set_hl()
for _, v in ipairs(order) do
hl_handler[v]()
end
end
local function clear_tmp_info()
pos_info = {}
line = 0
end
local function get_visual_selection()
local s_start = vim.fn.getpos("'<")
local s_end = vim.fn.getpos("'>")
assert(s_end[2] == s_start[2])
local lin = vim.api.nvim_buf_get_lines(0, s_start[2] - 1, s_end[2], false)[1]
local word = string.sub(lin, s_start[3], s_end[3])
return word
end
function M.query(mode)
assert(buf > 0)
local word = ''
if mode == 'n' then
word = vim.fn.expand('<cword>')
elseif mode == 'v' then
word = get_visual_selection()
elseif mode == 'I' then
word = vim.fn.input('请输入您要查询的单词: ')
-- vim.ui.input({prompt = '请输入您要查询的单词: '}, function (input)
-- word = input
-- end)
else
error('mode argument is invalid')
end
local res = require("Trans.database").query(word)
local width, height = set_text(res)
show_win(width, height)
if res then
set_hl()
clear_tmp_info()
end
if auto_close then
api.nvim_create_autocmd(
{ 'InsertEnter', 'CursorMoved', 'BufLeave', }, {
buffer = 0,
once = true,
callback = M.close_win,
})
end
end
function M.query_cursor()
M.query('n')
end
function M.query_select()
M.query('v')
end
function M.query_input()
M.query('I')
end
function M.close_win()
if win > 0 then
api.nvim_win_close(win, true)
win = 0
end
end
-- function M.enter_win()
-- if api.nvim_win_is_valid(win) then
-- else
-- error('current win is not valid')
-- end
-- end
return M

View File

@ -1,26 +0,0 @@
local M = {}
M.hlgroup = {
word = 'TransWord',
phonetic = 'TransPhonetic',
ref = 'TransRef',
tag = 'TransTag',
exchange = 'TransExchange',
pos = 'TransPos',
zh = 'TransZh',
en = 'TransEn',
}
function M.set_hl()
local set_hl = vim.api.nvim_set_hl
set_hl(0, M.hlgroup.word, { fg = '#7ee787', bold = true })
set_hl(0, M.hlgroup.phonetic, { fg = '#8b949e' })
set_hl(0, M.hlgroup.ref, { fg = '#75beff', bold = true })
set_hl(0, M.hlgroup.tag, { fg = '#e5c07b' })
set_hl(0, M.hlgroup.pos, { link = M.hlgroup.tag })
set_hl(0, M.hlgroup.exchange, { link = M.hlgroup.tag })
set_hl(0, M.hlgroup.zh, { link = M.hlgroup.word })
set_hl(0, M.hlgroup.en, { fg = '#bc8cff' })
end
return M

View File

@ -1,31 +1,12 @@
local M = {}
M.conf = require("Trans.conf")
function M.setup(conf)
conf = conf or {}
if conf.display then
conf.display = vim.tbl_extend('force', M.conf.display, conf.display)
end
if conf.icon then
conf.icon = vim.tbl_extend('force', M.conf.icon, conf.icon)
end
M.conf = vim.tbl_extend('force', M.conf, conf)
M.setup = function(opts)
require('Trans.conf.loader').load_conf(opts)
require("Trans.setup")
M.translate = require('Trans.core.translate')
end
local res = vim.fn.executable('sqlite3')
if res ~= 1 then
error('Please check out sqlite3')
end
local status, db = pcall(require, 'sqlite.db')
if not status then
error('Please check out sqlite.lua')
end
M.db = db
M.translate = nil
M.augroup = vim.api.nvim_create_augroup('Trans', {clear = true})
return M

View File

@ -1,23 +1,25 @@
local db = require("Trans").db
-- local conf = require("Trans").conf
vim.api.nvim_create_user_command('TranslateCursorWord', require("Trans.display").query_cursor, {})
vim.api.nvim_create_user_command('TranslateSelectWord', require("Trans.display").query_select, {})
vim.api.nvim_create_user_command('TranslateInputWord', require("Trans.display").query_input, {})
local group = vim.api.nvim_create_augroup("Trans", { clear = true })
vim.api.nvim_create_autocmd('VimLeave', {
group = group,
pattern = '*',
callback = function()
if db:isopen() then
db:close()
if vim.fn.executable('sqlite3') ~= 1 then
error('Please check out sqlite3')
end
end,
vim.api.nvim_create_user_command('Translate', function ()
require("Trans").translate()
end, {
desc = ' 单词翻译',
})
-- vim.keymap.set('n', 'mm', '<cmd>TranslateCurosorWord<cr>')
-- vim.keymap.set('v', 'mm', '<Esc><cmd>TranslateSelectWord<cr>')
require("Trans.highlight").set_hl()
vim.api.nvim_create_user_command('TranslateInput', function ()
require("Trans").translate {
method = 'input',
}
end, {desc = ' 搜索翻译'})
-- TODO
-- vim.api.nvim_create_user_command('TranslateHistory', require("Trans.core").query_input, {
-- desc = '翻译输入的单词',
-- })
local highlights = require("Trans.conf.loader").loaded_conf.ui.highlight
for highlight, opt in pairs(highlights) do
vim.api.nvim_set_hl(0, highlight, opt)
end

39
lua/Trans/util/base64.lua Normal file
View File

@ -0,0 +1,39 @@
local ffi = require('ffi')
local base64 = {}
local b64 = ffi.new('unsigned const char[65]',
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
function base64.encode(str)
local band, bor, lsh, rsh = bit.band, bit.bor, bit.lshift, bit.rshift
local len = #str
local enc_len = 4 * math.ceil(len / 3) -- (len + 2) // 3 * 4 after Lua 5.3
local src = ffi.new('unsigned const char[?]', len+1, str)
local enc = ffi.new('unsigned char[?]', enc_len+1)
local i, j = 0, 0
while i < len-2 do
enc[j] = b64[band(rsh(src[i], 2), 0x3F)]
enc[j+1] = b64[bor(lsh(band(src[i], 0x3), 4), rsh(band(src[i+1], 0xF0), 4))]
enc[j+2] = b64[bor(lsh(band(src[i+1], 0xF), 2), rsh(band(src[i+2], 0xC0), 6))]
enc[j+3] = b64[band(src[i+2], 0x3F)]
i, j = i+3, j+4
end
if i < len then
enc[j] = b64[band(rsh(src[i], 2), 0x3F)]
if i == len-1 then
enc[j+1] = b64[lsh(band(src[i], 0x3), 4)]
enc[j+2] = 0x3D
else
enc[j+1] = b64[bor(lsh(band(src[i], 0x3), 4), rsh(band(src[i+1], 0xF0), 4))]
enc[j+2] = b64[lsh(band(src[i+1], 0xF), 2)]
end
enc[j+3] = 0x3D
end
return ffi.string(enc, enc_len)
end
return base64

View File

@ -0,0 +1,186 @@
local M = {}
-- local type_check = require("Trans.util.debug").type_check
-- NOTE :中文字符及占两个字节宽但是在lua里是3个字节长度
-- 为了解决中文字符在lua的长度和neovim显示不一致的问题
function string:width()
return vim.fn.strdisplaywidth(self)
end
-- 各种风格的基础宽度
local style_width = {
-- float = require("Trans.conf.window").float.width, -- NOTE : need window parsed conf
cursor = 60,
}
local s_to_b = true -- 从小到大排列
local m_fields -- 待格式化的字段
local m_indent -- 每行的行首缩进
local m_tot_width -- 所有字段加起来的长度(不包括缩进和间隔)
local m_interval -- 每个字段的间隔
local m_win_width -- 需要被格式化窗口的高度
local m_item_width -- 每个字段的宽度
local m_size
local function caculate_format()
local width = m_win_width - m_item_width[1]
local cols = 0
for i = 2, #m_fields do
width = width - m_item_width[i] - m_interval
if width < 0 then
cols = i - 1
break
else
cols = i
end
end
return math.ceil(#m_fields / cols), cols
end
local function format_to_line()
local line = m_fields[1]
if m_size == 1 then
--- Center Align
local space = math.floor((m_win_width - m_item_width[1]) / 2)
line = (' '):rep(space) .. line
else
local space = math.floor((m_win_width - m_tot_width) / m_size - 1)
for i = 2, m_size do
line = line .. (' '):rep(space) .. m_fields[i]
end
end
return line
end
local function sort_tables()
table.sort(m_item_width, function (a, b)
return a > b
end)
table.sort(m_fields, function (a, b)
return a:width() > b:width()
end)
end
local function format_to_multilines()
local lines = {}
sort_tables()
--- NOTE 计算应该格式化成多少行和列
local rows, cols = caculate_format()
local rest = #m_fields % cols
if rest == 0 then
rest = cols
end
local s_width = m_item_width[1] -- 列中最宽的字符串宽度
-- NOTE : 第一列不需要加空格
for i = 1, rows do
local idx = s_to_b and rows - i + 1 or i
local space = (' '):rep(s_width - m_item_width[i])
lines[idx] = m_fields[i] .. space -- NOTE 由大到小
end
local index = rows + 1 -- 最宽字符的下标
local interval = (' '):rep(m_interval) -- 每个字符串间的间隙
for j = 2, cols do -- 以列为单位遍历
s_width = m_item_width[index]
local stop = (j > rest and rows - 1 or rows)
for i = 1, stop do
local idx = s_to_b and stop - i + 1 or i -- 当前操作的行数
local item = index + i - 1 -- 当前操作的字段数
local space = (' '):rep(s_width - m_item_width[item]) -- 对齐空格
lines[idx] = lines[idx] .. interval .. m_fields[item] .. space -- NOTE 从大到小
end
index = index + stop -- 更新最宽字符的下标
end
return lines
end
local function get_formatted_lines()
local lines = {}
-- NOTE : 判断能否格式化成一行
local line_size = m_tot_width + (#m_fields * m_interval)
if line_size > m_win_width then
lines = format_to_multilines()
else
lines[1] = format_to_line()
end
-- NOTE :进行缩进
if m_indent > 0 then
for i, v in ipairs(lines) do
lines[i] = (' '):rep(m_indent) .. v
end
end
return lines
end
---将组件格式化成相应的vim支持的lines格式
---@param style string 窗口的风格
---@param fields string[] 需要格式化的字段
---@param indent? number 缩进的长度
---@return string[] lines 便于vim.api.nvim_buf_set_lines
M.to_lines = function(style, fields, indent)
local length = 0
local width = 0
local item_size = {}
for i, v in ipairs(fields) do
width = v:width()
item_size[i] = width
length = length + width
end
m_indent = indent or 0
m_win_width = style_width[style] - m_indent
m_fields = fields
m_tot_width = length
m_item_width = item_size
m_interval = m_win_width > 50 and 6 or 4
m_size = #fields
return get_formatted_lines()
end
local test = {
'isjlk测试dj',
'测试一下..',
}
local lines = M.to_lines('cursor', test)
-- print('===========================================')
-- for _, v in ipairs(test) do
-- print(v .. ' width:', v:width())
-- end
-- print('===========================================')
-- print('===========================================')
-- print('===========================================')
-- print('type is :' .. type(lines) .. ' size is :' .. #lines[1])
for _, v in ipairs(test) do
print(v:width())
end
-- lines = M.to_lines('cursor', {
-- 'ajlkasj',
-- 'jklasjldajjnn测试',
-- '测试将安得拉蓝色',
-- 'cool this',
-- }, 4)
-- for _, v in ipairs(lines) do
-- print(v)
-- end
return M

View File

@ -0,0 +1,15 @@
local M = {}
-- local type_check = require("Trans.util.debug").type_check
---@param str string
local function is_Chinese(str)
for i = 1, #str do
if not str:byte(i) >= [[\u4e00]] then
return false
end
end
return true
end
return M

View File

@ -0,0 +1,71 @@
local M = {}
-- local type_check = require("Trans.util.debug").type_check
local salt = '96836db9-1e28-4789-b5a6-fb7bb67e1259'
local appKey = '1858465a8708c121'
local appPasswd = 'fG0sitfk16nJOlIlycnLPYZn1optxUxL'
local curtime
local word
local function caculate_input()
local input
local len = #word
if len > 20 then
input = word:sub(1, 10) .. len .. word:sub(-10)
else
input = word
end
return input
end
local function caculate_sign()
-- sign=sha256(应用ID+input+salt+curtime+应用密钥)
local hash = appKey .. caculate_input() .. salt .. curtime .. appPasswd
return vim.fn.sha256(hash)
end
local function test()
local query = {
q = word,
from = 'auto',
to = 'zh-CHS',
-- dicts = 'ec',
signType = 'v3',
appKey = appKey,
salt = salt,
curtime = curtime,
sign = caculate_sign(),
}
return query
end
-- curl --data {{'{"name":"bob"}'}} --header {{'Content-Type: application/json'}} {{http://example.com/users/1234}}
local function query_word(q)
local ok, curl = pcall(require, 'plenary.curl')
if ok then
-- TODO
else
local field = (
[[curl -s --header 'Content-Type: application/x-www-form-urlencoded' https://openapi.youdao.com/api]])
for k, v in pairs(q) do
field = field .. ([[ -d '%s=%s']]):format(k, v)
end
local output = vim.fn.system(field)
local tb = vim.fn.json_decode(output)
return tb
end
end
M.test = function(query)
curtime = tostring(os.time()) -- 更新一下time
word = query or 'as'
-- local json = vim.fn.json_encode(test())
query_word(test())
-- vim.pretty_print(vim.fn.json_encode(json))
end
return M

View File

@ -0,0 +1,18 @@
local a = {
'test1',
'test2',
'test3'
}
local function test(tmp)
tmp = {
'bbbbbb'
}
end
test(a)
for i, v in ipairs(a) do
print(v)
end