diff --git a/lua/Trans/README.md b/lua/Trans/README.md new file mode 100644 index 0000000..8faec8a --- /dev/null +++ b/lua/Trans/README.md @@ -0,0 +1,58 @@ +# 字段说明 + +## 本地 +- `word` +查询的字符串 +- `phonetic` +音标 +- `collins` +柯林斯星级: integer +- `oxford` +是否为牛津词汇: integer (1为是) +- `tag` +标签 +- `pos` +词性 +- `exchange` +词态变化 +- `translation` +中文翻译 +- `definition` +英文注释 + +## 有道 +### 中英 + +basic JSONObject 简明释义 +phonetic text 词典音标 +usPhonetic text 美式音标 +ukPhonetic text 英式音标 +ukSpeech text 英式发音 +usSpeech text 美式发音 +explains text 基本释义 +text text 短语 +explain String Array 词义解释列表 +wordFormats Object Array 单词形式变化列表 +name String 形式名称,例如:复数 +web JSONArray 网络释义 +phrase String 词组 +meaning String 含义 +synonyms JSONObject 近义词 +pos String 词性 +words String Array 近义词列表 +trans String 释义 +antonyms ObjectArray 反义词 +relatedWords JSONArray 相关词 +wordNet JSONObject 汉语词典网络释义 +phonetic String 发音 +meanings ObjectArray 释义 +meaning String 释义 +example array 示例 +dict String 词典deeplink +webDict String 词典网页deeplink +sentenceSample text 例句 +sentence text 例句 +sentenceBold text 将查询内容加粗的例句 +translation text 例句翻译 +wfs text 单词形式变化 +exam_type text 考试类型 diff --git a/lua/Trans/api/README.md b/lua/Trans/api/README.md index bdc0653..3b4d0ef 100644 --- a/lua/Trans/api/README.md +++ b/lua/Trans/api/README.md @@ -1,7 +1,132 @@ # API说明 -## 概述 -- 翻译查询 - - `` -- 字段处理 -- 窗口显示 + +- [API说明](#api说明) + - [数据结构](#数据结构) + - [翻译](#翻译) + - [窗口](#窗口) + - [翻译结果](#翻译结果) + - [内容单位](#内容单位) +- [窗口绘制逻辑](#窗口绘制逻辑) + - [hover](#hover) + - [float](#float) + + +## 数据结构 + +### 翻译 +- `word` +待翻译的字符串: string +- `sentence` +是否为句子: boolean +- `result` +翻译查询的结果: table + > 见: [翻译结果](#翻译结果) +- `engine` + +### 窗口 +- `style` +风格: string +- `height` +高度: integer +- `width` +宽度: integer +- `border` +边框样式: string +- `winhl` +窗口的高亮: string + +### 翻译结果 +**无特殊说明, 所有字段均为`string`类型** + +- `word` +查询的字符串 +- `phonetic` +音标 +- `collins` +柯林斯星级: integer +- `oxford` +是否为牛津词汇: integer (1为是) +- `tag` +标签 +- `pos` +词性 +- `exchange` +词态变化 +- `translation` +中文翻译 +- `definition` +英文注释 + + +### 内容单位 +- `field` 字段 + > 是展示的最小单位 + + **属性** + - `1` + 存储的文本: string + - `2` (optional) + 对应的高亮: string + - `_start` + 起始行: integer + - `_end` + 结束行: integer + > **注意:** `_start` 和`_end` 字段只有已经被展示到窗口后才会被设置 + + **方法** + - 无 + +- `line` 行 + > 窗口展示的每一行, 一个行有多个`field` + + **属性** + - `text` + 当前保存的字符串: string + - `hls` + 行内的高亮: table + - `index` + 行号[0为起始下标] + - `fields` + 行内保存的`field` + - `status` + 行内是否需要更新: boolean + > **注意:** `num` 只有已经被展示到窗口后才会被设置 + + **方法** + - `update` + 更新`text`和`hls` + - `data` + 获取行的text + - `insert` + 添加新`field` + - `add_highlight` + 添加高亮 + > 参数: bufnr + +- `content` 内容 + > 窗口展示的单位, 一个内容内有多个`line` + + **方法** + - `data` + 返回lines和highlights + - `insert` + 插入新的行 + - `attach` + 将内容展示到buffer + > 参数: bufnr + +# 窗口绘制逻辑 + +- 获取所有组件 +## hover +- 按照order顺序加载 +- 按组件间距为4计算组件个数能否在一行以内放下 + - 放不下则按照组件间距为4 计算组件个数并对齐 + - 放得下则重新计算组间距 + +获得组件行内容, 设置到行内 +获取组件高亮, 设置高亮 + +## float +由定义好的逻辑,绘制整个窗口 diff --git a/lua/Trans/api/query.lua b/lua/Trans/api/query.lua index ad0014b..bee4c5f 100644 --- a/lua/Trans/api/query.lua +++ b/lua/Trans/api/query.lua @@ -3,23 +3,12 @@ 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 path = require('Trans').conf.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 = { +local query_fields = { 'word', 'phonetic', 'definition', @@ -31,19 +20,40 @@ local query_field = { 'exchange', } + +local routes = { + offline = function(word) + local res = dict:select('stardict', { + where = { + word = word, + }, + keys = query_fields, + }) + return res[1] + end, +} + + +-- INFO :Auto Close +vim.api.nvim_create_autocmd('VimLeavePre', { + group = require("Trans").augroup, + callback = function() + if db:isopen() then + db:close() + end + end +}) + + -- NOTE : local query -M.query = function(arg) +M.query = function(engine, word) -- TODO : more opts - type_check { - arg = { arg, 'string' }, + vim.validate { + word = {word, 's'}, + engine = {word, 's'}, } - local res = dict:select('stardict', { - where = { - word = arg, - }, - keys = query_field, - }) - return res[1] + + return routes[engine](word) end diff --git a/lua/Trans/component/content.lua b/lua/Trans/component/content.lua deleted file mode 100644 index c04ee90..0000000 --- a/lua/Trans/component/content.lua +++ /dev/null @@ -1,143 +0,0 @@ -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 diff --git a/lua/Trans/component/items.lua b/lua/Trans/component/items.lua deleted file mode 100644 index d1c775e..0000000 --- a/lua/Trans/component/items.lua +++ /dev/null @@ -1,20 +0,0 @@ -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 diff --git a/lua/Trans/component/offline/Definition.lua b/lua/Trans/component/offline/Definition.lua deleted file mode 100644 index 54d9f77..0000000 --- a/lua/Trans/component/offline/Definition.lua +++ /dev/null @@ -1,68 +0,0 @@ -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 ---]] diff --git a/lua/Trans/component/offline/Exchange.lua b/lua/Trans/component/offline/Exchange.lua deleted file mode 100644 index 90cb93c..0000000 --- a/lua/Trans/component/offline/Exchange.lua +++ /dev/null @@ -1,45 +0,0 @@ -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 diff --git a/lua/Trans/component/offline/Pos.lua b/lua/Trans/component/offline/Pos.lua deleted file mode 100644 index 7d52ae5..0000000 --- a/lua/Trans/component/offline/Pos.lua +++ /dev/null @@ -1,20 +0,0 @@ -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 diff --git a/lua/Trans/component/offline/Tag.lua b/lua/Trans/component/offline/Tag.lua deleted file mode 100644 index 9ccf18e..0000000 --- a/lua/Trans/component/offline/Tag.lua +++ /dev/null @@ -1,46 +0,0 @@ -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 diff --git a/lua/Trans/component/offline/Title.lua b/lua/Trans/component/offline/Title.lua deleted file mode 100644 index 3613893..0000000 --- a/lua/Trans/component/offline/Title.lua +++ /dev/null @@ -1,67 +0,0 @@ -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 diff --git a/lua/Trans/component/offline/Translation.lua b/lua/Trans/component/offline/Translation.lua deleted file mode 100644 index bdfe0e0..0000000 --- a/lua/Trans/component/offline/Translation.lua +++ /dev/null @@ -1,28 +0,0 @@ -local M = {} - -M.component = function(field, max_size) - if field.translation then - local ref = { - { '中文翻译', 'TransRef' } - } - - local translations = { - highlight = 'TransTranslation', - indent = 4, - emptyline = true, - needformat = true, - } - local size = 0 - for trans in vim.gsplit(field.translation, '\n', true) do - size = size + 1 - table.insert(translations, trans) - if size == max_size then - break - end - end - - return { ref, translations } - end -end - -return M diff --git a/lua/Trans/conf/default.lua b/lua/Trans/conf/default.lua deleted file mode 100644 index 1554085..0000000 --- a/lua/Trans/conf/default.lua +++ /dev/null @@ -1,119 +0,0 @@ -local M = {} - -M.conf = { - style = { - ui = { - input = 'float', - n = 'cursor', - v = 'cursor', - }, - window = { - cursor = { - border = 'rounded', - width = 40, - height = 50, - }, - float = { - border = 'rounded', - width = 0.9, - height = 0.8, - }, - }, - }, - - order = { - offline = { - 'Title', - 'Tag', - 'Pos', - 'Exchange', - 'Translation', - -- NOTE :如果你想限制某个组件的行数,可以设置max_size - -- { '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 diff --git a/lua/Trans/conf/loader.lua b/lua/Trans/conf/loader.lua deleted file mode 100644 index 8245116..0000000 --- a/lua/Trans/conf/loader.lua +++ /dev/null @@ -1,60 +0,0 @@ --- -@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 diff --git a/lua/Trans/core/backup.lua b/lua/Trans/core/backup.lua deleted file mode 100644 index fb8cd0e..0000000 --- a/lua/Trans/core/backup.lua +++ /dev/null @@ -1,294 +0,0 @@ -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('') --- 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 diff --git a/lua/Trans/core/content.lua b/lua/Trans/core/content.lua new file mode 100644 index 0000000..40f659b --- /dev/null +++ b/lua/Trans/core/content.lua @@ -0,0 +1,145 @@ +local M = {} +M.__index = M + +local get_width = vim.fn.strdisplaywidth +-- local get_width = vim.fn.strwidth +-- local get_width = vim.api.nvim_strwidth + +---@alias block table add_hl(key, hl_name) +---返回分配的块状区域, e_col 设置为-1则为末尾 +---@param s_row integer 起始行 +---@param s_col integer 起始列 +---@param height integer 行数 +---@param width integer 块的宽度 +---@return block +function M:alloc_block(s_row, s_col, height, width) + -- -1为到行尾 + width = width == -1 and self.width or width + local e_col = s_col + width + + local block = { + add_hl = function(key, hl_name) + table.insert(self.highlight[s_row + key], { + name = hl_name, + _start = s_col, + _end = e_col, + }) + end + } + + return setmetatable(block, { + -- 访问该表的操作, 映射成lines + __index = function(_, key) + assert(0 < key and key <= height) + --- FIXME : Unicode stirng sub + return self.lines[s_row + key]:sub(s_col, e_col) + end, + + __newindex = function(_, key, value) + assert(0 < key and key <= height) + local wid = get_width(value) + if wid > width then + error('check out the str width: Max ->' .. self.width .. ' str ->' .. wid) + else + value = value .. (' '):rep(width - wid) + end + + local line = s_row + key - 1 + self.lines[line] = self.lines[line]:sub(1, s_col - 1) .. value .. self.lines[line]:sub(e_col + 1) + end, + }) +end + +function M:alloc_newline() + self.len = self.len + 1 + local items = {} + local len = 0 + local size = 0 + return { + add_item = function(item, highlight) + size = size + 1 + local wid = get_width(item) + items[size] = { item, highlight } + len = len + wid + end, + + load_line = function() + local space = math.floor((self.width - len) / (size - 1)) + assert(space > 0) + local interval = (' '):rep(space) + local value = '' + local function load_item(index) + if items[index][2] then + local _start = #value + local _end = _start + #items[index][2] + table.insert(self.highlights[self.len], { + name = items[index][2], + _start = _start, + _end = _end + }) + end + value = value .. items[index][1] + end + + load_item(1) + for i = 2, size do + value = value .. interval + load_item(i) + end + + self.lines[self.len] = value + end + } +end + +function M:addline(text, highlight) + self.len = self.len + 1 + if highlight then + table.insert(self.highlights[self.len], { + name = highlight, + _start = 1, + _end = -1 + }) + end + self.lines[self.len] = text +end + +-- 窗口宽度 +function M:new(width) + vim.validate { + width = { width, 'n' } + } + local default = (' '):rep(width) -- default value is empty line + local new_content = { + width = width, + len = 0, + highlights = setmetatable({}, { -- always has default value + __index = function(tbl, key) + tbl[key] = {} + return tbl[key] + end + }), + + } + + new_content.lines = setmetatable({}, { + __index = function(tbl, key) + tbl[key] = default + return tbl[key] + end, + + __newindex = function(tbl, key, value) + if value then + for _ = new_content.len + 1, key - 1 do + rawset(tbl, key, '') + end + + rawset(tbl, key, value) + new_content.len = key + end + end + }) + return setmetatable(new_content, self) +end + +return M diff --git a/lua/Trans/core/handler.lua b/lua/Trans/core/handler.lua new file mode 100644 index 0000000..f96ac01 --- /dev/null +++ b/lua/Trans/core/handler.lua @@ -0,0 +1,152 @@ +local icon = require('Trans').conf.icon + +-- local components = { +-- 'title', +-- 'tag', +-- 'pos', +-- 'exchange', +-- 'translation', +-- 'definition' +-- } + +local tag_map = { + zk = '中考', + gk = '高考', + ky = '考研', + cet4 = '四级', + cet6 = '六级', + ielts = '雅思', + toefl = '托福', + gre = 'gre ', +} +local pos_map = { + a = '代词pron ', + c = '连接词conj ', + i = '介词prep ', + j = '形容词adj ', + m = '数词num ', + n = '名词n ', + p = '代词pron ', + r = '副词adv ', + u = '感叹词int ', + v = '动词v ', + x = '否定标记not ', + t = '不定式标记infm ', + d = '限定词determiner', +} + + +local exchange_map = { + ['p'] = '过去式 ', + ['d'] = '过去分词 ', + ['i'] = '现在分词 ', + ['r'] = '比较级 ', + ['t'] = '最高级 ', + ['s'] = '复数 ', + ['0'] = '原型 ', + ['1'] = '类别 ', + ['3'] = '第三人称单数', + -- ['f'] = '第三人称单数', +} + +local function exist(res) + return res and res ~= '' +end + +M = { + title = function(result, content) + local line = content:alloc_newline() + line.add_item(result.word, 'TransWord') + local pho = ('[' .. (exist(result.phonetic) and result.phonetic or icon.notfound) .. ']') + -- line.add_item(pho, 'TransPhonetic', #pho) + line.add_item(pho, 'TransPhonetic') + line.add_item((exist(result.collins) and icon.star:rep(result.collins) or icon.notfound)) + line.add_item((exist(result.oxford) and icon.yes or icon.no)) + line.load_line() + end, + + tag = function(result, content) + if exist(result.tag) then + content:addline('标签:', 'TransRef') + local tags = vim.tbl_map(function(tag) + return tag_map[tag] + end, vim.split(result.tag, ' ', { plain = true, trimempry = true })) + + local size = #tags + local i = 1 + while i <= size do + content:addline(' ' .. tags[i] .. ' ' .. (tags[i + 1] or '') .. ' ' .. (tags[i + 2] or ''), + 'TransTag') + i = i + 3 + end + content:addline('') + end + end, + + pos = function(result, content) + if exist(result.pos) then + content:addline('词性:', 'TransRef') + vim.tbl_map(function(pos) + content:addline(' ' .. pos_map[pos:sub(1, 1)] .. pos:sub(3) .. '%', 'TransPos') + end, vim.split(result.pos, '/', { plain = true, trimempry = true })) + + content:addline('') + end + end, + + exchange = function(result, content) + if exist(result.exchange) then + content:addline('词性变化:', 'TransRef') + vim.tbl_map(function(exc) + content:addline(' ' .. exchange_map[exc:sub(1, 1)] .. ' ' .. exc:sub(3), 'TransExchange') + -- content:addline(' ' .. exchange_map[exc:sub(1, 1)] .. exc:sub(2), 'TransExchange') + end, vim.split(result.exchange, '/', { plain = true, trimempry = true })) + + content:addline('') + end + end, + + translation = function(result, content) + if result.translation and result.translation ~= '' then + local ref = { + { '中文翻译:', 'TransRef' } + } + + local translations = { + highlight = 'TransTranslation', + indent = 4, + emptyline = true, + } + for trans in vim.gsplit(result.translation, '\n', true) do + table.insert(translations, trans) + end + + return { ref, translations } + end + end, + + + definition = function(result, content) + if result.definition and result.definition ~= '' then + local ref = { + { '英文注释:', 'TransRef' } + } + + local definitions = { + highlight = 'TransDefinition', + indent = 4, + emptyline = true, + } + + for defin in vim.gsplit(result.definition, '\n', true) do + if defin ~= '' then + table.insert(definitions, defin) + end + end + + return { ref, definitions } + end + end +} + +return M diff --git a/lua/Trans/core/init.lua b/lua/Trans/core/init.lua index 8a499bd..e5de1fe 100644 --- a/lua/Trans/core/init.lua +++ b/lua/Trans/core/init.lua @@ -1,7 +1,59 @@ local M = {} +local conf = require('Trans').conf +local api = require('Trans.api') +local win = require('Trans.core.window') +local handler = require('Trans.core.handler') +local c = require('Trans.core.content') + + +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, '') +end + +M.translate = function(method, view) + method = method or vim.api.nvim_get_mode().mode + view = view or conf.view[method] + local word + if method == 'input' then + ---@diagnostic disable-next-line: param-type-mismatch + word = vim.fn.input('请输入您要查询的单词:') -- TODO Use Telescope with fuzzy finder + elseif method == 'n' then + word = vim.fn.expand('') + elseif method == 'v' then + word = get_select() + end + + + win.init(view) + local result = api.query('offline', word) + + if result then + local content = c:new(win.width) + + for i = 1, #conf.order do + handler[conf.order[i]](result, content) + end + + win.draw(content) + else + local line = { '⚠️ 本地没有找到相应的结果' } + vim.api.nvim_buf_set_lines(win.bufnr, 0, -1, false, line) + local wid = vim.fn.strdisplaywidth(line[1]) + vim.api.nvim_win_set_width(win.id, wid) + vim.api.nvim_win_set_height(win.id, #line) + win.auto_close() + end +end -M.process = require('Trans.core.process') -M.query = require('Trans.core.query') --- M.show_win = require('Trans.core.show_win') return M diff --git a/lua/Trans/core/process.lua b/lua/Trans/core/process.lua deleted file mode 100644 index aba65bf..0000000 --- a/lua/Trans/core/process.lua +++ /dev/null @@ -1,170 +0,0 @@ -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 diff --git a/lua/Trans/core/query.lua b/lua/Trans/core/query.lua deleted file mode 100644 index 5fdb0b0..0000000 --- a/lua/Trans/core/query.lua +++ /dev/null @@ -1,43 +0,0 @@ -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('') - - 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 diff --git a/lua/Trans/core/translate.lua b/lua/Trans/core/translate.lua deleted file mode 100644 index 26177a4..0000000 --- a/lua/Trans/core/translate.lua +++ /dev/null @@ -1,110 +0,0 @@ --- Default conf -local conf = require("Trans.conf.loader").loaded_conf -local core = require("Trans.core") - - -local function get_opts(opts) - - local mode = vim.api.nvim_get_mode().mode - local default_conf = { - method = mode, - engine = conf.base.engine, - win = { - style = conf.style.ui[opts.method or mode], - 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 diff --git a/lua/Trans/core/window.lua b/lua/Trans/core/window.lua new file mode 100644 index 0000000..ac64a6b --- /dev/null +++ b/lua/Trans/core/window.lua @@ -0,0 +1,94 @@ +local M = {} +local api = vim.api +local conf = require('Trans').conf +M.id = 0 +M.bufnr = 0 +M.ns = api.nvim_create_namespace('Trans') + + +function M.init(view) + vim.validate { + view = { view, 's' } + } + + M.view = view + M.float = view == 'float' + M.height = conf.window[view].height + M.width = conf.window[view].width + + local opts = { + relative = M.float and 'editor' or 'cursor', + width = M.width, + height = M.height, + style = 'minimal', + border = conf.window.border, + title = 'Trans', + title_pos = 'center', + focusable = true, + zindex = 100, + } + + if M.float then + opts.row = math.floor((vim.o.lines - M.height) / 2) + opts.col = math.floor((vim.o.columns - M.width) / 2) + else + opts.row = 2 + opts.col = 2 + end + + M.bufnr = api.nvim_create_buf(false, true) + M.id = api.nvim_open_win(M.bufnr, M.float, opts) +end + +M.draw = function(content) + api.nvim_buf_set_lines(M.bufnr, 0, -1, false, content.lines) + + if content.highlights then + for l, _hl in pairs(content.highlights) do + for _, hl in ipairs(_hl) do + api.nvim_buf_add_highlight(M.bufnr, M.ns, hl.name, l - 1, hl._start, hl._end) -- zero index + end + end + end + + local len = #content.lines + if M.height > len then + api.nvim_win_set_height(M.id, len + 1) + end + + + api.nvim_buf_set_option(M.bufnr, 'modifiable', false) + api.nvim_buf_set_option(M.bufnr, 'filetype', 'Trans') + api.nvim_win_set_option(M.id, 'wrap', not M.float) + api.nvim_win_set_option(M.id, 'winhl', 'Normal:TransCursorWin,FloatBorder:TransCursorBorder') + + if M.float then + vim.keymap.set('n', 'q', function() + if api.nvim_win_is_valid(M.id) then + api.nvim_win_close(M.id, true) + end + end, { buffer = M.bufnr, silent = true }) + + else + -- TODO : set keymaps for window + M.auto_close() + end +end + + +M.auto_close = function() + api.nvim_create_autocmd( + { 'InsertEnter', 'CursorMoved', 'BufLeave', }, { + buffer = 0, + once = true, + callback = function() + if api.nvim_win_is_valid(M.id) then + api.nvim_win_close(M.id, true) + end + end, + }) + +end + + +return M diff --git a/lua/Trans/init.lua b/lua/Trans/init.lua index 317547b..c23a858 100644 --- a/lua/Trans/init.lua +++ b/lua/Trans/init.lua @@ -1,12 +1,84 @@ local M = {} +M.conf = { + view = { + input = 'hover', + n = 'hover', + v = 'hover', + }, + window = { + border = 'rounded', + hover = { + width = 36, + height = 23, + }, + float = { + width = 0.8, + height = 0.8, + }, + }, + + order = { + -- offline = { + 'title', + 'tag', + 'pos', + 'exchange', + -- 'translation', + -- NOTE :如果你想限制某个组件的行数,可以设置max_size + -- { 'Definition', max_size = 4 }, + -- }, + -- online = { + -- -- TODO + -- }, + }, + icon = { + star = '⭐', + notfound = '❔', + yes = '✔️', + no = '❌' + }, + db_path = '$HOME/.vim/dict/ultimate.db', + -- TODO : + -- engine = { + -- -- TODO + -- 'offline', + -- } + -- map = { + -- -- TODO + -- }, + -- history = { + -- -- TOOD + -- } + + -- TODO add online translate engine + -- online_search = { + -- enable = false, + -- engine = {}, + -- } + + -- TODO register word +} + + M.setup = function(opts) - require('Trans.conf.loader').load_conf(opts) + if opts then + M.conf = vim.tbl_deep_extend('force', M.conf, opts) + end + local window = M.conf.window + assert(window.hover.width > 1 and window.hover.height > 1) + assert(0 < window.float.width and window.float.width <= 1) + assert(0 < window.float.height and window.float.height <= 1) + + window.float.height = math.floor((vim.o.lines - vim.o.cmdheight - 1) * window.float.height) + window.float.width = math.floor(vim.o.columns * window.float.width) + + -- TODO : replace the height and width for float options + M.translate = require('Trans.core').translate require("Trans.setup") - M.translate = require('Trans.core.translate') end -M.translate = nil -M.augroup = vim.api.nvim_create_augroup('Trans', {clear = true}) + +M.augroup = vim.api.nvim_create_augroup('Trans', { clear = true }) return M diff --git a/lua/Trans/setup.lua b/lua/Trans/setup.lua index 1fa8aef..840f4e7 100644 --- a/lua/Trans/setup.lua +++ b/lua/Trans/setup.lua @@ -2,24 +2,59 @@ if vim.fn.executable('sqlite3') ~= 1 then error('Please check out sqlite3') end -vim.api.nvim_create_user_command('Translate', function () +vim.api.nvim_create_user_command('Translate', function() require("Trans").translate() end, { desc = ' 单词翻译', }) -vim.api.nvim_create_user_command('TranslateInput', function () - require("Trans").translate { - method = 'input', - } -end, {desc = ' 搜索翻译'}) +vim.api.nvim_create_user_command('TranslateInput', function() + require("Trans").translate('input') +end, { desc = ' 搜索翻译' }) --- TODO + +local highlights = { + 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', + } +} + +-- 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 diff --git a/lua/Trans/util/test/format.lua b/lua/Trans/util/test/format.lua index 62df1fe..12d799b 100644 --- a/lua/Trans/util/test/format.lua +++ b/lua/Trans/util/test/format.lua @@ -1,186 +1,188 @@ -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('===========================================') +-- 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:', v:width()) +-- print(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 - +-- +-- -- lines = M.to_lines('cursor', { +-- -- 'ajlkasj', +-- -- 'jklasjldajjnn测试', +-- -- '测试将安得拉蓝色', +-- -- 'cool this', +-- -- }, 4) +-- +-- -- for _, v in ipairs(lines) do +-- -- print(v) +-- -- end +-- return M +-- diff --git a/lua/Trans/util/test/test.lua b/lua/Trans/util/test/test.lua index 72af591..cc11b32 100644 --- a/lua/Trans/util/test/test.lua +++ b/lua/Trans/util/test/test.lua @@ -1,18 +1,12 @@ -local a = { - 'test1', - 'test2', - 'test3' -} +-- 记录开始时间 +local starttime = os.clock(); --> os.clock()用法 - -local function test(tmp) - tmp = { - 'bbbbbb' - } +for i = 1, 10, 2 do + print(i) end -test(a) -for i, v in ipairs(a) do - print(v) -end +-- 记录结束时间 +local endtime = os.clock(); --> os.clock()用法 +print(string.format("end time : %.4f", endtime)); +print(string.format("cost time : %.4f", endtime - starttime));