Compare commits

4 Commits
v0.0.1 ... main

Author SHA1 Message Date
cd71c38c1a 新增:whichkey插件 2025-07-16 16:09:06 +08:00
dbb3f1db68 基础插件管理界面与命令 2025-07-15 13:54:48 +08:00
446b801c05 基础插件管理 2025-07-14 18:31:16 +08:00
b2b778feba 新增:事件-复制时高亮 2025-07-14 11:09:10 +08:00
13 changed files with 448 additions and 1 deletions

3
.gitignore vendored
View File

@ -19,3 +19,6 @@ tags
# Persistent undo
[._]*.un~
# Plug
plugged

View File

@ -2,6 +2,11 @@ vim9script
export def Init(tui_home: string)
tui#core#cached#Set("tui.home", tui_home)
# <20><><EFBFBD>ز<EFBFBD><EFBFBD><EFBFBD>
tui#core#plug#Init(expand(tui_home .. "/plugged"))
# <20><><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD>
tui#core#layer#Init(expand(tui_home .. "/layers"))
# <20><><EFBFBD>ز<EFBFBD><EFBFBD><EFBFBD>
tui#core#plug#Hook()
enddef

247
autoload/tui/core/plug.vim Normal file
View File

@ -0,0 +1,247 @@
vim9script
# tui_plug: {
# home: 'xxx',
# registed: {
# plug_id: {
# id: 'xxx/yyy',
# deps: [],
# path: '/path/to/plug/installed'
# }
# },
# enabled: [],
# disabled: []
# }
export def Init(plug_home: string = v:none)
if !tui#core#cached#Has("tui.plug.home")
# <20><>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>
const tui_home = tui#core#cached#Get("tui.home")
var tui_plug_home = plug_home
if empty(tui_plug_home) || empty(split(tui_plug_home))
tui_plug_home = tui#util#path#Join(tui_home, "plugged")
endif
tui#core#cached#Set("tui.plug.home", tui_plug_home)
# <20><>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
tui#core#cached#Set('tui.plug.registed', {})
tui#core#cached#Set('tui.plug.enabled', [])
tui#core#cached#Set('tui.plug.disabled', [])
command! -nargs=0 -bar TuiPlug call tui#core#plug#Install()
endif
enddef
export def IsPlugRegisted(plug_id: string): bool
assert_true(!empty(plug_id) && !empty(split(plug_id)))
var tui_plug_registed = tui#core#cached#Get('tui.plug.registed', {})
return has_key(tui_plug_registed, plug_id)
enddef
export def IsPlugEnabled(plug_id: string): bool
assert_true(!empty(plug_id) && !empty(split(plug_id)))
var tui_plug_registed = tui#core#cached#Get('tui.plug.registed', {})
var tui_plug_enabled = tui#core#cached#Get('tui.plug.enabled', [])
return has_key(tui_plug_registed, plug_id) && index(tui_plug_enabled, plug_id) >= 0
enddef
export def GetPlug(plug_id: string): dict<any>
assert_true(!empty(plug_id) && !empty(split(plug_id)))
var tui_plug_registed = tui#core#cached#Get('tui.plug.registed', {})
return tui_plug_registed[plug_id]
enddef
export def Regist(plug_id: string, plug_options: dict<any> = {})
assert_true(!empty(plug_id) && !empty(split(plug_id)))
if IsPlugRegisted(plug_id)
return
endif
var tui_plug_registed = tui#core#cached#Get('tui.plug.registed', {})
var plug_info = Parse(plug_id, plug_options)
var plug_deps = []
for plug_dep_info in plug_info['deps']
var plug_dep_id = plug_dep_info['id']
if !has_key(tui_plug_registed, plug_dep_id)
tui_plug_registed[plug_dep_id] = plug_dep_info
endif
add(plug_deps, plug_dep_id)
endfor
plug_info['deps'] = plug_deps
tui_plug_registed[plug_id] = plug_info
tui#core#cached#Set('tui.plug.registed', tui_plug_registed)
enddef
def Parse(plug_id: string, plug_options: dict<any> = {}): dict<any>
assert_true(!empty(plug_id) && !empty(split(plug_id)))
if IsPlugRegisted(plug_id)
return GetPlug(plug_id)
endif
var plug_info = {}
# id: string
plug_info['id'] = plug_id
# path: string
const plug_path = tui#util#path#Join(tui#core#cached#Get('tui.plug.home'), split(plug_id, '/')[-1])
plug_info['path'] = plug_path
# enabled: bool / func
var plug_enabled = v:true
if has_key(plug_options, 'enabled')
var Tmp_plug_enabled = plug_options['enabled']
assert_true(type(Tmp_plug_enabled) == v:t_number || type(Tmp_plug_enabled) == v:t_bool || type(Tmp_plug_enabled) == v:t_func)
if type(Tmp_plug_enabled) == v:t_number
assert_true(Tmp_plug_enabled == 0 || Tmp_plug_enabled == 1)
if Tmp_plug_enabled != 1
plug_enabled = v:false
endif
elseif type(Tmp_plug_enabled) == v:t_bool
plug_enabled = Tmp_plug_enabled
else
plug_enabled = Tmp_plug_enabled()
endif
endif
plug_info['enabled'] = plug_enabled
# config: func
if has_key(plug_options, 'config')
assert_true(type(plug_options['config']) == v:t_func)
plug_info['config'] = plug_options['config']
endif
# deps
var plug_deps = []
if has_key(plug_options, 'deps')
var tmp_plug_deps = plug_options['deps']
assert_true(type(tmp_plug_deps) == v:t_list)
for tmp_plug_dep in tmp_plug_deps
assert_true(type(tmp_plug_dep) == v:t_string || type(tmp_plug_dep) == v:t_dict)
if type(tmp_plug_dep) == v:t_string
var tmp_plug_dep_id = tmp_plug_dep
var tmp_plug_dep_info = Parse(tmp_plug_dep_id, {})
add(plug_deps, tmp_plug_dep_info)
else
assert_true(has_key(tmp_plug_dep, 'id'))
var tmp_plug_dep_id = tmp_plug_dep['id']
var tmp_plug_dep_info = Parse(tmp_plug_dep_id, tmp_plug_dep)
add(plug_deps, tmp_plug_dep_info)
endif
endfor
endif
plug_info['deps'] = plug_deps
return plug_info
enddef
export def Install()
var tui_plug_registed = tui#core#cached#Get('tui.plug.registed', {})
var tui_plugs = []
var tui_plug_ids = keys(tui_plug_registed)
if !empty(tui_plug_registed)
var index = 1
for plug_id in tui_plug_ids
var item = '' .. index .. ' '
var plug_info = tui_plug_registed[plug_id]
var plug_state = ' '
if isdirectory(plug_info['path'])
plug_state = 'D'
if plug_info['enabled']
plug_state = 'E'
endif
endif
item = item .. '[' .. plug_state .. '] '
item = item .. split(plug_id, '/')[-1]
add(tui_plugs, item)
index += 1
endfor
endif
var minwidth = 60
popup_menu(tui_plugs, {
title: 'Plugins - ' .. tui#core#cached#Get('tui.plug.home'),
minwidth: minwidth,
callback: (_, index) => {
if index > 0
var plug_id = tui_plug_ids[index - 1]
var plug_info = tui_plug_registed[plug_id]
if !isdirectory(plug_info['path'])
var cmds = 'git clone https://github.com/' .. plug_id .. ' ' .. plug_info['path']
echo "executing " .. cmds
system(cmds)
echo "install success"
else
echo 'plug: ' .. plug_id .. ' already install.'
endif
endif
},
filter: (id, key) => {
if key == ' '
return false
elseif key == 'i' || key == 'I'
return popup_filter_menu(id, ' ')
else
return popup_filter_menu(id, key)
endif
}
})
enddef
export def Hook()
var tui_plug_registed = tui#core#cached#Get('tui.plug.registed', {})
var tui_plug_enabled = tui#core#cached#Get('tui.plug.enabled', [])
var tui_plug_disabled = tui#core#cached#Get('tui.plug.disabled', [])
for plug_id in keys(tui_plug_registed)
var plug_info = tui_plug_registed[plug_id]
if Hook_loading(plug_id, plug_info)
if index(tui_plug_enabled, plug_id) == -1
add(tui_plug_enabled, plug_id)
endif
else
if index(tui_plug_disabled, plug_id) == -1
add(tui_plug_disabled, plug_id)
endif
endif
endfor
tui#core#cached#Set('tui.plug.enabled', tui_plug_enabled)
tui#core#cached#Set('tui.plug.disabled', tui_plug_disabled)
enddef
def Hook_loading(plug_id: string, plug_info: dict<any>): bool
var plug_path = plug_info['path']
var plug_enabled = plug_info['enabled']
if !plug_enabled || !isdirectory(plug_path)
return v:false
endif
var dep_loaded = v:true
for plug_dep_id in plug_info['deps']
var plug_dep = GetPlug(plug_dep_id)
if !Hook_loading(plug_dep_id, plug_dep)
dep_loaded = v:false
break
endif
endfor
if dep_loaded
execute('set rtp+=' .. plug_path)
if has_key(plug_info, 'config')
plug_info['config']()
endif
endif
return dep_loaded
enddef

View File

@ -0,0 +1,13 @@
vim9script
export def IsOsWindows(): bool
return has('win32')
enddef
export def GetFileSeparator(): string
return IsOsWindows() ? '\' : '/'
enddef
export def GetLineSeparator(): string
return IsOsWindows() ? "\r\n" : "\n"
enddef

View File

@ -0,0 +1,22 @@
vim9script
import autoload "tui/util/osinfo.vim"
export def GetFileSeparator(): string
return osinfo.GetFileSeparator()
enddef
export def Normalize(path: string): string
assert_true(!empty(path) && !empty(split(path)))
const path_joined = join(filter(split(path, '[/\\]'), '!empty(split(v:val))'), GetFileSeparator())
return expand(match(path, '^[/\\]') != -1 ? (GetFileSeparator() .. path_joined) : path_joined)
enddef
export def Join(parent: string, ...subs: list<string>): string
assert_true(!empty(parent) && !empty(split(parent)))
const path_joined = join(extend([parent], subs), GetFileSeparator())
return Normalize(path_joined)
enddef

View File

@ -0,0 +1,102 @@
vim9script noclear
# Highlight yanked text.
# Maintainer: Ubaldo Tiberi
# License: BSD3-Clause
# GetLatestVimScripts: 6075 1 :AutoInstall: hlyanked.vim
if !has('vim9script') || v:version < 900
finish
endif
if exists('g:hlyanked_loaded')
finish
endif
g:hlyanked_loaded = 1
if !exists('g:hlyanked_hlgroup')
g:hlyanked_hlgroup = 'Visual'
endif
if !exists('g:hlyanked_timeout')
g:hlyanked_timeout = 400
endif
if !exists('g:hlyanked_save_yanks')
g:hlyanked_save_yanks = true
endif
# ----------------------------------------------------
# The real deal follows
var timer_id = -1
var match_id = -1
def HighlightYanked()
# Remove leftover timers and highlights
KillHighlight()
# Get extremes of yanking: start = (l0, c0), end = (l1, c1)
var l0 = line("'[")
var c0 = col("'[")
var l1 = line("']")
var delta = 0
if l0 == l1
delta = len(v:event.regcontents[-1]) - (col("']") - c0)
endif
var c1 = col("']") + delta
# For understanding the following regex read :h \% and mind that \_.* are
# all characters including new lines.
# The regex reads:
# 'Take all characters, including newlines, from (l0,c0) to (l1,c1)'
var match_pattern = $'\%{l0}l\%{c0}c\_.*\%{l1}l\%{c1}c'
match_id = matchadd(g:hlyanked_hlgroup, match_pattern)
timer_id = timer_start(g:hlyanked_timeout, 'RemoveHighlight')
enddef
def RemoveHighlight(timer: number)
if match_id != -1
matchdelete(match_id)
match_id = -1
endif
enddef
def StopTimer(timer: number)
if timer_id != -1
timer_stop(timer_id)
timer_id = -1
endif
enddef
def KillHighlight()
StopTimer(timer_id)
RemoveHighlight(match_id)
enddef
augroup HighlightYanked
autocmd!
autocmd TextYankPost * if !v:event.visual && v:event.operator == 'y' && !empty(v:event.regtype)
| HighlightYanked()
| endif
augroup END
augroup KillHighlight
autocmd!
autocmd WinLeave * KillHighlight()
augroup END
def ShiftRegisters()
for ii in [8, 7, 6, 5, 4, 3, 2, 1, 0]
setreg(string(ii + 1), getreg(string(ii)))
endfor
enddef
if g:hlyanked_save_yanks
augroup YankShiftRegisters
autocmd!
autocmd TextYankPost * if v:event.operator == 'y' | ShiftRegisters() | endif
augroup END
endif

View File

@ -1,3 +1,8 @@
vim9script
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
TuiLayer base.options
# <20><>չ<EFBFBD><EFBFBD><EFBFBD>ã<EFBFBD><EFBFBD>¼<EFBFBD>
TuiLayer base.events

View File

@ -2,4 +2,4 @@ vim9script
TuiLayer base
# TuiLayer plug
TuiLayer plug

View File

@ -0,0 +1,4 @@
vim9script
tui#core#plug#Regist('flazz/vim-colorschemes')

9
layers/plug/init.vim Normal file
View File

@ -0,0 +1,9 @@
vim9script
TuiLayer plug.colorscheme
TuiLayer plug.nerdtree
TuiLayer plug.poplar
TuiLayer plug.whichkey

13
layers/plug/nerdtree.vim Normal file
View File

@ -0,0 +1,13 @@
vim9script
tui#core#plug#Regist('preservim/nerdtree', {
deps: [
'ryanoasis/vim-devicons',
'tiagofumo/vim-nerdtree-syntax-highlight',
],
config: () => {
final g:NERDTreeFileLines = 1
nnoremap <SPACE>vf :NERDTreeToggle<CR>
}
})

4
layers/plug/poplar.vim Normal file
View File

@ -0,0 +1,4 @@
vim9script
tui#core#plug#Regist('ycm/poplar.vim')

20
layers/plug/whichkey.vim Normal file
View File

@ -0,0 +1,20 @@
vim9script
const g:which_key_map = {
f: {
name: 'File',
o: ['Poplar', 'open']
},
v: {
name: 'View',
f: [":NERDTreeToggle", "nerdtree"]
}
}
tui#core#plug#Regist('liuchengxu/vim-which-key', {
config: () => {
which_key#register('<Space>', "g:which_key_map")
nnoremap <silent> <Space> :WhichKey '<Space>'<CR>
}
})