mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
Compare commits
51 Commits
493804eece
...
8e44cc44d2
Author | SHA1 | Date | |
---|---|---|---|
|
8e44cc44d2 | ||
|
a0d8c2b86e | ||
|
549c00c791 | ||
|
23dcd7cd73 | ||
|
3f15e57b26 | ||
|
a9031cc4a6 | ||
|
5e7933693b | ||
|
f408603f4f | ||
|
78b8510933 | ||
|
057d27a9d6 | ||
|
5792546777 | ||
|
3b54adc6c6 | ||
|
325d349f9d | ||
|
5191a11d66 | ||
|
90585e47fe | ||
|
f2173b1aa2 | ||
|
67d6b6f27e | ||
|
8512f669f0 | ||
|
755512ed60 | ||
|
4c23b83456 | ||
|
5284a2a793 | ||
|
b9b408a56c | ||
|
8654a97006 | ||
|
057314345a | ||
|
931b3f0693 | ||
|
e78ed6f347 | ||
|
66df7c3413 | ||
|
66715e9ce3 | ||
|
9ebcc1131b | ||
|
8985e9292b | ||
|
0e110d180f | ||
|
7d07185d99 | ||
|
729fa8ea7d | ||
|
eae703b044 | ||
|
867ee3a892 | ||
|
65f6a6b76b | ||
|
209fc88c8f | ||
|
9247062387 | ||
|
d9a3363896 | ||
|
0371a006d0 | ||
|
34cf5a3fee | ||
|
f40e576ebc | ||
|
e76b0f2990 | ||
|
d21977db34 | ||
|
b3119cba2b | ||
|
027ee9ebed | ||
|
17e1d68529 | ||
|
95c2958324 | ||
|
bcd5311279 | ||
|
7e63013bd3 | ||
|
0db7e59325 |
18
.github/workflows/backport.yml
vendored
18
.github/workflows/backport.yml
vendored
@ -26,3 +26,21 @@ jobs:
|
||||
pull_title: "${pull_title}"
|
||||
label_pattern: "^ci:backport ([^ ]+)$"
|
||||
github_token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Create failed backport label
|
||||
if: ${{ steps.backport.outputs.was_successful == 'false' }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['needs:backport']
|
||||
})
|
||||
|
||||
- name: Enable automerge
|
||||
if: ${{ steps.backport.outputs.was_successful == 'true' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh pr merge --rebase --auto ${{ steps.backport.outputs.created_pull_numbers }}
|
||||
|
2
.github/workflows/reviewers_add.yml
vendored
2
.github/workflows/reviewers_add.yml
vendored
@ -5,7 +5,7 @@ on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
request-reviewer:
|
||||
if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false
|
||||
if: github.event.pull_request.state == 'open' && github.event.pull_request.draft == false && !endsWith(github.actor, '[bot]')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
@ -7,8 +7,8 @@ LUAJIT_SHA256 2b5514bd6a6573cb6111b43d013e952cbaf46762d14ebe26c872ddb80b5a84e0
|
||||
LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz
|
||||
LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
|
||||
|
||||
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/ab28a2ddb86a144194a798bcf1fef1ab4f981ab7.tar.gz
|
||||
UNIBILIUM_SHA256 7c0386fc48452632868e0dcb8e7ed140d67a3da79e39ceee54c1ba7a495ad625
|
||||
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/v2.1.2.tar.gz
|
||||
UNIBILIUM_SHA256 370ecb07fbbc20d91d1b350c55f1c806b06bf86797e164081ccc977fc9b3af7a
|
||||
|
||||
LUV_URL https://github.com/luvit/luv/releases/download/1.48.0-2/luv-1.48.0-2.tar.gz
|
||||
LUV_SHA256 2c3a1ddfebb4f6550293a40ee789f7122e97647eede51511f57203de48c03b7a
|
||||
|
@ -23,6 +23,7 @@
|
||||
" 2024 Aug 15 by Vim Project: style changes, prevent E121 (#15501)
|
||||
" 2024 Aug 22 by Vim Project: fix mf-selection highlight (#15551)
|
||||
" 2024 Aug 22 by Vim Project: adjust echo output of mx command (#15550)
|
||||
" 2024 Sep 15 by Vim Project: more strict confirmation dialog (#15680)
|
||||
" }}}
|
||||
" Former Maintainer: Charles E Campbell
|
||||
" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
|
||||
@ -11275,7 +11276,7 @@ fun! s:NetrwLocalRm(path) range
|
||||
let ok= s:NetrwLocalRmFile(a:path,fname,all)
|
||||
if ok =~# 'q\%[uit]' || ok == "no"
|
||||
break
|
||||
elseif ok =~# 'a\%[ll]'
|
||||
elseif ok =~# '^a\%[ll]$'
|
||||
let all= 1
|
||||
endif
|
||||
endfor
|
||||
@ -11304,7 +11305,7 @@ fun! s:NetrwLocalRm(path) range
|
||||
let ok= s:NetrwLocalRmFile(a:path,curword,all)
|
||||
if ok =~# 'q\%[uit]' || ok == "no"
|
||||
break
|
||||
elseif ok =~# 'a\%[ll]'
|
||||
elseif ok =~# '^a\%[ll]$'
|
||||
let all= 1
|
||||
endif
|
||||
let ctr= ctr + 1
|
||||
@ -11351,12 +11352,12 @@ fun! s:NetrwLocalRmFile(path,fname,all)
|
||||
" call Decho("response: ok<".ok.">",'~'.expand("<slnum>"))
|
||||
let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
|
||||
" call Decho("response: ok<".ok."> (after sub)",'~'.expand("<slnum>"))
|
||||
if ok =~# 'a\%[ll]'
|
||||
if ok =~# '^a\%[ll]$'
|
||||
let all= 1
|
||||
endif
|
||||
endif
|
||||
|
||||
if all || ok =~# 'y\%[es]' || ok == ""
|
||||
if all || ok =~# '^y\%[es]$' || ok == ""
|
||||
let ret= s:NetrwDelete(rmfile)
|
||||
" call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>"))
|
||||
endif
|
||||
@ -11372,13 +11373,13 @@ fun! s:NetrwLocalRmFile(path,fname,all)
|
||||
if ok == ""
|
||||
let ok="no"
|
||||
endif
|
||||
if ok =~# 'a\%[ll]'
|
||||
if ok =~# '^a\%[ll]$'
|
||||
let all= 1
|
||||
endif
|
||||
endif
|
||||
let rmfile= substitute(rmfile,'[\/]$','','e')
|
||||
|
||||
if all || ok =~# 'y\%[es]' || ok == ""
|
||||
if all || ok =~# '^y\%[es]$' || ok == ""
|
||||
if delete(rmfile,"rf")
|
||||
call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103)
|
||||
endif
|
||||
|
@ -2559,7 +2559,7 @@ vim.ui.input({opts}, {on_confirm}) *vim.ui.input()*
|
||||
typed (it might be an empty string if nothing was
|
||||
entered), or `nil` if the user aborted the dialog.
|
||||
|
||||
vim.ui.open({path}) *vim.ui.open()*
|
||||
vim.ui.open({path}, {opt}) *vim.ui.open()*
|
||||
Opens `path` with the system default handler (macOS `open`, Windows
|
||||
`explorer.exe`, Linux `xdg-open`, …), or returns (but does not show) an
|
||||
error message on failure.
|
||||
@ -2570,6 +2570,8 @@ vim.ui.open({path}) *vim.ui.open()*
|
||||
-- Asynchronous.
|
||||
vim.ui.open("https://neovim.io/")
|
||||
vim.ui.open("~/path/to/file")
|
||||
-- Use the "osurl" command to handle the path or URL.
|
||||
vim.ui.open("gh#neovim/neovim!29490", { cmd = { 'osurl' } })
|
||||
-- Synchronous (wait until the process exits).
|
||||
local cmd, err = vim.ui.open("$VIMRUNTIME")
|
||||
if cmd then
|
||||
@ -2579,6 +2581,8 @@ vim.ui.open({path}) *vim.ui.open()*
|
||||
|
||||
Parameters: ~
|
||||
• {path} (`string`) Path or URL to open
|
||||
• {opt} (`{ cmd?: string[] }?`) Options
|
||||
• cmd string[]|nil Command used to open the path or URL.
|
||||
|
||||
Return (multiple): ~
|
||||
(`vim.SystemObj?`) Command object, or nil if not found.
|
||||
|
@ -194,7 +194,10 @@ TUI
|
||||
|
||||
UI
|
||||
|
||||
• TODO
|
||||
• |vim.ui.open()| (by default bound to |gx|) accepts an `opt.cmd` parameter
|
||||
which controls the tool used to open the given path or URL. If you want to
|
||||
globally set this, you can override vim.ui.open using the same approach
|
||||
described at |vim.paste()|.
|
||||
|
||||
==============================================================================
|
||||
CHANGED FEATURES *news-changed*
|
||||
|
@ -1683,6 +1683,12 @@ In order to highlight nested parens with different colors, define colors for
|
||||
or >
|
||||
:hi javaParen ctermfg=blue guifg=#0000ff
|
||||
|
||||
Certain modifiers are incompatible with each other, e.g. `abstract` and
|
||||
`final`: >
|
||||
:syn list javaConceptKind
|
||||
and can be differently highlighted as a group than other modifiers with >
|
||||
:hi link javaConceptKind NonText
|
||||
|
||||
If you notice highlighting errors while scrolling backwards, which are fixed
|
||||
when redrawing with CTRL-L, try setting the "g:java_minlines" variable to
|
||||
a larger number: >
|
||||
|
@ -9,8 +9,8 @@
|
||||
local function get_commentstring(ref_position)
|
||||
local buf_cs = vim.bo.commentstring
|
||||
|
||||
local has_ts_parser, ts_parser = pcall(vim.treesitter.get_parser)
|
||||
if not has_ts_parser then
|
||||
local ts_parser = vim.treesitter._get_parser()
|
||||
if not ts_parser then
|
||||
return buf_cs
|
||||
end
|
||||
|
||||
|
@ -213,7 +213,6 @@ end
|
||||
--- Default menus
|
||||
do
|
||||
--- Right click popup menu
|
||||
local function def_menu(ctx)
|
||||
vim.cmd([[
|
||||
anoremenu PopUp.Go\ to\ definition <Cmd>lua vim.lsp.buf.definition()<CR>
|
||||
amenu PopUp.Open\ in\ web\ browser gx
|
||||
@ -229,7 +228,10 @@ do
|
||||
inoremenu PopUp.Select\ All <C-Home><C-O>VG
|
||||
anoremenu PopUp.-2- <Nop>
|
||||
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
|
||||
]])
|
||||
|
||||
local function enable_ctx_menu(ctx)
|
||||
vim.cmd([[
|
||||
amenu disable PopUp.Go\ to\ definition
|
||||
amenu disable PopUp.Open\ in\ web\ browser
|
||||
]])
|
||||
@ -240,7 +242,6 @@ do
|
||||
vim.cmd([[anoremenu enable PopUp.Go\ to\ definition]])
|
||||
end
|
||||
end
|
||||
def_menu()
|
||||
|
||||
local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim_popupmenu', {})
|
||||
vim.api.nvim_create_autocmd('MenuPopup', {
|
||||
@ -252,7 +253,7 @@ do
|
||||
local urls = require('vim.ui')._get_urls()
|
||||
local url = vim.startswith(urls[1], 'http')
|
||||
local ctx = url and 'url' or (vim.lsp.get_clients({ bufnr = 0 })[1] and 'lsp' or nil)
|
||||
def_menu(ctx)
|
||||
enable_ctx_menu(ctx)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
14
runtime/lua/vim/_meta/vimfn.lua
generated
14
runtime/lua/vim/_meta/vimfn.lua
generated
@ -6703,7 +6703,7 @@ function vim.fn.printf(fmt, expr1) end
|
||||
--- If the buffer doesn't exist or isn't a prompt buffer, an empty
|
||||
--- string is returned.
|
||||
---
|
||||
--- @param buf any
|
||||
--- @param buf integer|string
|
||||
--- @return any
|
||||
function vim.fn.prompt_getprompt(buf) end
|
||||
|
||||
@ -6738,8 +6738,8 @@ function vim.fn.prompt_getprompt(buf) end
|
||||
--- endfunc
|
||||
--- call prompt_setcallback(bufnr(), function('s:TextEntered'))
|
||||
---
|
||||
--- @param buf any
|
||||
--- @param expr any
|
||||
--- @param buf integer|string
|
||||
--- @param expr string|function
|
||||
--- @return any
|
||||
function vim.fn.prompt_setcallback(buf, expr) end
|
||||
|
||||
@ -6751,8 +6751,8 @@ function vim.fn.prompt_setcallback(buf, expr) end
|
||||
--- mode. Without setting a callback Vim will exit Insert mode,
|
||||
--- as in any buffer.
|
||||
---
|
||||
--- @param buf any
|
||||
--- @param expr any
|
||||
--- @param buf integer|string
|
||||
--- @param expr string|function
|
||||
--- @return any
|
||||
function vim.fn.prompt_setinterrupt(buf, expr) end
|
||||
|
||||
@ -6763,8 +6763,8 @@ function vim.fn.prompt_setinterrupt(buf, expr) end
|
||||
--- call prompt_setprompt(bufnr(''), 'command: ')
|
||||
--- <
|
||||
---
|
||||
--- @param buf any
|
||||
--- @param text any
|
||||
--- @param buf integer|string
|
||||
--- @param text string
|
||||
--- @return any
|
||||
function vim.fn.prompt_setprompt(buf, text) end
|
||||
|
||||
|
@ -280,12 +280,7 @@ local extension = {
|
||||
cfi = 'cf',
|
||||
hgrc = 'cfg',
|
||||
cfg = detect.cfg,
|
||||
cfG = detect.cfg,
|
||||
cFg = detect.cfg,
|
||||
cFG = detect.cfg,
|
||||
Cfg = detect.cfg,
|
||||
CfG = detect.cfg,
|
||||
CFg = detect.cfg,
|
||||
CFG = detect.cfg,
|
||||
chf = 'ch',
|
||||
chai = 'chaiscript',
|
||||
@ -370,12 +365,7 @@ local extension = {
|
||||
drt = 'dart',
|
||||
ds = 'datascript',
|
||||
dat = detect.dat,
|
||||
daT = detect.dat,
|
||||
dAt = detect.dat,
|
||||
dAT = detect.dat,
|
||||
Dat = detect.dat,
|
||||
DaT = detect.dat,
|
||||
DAt = detect.dat,
|
||||
DAT = detect.dat,
|
||||
dcd = 'dcd',
|
||||
decl = detect.decl,
|
||||
@ -658,12 +648,7 @@ local extension = {
|
||||
kt = 'kotlin',
|
||||
ktm = 'kotlin',
|
||||
sub = 'krl',
|
||||
suB = 'krl',
|
||||
sUb = 'krl',
|
||||
sUB = 'krl',
|
||||
Sub = 'krl',
|
||||
SuB = 'krl',
|
||||
SUb = 'krl',
|
||||
SUB = 'krl',
|
||||
ks = 'kscript',
|
||||
k = 'kwt',
|
||||
@ -699,12 +684,7 @@ local extension = {
|
||||
lite = 'lite',
|
||||
livemd = 'livebook',
|
||||
log = detect.log,
|
||||
loG = detect.log,
|
||||
lOg = detect.log,
|
||||
lOG = detect.log,
|
||||
Log = detect.log,
|
||||
LoG = detect.log,
|
||||
LOg = detect.log,
|
||||
LOG = detect.log,
|
||||
lgt = 'logtalk',
|
||||
lotos = 'lotos',
|
||||
@ -773,12 +753,7 @@ local extension = {
|
||||
mmp = 'mmp',
|
||||
mms = detect.mms,
|
||||
mod = detect.mod,
|
||||
moD = detect.mod,
|
||||
mOd = detect.mod,
|
||||
mOD = detect.mod,
|
||||
Mod = detect.mod,
|
||||
MoD = detect.mod,
|
||||
MOd = detect.mod,
|
||||
MOD = detect.mod,
|
||||
DEF = 'modula2',
|
||||
m3 = 'modula3',
|
||||
@ -948,12 +923,7 @@ local extension = {
|
||||
ih = 'ppwiz',
|
||||
action = 'privoxy',
|
||||
prg = detect.prg,
|
||||
prG = detect.prg,
|
||||
pRg = detect.prg,
|
||||
pRG = detect.prg,
|
||||
Prg = detect.prg,
|
||||
PrG = detect.prg,
|
||||
PRg = detect.prg,
|
||||
PRG = detect.prg,
|
||||
pc = 'proc',
|
||||
pdb = 'prolog',
|
||||
@ -1133,12 +1103,7 @@ local extension = {
|
||||
sqr = 'sqr',
|
||||
nut = 'squirrel',
|
||||
src = detect.src,
|
||||
srC = detect.src,
|
||||
sRc = detect.src,
|
||||
sRC = detect.src,
|
||||
Src = detect.src,
|
||||
SrC = detect.src,
|
||||
SRc = detect.src,
|
||||
SRC = detect.src,
|
||||
s28 = 'srec',
|
||||
s37 = 'srec',
|
||||
@ -1168,12 +1133,7 @@ local extension = {
|
||||
swig = 'swig',
|
||||
swg = 'swig',
|
||||
sys = detect.sys,
|
||||
syS = detect.sys,
|
||||
sYs = detect.sys,
|
||||
sYS = detect.sys,
|
||||
Sys = detect.sys,
|
||||
SyS = detect.sys,
|
||||
SYs = detect.sys,
|
||||
SYS = detect.sys,
|
||||
svh = 'systemverilog',
|
||||
sv = 'systemverilog',
|
||||
|
@ -447,11 +447,9 @@ function M.document_symbol(opts)
|
||||
request_with_opts(ms.textDocument_documentSymbol, params, opts)
|
||||
end
|
||||
|
||||
--- @param call_hierarchy_items lsp.CallHierarchyItem[]?
|
||||
--- @param call_hierarchy_items lsp.CallHierarchyItem[]
|
||||
--- @return lsp.CallHierarchyItem?
|
||||
local function pick_call_hierarchy_item(call_hierarchy_items)
|
||||
if not call_hierarchy_items then
|
||||
return
|
||||
end
|
||||
if #call_hierarchy_items == 1 then
|
||||
return call_hierarchy_items[1]
|
||||
end
|
||||
@ -476,7 +474,7 @@ local function call_hierarchy(method)
|
||||
vim.notify(err.message, vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
if not result then
|
||||
if not result or vim.tbl_isempty(result) then
|
||||
vim.notify('No item resolved', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
@ -154,7 +154,7 @@ end
|
||||
---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16
|
||||
---@return integer byte (utf-8) index of `encoding` index `index` in `line`
|
||||
function M._str_byteindex_enc(line, index, encoding)
|
||||
local len = vim.fn.strlen(line)
|
||||
local len = #line
|
||||
if index > len then
|
||||
-- LSP spec: if character > line length, default to the line length.
|
||||
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
|
||||
@ -167,7 +167,7 @@ function M._str_byteindex_enc(line, index, encoding)
|
||||
if index then
|
||||
return index
|
||||
else
|
||||
return #line
|
||||
return len
|
||||
end
|
||||
elseif encoding == 'utf-16' then
|
||||
return vim.str_byteindex(line, index, true)
|
||||
|
@ -74,14 +74,14 @@ end
|
||||
|
||||
--- Returns the parser for a specific buffer and attaches it to the buffer
|
||||
---
|
||||
--- If needed, this will create the parser.
|
||||
--- If needed, this will create the parser. If no parser can be found or created, returns `nil`.
|
||||
---
|
||||
---@param bufnr (integer|nil) Buffer the parser should be tied to (default: current buffer)
|
||||
---@param lang (string|nil) Language of this parser (default: from buffer filetype)
|
||||
---@param opts (table|nil) Options to pass to the created language tree
|
||||
---
|
||||
---@return vim.treesitter.LanguageTree object to use for parsing
|
||||
function M.get_parser(bufnr, lang, opts)
|
||||
---@return vim.treesitter.LanguageTree? object to use for parsing, or `nil` if not found
|
||||
function M._get_parser(bufnr, lang, opts)
|
||||
opts = opts or {}
|
||||
|
||||
if bufnr == nil or bufnr == 0 then
|
||||
@ -94,18 +94,14 @@ function M.get_parser(bufnr, lang, opts)
|
||||
|
||||
if not valid_lang(lang) then
|
||||
if not parsers[bufnr] then
|
||||
error(
|
||||
string.format(
|
||||
'There is no parser available for buffer %d and one could not be'
|
||||
.. ' created because lang could not be determined. Either pass lang'
|
||||
.. ' or set the buffer filetype',
|
||||
bufnr
|
||||
)
|
||||
)
|
||||
return nil
|
||||
end
|
||||
elseif parsers[bufnr] == nil or parsers[bufnr]:lang() ~= lang then
|
||||
assert(lang, 'lang should be valid')
|
||||
parsers[bufnr] = M._create_parser(bufnr, lang, opts)
|
||||
local parser = vim.F.npcall(M._create_parser, bufnr, lang, opts)
|
||||
if not parser then
|
||||
return nil
|
||||
end
|
||||
parsers[bufnr] = parser
|
||||
end
|
||||
|
||||
parsers[bufnr]:register_cbs(opts.buf_attach_cbs)
|
||||
@ -113,6 +109,29 @@ function M.get_parser(bufnr, lang, opts)
|
||||
return parsers[bufnr]
|
||||
end
|
||||
|
||||
--- Returns the parser for a specific buffer and attaches it to the buffer
|
||||
---
|
||||
--- If needed, this will create the parser.
|
||||
---
|
||||
---@param bufnr (integer|nil) Buffer the parser should be tied to (default: current buffer)
|
||||
---@param lang (string|nil) Language of this parser (default: from buffer filetype)
|
||||
---@param opts (table|nil) Options to pass to the created language tree
|
||||
---
|
||||
---@return vim.treesitter.LanguageTree object to use for parsing
|
||||
function M.get_parser(bufnr, lang, opts)
|
||||
-- TODO(ribru17): Remove _get_parser and move that logic back here once the breaking function
|
||||
-- signature change is acceptable.
|
||||
local parser = M._get_parser(bufnr, lang, opts)
|
||||
if not parser then
|
||||
vim.notify_once(
|
||||
'WARNING: vim.treesitter.get_parser will return nil instead of raising an error in Neovim 0.12',
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
error('Parser not found.')
|
||||
end
|
||||
return parser
|
||||
end
|
||||
|
||||
--- Returns a string parser
|
||||
---
|
||||
---@param str string Text to parse
|
||||
@ -386,7 +405,7 @@ function M.get_node(opts)
|
||||
|
||||
local ts_range = { row, col, row, col }
|
||||
|
||||
local root_lang_tree = M.get_parser(bufnr, opts.lang)
|
||||
local root_lang_tree = M._get_parser(bufnr, opts.lang)
|
||||
if not root_lang_tree then
|
||||
return
|
||||
end
|
||||
@ -419,7 +438,11 @@ end
|
||||
---@param lang (string|nil) Language of the parser (default: from buffer filetype)
|
||||
function M.start(bufnr, lang)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
local parser = M.get_parser(bufnr, lang)
|
||||
local parser = M._get_parser(bufnr, lang)
|
||||
if not parser then
|
||||
vim.notify('No parser for the given buffer.', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
M.highlighter.new(parser)
|
||||
end
|
||||
|
||||
|
@ -114,7 +114,7 @@ local function compute_folds_levels(bufnr, info, srow, erow, parse_injections)
|
||||
srow = srow or 0
|
||||
erow = erow or api.nvim_buf_line_count(bufnr)
|
||||
|
||||
local parser = ts.get_parser(bufnr)
|
||||
local parser = assert(ts._get_parser(bufnr))
|
||||
|
||||
parser:parse(parse_injections and { srow, erow } or nil)
|
||||
|
||||
@ -392,7 +392,7 @@ function M.foldexpr(lnum)
|
||||
lnum = lnum or vim.v.lnum
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
|
||||
local parser = vim.F.npcall(ts.get_parser, bufnr)
|
||||
local parser = ts._get_parser(bufnr)
|
||||
if not parser then
|
||||
return '0'
|
||||
end
|
||||
|
@ -172,7 +172,7 @@ function M.lint(buf, opts)
|
||||
--- @type (table|nil)
|
||||
local parser_info = vim.F.npcall(vim.treesitter.language.inspect, lang)
|
||||
|
||||
local parser = vim.treesitter.get_parser(buf)
|
||||
local parser = assert(vim.treesitter._get_parser(buf), 'query parser not found.')
|
||||
parser:parse()
|
||||
parser:for_each_tree(function(tree, ltree)
|
||||
if ltree:lang() == 'query' then
|
||||
|
@ -76,10 +76,9 @@ end
|
||||
---
|
||||
---@package
|
||||
function TSTreeView:new(bufnr, lang)
|
||||
local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang)
|
||||
if not ok then
|
||||
local err = parser --[[ @as string ]]
|
||||
return nil, 'No parser available for the given buffer:\n' .. err
|
||||
local parser = vim.treesitter._get_parser(bufnr or 0, lang)
|
||||
if not parser then
|
||||
return nil, 'No parser available for the given buffer.'
|
||||
end
|
||||
|
||||
-- For each child tree (injected language), find the root of the tree and locate the node within
|
||||
@ -539,7 +538,7 @@ local edit_ns = api.nvim_create_namespace('treesitter/dev-edit')
|
||||
local function update_editor_highlights(query_win, base_win, lang)
|
||||
local base_buf = api.nvim_win_get_buf(base_win)
|
||||
local query_buf = api.nvim_win_get_buf(query_win)
|
||||
local parser = vim.treesitter.get_parser(base_buf, lang)
|
||||
local parser = assert(vim.treesitter._get_parser(base_buf, lang))
|
||||
api.nvim_buf_clear_namespace(base_buf, edit_ns, 0, -1)
|
||||
local query_content = table.concat(api.nvim_buf_get_lines(query_buf, 0, -1, false), '\n')
|
||||
|
||||
@ -596,8 +595,8 @@ function M.edit_query(lang)
|
||||
end
|
||||
vim.cmd(cmd)
|
||||
|
||||
local ok, parser = pcall(vim.treesitter.get_parser, buf, lang)
|
||||
if not ok then
|
||||
local parser = vim.treesitter._get_parser(buf, lang)
|
||||
if not parser then
|
||||
return nil, 'No parser available for the given buffer'
|
||||
end
|
||||
lang = parser:lang()
|
||||
|
@ -117,6 +117,8 @@ end
|
||||
--- -- Asynchronous.
|
||||
--- vim.ui.open("https://neovim.io/")
|
||||
--- vim.ui.open("~/path/to/file")
|
||||
--- -- Use the "osurl" command to handle the path or URL.
|
||||
--- vim.ui.open("gh#neovim/neovim!29490", { cmd = { 'osurl' } })
|
||||
--- -- Synchronous (wait until the process exits).
|
||||
--- local cmd, err = vim.ui.open("$VIMRUNTIME")
|
||||
--- if cmd then
|
||||
@ -125,12 +127,14 @@ end
|
||||
--- ```
|
||||
---
|
||||
---@param path string Path or URL to open
|
||||
---@param opt? { cmd?: string[] } Options
|
||||
--- - cmd string[]|nil Command used to open the path or URL.
|
||||
---
|
||||
---@return vim.SystemObj|nil # Command object, or nil if not found.
|
||||
---@return nil|string # Error message on failure, or nil on success.
|
||||
---
|
||||
---@see |vim.system()|
|
||||
function M.open(path)
|
||||
function M.open(path, opt)
|
||||
vim.validate({
|
||||
path = { path, 'string' },
|
||||
})
|
||||
@ -139,12 +143,13 @@ function M.open(path)
|
||||
path = vim.fs.normalize(path)
|
||||
end
|
||||
|
||||
local cmd --- @type string[]
|
||||
local opts --- @type vim.SystemOpts
|
||||
opt = opt or {}
|
||||
local cmd ---@type string[]
|
||||
local job_opt = { text = true, detach = true } --- @type vim.SystemOpts
|
||||
|
||||
opts = { text = true, detach = true }
|
||||
|
||||
if vim.fn.has('mac') == 1 then
|
||||
if opt.cmd then
|
||||
cmd = vim.list_extend(opt.cmd --[[@as string[] ]], { path })
|
||||
elseif vim.fn.has('mac') == 1 then
|
||||
cmd = { 'open', path }
|
||||
elseif vim.fn.has('win32') == 1 then
|
||||
if vim.fn.executable('rundll32') == 1 then
|
||||
@ -154,8 +159,8 @@ function M.open(path)
|
||||
end
|
||||
elseif vim.fn.executable('xdg-open') == 1 then
|
||||
cmd = { 'xdg-open', path }
|
||||
opts.stdout = false
|
||||
opts.stderr = false
|
||||
job_opt.stdout = false
|
||||
job_opt.stderr = false
|
||||
elseif vim.fn.executable('wslview') == 1 then
|
||||
cmd = { 'wslview', path }
|
||||
elseif vim.fn.executable('explorer.exe') == 1 then
|
||||
@ -164,7 +169,7 @@ function M.open(path)
|
||||
return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)'
|
||||
end
|
||||
|
||||
return vim.system(cmd, opts), nil
|
||||
return vim.system(cmd, job_opt), nil
|
||||
end
|
||||
|
||||
--- Returns all URLs at cursor, if any.
|
||||
|
@ -33,7 +33,7 @@ end
|
||||
--- Show a table of contents for the help buffer in a loclist
|
||||
function M.show_toc()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local parser = vim.treesitter.get_parser(bufnr, 'vimdoc')
|
||||
local parser = assert(vim.treesitter._get_parser(bufnr, 'vimdoc'), 'vimdoc parser not found.')
|
||||
local query = vim.treesitter.query.parse(
|
||||
parser:lang(),
|
||||
[[
|
||||
|
@ -3,7 +3,7 @@
|
||||
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
|
||||
" Former Maintainer: Claudio Fleiner <claudio@fleiner.com>
|
||||
" Repository: https://github.com/zzzyxwvut/java-vim.git
|
||||
" Last Change: 2024 Sep 10
|
||||
" Last Change: 2024 Sep 11
|
||||
|
||||
" Please check :help java.vim for comments on some of the options available.
|
||||
|
||||
@ -663,7 +663,7 @@ hi def link javaStorageClass StorageClass
|
||||
hi def link javaMethodDecl javaStorageClass
|
||||
hi def link javaClassDecl javaStorageClass
|
||||
hi def link javaScopeDecl javaStorageClass
|
||||
hi def link javaConceptKind NonText
|
||||
hi def link javaConceptKind javaStorageClass
|
||||
|
||||
hi def link javaBoolean Boolean
|
||||
hi def link javaSpecial Special
|
||||
|
@ -685,7 +685,7 @@ if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_novimfunctionerror")
|
||||
syn match vimBufnrWarn /\<bufnr\s*(\s*["']\.['"]\s*)/
|
||||
endif
|
||||
|
||||
syn match vimNotFunc "\<if\>\|\<el\%[seif]\>\|\<retu\%[rn]\>\|\<while\>" skipwhite nextgroup=vimOper,vimOperParen,vimVar,vimFunc,vimNotation
|
||||
syn match vimNotFunc "\<if\>\|\<el\%[seif]\>\|\<retu\%[rn]\>\|\<while\>" skipwhite nextgroup=@vimExprList,vimNotation
|
||||
|
||||
" Match: {{{2
|
||||
" =====
|
||||
|
@ -786,7 +786,7 @@ local function parse_buf(fname, parser_path)
|
||||
if parser_path then
|
||||
vim.treesitter.language.add('vimdoc', { path = parser_path })
|
||||
end
|
||||
local lang_tree = vim.treesitter.get_parser(buf)
|
||||
local lang_tree = assert(vim.treesitter._get_parser(buf), 'vimdoc parser not found.')
|
||||
return lang_tree, buf
|
||||
end
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
Memcheck:Leak
|
||||
fun:malloc
|
||||
fun:uv_spawn
|
||||
fun:libuv_process_spawn
|
||||
fun:process_spawn
|
||||
fun:libuv_proc_spawn
|
||||
fun:proc_spawn
|
||||
fun:job_start
|
||||
}
|
||||
|
@ -848,7 +848,7 @@ def CheckIncludes(filename, lines, error):
|
||||
or filename.endswith('.in.h')
|
||||
or FileInfo(filename).RelativePath() in {
|
||||
'func_attr.h',
|
||||
'os/pty_process.h',
|
||||
'os/pty_proc.h',
|
||||
}):
|
||||
return
|
||||
|
||||
@ -869,7 +869,7 @@ def CheckIncludes(filename, lines, error):
|
||||
"src/nvim/msgpack_rpc/unpacker.h",
|
||||
"src/nvim/option.h",
|
||||
"src/nvim/os/pty_conpty_win.h",
|
||||
"src/nvim/os/pty_process_win.h",
|
||||
"src/nvim/os/pty_proc_win.h",
|
||||
]
|
||||
|
||||
skip_headers = [
|
||||
|
@ -417,10 +417,10 @@ list(SORT NVIM_HEADERS)
|
||||
|
||||
foreach(sfile ${NVIM_SOURCES})
|
||||
get_filename_component(f ${sfile} NAME)
|
||||
if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$")
|
||||
if(WIN32 AND ${f} MATCHES "^(pty_proc_unix.c)$")
|
||||
list(REMOVE_ITEM NVIM_SOURCES ${sfile})
|
||||
endif()
|
||||
if(NOT WIN32 AND ${f} MATCHES "^(pty_process_win.c)$")
|
||||
if(NOT WIN32 AND ${f} MATCHES "^(pty_proc_win.c)$")
|
||||
list(REMOVE_ITEM NVIM_SOURCES ${sfile})
|
||||
endif()
|
||||
if(NOT WIN32 AND ${f} MATCHES "^(pty_conpty_win.c)$")
|
||||
@ -436,7 +436,7 @@ foreach(hfile ${NVIM_HEADERS})
|
||||
if(WIN32 AND ${f} MATCHES "^(unix_defs.h)$")
|
||||
list(REMOVE_ITEM NVIM_HEADERS ${hfile})
|
||||
endif()
|
||||
if(WIN32 AND ${f} MATCHES "^(pty_process_unix.h)$")
|
||||
if(WIN32 AND ${f} MATCHES "^(pty_proc_unix.h)$")
|
||||
list(REMOVE_ITEM NVIM_HEADERS ${hfile})
|
||||
endif()
|
||||
if(NOT WIN32 AND ${f} MATCHES "^(win_defs.h)$")
|
||||
@ -832,12 +832,12 @@ find_program(CLANG_TIDY_PRG clang-tidy)
|
||||
set(EXCLUDE_CLANG_TIDY typval_encode.c.h ui_events.in.h)
|
||||
if(WIN32)
|
||||
list(APPEND EXCLUDE_CLANG_TIDY
|
||||
os/pty_process_unix.h
|
||||
os/pty_proc_unix.h
|
||||
os/unix_defs.h)
|
||||
else()
|
||||
list(APPEND EXCLUDE_CLANG_TIDY
|
||||
os/win_defs.h
|
||||
os/pty_process_win.h
|
||||
os/pty_proc_win.h
|
||||
os/pty_conpty_win.h
|
||||
os/os_win_console.h)
|
||||
endif()
|
||||
|
@ -70,7 +70,7 @@
|
||||
#include "nvim/optionstr.h"
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/os/process.h"
|
||||
#include "nvim/os/proc.h"
|
||||
#include "nvim/popupmenu.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/runtime.h"
|
||||
|
@ -763,6 +763,8 @@ struct diffblock_S {
|
||||
linenr_T df_count[DB_COUNT]; // nr of inserted/changed lines
|
||||
bool is_linematched; // has the linematch algorithm ran on this diff hunk to divide it into
|
||||
// smaller diff hunks?
|
||||
size_t n_charmatch;
|
||||
int *charmatchp; // values for charmatch
|
||||
};
|
||||
|
||||
#define SNAP_HELP_IDX 0
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/event/socket.h"
|
||||
#include "nvim/event/stream.h"
|
||||
@ -88,7 +88,7 @@ void channel_free_all_mem(void)
|
||||
bool channel_close(uint64_t id, ChannelPart part, const char **error)
|
||||
{
|
||||
Channel *chan;
|
||||
Process *proc;
|
||||
Proc *proc;
|
||||
|
||||
const char *dummy;
|
||||
if (!error) {
|
||||
@ -139,8 +139,8 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
|
||||
if (part == kChannelPartStderr || part == kChannelPartAll) {
|
||||
rstream_may_close(&proc->err);
|
||||
}
|
||||
if (proc->type == kProcessTypePty && part == kChannelPartAll) {
|
||||
pty_process_close_master(&chan->stream.pty);
|
||||
if (proc->type == kProcTypePty && part == kChannelPartAll) {
|
||||
pty_proc_close_master(&chan->stream.pty);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -289,7 +289,7 @@ static void channel_destroy(Channel *chan)
|
||||
}
|
||||
|
||||
if (chan->streamtype == kChannelStreamProc) {
|
||||
process_free(&chan->stream.proc);
|
||||
proc_free(&chan->stream.proc);
|
||||
}
|
||||
|
||||
callback_reader_free(&chan->on_data);
|
||||
@ -376,7 +376,7 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s
|
||||
*status_out = 0;
|
||||
return NULL;
|
||||
}
|
||||
chan->stream.pty = pty_process_init(&main_loop, chan);
|
||||
chan->stream.pty = pty_proc_init(&main_loop, chan);
|
||||
if (pty_width > 0) {
|
||||
chan->stream.pty.width = pty_width;
|
||||
}
|
||||
@ -384,22 +384,22 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s
|
||||
chan->stream.pty.height = pty_height;
|
||||
}
|
||||
} else {
|
||||
chan->stream.uv = libuv_process_init(&main_loop, chan);
|
||||
chan->stream.uv = libuv_proc_init(&main_loop, chan);
|
||||
}
|
||||
|
||||
Process *proc = &chan->stream.proc;
|
||||
Proc *proc = &chan->stream.proc;
|
||||
proc->argv = argv;
|
||||
proc->exepath = exepath;
|
||||
proc->cb = channel_process_exit_cb;
|
||||
proc->cb = channel_proc_exit_cb;
|
||||
proc->events = chan->events;
|
||||
proc->detach = detach;
|
||||
proc->cwd = cwd;
|
||||
proc->env = env;
|
||||
proc->overlapped = overlapped;
|
||||
|
||||
char *cmd = xstrdup(process_get_exepath(proc));
|
||||
char *cmd = xstrdup(proc_get_exepath(proc));
|
||||
bool has_out, has_err;
|
||||
if (proc->type == kProcessTypePty) {
|
||||
if (proc->type == kProcTypePty) {
|
||||
has_out = true;
|
||||
has_err = false;
|
||||
} else {
|
||||
@ -410,7 +410,7 @@ Channel *channel_job_start(char **argv, const char *exepath, CallbackReader on_s
|
||||
|
||||
bool has_in = stdin_mode == kChannelStdinPipe;
|
||||
|
||||
int status = process_spawn(proc, has_in, has_out, has_err);
|
||||
int status = proc_spawn(proc, has_in, has_out, has_err);
|
||||
if (status) {
|
||||
semsg(_(e_jobspawn), os_strerror(status), cmd);
|
||||
xfree(cmd);
|
||||
@ -760,7 +760,7 @@ void channel_reader_callbacks(Channel *chan, CallbackReader *reader)
|
||||
}
|
||||
}
|
||||
|
||||
static void channel_process_exit_cb(Process *proc, int status, void *data)
|
||||
static void channel_proc_exit_cb(Proc *proc, int status, void *data)
|
||||
{
|
||||
Channel *chan = data;
|
||||
if (chan->term) {
|
||||
@ -847,7 +847,7 @@ static void term_write(const char *buf, size_t size, void *data)
|
||||
static void term_resize(uint16_t width, uint16_t height, void *data)
|
||||
{
|
||||
Channel *chan = data;
|
||||
pty_process_resize(&chan->stream.pty, width, height);
|
||||
pty_proc_resize(&chan->stream.pty, width, height);
|
||||
}
|
||||
|
||||
static inline void term_delayed_free(void **argv)
|
||||
@ -867,7 +867,7 @@ static inline void term_delayed_free(void **argv)
|
||||
static void term_close(void *data)
|
||||
{
|
||||
Channel *chan = data;
|
||||
process_stop(&chan->stream.proc);
|
||||
proc_stop(&chan->stream.proc);
|
||||
multiqueue_put(chan->events, term_delayed_free, data);
|
||||
}
|
||||
|
||||
@ -907,7 +907,7 @@ bool channel_job_running(uint64_t id)
|
||||
Channel *chan = find_channel(id);
|
||||
return (chan
|
||||
&& chan->streamtype == kChannelStreamProc
|
||||
&& !process_is_stopped(&chan->stream.proc));
|
||||
&& !proc_is_stopped(&chan->stream.proc));
|
||||
}
|
||||
|
||||
Dictionary channel_info(uint64_t id, Arena *arena)
|
||||
@ -924,8 +924,8 @@ Dictionary channel_info(uint64_t id, Arena *arena)
|
||||
switch (chan->streamtype) {
|
||||
case kChannelStreamProc: {
|
||||
stream_desc = "job";
|
||||
if (chan->stream.proc.type == kProcessTypePty) {
|
||||
const char *name = pty_process_tty_name(&chan->stream.pty);
|
||||
if (chan->stream.proc.type == kProcTypePty) {
|
||||
const char *name = pty_proc_tty_name(&chan->stream.pty);
|
||||
PUT_C(info, "pty", CSTR_TO_ARENA_OBJ(arena, name));
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
#include "nvim/channel_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/event/defs.h"
|
||||
#include "nvim/event/libuv_process.h"
|
||||
#include "nvim/event/libuv_proc.h"
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/map_defs.h"
|
||||
#include "nvim/msgpack_rpc/channel_defs.h"
|
||||
#include "nvim/os/pty_process.h"
|
||||
#include "nvim/os/pty_proc.h"
|
||||
#include "nvim/types_defs.h"
|
||||
|
||||
struct Channel {
|
||||
@ -21,9 +21,9 @@ struct Channel {
|
||||
|
||||
ChannelStreamType streamtype;
|
||||
union {
|
||||
Process proc;
|
||||
LibuvProcess uv;
|
||||
PtyProcess pty;
|
||||
Proc proc;
|
||||
LibuvProc uv;
|
||||
PtyProc pty;
|
||||
RStream socket;
|
||||
StdioPair stdio;
|
||||
StderrState err;
|
||||
|
372
src/nvim/diff.c
372
src/nvim/diff.c
@ -10,6 +10,7 @@
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -84,11 +85,15 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
|
||||
#define DIFF_CLOSE_OFF 0x400 // diffoff when closing window
|
||||
#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
|
||||
#define DIFF_LINEMATCH 0x1000 // match most similar lines within diff
|
||||
#define DIFF_CHARDIFF 0x2000 // character-wise matching
|
||||
#define DIFF_WORDDIFF 0x4000 // character-wise matching
|
||||
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
|
||||
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
|
||||
|
||||
static int diff_algorithm = 0;
|
||||
static int linematch_lines = 0;
|
||||
static int chardiff_chars = 0;
|
||||
static int worddiff_words = 0;
|
||||
|
||||
#define LBUFLEN 50 // length of line in diff file
|
||||
|
||||
@ -130,6 +135,11 @@ typedef enum {
|
||||
DIFF_NONE,
|
||||
} diffstyle_T;
|
||||
|
||||
typedef enum {
|
||||
LINEMATCH,
|
||||
CHARMATCH,
|
||||
WORDMATCH,
|
||||
} diff_allignment_T;
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "diff.c.generated.h"
|
||||
#endif
|
||||
@ -522,6 +532,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
|
||||
{
|
||||
diff_T *dnew = xmalloc(sizeof(*dnew));
|
||||
|
||||
dnew->charmatchp = NULL;
|
||||
dnew->is_linematched = false;
|
||||
dnew->df_next = dp;
|
||||
if (dprev == NULL) {
|
||||
@ -536,6 +547,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
|
||||
static diff_T *diff_free(tabpage_T *tp, diff_T *dprev, diff_T *dp)
|
||||
{
|
||||
diff_T *ret = dp->df_next;
|
||||
xfree(dp->charmatchp);
|
||||
xfree(dp);
|
||||
|
||||
if (dprev == NULL) {
|
||||
@ -1647,6 +1659,7 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
|
||||
|
||||
while (dn != dp->df_next) {
|
||||
dpl = dn->df_next;
|
||||
xfree(dn->charmatchp);
|
||||
xfree(dn);
|
||||
dn = dpl;
|
||||
}
|
||||
@ -1753,11 +1766,18 @@ void diff_clear(tabpage_T *tp)
|
||||
diff_T *next_p;
|
||||
for (diff_T *p = tp->tp_first_diff; p != NULL; p = next_p) {
|
||||
next_p = p->df_next;
|
||||
xfree(p->charmatchp);
|
||||
xfree(p);
|
||||
}
|
||||
tp->tp_first_diff = NULL;
|
||||
}
|
||||
|
||||
/// Return true if char diff option is enabled.
|
||||
bool chardiff(void)
|
||||
{
|
||||
return (diff_flags & DIFF_CHARDIFF) || (diff_flags & DIFF_WORDDIFF);
|
||||
}
|
||||
|
||||
/// Return true if the options are set to use diff linematch.
|
||||
bool diff_linematch(diff_T *dp)
|
||||
{
|
||||
@ -2001,13 +2021,23 @@ static void apply_linematch_results(diff_T *dp, size_t decisions_length, const i
|
||||
dp->is_linematched = true;
|
||||
}
|
||||
|
||||
static void run_linematch_algorithm(diff_T *dp)
|
||||
static void run_alignment_algorithm(diff_T *dp, diff_allignment_T diff_allignment)
|
||||
{
|
||||
// define buffers for diff algorithm
|
||||
mmfile_t diffbufs_mm[DB_COUNT];
|
||||
const char *diffbufs[DB_COUNT];
|
||||
int diff_length[DB_COUNT];
|
||||
mmfile_t diffbufs_mm[DB_COUNT] = { 0 };
|
||||
char *diffbufs[DB_COUNT] = { 0 };
|
||||
int diff_length[DB_COUNT] = { 0 };
|
||||
int diff_lines[DB_COUNT] = { 0 };
|
||||
size_t ndiffs = 0;
|
||||
size_t total_word_count = 0;
|
||||
size_t total_chars_length = 0;
|
||||
size_t *word_offset_size[DB_COUNT] = { 0 }; // mapping array used for charmatch
|
||||
size_t *word_offset[DB_COUNT] = { 0 }; // mapping array used for charmatch
|
||||
size_t word_offset_result_index[DB_COUNT] = { 0 }; // mapping array used for charmatch
|
||||
size_t *iwhite_index_offset = NULL; // mapping array used for charmatch
|
||||
size_t result_diff_start_pos[DB_COUNT]; // the position in the result array where this
|
||||
// an array for index mapping with iwhite
|
||||
const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
// write the contents of the entire buffer to
|
||||
@ -2019,28 +2049,216 @@ static void run_linematch_algorithm(diff_T *dp)
|
||||
// we add it to the array of char*, diffbufs
|
||||
diffbufs[ndiffs] = diffbufs_mm[ndiffs].ptr;
|
||||
|
||||
diff_lines[ndiffs] = dp->df_count[i];
|
||||
if (diff_allignment == CHARMATCH || diff_allignment == WORDMATCH) {
|
||||
// before removing whitespace for charmatch
|
||||
result_diff_start_pos[ndiffs] = total_chars_length;
|
||||
// get the length of each of the diffs
|
||||
int lines = dp->df_count[i];
|
||||
const char *p = diffbufs[ndiffs];
|
||||
while (lines) {
|
||||
total_chars_length++; // increment the total characters counter
|
||||
if (*p == '\n') {
|
||||
lines--;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
} else if (diff_allignment == LINEMATCH) {
|
||||
// LINEMATCH
|
||||
// keep track of the length of this diff block to pass it to the linematch
|
||||
// algorithm
|
||||
diff_length[ndiffs] = dp->df_count[i];
|
||||
}
|
||||
|
||||
// increment the amount of diff buffers we are passing to the algorithm
|
||||
ndiffs++;
|
||||
}
|
||||
}
|
||||
|
||||
// we will get the output of the linematch algorithm in the format of an array
|
||||
// of integers (*decisions) and the length of that array (decisions_length)
|
||||
int *decisions = NULL;
|
||||
const bool iwhite = (diff_flags & (DIFF_IWHITEALL | DIFF_IWHITE)) > 0;
|
||||
size_t decisions_length = linematch_nbuffers(diffbufs, diff_length, ndiffs, &decisions, iwhite);
|
||||
|
||||
// allocate all the memory we will need to keep track of tokens positions and their respective
|
||||
// lengths. For word matching, this is the 'word' as vim defines it, for character matching, the
|
||||
// token is the one or more 8 bit 'chars' that make up a utf character
|
||||
if (diff_allignment == WORDMATCH || diff_allignment == CHARMATCH) {
|
||||
// are we ignoring whitespace in the comparison?
|
||||
if (iwhite) {
|
||||
// allocate array for index mapping of result array
|
||||
iwhite_index_offset = xmalloc(total_chars_length * sizeof(size_t));
|
||||
}
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
XFREE_CLEAR(diffbufs_mm[i].ptr);
|
||||
word_offset[i] = xmalloc(total_chars_length * sizeof(size_t));
|
||||
word_offset_size[i] = xmalloc(total_chars_length * sizeof(size_t));
|
||||
for (size_t j = 0; j < total_chars_length; j++) {
|
||||
word_offset_size[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// calculate the token lengths and white space offset and pre process the contents of the diffs to
|
||||
// remove white space if necessary
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
int cls = INT_MIN; // keep track of what type of character this is, to determine when we are
|
||||
// moving to a different word
|
||||
|
||||
size_t j = 0; // j will iterate over each character in each of the diffs
|
||||
|
||||
size_t k = 0; // k represents the index of the current character if there were no white spaces,
|
||||
// so we will use k and j to calculate the white space offset and use it later to
|
||||
// populate the final results for drawing to the screen
|
||||
// if 'iwhite' is not used, k will always be the same as j
|
||||
|
||||
size_t lines = (size_t)diff_lines[i]; // we iterate over each line of this part of the diff
|
||||
|
||||
size_t w = result_diff_start_pos[i]; // keep track of the offset of all the characters without
|
||||
// any whitespace, so that we can ignore the white space
|
||||
// while calculating the diff, and then use this to
|
||||
// populate the results
|
||||
size_t cur_char_length = 0;
|
||||
|
||||
while (lines > 0) {
|
||||
if (iwhite && (diffbufs[i][j] == ' ' || diffbufs[i][j] == '\t')) {
|
||||
// we are using 'iwhite' and this is a whitespace, so it will not be included as a token in
|
||||
// the diff algorithm
|
||||
// we are ignoring whitespace and this is a whitespace ' ' or '\t' reset the class definition
|
||||
cls = INT_MIN;
|
||||
} else {
|
||||
// we have a character which is not a blank (or we are not using iwhite)
|
||||
|
||||
// how we determine when there is a new token depends on if this is chardiff or worddiff
|
||||
if (diff_allignment == WORDMATCH) {
|
||||
// WORDMATCH
|
||||
if (utf_class(diffbufs[i][j]) != cls || diffbufs[i][j] == '\n') {
|
||||
// this is a new token
|
||||
word_offset[i][diff_length[i]] = k; // mark the offset of this without whitespace
|
||||
diff_length[i]++; // this diff length has another token, so it gets longer
|
||||
total_word_count++;
|
||||
}
|
||||
cls = utf_class(diffbufs[i][j]);
|
||||
word_offset_size[i][diff_length[i] - 1]++; // still the same class (iterating over the
|
||||
// same type of word), so the current word
|
||||
// length is getting longer
|
||||
} else if (diff_allignment == CHARMATCH) {
|
||||
// CHARMATCH
|
||||
if (cur_char_length == 0) {
|
||||
// get the length of current character
|
||||
if (diffbufs[i][j] == '\n') {
|
||||
// this is the last character of the line
|
||||
cur_char_length = 1;
|
||||
} else {
|
||||
cur_char_length = (size_t)utfc_ptr2len((const char *const)&diffbufs[i][j]);
|
||||
}
|
||||
word_offset[i][diff_length[i]] = k;
|
||||
diff_length[i]++;
|
||||
total_word_count++;
|
||||
// the token size is the length of this utf character
|
||||
word_offset_size[i][diff_length[i] - 1] = cur_char_length;
|
||||
}
|
||||
cur_char_length--;
|
||||
}
|
||||
// if ignoring whitespace, keep track of the white space index
|
||||
if (iwhite && (diff_allignment == CHARMATCH || diff_allignment == WORDMATCH)) {
|
||||
// keep track of the index offset with ignoring whitespace to use when populating the results
|
||||
iwhite_index_offset[w++] = j - k;
|
||||
}
|
||||
diffbufs[i][k++] = diffbufs[i][j];
|
||||
}
|
||||
if (diffbufs[i][j++] == '\n') {
|
||||
lines--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we will get the output of the linematch algorithm in the format of an array
|
||||
// of integers (*decisions) and the length of that array (decisions_length)
|
||||
if (diff_allignment == LINEMATCH) {
|
||||
int *decisions = NULL;
|
||||
size_t decisions_length = linematch_nbuffers((const char **)diffbufs, diff_length, ndiffs,
|
||||
&decisions, 0, NULL, NULL);
|
||||
apply_linematch_results(dp, decisions_length, decisions);
|
||||
|
||||
xfree(decisions);
|
||||
} else if (diff_allignment == CHARMATCH || diff_allignment == WORDMATCH) {
|
||||
dp->charmatchp = xmalloc(total_chars_length * sizeof(int)); // will hold results
|
||||
dp->n_charmatch = total_chars_length;
|
||||
|
||||
bool lim_exceeded = false;
|
||||
if (diff_allignment == CHARMATCH && total_chars_length > (size_t)chardiff_chars) {
|
||||
lim_exceeded = true;
|
||||
} else if (diff_allignment == WORDMATCH && total_word_count > (size_t)worddiff_words) {
|
||||
lim_exceeded = true;
|
||||
}
|
||||
|
||||
if (lim_exceeded == true) {
|
||||
// do not run charmatch on the entire diff block
|
||||
// we will attempt to run charmatch on the individual lines later
|
||||
// for now, just initialize the result memory
|
||||
for (size_t i = 0; i < total_chars_length; i++) {
|
||||
dp->charmatchp[i] = -1; // -1 indicates that algorithm has not yet ran
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < total_chars_length; i++) {
|
||||
dp->charmatchp[i] = 0; // default to not highlighted
|
||||
}
|
||||
|
||||
// check is this a line that does not exist in other buffers?
|
||||
// if so, highlight it as a 'newline', and we don't need to run the algorithm
|
||||
bool newline = true;
|
||||
for (size_t i = 0, c = 0; i < ndiffs; i++) {
|
||||
if (diff_length[i] > 0) {
|
||||
c++;
|
||||
}
|
||||
if (c > 1) {
|
||||
newline = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newline == true) {
|
||||
for (size_t i = 0; i < total_chars_length; i++) {
|
||||
dp->charmatchp[i] = 2;
|
||||
}
|
||||
} else {
|
||||
int *decisions = NULL;
|
||||
size_t decisions_length = linematch_nbuffers((const char **)diffbufs, diff_length, ndiffs,
|
||||
&decisions, 1, word_offset, word_offset_size);
|
||||
for (size_t i = 0; i < decisions_length; i++) {
|
||||
if (decisions[i] == (pow(2, (double)ndiffs) - 1)) {
|
||||
// it's a comparison of all the buffers (don't highlight)
|
||||
for (size_t j = 0; j < ndiffs; j++) {
|
||||
for (size_t k = 0;
|
||||
k <
|
||||
(diff_allignment ==
|
||||
WORDMATCH ? word_offset_size[j][word_offset_result_index[j]] : 1); k++) {
|
||||
size_t l = result_diff_start_pos[j]++;
|
||||
dp->charmatchp[iwhite_index_offset ? iwhite_index_offset[l] + l : l] = 0;
|
||||
}
|
||||
word_offset_result_index[j]++;
|
||||
}
|
||||
} else {
|
||||
// it's a skip in a single buffer (highlight as changed)
|
||||
for (size_t j = 0; j < ndiffs; j++) {
|
||||
if (decisions[i] & (1 << j)) {
|
||||
for (size_t k = 0;
|
||||
k <
|
||||
(diff_allignment ==
|
||||
WORDMATCH ? word_offset_size[j][word_offset_result_index[j]] : 1); k++) {
|
||||
size_t l = result_diff_start_pos[j]++;
|
||||
dp->charmatchp[iwhite_index_offset ? iwhite_index_offset[l] + l : l] = 1;
|
||||
}
|
||||
word_offset_result_index[j]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
xfree(decisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
xfree(word_offset[i]);
|
||||
xfree(word_offset_size[i]);
|
||||
XFREE_CLEAR(diffbufs_mm[i].ptr);
|
||||
}
|
||||
xfree(iwhite_index_offset);
|
||||
}
|
||||
|
||||
/// Check diff status for line "lnum" in buffer "buf":
|
||||
@ -2105,7 +2323,7 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
|
||||
// above the screen.
|
||||
if (lnum >= wp->w_topline && lnum < wp->w_botline
|
||||
&& !dp->is_linematched && diff_linematch(dp)) {
|
||||
run_linematch_algorithm(dp);
|
||||
run_alignment_algorithm(dp, LINEMATCH);
|
||||
}
|
||||
|
||||
if (dp->is_linematched) {
|
||||
@ -2412,6 +2630,8 @@ int diffopt_changed(void)
|
||||
{
|
||||
int diff_context_new = 6;
|
||||
int linematch_lines_new = 0;
|
||||
int chardiff_chars_new = 0;
|
||||
int worddiff_words_new = 0;
|
||||
int diff_flags_new = 0;
|
||||
int diff_foldcolumn_new = 2;
|
||||
int diff_algorithm_new = 0;
|
||||
@ -2487,6 +2707,14 @@ int diffopt_changed(void)
|
||||
p += 10;
|
||||
linematch_lines_new = getdigits_int(&p, false, linematch_lines_new);
|
||||
diff_flags_new |= DIFF_LINEMATCH;
|
||||
} else if ((strncmp(p, "chardiff:", 9) == 0) && ascii_isdigit(p[9])) {
|
||||
p += 9;
|
||||
chardiff_chars_new = getdigits_int(&p, false, chardiff_chars_new);
|
||||
diff_flags_new |= DIFF_CHARDIFF;
|
||||
} else if ((strncmp(p, "worddiff:", 9) == 0) && ascii_isdigit(p[9])) {
|
||||
p += 9;
|
||||
worddiff_words_new = getdigits_int(&p, false, worddiff_words_new);
|
||||
diff_flags_new |= DIFF_WORDDIFF;
|
||||
}
|
||||
|
||||
if ((*p != ',') && (*p != NUL)) {
|
||||
@ -2516,6 +2744,8 @@ int diffopt_changed(void)
|
||||
diff_flags = diff_flags_new;
|
||||
diff_context = diff_context_new == 0 ? 1 : diff_context_new;
|
||||
linematch_lines = linematch_lines_new;
|
||||
chardiff_chars = chardiff_chars_new;
|
||||
worddiff_words = worddiff_words_new;
|
||||
diff_foldcolumn = diff_foldcolumn_new;
|
||||
diff_algorithm = diff_algorithm_new;
|
||||
|
||||
@ -2563,7 +2793,8 @@ bool diffopt_filler(void)
|
||||
/// @param endp last char of the change
|
||||
///
|
||||
/// @return true if the line was added, no other buffer has it.
|
||||
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
|
||||
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp, int **hlresult,
|
||||
bool *diffchars_lim_exceeded, size_t *diffchars_line_len)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Make a copy of the line, the next ml_get() will invalidate it.
|
||||
@ -2604,6 +2835,97 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
|
||||
bool added = true;
|
||||
|
||||
linenr_T off = lnum - dp->df_lnum[idx];
|
||||
if (chardiff()) {
|
||||
diff_allignment_T diff_allignment;
|
||||
if (diff_flags & DIFF_CHARDIFF) {
|
||||
// if both chardiff & worddiff are enabled, it will pick chardiff
|
||||
diff_allignment = CHARMATCH;
|
||||
} else if (diff_flags & DIFF_WORDDIFF) {
|
||||
diff_allignment = WORDMATCH;
|
||||
}
|
||||
if (dp->charmatchp == NULL) {
|
||||
// get the first buffers
|
||||
// try running on the whole diff buffer first
|
||||
run_alignment_algorithm(dp, diff_allignment);
|
||||
}
|
||||
size_t charcount = 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
// for each diff buffer
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
for (int j = 0; j < dp->df_count[i]; j++) {
|
||||
// for each line in that buffer
|
||||
// get a pointer to the line
|
||||
char *diffline = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + j);
|
||||
while (*diffline != '\0') {
|
||||
diffline++; charcount++;
|
||||
}
|
||||
charcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dp->n_charmatch != charcount) {
|
||||
// we need to re run if the length of the diff has changed
|
||||
// count the number of characters in this diff
|
||||
// the line is currently being edited in insert mode, so pause highlighting until the diff is
|
||||
// recalculated, then resume the charmatch highlighting
|
||||
(*hlresult) = NULL;
|
||||
} else {
|
||||
// charmatchp is not null, is the whole thing already diffed?
|
||||
// get the correct offset for hlresult
|
||||
//
|
||||
// if the character count is not null
|
||||
size_t hlresult_line_offset = 0;
|
||||
// get the offset for the highlight of this line
|
||||
*diffchars_line_len = strlen(ml_get_buf(curtab->tp_diffbuf[idx], dp->df_lnum[idx] + off));
|
||||
hlresult_line_offset = get_buffer_position(idx, dp, off);
|
||||
if (*(dp->charmatchp + hlresult_line_offset) == -1) {
|
||||
diff_T dp_tmp;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
dp_tmp.df_lnum[i] = dp->df_lnum[i] + off;
|
||||
dp_tmp.df_count[i] = off < dp->df_count[i] ? 1 : 0;
|
||||
}
|
||||
}
|
||||
// this line has not yet been calculated
|
||||
// run charmatch on this line of the diff
|
||||
// figure out how many buffers we are diffing
|
||||
// what line number is this in each buffer?
|
||||
run_alignment_algorithm(&dp_tmp, diff_allignment);
|
||||
if (dp_tmp.n_charmatch > 0) {
|
||||
for (int i = 0, p = 0; i < DB_COUNT; i++) {
|
||||
if (curtab->tp_diffbuf[i] != NULL) {
|
||||
// get the offset in the original charmatchp
|
||||
if (off < dp->df_count[i]) {
|
||||
size_t length = strlen(ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + off)) + 1;
|
||||
size_t k = get_buffer_position(i, dp, off);
|
||||
for (size_t m = 0; m < length; m++) {
|
||||
int val = dp_tmp.charmatchp[p++];
|
||||
dp->charmatchp[k + m] = val == -1 ? -2 : val; // if this individual line is still
|
||||
// too long to diff, mark it as a
|
||||
// -2, meaning it's been attempted
|
||||
// already
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// extract the results from here
|
||||
}
|
||||
xfree(dp_tmp.charmatchp);
|
||||
}
|
||||
(*hlresult) = dp->charmatchp + hlresult_line_offset;
|
||||
}
|
||||
if ((*hlresult) == NULL) {
|
||||
xfree(line_org);
|
||||
return false;
|
||||
} else if ((*hlresult)[0] != -2) { // -2 indicates that we've attempted a character wise diff with the
|
||||
xfree(line_org);
|
||||
return false; // entire block, and with this individual line, and still exceeded
|
||||
} else { // the character limit
|
||||
//
|
||||
*diffchars_lim_exceeded = true; // go to the default highlighting behaviour without character
|
||||
} // wise matching
|
||||
}
|
||||
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) {
|
||||
// Skip lines that are not in the other change (filler lines).
|
||||
@ -3440,3 +3762,25 @@ static int xdiff_out(int start_a, int count_a, int start_b, int count_b, void *p
|
||||
}));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get the position in the character diff buffer of this line
|
||||
static size_t get_buffer_position(const int idx, const diff_T *dp, linenr_T offset)
|
||||
{
|
||||
size_t comparison_mem_offset = 0;
|
||||
for (int i = 0; i < DB_COUNT; i++) {
|
||||
if ((curtab->tp_diffbuf[i] != NULL)) {
|
||||
for (int j = 0; j < ((i == idx) ? offset : dp->df_count[i]); j++) {
|
||||
char *diffline = ml_get_buf(curtab->tp_diffbuf[i], dp->df_lnum[i] + j);
|
||||
while (*diffline != '\0') {
|
||||
diffline++; comparison_mem_offset++;
|
||||
}
|
||||
comparison_mem_offset++; // count the '\0' character as the newline marker for each line
|
||||
}
|
||||
if (i == idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// what is the line length for this pointer?
|
||||
return comparison_mem_offset;
|
||||
}
|
||||
|
@ -170,28 +170,26 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
|
||||
// cache previous calculations depending on w_virtcol
|
||||
static int saved_w_virtcol;
|
||||
static win_T *prev_wp;
|
||||
static int prev_width1;
|
||||
static int prev_width2;
|
||||
static int prev_left_col;
|
||||
static int prev_right_col;
|
||||
static int prev_col_off;
|
||||
|
||||
int cur_col_off = win_col_off(wp);
|
||||
int width1;
|
||||
int width2;
|
||||
int width1 = wp->w_width_inner - cur_col_off;
|
||||
int width2 = width1 + win_col_off2(wp);
|
||||
|
||||
if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp
|
||||
&& prev_col_off == cur_col_off) {
|
||||
&& prev_width1 == width1 && prev_width2 == width2) {
|
||||
*right_col = prev_right_col;
|
||||
*left_col = prev_left_col;
|
||||
return;
|
||||
}
|
||||
|
||||
width1 = wp->w_width_inner - cur_col_off;
|
||||
width2 = width1 + win_col_off2(wp);
|
||||
|
||||
*left_col = 0;
|
||||
*right_col = width1;
|
||||
|
||||
if (wp->w_virtcol >= (colnr_T)width1) {
|
||||
if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
|
||||
*right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2;
|
||||
}
|
||||
if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
|
||||
@ -202,8 +200,9 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
|
||||
prev_left_col = *left_col;
|
||||
prev_right_col = *right_col;
|
||||
prev_wp = wp;
|
||||
prev_width1 = width1;
|
||||
prev_width2 = width2;
|
||||
saved_w_virtcol = wp->w_virtcol;
|
||||
prev_col_off = cur_col_off;
|
||||
}
|
||||
|
||||
/// Put a single char from an UTF-8 buffer into a line buffer.
|
||||
@ -1137,10 +1136,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
int bg_attr = win_bg_attr(wp);
|
||||
|
||||
int linestatus = 0;
|
||||
bool diffchars_lim_exceeded = false;
|
||||
size_t diffchars_line_len = 0;
|
||||
int *hlresult = NULL;
|
||||
wlv.filler_lines = diff_check_with_linestatus(wp, lnum, &linestatus);
|
||||
if (wlv.filler_lines < 0 || linestatus < 0) {
|
||||
if (wlv.filler_lines == -1 || linestatus == -1) {
|
||||
if (diff_find_change(wp, lnum, &change_start, &change_end)) {
|
||||
if (diff_find_change(wp, lnum, &change_start, &change_end, &hlresult,
|
||||
&diffchars_lim_exceeded, &diffchars_line_len)) {
|
||||
wlv.diff_hlf = HLF_ADD; // added line
|
||||
} else if (change_start == 0) {
|
||||
wlv.diff_hlf = HLF_TXD; // changed text
|
||||
@ -1721,6 +1724,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
}
|
||||
|
||||
if (wlv.diff_hlf != (hlf_T)0) {
|
||||
if (chardiff() && !diffchars_lim_exceeded) {
|
||||
if (wlv.diff_hlf != HLF_ADD) {
|
||||
if (hlresult == NULL) {
|
||||
wlv.diff_hlf = HLF_CHD;
|
||||
} else if (hlresult[0] == 2) {
|
||||
wlv.diff_hlf = HLF_ADD;
|
||||
} else if ((size_t)(ptr - line) < diffchars_line_len
|
||||
&& (hlresult[ptr - line] == 1 || hlresult[ptr - line] == -2)) {
|
||||
wlv.diff_hlf = HLF_TXD;
|
||||
} else {
|
||||
wlv.diff_hlf = HLF_CHD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// When there is extra text (eg: virtual text) it gets the
|
||||
// diff highlighting for the line, but not for changed text.
|
||||
if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start
|
||||
@ -1731,6 +1748,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
|| (wlv.n_extra > 0 && wlv.extra_for_extmark))) {
|
||||
wlv.diff_hlf = HLF_CHD; // changed line
|
||||
}
|
||||
}
|
||||
|
||||
wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf);
|
||||
// Overlay CursorLine onto diff-mode highlight.
|
||||
if (wlv.cul_attr) {
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/time.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
@ -8506,7 +8506,7 @@ Channel *find_job(uint64_t id, bool show_error)
|
||||
{
|
||||
Channel *data = find_channel(id);
|
||||
if (!data || data->streamtype != kChannelStreamProc
|
||||
|| process_is_stopped(&data->stream.proc)) {
|
||||
|| proc_is_stopped(&data->stream.proc)) {
|
||||
if (show_error) {
|
||||
if (data && data->streamtype != kChannelStreamProc) {
|
||||
emsg(_(e_invchanjob));
|
||||
|
@ -8044,7 +8044,7 @@ M.funcs = {
|
||||
|
||||
]=],
|
||||
name = 'prompt_getprompt',
|
||||
params = { { 'buf', 'any' } },
|
||||
params = { { 'buf', 'integer|string' } },
|
||||
signature = 'prompt_getprompt({buf})',
|
||||
},
|
||||
prompt_setcallback = {
|
||||
@ -8084,7 +8084,7 @@ M.funcs = {
|
||||
|
||||
]=],
|
||||
name = 'prompt_setcallback',
|
||||
params = { { 'buf', 'any' }, { 'expr', 'any' } },
|
||||
params = { { 'buf', 'integer|string' }, { 'expr', 'string|function' } },
|
||||
signature = 'prompt_setcallback({buf}, {expr})',
|
||||
},
|
||||
prompt_setinterrupt = {
|
||||
@ -8101,7 +8101,7 @@ M.funcs = {
|
||||
|
||||
]=],
|
||||
name = 'prompt_setinterrupt',
|
||||
params = { { 'buf', 'any' }, { 'expr', 'any' } },
|
||||
params = { { 'buf', 'integer|string' }, { 'expr', 'string|function' } },
|
||||
signature = 'prompt_setinterrupt({buf}, {expr})',
|
||||
},
|
||||
prompt_setprompt = {
|
||||
@ -8116,7 +8116,7 @@ M.funcs = {
|
||||
<
|
||||
]=],
|
||||
name = 'prompt_setprompt',
|
||||
params = { { 'buf', 'any' }, { 'text', 'any' } },
|
||||
params = { { 'buf', 'integer|string' }, { 'text', 'string' } },
|
||||
signature = 'prompt_setprompt({buf}, {text})',
|
||||
},
|
||||
pum_getpos = {
|
||||
|
@ -49,7 +49,7 @@
|
||||
#include "nvim/event/defs.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/time.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
@ -101,7 +101,7 @@
|
||||
#include "nvim/os/fs.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/os/pty_process.h"
|
||||
#include "nvim/os/pty_proc.h"
|
||||
#include "nvim/os/shell.h"
|
||||
#include "nvim/os/stdpaths_defs.h"
|
||||
#include "nvim/os/time.h"
|
||||
@ -1304,6 +1304,9 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
static int change_start = 0;
|
||||
static int change_end = 0;
|
||||
static hlf_T hlID = (hlf_T)0;
|
||||
bool diffchars_lim_exceeded = false;
|
||||
size_t diffchars_line_len = 0;
|
||||
int *hlresult = NULL;
|
||||
|
||||
if (lnum < 0) { // ignore type error in {lnum} arg
|
||||
lnum = 0;
|
||||
@ -1318,7 +1321,8 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
if (filler_lines == -1 || linestatus == -1) {
|
||||
change_start = MAXCOL;
|
||||
change_end = -1;
|
||||
if (diff_find_change(curwin, lnum, &change_start, &change_end)) {
|
||||
if (diff_find_change(curwin, lnum, &change_start, &change_end, &hlresult,
|
||||
&diffchars_lim_exceeded, &diffchars_line_len)) {
|
||||
hlID = HLF_ADD; // added line
|
||||
} else {
|
||||
hlID = HLF_CHD; // changed line
|
||||
@ -1336,12 +1340,28 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|
||||
if (hlID == HLF_CHD || hlID == HLF_TXD) {
|
||||
int col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}.
|
||||
//
|
||||
if (chardiff() && !diffchars_lim_exceeded) {
|
||||
if (hlID != HLF_ADD) {
|
||||
if (hlresult == NULL) {
|
||||
hlID = HLF_CHD;
|
||||
} else if (hlresult[0] == 2) {
|
||||
hlID = HLF_ADD;
|
||||
} else if ((size_t)col < diffchars_line_len
|
||||
&& (hlresult[col] == 1 || hlresult[col] == -2)) {
|
||||
hlID = HLF_TXD;
|
||||
} else {
|
||||
hlID = HLF_CHD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (col >= change_start && col <= change_end) {
|
||||
hlID = HLF_TXD; // Changed text.
|
||||
} else {
|
||||
hlID = HLF_CHD; // Changed line.
|
||||
}
|
||||
}
|
||||
}
|
||||
rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (hlID + 1);
|
||||
}
|
||||
|
||||
@ -3770,7 +3790,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
Process *proc = &data->stream.proc;
|
||||
Proc *proc = &data->stream.proc;
|
||||
rettv->vval.v_number = proc->pid;
|
||||
}
|
||||
|
||||
@ -3796,12 +3816,12 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->stream.proc.type != kProcessTypePty) {
|
||||
if (data->stream.proc.type != kProcTypePty) {
|
||||
emsg(_(e_channotpty));
|
||||
return;
|
||||
}
|
||||
|
||||
pty_process_resize(&data->stream.pty, (uint16_t)argvars[1].vval.v_number,
|
||||
pty_proc_resize(&data->stream.pty, (uint16_t)argvars[1].vval.v_number,
|
||||
(uint16_t)argvars[2].vval.v_number);
|
||||
rettv->vval.v_number = 1;
|
||||
}
|
||||
@ -4077,7 +4097,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
// Ignore return code, but show error later.
|
||||
channel_close(data->id, kChannelPartRpc, &error);
|
||||
}
|
||||
process_stop(&data->stream.proc);
|
||||
proc_stop(&data->stream.proc);
|
||||
rettv->vval.v_number = 1;
|
||||
if (error) {
|
||||
emsg(error);
|
||||
@ -4113,10 +4133,10 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|| !(chan = find_channel((uint64_t)TV_LIST_ITEM_TV(arg)->vval.v_number))
|
||||
|| chan->streamtype != kChannelStreamProc) {
|
||||
jobs[i] = NULL; // Invalid job.
|
||||
} else if (process_is_stopped(&chan->stream.proc)) {
|
||||
} else if (proc_is_stopped(&chan->stream.proc)) {
|
||||
// Job is stopped but not fully destroyed.
|
||||
// Ensure all callbacks on its event queue are executed. #15402
|
||||
process_wait(&chan->stream.proc, -1, NULL);
|
||||
proc_wait(&chan->stream.proc, -1, NULL);
|
||||
jobs[i] = NULL; // Invalid job.
|
||||
} else {
|
||||
jobs[i] = chan;
|
||||
@ -4144,7 +4164,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
if (jobs[i] == NULL) {
|
||||
continue; // Invalid job, will assign status=-3 below.
|
||||
}
|
||||
int status = process_wait(&jobs[i]->stream.proc, remaining,
|
||||
int status = proc_wait(&jobs[i]->stream.proc, remaining,
|
||||
waiting_jobs);
|
||||
if (status < 0) {
|
||||
break; // Interrupted (CTRL-C) or timeout, skip remaining jobs.
|
||||
@ -8207,7 +8227,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
int pid = chan->stream.pty.process.pid;
|
||||
int pid = chan->stream.pty.proc.pid;
|
||||
|
||||
// "./…" => "/home/foo/…"
|
||||
vim_FullName(cwd, NameBuff, sizeof(NameBuff), false);
|
||||
|
@ -142,30 +142,31 @@ struct socket_watcher {
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
kProcessTypeUv,
|
||||
kProcessTypePty,
|
||||
} ProcessType;
|
||||
kProcTypeUv,
|
||||
kProcTypePty,
|
||||
} ProcType;
|
||||
|
||||
typedef struct process Process;
|
||||
typedef void (*process_exit_cb)(Process *proc, int status, void *data);
|
||||
typedef void (*internal_process_cb)(Process *proc);
|
||||
/// OS process
|
||||
typedef struct proc Proc;
|
||||
typedef void (*proc_exit_cb)(Proc *proc, int status, void *data);
|
||||
typedef void (*internal_proc_cb)(Proc *proc);
|
||||
|
||||
struct process {
|
||||
ProcessType type;
|
||||
struct proc {
|
||||
ProcType type;
|
||||
Loop *loop;
|
||||
void *data;
|
||||
int pid, status, refcount;
|
||||
uint8_t exit_signal; // Signal used when killing (on Windows).
|
||||
uint64_t stopped_time; // process_stop() timestamp
|
||||
uint64_t stopped_time; // proc_stop() timestamp
|
||||
const char *cwd;
|
||||
char **argv;
|
||||
const char *exepath;
|
||||
dict_T *env;
|
||||
Stream in;
|
||||
RStream out, err;
|
||||
/// Exit handler. If set, user must call process_free().
|
||||
process_exit_cb cb;
|
||||
internal_process_cb internal_exit_cb, internal_close_cb;
|
||||
/// Exit handler. If set, user must call proc_free().
|
||||
proc_exit_cb cb;
|
||||
internal_proc_cb internal_exit_cb, internal_close_cb;
|
||||
bool closed, detach, overlapped, fwd_err;
|
||||
MultiQueue *events;
|
||||
};
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/event/defs.h"
|
||||
#include "nvim/event/libuv_process.h"
|
||||
#include "nvim/event/libuv_proc.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
@ -15,15 +15,15 @@
|
||||
#include "nvim/ui_client.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "event/libuv_process.c.generated.h"
|
||||
# include "event/libuv_proc.c.generated.h"
|
||||
#endif
|
||||
|
||||
/// @returns zero on success, or negative error code
|
||||
int libuv_process_spawn(LibuvProcess *uvproc)
|
||||
int libuv_proc_spawn(LibuvProc *uvproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
Process *proc = (Process *)uvproc;
|
||||
uvproc->uvopts.file = process_get_exepath(proc);
|
||||
Proc *proc = (Proc *)uvproc;
|
||||
uvproc->uvopts.file = proc_get_exepath(proc);
|
||||
uvproc->uvopts.args = proc->argv;
|
||||
uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE;
|
||||
#ifdef MSWIN
|
||||
@ -101,7 +101,7 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
||||
return status;
|
||||
}
|
||||
|
||||
void libuv_process_close(LibuvProcess *uvproc)
|
||||
void libuv_proc_close(LibuvProc *uvproc)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
uv_close((uv_handle_t *)&uvproc->uv, close_cb);
|
||||
@ -109,11 +109,11 @@ void libuv_process_close(LibuvProcess *uvproc)
|
||||
|
||||
static void close_cb(uv_handle_t *handle)
|
||||
{
|
||||
Process *proc = handle->data;
|
||||
Proc *proc = handle->data;
|
||||
if (proc->internal_close_cb) {
|
||||
proc->internal_close_cb(proc);
|
||||
}
|
||||
LibuvProcess *uvproc = (LibuvProcess *)proc;
|
||||
LibuvProc *uvproc = (LibuvProc *)proc;
|
||||
if (uvproc->uvopts.env) {
|
||||
os_free_fullenv(uvproc->uvopts.env);
|
||||
}
|
||||
@ -121,7 +121,7 @@ static void close_cb(uv_handle_t *handle)
|
||||
|
||||
static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
|
||||
{
|
||||
Process *proc = handle->data;
|
||||
Proc *proc = handle->data;
|
||||
#if defined(MSWIN)
|
||||
// Use stored/expected signal.
|
||||
term_signal = proc->exit_signal;
|
||||
@ -130,10 +130,10 @@ static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
|
||||
proc->internal_exit_cb(proc);
|
||||
}
|
||||
|
||||
LibuvProcess libuv_process_init(Loop *loop, void *data)
|
||||
LibuvProc libuv_proc_init(Loop *loop, void *data)
|
||||
{
|
||||
LibuvProcess rv = {
|
||||
.process = process_init(loop, kProcessTypeUv, data)
|
||||
LibuvProc rv = {
|
||||
.proc = proc_init(loop, kProcTypeUv, data)
|
||||
};
|
||||
return rv;
|
||||
}
|
@ -5,12 +5,12 @@
|
||||
#include "nvim/event/defs.h"
|
||||
|
||||
typedef struct {
|
||||
Process process;
|
||||
Proc proc;
|
||||
uv_process_t uv;
|
||||
uv_process_options_t uvopts;
|
||||
uv_stdio_container_t uvstdio[4];
|
||||
} LibuvProcess;
|
||||
} LibuvProc;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "event/libuv_process.h.generated.h"
|
||||
# include "event/libuv_proc.h.generated.h"
|
||||
#endif
|
@ -4,24 +4,24 @@
|
||||
#include <uv.h>
|
||||
|
||||
#include "klib/klist.h"
|
||||
#include "nvim/event/libuv_process.h"
|
||||
#include "nvim/event/libuv_proc.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/event/stream.h"
|
||||
#include "nvim/event/wstream.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/os/process.h"
|
||||
#include "nvim/os/pty_process.h"
|
||||
#include "nvim/os/proc.h"
|
||||
#include "nvim/os/pty_proc.h"
|
||||
#include "nvim/os/shell.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/ui_client.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "event/process.c.generated.h"
|
||||
# include "event/proc.c.generated.h"
|
||||
#endif
|
||||
|
||||
// Time for a process to exit cleanly before we send KILL.
|
||||
@ -33,13 +33,13 @@
|
||||
void __gcov_flush(void);
|
||||
#endif
|
||||
|
||||
static bool process_is_tearing_down = false;
|
||||
static bool proc_is_tearing_down = false;
|
||||
|
||||
// Delay exit until handles are closed, to avoid deadlocks
|
||||
static int exit_need_delay = 0;
|
||||
|
||||
/// @returns zero on success, or negative error code
|
||||
int process_spawn(Process *proc, bool in, bool out, bool err)
|
||||
int proc_spawn(Proc *proc, bool in, bool out, bool err)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// forwarding stderr contradicts with processing it internally
|
||||
@ -70,11 +70,11 @@ int process_spawn(Process *proc, bool in, bool out, bool err)
|
||||
|
||||
int status;
|
||||
switch (proc->type) {
|
||||
case kProcessTypeUv:
|
||||
status = libuv_process_spawn((LibuvProcess *)proc);
|
||||
case kProcTypeUv:
|
||||
status = libuv_proc_spawn((LibuvProc *)proc);
|
||||
break;
|
||||
case kProcessTypePty:
|
||||
status = pty_process_spawn((PtyProcess *)proc);
|
||||
case kProcTypePty:
|
||||
status = pty_proc_spawn((PtyProc *)proc);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -89,12 +89,12 @@ int process_spawn(Process *proc, bool in, bool out, bool err)
|
||||
uv_close((uv_handle_t *)&proc->err.s.uv.pipe, NULL);
|
||||
}
|
||||
|
||||
if (proc->type == kProcessTypeUv) {
|
||||
uv_close((uv_handle_t *)&(((LibuvProcess *)proc)->uv), NULL);
|
||||
if (proc->type == kProcTypeUv) {
|
||||
uv_close((uv_handle_t *)&(((LibuvProc *)proc)->uv), NULL);
|
||||
} else {
|
||||
process_close(proc);
|
||||
proc_close(proc);
|
||||
}
|
||||
process_free(proc);
|
||||
proc_free(proc);
|
||||
proc->status = -1;
|
||||
return status;
|
||||
}
|
||||
@ -102,52 +102,52 @@ int process_spawn(Process *proc, bool in, bool out, bool err)
|
||||
if (in) {
|
||||
stream_init(NULL, &proc->in, -1, (uv_stream_t *)&proc->in.uv.pipe);
|
||||
proc->in.internal_data = proc;
|
||||
proc->in.internal_close_cb = on_process_stream_close;
|
||||
proc->in.internal_close_cb = on_proc_stream_close;
|
||||
proc->refcount++;
|
||||
}
|
||||
|
||||
if (out) {
|
||||
stream_init(NULL, &proc->out.s, -1, (uv_stream_t *)&proc->out.s.uv.pipe);
|
||||
proc->out.s.internal_data = proc;
|
||||
proc->out.s.internal_close_cb = on_process_stream_close;
|
||||
proc->out.s.internal_close_cb = on_proc_stream_close;
|
||||
proc->refcount++;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
stream_init(NULL, &proc->err.s, -1, (uv_stream_t *)&proc->err.s.uv.pipe);
|
||||
proc->err.s.internal_data = proc;
|
||||
proc->err.s.internal_close_cb = on_process_stream_close;
|
||||
proc->err.s.internal_close_cb = on_proc_stream_close;
|
||||
proc->refcount++;
|
||||
}
|
||||
|
||||
proc->internal_exit_cb = on_process_exit;
|
||||
proc->internal_exit_cb = on_proc_exit;
|
||||
proc->internal_close_cb = decref;
|
||||
proc->refcount++;
|
||||
kl_push(WatcherPtr, proc->loop->children, proc);
|
||||
DLOG("new: pid=%d exepath=[%s]", proc->pid, process_get_exepath(proc));
|
||||
DLOG("new: pid=%d exepath=[%s]", proc->pid, proc_get_exepath(proc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
|
||||
void proc_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
process_is_tearing_down = true;
|
||||
proc_is_tearing_down = true;
|
||||
kl_iter(WatcherPtr, loop->children, current) {
|
||||
Process *proc = (*current)->data;
|
||||
if (proc->detach || proc->type == kProcessTypePty) {
|
||||
Proc *proc = (*current)->data;
|
||||
if (proc->detach || proc->type == kProcTypePty) {
|
||||
// Close handles to process without killing it.
|
||||
CREATE_EVENT(loop->events, process_close_handles, proc);
|
||||
CREATE_EVENT(loop->events, proc_close_handles, proc);
|
||||
} else {
|
||||
process_stop(proc);
|
||||
proc_stop(proc);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until all children exit and all close events are processed.
|
||||
LOOP_PROCESS_EVENTS_UNTIL(loop, loop->events, -1,
|
||||
kl_empty(loop->children) && multiqueue_empty(loop->events));
|
||||
pty_process_teardown(loop);
|
||||
pty_proc_teardown(loop);
|
||||
}
|
||||
|
||||
void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
void proc_close_streams(Proc *proc) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
wstream_may_close(&proc->in);
|
||||
rstream_may_close(&proc->out);
|
||||
@ -162,7 +162,7 @@ void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
/// @return Exit code of the process. proc->status will have the same value.
|
||||
/// -1 if the timeout expired while the process is still running.
|
||||
/// -2 if the user interrupted the wait.
|
||||
int process_wait(Process *proc, int ms, MultiQueue *events)
|
||||
int proc_wait(Proc *proc, int ms, MultiQueue *events)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
if (!proc->refcount) {
|
||||
@ -186,7 +186,7 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
|
||||
// Assume that a user hitting CTRL-C does not like the current job. Kill it.
|
||||
if (got_int) {
|
||||
got_int = false;
|
||||
process_stop(proc);
|
||||
proc_stop(proc);
|
||||
if (ms == -1) {
|
||||
// We can only return if all streams/handles are closed and the job
|
||||
// exited.
|
||||
@ -214,7 +214,7 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
|
||||
}
|
||||
|
||||
/// Ask a process to terminate and eventually kill if it doesn't respond
|
||||
void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
void proc_stop(Proc *proc) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
bool exited = (proc->status >= 0);
|
||||
if (exited || proc->stopped_time) {
|
||||
@ -224,13 +224,13 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
proc->exit_signal = SIGTERM;
|
||||
|
||||
switch (proc->type) {
|
||||
case kProcessTypeUv:
|
||||
case kProcTypeUv:
|
||||
os_proc_tree_kill(proc->pid, SIGTERM);
|
||||
break;
|
||||
case kProcessTypePty:
|
||||
case kProcTypePty:
|
||||
// close all streams for pty processes to send SIGHUP to the process
|
||||
process_close_streams(proc);
|
||||
pty_process_close_master((PtyProcess *)proc);
|
||||
proc_close_streams(proc);
|
||||
pty_proc_close_master((PtyProc *)proc);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
}
|
||||
|
||||
/// Frees process-owned resources.
|
||||
void process_free(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
void proc_free(Proc *proc) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (proc->argv != NULL) {
|
||||
shell_free_argv(proc->argv);
|
||||
@ -249,19 +249,19 @@ void process_free(Process *proc) FUNC_ATTR_NONNULL_ALL
|
||||
}
|
||||
|
||||
/// Sends SIGKILL (or SIGTERM..SIGKILL for PTY jobs) to processes that did
|
||||
/// not terminate after process_stop().
|
||||
/// not terminate after proc_stop().
|
||||
static void children_kill_cb(uv_timer_t *handle)
|
||||
{
|
||||
Loop *loop = handle->loop->data;
|
||||
|
||||
kl_iter(WatcherPtr, loop->children, current) {
|
||||
Process *proc = (*current)->data;
|
||||
Proc *proc = (*current)->data;
|
||||
bool exited = (proc->status >= 0);
|
||||
if (exited || !proc->stopped_time) {
|
||||
continue;
|
||||
}
|
||||
uint64_t term_sent = UINT64_MAX == proc->stopped_time;
|
||||
if (kProcessTypePty != proc->type || term_sent) {
|
||||
if (kProcTypePty != proc->type || term_sent) {
|
||||
proc->exit_signal = SIGKILL;
|
||||
os_proc_tree_kill(proc->pid, SIGKILL);
|
||||
} else {
|
||||
@ -275,19 +275,19 @@ static void children_kill_cb(uv_timer_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
static void process_close_event(void **argv)
|
||||
static void proc_close_event(void **argv)
|
||||
{
|
||||
Process *proc = argv[0];
|
||||
Proc *proc = argv[0];
|
||||
if (proc->cb) {
|
||||
// User (hint: channel_job_start) is responsible for calling
|
||||
// process_free().
|
||||
// proc_free().
|
||||
proc->cb(proc, proc->status, proc->data);
|
||||
} else {
|
||||
process_free(proc);
|
||||
proc_free(proc);
|
||||
}
|
||||
}
|
||||
|
||||
static void decref(Process *proc)
|
||||
static void decref(Proc *proc)
|
||||
{
|
||||
if (--proc->refcount != 0) {
|
||||
return;
|
||||
@ -303,13 +303,13 @@ static void decref(Process *proc)
|
||||
}
|
||||
assert(node);
|
||||
kl_shift_at(WatcherPtr, loop->children, node);
|
||||
CREATE_EVENT(proc->events, process_close_event, proc);
|
||||
CREATE_EVENT(proc->events, proc_close_event, proc);
|
||||
}
|
||||
|
||||
static void process_close(Process *proc)
|
||||
static void proc_close(Proc *proc)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
if (process_is_tearing_down && (proc->detach || proc->type == kProcessTypePty)
|
||||
if (proc_is_tearing_down && (proc->detach || proc->type == kProcTypePty)
|
||||
&& proc->closed) {
|
||||
// If a detached/pty process dies while tearing down it might get closed
|
||||
// twice.
|
||||
@ -319,17 +319,17 @@ static void process_close(Process *proc)
|
||||
proc->closed = true;
|
||||
|
||||
if (proc->detach) {
|
||||
if (proc->type == kProcessTypeUv) {
|
||||
uv_unref((uv_handle_t *)&(((LibuvProcess *)proc)->uv));
|
||||
if (proc->type == kProcTypeUv) {
|
||||
uv_unref((uv_handle_t *)&(((LibuvProc *)proc)->uv));
|
||||
}
|
||||
}
|
||||
|
||||
switch (proc->type) {
|
||||
case kProcessTypeUv:
|
||||
libuv_process_close((LibuvProcess *)proc);
|
||||
case kProcTypeUv:
|
||||
libuv_proc_close((LibuvProc *)proc);
|
||||
break;
|
||||
case kProcessTypePty:
|
||||
pty_process_close((PtyProcess *)proc);
|
||||
case kProcTypePty:
|
||||
pty_proc_close((PtyProc *)proc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -338,7 +338,7 @@ static void process_close(Process *proc)
|
||||
///
|
||||
/// @param proc Process, for which an output stream should be flushed.
|
||||
/// @param stream Stream to flush.
|
||||
static void flush_stream(Process *proc, RStream *stream)
|
||||
static void flush_stream(Proc *proc, RStream *stream)
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
if (!stream || stream->s.closed) {
|
||||
@ -382,16 +382,16 @@ static void flush_stream(Process *proc, RStream *stream)
|
||||
}
|
||||
}
|
||||
|
||||
static void process_close_handles(void **argv)
|
||||
static void proc_close_handles(void **argv)
|
||||
{
|
||||
Process *proc = argv[0];
|
||||
Proc *proc = argv[0];
|
||||
|
||||
exit_need_delay++;
|
||||
flush_stream(proc, &proc->out);
|
||||
flush_stream(proc, &proc->err);
|
||||
|
||||
process_close_streams(proc);
|
||||
process_close(proc);
|
||||
proc_close_streams(proc);
|
||||
proc_close(proc);
|
||||
exit_need_delay--;
|
||||
}
|
||||
|
||||
@ -426,7 +426,7 @@ void exit_from_channel(int status)
|
||||
multiqueue_put(main_loop.fast_events, exit_event, (void *)(intptr_t)status);
|
||||
}
|
||||
|
||||
static void on_process_exit(Process *proc)
|
||||
static void on_proc_exit(Proc *proc)
|
||||
{
|
||||
Loop *loop = proc->loop;
|
||||
ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status,
|
||||
@ -439,13 +439,13 @@ static void on_process_exit(Process *proc)
|
||||
// Process has terminated, but there could still be data to be read from the
|
||||
// OS. We are still in the libuv loop, so we cannot call code that polls for
|
||||
// more data directly. Instead delay the reading after the libuv loop by
|
||||
// queueing process_close_handles() as an event.
|
||||
// queueing proc_close_handles() as an event.
|
||||
MultiQueue *queue = proc->events ? proc->events : loop->events;
|
||||
CREATE_EVENT(queue, process_close_handles, proc);
|
||||
CREATE_EVENT(queue, proc_close_handles, proc);
|
||||
}
|
||||
|
||||
static void on_process_stream_close(Stream *stream, void *data)
|
||||
static void on_proc_stream_close(Stream *stream, void *data)
|
||||
{
|
||||
Process *proc = data;
|
||||
Proc *proc = data;
|
||||
decref(proc);
|
||||
}
|
@ -6,9 +6,9 @@
|
||||
#include "nvim/event/defs.h" // IWYU pragma: keep
|
||||
#include "nvim/types_defs.h"
|
||||
|
||||
static inline Process process_init(Loop *loop, ProcessType type, void *data)
|
||||
static inline Proc proc_init(Loop *loop, ProcType type, void *data)
|
||||
{
|
||||
return (Process) {
|
||||
return (Proc) {
|
||||
.type = type,
|
||||
.data = data,
|
||||
.loop = loop,
|
||||
@ -33,17 +33,17 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data)
|
||||
}
|
||||
|
||||
/// Get the path to the executable of the process.
|
||||
static inline const char *process_get_exepath(Process *proc)
|
||||
static inline const char *proc_get_exepath(Proc *proc)
|
||||
{
|
||||
return proc->exepath != NULL ? proc->exepath : proc->argv[0];
|
||||
}
|
||||
|
||||
static inline bool process_is_stopped(Process *proc)
|
||||
static inline bool proc_is_stopped(Proc *proc)
|
||||
{
|
||||
bool exited = (proc->status >= 0);
|
||||
return exited || (proc->stopped_time != 0);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "event/process.h.generated.h"
|
||||
# include "event/proc.h.generated.h"
|
||||
#endif
|
@ -38,38 +38,6 @@ static size_t line_len(const char *s)
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
/// Same as matching_chars but ignore whitespace
|
||||
///
|
||||
/// @param s1
|
||||
/// @param s2
|
||||
static int matching_chars_iwhite(const char *s1, const char *s2)
|
||||
{
|
||||
// the newly processed strings that will be compared
|
||||
// delete the white space characters, and/or replace all upper case with lower
|
||||
char *strsproc[2];
|
||||
const char *strsorig[2] = { s1, s2 };
|
||||
for (int k = 0; k < 2; k++) {
|
||||
size_t d = 0;
|
||||
size_t i = 0;
|
||||
size_t slen = line_len(strsorig[k]);
|
||||
strsproc[k] = xmalloc((slen + 1) * sizeof(char));
|
||||
while (d + i < slen) {
|
||||
char e = strsorig[k][i + d];
|
||||
if (e != ' ' && e != '\t') {
|
||||
strsproc[k][i] = e;
|
||||
i++;
|
||||
} else {
|
||||
d++;
|
||||
}
|
||||
}
|
||||
strsproc[k][i] = NUL;
|
||||
}
|
||||
int matching = matching_chars(strsproc[0], strsproc[1]);
|
||||
xfree(strsproc[0]);
|
||||
xfree(strsproc[1]);
|
||||
return matching;
|
||||
}
|
||||
|
||||
#define MATCH_CHAR_MAX_LEN 800
|
||||
|
||||
/// Return matching characters between "s1" and "s2" whilst respecting sequence order.
|
||||
@ -119,7 +87,7 @@ static int matching_chars(const char *s1, const char *s2)
|
||||
/// @param sp
|
||||
/// @param fomvals
|
||||
/// @param n
|
||||
static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
|
||||
static int count_n_matched_chars(const char **sp, const size_t n)
|
||||
{
|
||||
int matched_chars = 0;
|
||||
int matched = 0;
|
||||
@ -127,9 +95,7 @@ static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
|
||||
for (size_t j = i + 1; j < n; j++) {
|
||||
if (sp[i] != NULL && sp[j] != NULL) {
|
||||
matched++;
|
||||
// TODO(lewis6991): handle whitespace ignoring higher up in the stack
|
||||
matched_chars += iwhite ? matching_chars_iwhite(sp[i], sp[j])
|
||||
: matching_chars(sp[i], sp[j]);
|
||||
matched_chars += matching_chars(sp[i], sp[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,7 +111,7 @@ static int count_n_matched_chars(const char **sp, const size_t n, bool iwhite)
|
||||
|
||||
void fastforward_buf_to_lnum(const char **s, linenr_T lnum)
|
||||
{
|
||||
for (int i = 0; i < lnum - 1; i++) {
|
||||
for (long i = 0; i < lnum; i++) {
|
||||
*s = strchr(*s, '\n');
|
||||
if (!*s) {
|
||||
return;
|
||||
@ -168,20 +134,32 @@ void fastforward_buf_to_lnum(const char **s, linenr_T lnum)
|
||||
static void try_possible_paths(const int *df_iters, const size_t *paths, const int npaths,
|
||||
const int path_idx, int *choice, diffcmppath_T *diffcmppath,
|
||||
const int *diff_len, const size_t ndiffs, const char **diff_blk,
|
||||
bool iwhite)
|
||||
bool charmatch, size_t **word_offset, size_t **word_offset_size)
|
||||
{
|
||||
if (path_idx == npaths) {
|
||||
if ((*choice) > 0) {
|
||||
int from_vals[LN_MAX_BUFS] = { 0 };
|
||||
const int *to_vals = df_iters;
|
||||
const char *current_lines[LN_MAX_BUFS];
|
||||
size_t word_len[LN_MAX_BUFS];
|
||||
for (size_t k = 0; k < ndiffs; k++) {
|
||||
from_vals[k] = df_iters[k];
|
||||
// get the index at all of the places
|
||||
if ((*choice) & (1 << k)) {
|
||||
from_vals[k]--;
|
||||
const char *p = diff_blk[k];
|
||||
fastforward_buf_to_lnum(&p, df_iters[k]);
|
||||
if (charmatch) {
|
||||
if (word_offset[k] != NULL) {
|
||||
// get start position
|
||||
p += word_offset[k][df_iters[k] - 1];
|
||||
word_len[k] = word_offset_size[k][df_iters[k] - 1];
|
||||
// index of the word for comparison
|
||||
} else {
|
||||
p += df_iters[k] - 1; // advance by the character count
|
||||
}
|
||||
} else {
|
||||
fastforward_buf_to_lnum(&p, df_iters[k] - 1);
|
||||
}
|
||||
current_lines[k] = p;
|
||||
} else {
|
||||
current_lines[k] = NULL;
|
||||
@ -189,7 +167,42 @@ static void try_possible_paths(const int *df_iters, const size_t *paths, const i
|
||||
}
|
||||
size_t unwrapped_idx_from = unwrap_indexes(from_vals, diff_len, ndiffs);
|
||||
size_t unwrapped_idx_to = unwrap_indexes(to_vals, diff_len, ndiffs);
|
||||
int matched_chars = count_n_matched_chars(current_lines, ndiffs, iwhite);
|
||||
|
||||
int matched_chars = 0;
|
||||
if (charmatch) {
|
||||
// only two valid options for charmatch
|
||||
// 1. skip of a single character
|
||||
// 2. a combination of 'ndiffs' characters, when not equal to '\n'
|
||||
char t[256];
|
||||
t[0] = '\0';
|
||||
size_t t_l = 0;
|
||||
size_t compared = 0;
|
||||
for (size_t i = 0; i < ndiffs; i++) {
|
||||
if (current_lines[i] != NULL) {
|
||||
compared++;
|
||||
if ((t[0] != '\0' && !compare(current_lines[i],
|
||||
(word_offset[i] != NULL ? word_len[i] : 1), t, t_l)) // if theres more than one to compare, and
|
||||
// they're not matching
|
||||
|| (t[0] != '\0' && current_lines[i][0] == '\n')) { // if there's more than one to compare, and
|
||||
// at least one is a '\n'
|
||||
return; // not a possible path
|
||||
} else if (t[0] != '\0') {
|
||||
matched_chars = 1; // comparison of all buffers
|
||||
}
|
||||
size_t this_word_length = word_offset[i] != NULL ? word_len[i] : 1;
|
||||
t_l = this_word_length;
|
||||
for (size_t l = 0; l < this_word_length; l++) {
|
||||
t[l] = current_lines[i][l];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(compared == ndiffs || compared == 1)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
matched_chars = count_n_matched_chars(current_lines, ndiffs);
|
||||
}
|
||||
|
||||
int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars;
|
||||
if (score > diffcmppath[unwrapped_idx_to].df_lev_score) {
|
||||
diffcmppath[unwrapped_idx_to].df_path_n = 1;
|
||||
@ -207,10 +220,12 @@ static void try_possible_paths(const int *df_iters, const size_t *paths, const i
|
||||
size_t bit_place = paths[path_idx];
|
||||
*(choice) |= (1 << bit_place); // set it to 1
|
||||
try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
|
||||
diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
|
||||
diffcmppath, diff_len, ndiffs, diff_blk, charmatch, word_offset,
|
||||
word_offset_size);
|
||||
*(choice) &= ~(1 << bit_place); // set it to 0
|
||||
try_possible_paths(df_iters, paths, npaths, path_idx + 1, choice,
|
||||
diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
|
||||
diffcmppath, diff_len, ndiffs, diff_blk, charmatch, word_offset,
|
||||
word_offset_size);
|
||||
}
|
||||
|
||||
/// unwrap indexes to access n dimensional tensor
|
||||
@ -245,7 +260,7 @@ static size_t unwrap_indexes(const int *values, const int *diff_len, const size_
|
||||
/// @param diff_blk
|
||||
static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *diffcmppath,
|
||||
const int *diff_len, const size_t ndiffs, const char **diff_blk,
|
||||
bool iwhite)
|
||||
bool charmatch, size_t **word_offset, size_t **word_offset_size)
|
||||
{
|
||||
if (ch_dim == ndiffs) {
|
||||
int npaths = 0;
|
||||
@ -259,16 +274,19 @@ static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *d
|
||||
}
|
||||
int choice = 0;
|
||||
size_t unwrapper_idx_to = unwrap_indexes(df_iters, diff_len, ndiffs);
|
||||
if (unwrapper_idx_to > 0) {
|
||||
diffcmppath[unwrapper_idx_to].df_lev_score = -1;
|
||||
}
|
||||
try_possible_paths(df_iters, paths, npaths, 0, &choice, diffcmppath,
|
||||
diff_len, ndiffs, diff_blk, iwhite);
|
||||
diff_len, ndiffs, diff_blk, charmatch, word_offset,
|
||||
word_offset_size);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= diff_len[ch_dim]; i++) {
|
||||
df_iters[ch_dim] = i;
|
||||
populate_tensor(df_iters, ch_dim + 1, diffcmppath, diff_len,
|
||||
ndiffs, diff_blk, iwhite);
|
||||
ndiffs, diff_blk, charmatch, word_offset, word_offset_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +346,8 @@ static void populate_tensor(int *df_iters, const size_t ch_dim, diffcmppath_T *d
|
||||
/// @param [out] [allocated] decisions
|
||||
/// @return the length of decisions
|
||||
size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size_t ndiffs,
|
||||
int **decisions, bool iwhite)
|
||||
int **decisions, bool charmatch, size_t **word_offset,
|
||||
size_t **word_offset_size)
|
||||
{
|
||||
assert(ndiffs <= LN_MAX_BUFS);
|
||||
|
||||
@ -353,7 +372,8 @@ size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size
|
||||
|
||||
// memory for avoiding repetitive calculations of score
|
||||
int df_iters[LN_MAX_BUFS];
|
||||
populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, iwhite);
|
||||
populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, charmatch,
|
||||
word_offset, word_offset_size);
|
||||
|
||||
const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs);
|
||||
diffcmppath_T *startNode = &diffcmppath[u];
|
||||
@ -402,3 +422,16 @@ static size_t test_charmatch_paths(diffcmppath_T *node, int lastdecision)
|
||||
}
|
||||
return (size_t)node->df_choice_mem[lastdecision];
|
||||
}
|
||||
// return true if these two strings are equal
|
||||
static bool compare(const char *s1, size_t l1, const char *s2, size_t l2)
|
||||
{
|
||||
if (l1 != l2) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < l1; i++) {
|
||||
if (s1[i] != s2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -67,14 +67,29 @@ static void get_linematch_results(lua_State *lstate, mmfile_t *ma, mmfile_t *mb,
|
||||
int count_a, int start_b, int count_b, bool iwhite)
|
||||
{
|
||||
// get the pointer to char of the start of the diff to pass it to linematch algorithm
|
||||
const char *diff_begin[2] = { ma->ptr, mb->ptr };
|
||||
char *diff_begin[2] = { ma->ptr, mb->ptr };
|
||||
int diff_length[2] = { count_a, count_b };
|
||||
|
||||
fastforward_buf_to_lnum(&diff_begin[0], (linenr_T)start_a + 1);
|
||||
fastforward_buf_to_lnum(&diff_begin[1], (linenr_T)start_b + 1);
|
||||
fastforward_buf_to_lnum((const char **)(&diff_begin[0]), (linenr_T)start_a + 1);
|
||||
fastforward_buf_to_lnum((const char **)(&diff_begin[1]), (linenr_T)start_b + 1);
|
||||
|
||||
if (iwhite) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
size_t j = 0, k = 0, lines = (size_t)diff_length[i];
|
||||
while (lines > 0) {
|
||||
if (diff_begin[i][j] != ' ' && diff_begin[i][j] != '\t') {
|
||||
diff_begin[i][k++] = diff_begin[i][j];
|
||||
}
|
||||
if (diff_begin[i][j++] == '\n') {
|
||||
lines--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int *decisions = NULL;
|
||||
size_t decisions_length = linematch_nbuffers(diff_begin, diff_length, 2, &decisions, iwhite);
|
||||
size_t decisions_length = linematch_nbuffers((const char **)diff_begin, diff_length, 2,
|
||||
&decisions, 0, NULL, NULL);
|
||||
|
||||
int lnuma = start_a;
|
||||
int lnumb = start_b;
|
||||
|
@ -43,7 +43,7 @@
|
||||
#include "nvim/eval/userfunc.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/stream.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
@ -174,7 +174,7 @@ bool event_teardown(void)
|
||||
loop_poll_events(&main_loop, 0); // Drain thread_events, fast_events.
|
||||
input_stop();
|
||||
channel_teardown();
|
||||
process_teardown(&main_loop);
|
||||
proc_teardown(&main_loop);
|
||||
timer_teardown();
|
||||
server_teardown();
|
||||
signal_teardown();
|
||||
@ -2207,7 +2207,7 @@ static void usage(void)
|
||||
printf(_(" --headless Don't start a user interface\n"));
|
||||
printf(_(" --listen <address> Serve RPC API from this address\n"));
|
||||
printf(_(" --remote[-subcommand] Execute commands remotely on a server\n"));
|
||||
printf(_(" --server <address> Specify RPC server to send commands to\n"));
|
||||
printf(_(" --server <address> Connect to this Nvim server\n"));
|
||||
printf(_(" --startuptime <file> Write startup timing messages to <file>\n"));
|
||||
printf(_("\nSee \":help startup-options\" for all options.\n"));
|
||||
}
|
||||
|
@ -84,7 +84,7 @@
|
||||
#include "nvim/os/input.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/os/process.h"
|
||||
#include "nvim/os/proc.h"
|
||||
#include "nvim/os/time.h"
|
||||
#include "nvim/os/time_defs.h"
|
||||
#include "nvim/path.h"
|
||||
@ -743,7 +743,7 @@ static void add_b0_fenc(ZeroBlock *b0p, buf_T *buf)
|
||||
/// @param swap_fname Name of the swapfile. If it's from before a reboot, the result is 0.
|
||||
///
|
||||
/// @return PID, or 0 if process is not running or the swapfile is from before a reboot.
|
||||
static int swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname)
|
||||
static int swapfile_proc_running(const ZeroBlock *b0p, const char *swap_fname)
|
||||
{
|
||||
FileInfo st;
|
||||
double uptime;
|
||||
@ -1214,7 +1214,7 @@ void ml_recover(bool checkext)
|
||||
msg(_("Recovery completed. Buffer contents equals file contents."), 0);
|
||||
}
|
||||
msg_puts(_("\nYou may want to delete the .swp file now."));
|
||||
if (swapfile_process_running(b0p, fname_used)) {
|
||||
if (swapfile_proc_running(b0p, fname_used)) {
|
||||
// Warn there could be an active Vim on the same file, the user may
|
||||
// want to kill it.
|
||||
msg_puts(_("\nNote: process STILL RUNNING: "));
|
||||
@ -1462,7 +1462,7 @@ char *make_percent_swname(char *dir, char *dir_end, const char *name)
|
||||
}
|
||||
|
||||
// PID of swapfile owner, or zero if not running.
|
||||
static int process_running;
|
||||
static int proc_running;
|
||||
|
||||
/// For Vimscript "swapinfo()".
|
||||
///
|
||||
@ -1488,7 +1488,7 @@ void swapfile_dict(const char *fname, dict_T *d)
|
||||
tv_dict_add_str_len(d, S_LEN("fname"), b0.b0_fname,
|
||||
B0_FNAME_SIZE_ORG);
|
||||
|
||||
tv_dict_add_nr(d, S_LEN("pid"), swapfile_process_running(&b0, fname));
|
||||
tv_dict_add_nr(d, S_LEN("pid"), swapfile_proc_running(&b0, fname));
|
||||
tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime));
|
||||
tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0);
|
||||
tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino));
|
||||
@ -1572,7 +1572,7 @@ static time_t swapfile_info(char *fname)
|
||||
if (char_to_long(b0.b0_pid) != 0) {
|
||||
msg_puts(_("\n process ID: "));
|
||||
msg_outnum((int)char_to_long(b0.b0_pid));
|
||||
if ((process_running = swapfile_process_running(&b0, fname))) {
|
||||
if ((proc_running = swapfile_proc_running(&b0, fname))) {
|
||||
msg_puts(_(" (STILL RUNNING)"));
|
||||
}
|
||||
}
|
||||
@ -1640,7 +1640,7 @@ static bool swapfile_unchanged(char *fname)
|
||||
}
|
||||
|
||||
// process must be known and not running.
|
||||
if (char_to_long(b0.b0_pid) == 0 || swapfile_process_running(&b0, fname)) {
|
||||
if (char_to_long(b0.b0_pid) == 0 || swapfile_proc_running(&b0, fname)) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
@ -3399,7 +3399,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
||||
fd = os_open(fname, O_RDONLY, 0);
|
||||
if (fd >= 0) {
|
||||
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
|
||||
process_running = swapfile_process_running(&b0, fname);
|
||||
proc_running = swapfile_proc_running(&b0, fname);
|
||||
|
||||
// If the swapfile has the same directory as the
|
||||
// buffer don't compare the directory names, they can
|
||||
@ -3459,7 +3459,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
||||
choice = SEA_CHOICE_READONLY;
|
||||
}
|
||||
|
||||
process_running = 0; // Set by attention_message..swapfile_info.
|
||||
proc_running = 0; // Set by attention_message..swapfile_info.
|
||||
if (choice == SEA_CHOICE_NONE) {
|
||||
// Show info about the existing swapfile.
|
||||
attention_message(buf, fname);
|
||||
@ -3491,12 +3491,12 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
||||
= do_dialog(VIM_WARNING,
|
||||
_("VIM - ATTENTION"),
|
||||
name,
|
||||
process_running
|
||||
proc_running
|
||||
? _("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort")
|
||||
: _("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"),
|
||||
1, NULL, false);
|
||||
|
||||
if (process_running && dialog_result >= 4) {
|
||||
if (proc_running && dialog_result >= 4) {
|
||||
// compensate for missing "Delete it" button
|
||||
dialog_result++;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "nvim/event/defs.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/event/wstream.h"
|
||||
#include "nvim/globals.h"
|
||||
|
@ -344,7 +344,7 @@ char *os_getenvname_at_index(size_t index)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Get the process ID of the Neovim process.
|
||||
/// Get the process ID of the Nvim process.
|
||||
///
|
||||
/// @return the process ID.
|
||||
int64_t os_get_pid(void)
|
||||
|
@ -37,24 +37,24 @@
|
||||
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/os/process.h"
|
||||
#include "nvim/os/proc.h"
|
||||
|
||||
#ifdef MSWIN
|
||||
# include "nvim/api/private/helpers.h"
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/process.c.generated.h"
|
||||
# include "os/proc.c.generated.h"
|
||||
#endif
|
||||
|
||||
#ifdef MSWIN
|
||||
static bool os_proc_tree_kill_rec(HANDLE process, int sig)
|
||||
static bool os_proc_tree_kill_rec(HANDLE proc, int sig)
|
||||
{
|
||||
if (process == NULL) {
|
||||
if (proc == NULL) {
|
||||
return false;
|
||||
}
|
||||
PROCESSENTRY32 pe;
|
||||
DWORD pid = GetProcessId(process);
|
||||
DWORD pid = GetProcessId(proc);
|
||||
|
||||
if (pid != 0) {
|
||||
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
@ -77,7 +77,7 @@ static bool os_proc_tree_kill_rec(HANDLE process, int sig)
|
||||
}
|
||||
|
||||
theend:
|
||||
return (bool)TerminateProcess(process, (unsigned)sig);
|
||||
return (bool)TerminateProcess(proc, (unsigned)sig);
|
||||
}
|
||||
/// Kills process `pid` and its descendants recursively.
|
||||
bool os_proc_tree_kill(int pid, int sig)
|
@ -7,5 +7,5 @@
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/process.h.generated.h"
|
||||
# include "os/proc.h.generated.h"
|
||||
#endif
|
@ -143,7 +143,7 @@ finished:
|
||||
return conpty_object;
|
||||
}
|
||||
|
||||
bool os_conpty_spawn(conpty_t *conpty_object, HANDLE *process_handle, wchar_t *name,
|
||||
bool os_conpty_spawn(conpty_t *conpty_object, HANDLE *proc_handle, wchar_t *name,
|
||||
wchar_t *cmd_line, wchar_t *cwd, wchar_t *env)
|
||||
{
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
@ -159,7 +159,7 @@ bool os_conpty_spawn(conpty_t *conpty_object, HANDLE *process_handle, wchar_t *n
|
||||
&pi)) {
|
||||
return false;
|
||||
}
|
||||
*process_handle = pi.hProcess;
|
||||
*proc_handle = pi.hProcess;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
7
src/nvim/os/pty_proc.h
Normal file
7
src/nvim/os/pty_proc.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef MSWIN
|
||||
# include "nvim/os/pty_proc_win.h"
|
||||
#else
|
||||
# include "nvim/os/pty_proc_unix.h"
|
||||
#endif
|
@ -34,16 +34,16 @@
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/event/defs.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/os/fs.h"
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/os/pty_process.h"
|
||||
#include "nvim/os/pty_process_unix.h"
|
||||
#include "nvim/os/pty_proc.h"
|
||||
#include "nvim/os/pty_proc_unix.h"
|
||||
#include "nvim/types_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/pty_process_unix.c.generated.h"
|
||||
# include "os/pty_proc_unix.c.generated.h"
|
||||
#endif
|
||||
|
||||
#if defined(__sun) && !defined(HAVE_FORKPTY)
|
||||
@ -158,7 +158,7 @@ static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct win
|
||||
#endif
|
||||
|
||||
/// @returns zero on success, or negative error code
|
||||
int pty_process_spawn(PtyProcess *ptyproc)
|
||||
int pty_proc_spawn(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// termios initialized at first use
|
||||
@ -168,7 +168,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
||||
}
|
||||
|
||||
int status = 0; // zero or negative error code (libuv convention)
|
||||
Process *proc = (Process *)ptyproc;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
assert(proc->err.s.closed);
|
||||
uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD);
|
||||
ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 };
|
||||
@ -224,29 +224,29 @@ error:
|
||||
return status;
|
||||
}
|
||||
|
||||
const char *pty_process_tty_name(PtyProcess *ptyproc)
|
||||
const char *pty_proc_tty_name(PtyProc *ptyproc)
|
||||
{
|
||||
return ptsname(ptyproc->tty_fd);
|
||||
}
|
||||
|
||||
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
|
||||
void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
ptyproc->winsize = (struct winsize){ height, width, 0, 0 };
|
||||
ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize);
|
||||
}
|
||||
|
||||
void pty_process_close(PtyProcess *ptyproc)
|
||||
void pty_proc_close(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
pty_process_close_master(ptyproc);
|
||||
Process *proc = (Process *)ptyproc;
|
||||
pty_proc_close_master(ptyproc);
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
if (proc->internal_close_cb) {
|
||||
proc->internal_close_cb(proc);
|
||||
}
|
||||
}
|
||||
|
||||
void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
|
||||
void pty_proc_close_master(PtyProc *ptyproc) FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (ptyproc->tty_fd >= 0) {
|
||||
close(ptyproc->tty_fd);
|
||||
@ -254,12 +254,12 @@ void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL
|
||||
}
|
||||
}
|
||||
|
||||
void pty_process_teardown(Loop *loop)
|
||||
void pty_proc_teardown(Loop *loop)
|
||||
{
|
||||
uv_signal_stop(&loop->children_watcher);
|
||||
}
|
||||
|
||||
static void init_child(PtyProcess *ptyproc)
|
||||
static void init_child(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#if defined(HAVE__NSGETENVIRON)
|
||||
@ -277,13 +277,13 @@ static void init_child(PtyProcess *ptyproc)
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
|
||||
Process *proc = (Process *)ptyproc;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
if (proc->cwd && os_chdir(proc->cwd) != 0) {
|
||||
ELOG("chdir(%s) failed: %s", proc->cwd, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
const char *prog = process_get_exepath(proc);
|
||||
const char *prog = proc_get_exepath(proc);
|
||||
|
||||
assert(proc->env);
|
||||
environ = tv_dict_to_env(proc->env);
|
||||
@ -388,7 +388,7 @@ static void chld_handler(uv_signal_t *handle, int signum)
|
||||
Loop *loop = handle->loop->data;
|
||||
|
||||
kl_iter(WatcherPtr, loop->children, current) {
|
||||
Process *proc = (*current)->data;
|
||||
Proc *proc = (*current)->data;
|
||||
do {
|
||||
pid = waitpid(proc->pid, &stat, WNOHANG);
|
||||
} while (pid < 0 && errno == EINTR);
|
||||
@ -406,10 +406,10 @@ static void chld_handler(uv_signal_t *handle, int signum)
|
||||
}
|
||||
}
|
||||
|
||||
PtyProcess pty_process_init(Loop *loop, void *data)
|
||||
PtyProc pty_proc_init(Loop *loop, void *data)
|
||||
{
|
||||
PtyProcess rv;
|
||||
rv.process = process_init(loop, kProcessTypePty, data);
|
||||
PtyProc rv;
|
||||
rv.proc = proc_init(loop, kProcTypePty, data);
|
||||
rv.width = 80;
|
||||
rv.height = 24;
|
||||
rv.tty_fd = -1;
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
// IWYU pragma: private, include "nvim/os/pty_process.h"
|
||||
// IWYU pragma: private, include "nvim/os/pty_proc.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -7,12 +7,12 @@
|
||||
#include "nvim/event/defs.h"
|
||||
|
||||
typedef struct {
|
||||
Process process;
|
||||
Proc proc;
|
||||
uint16_t width, height;
|
||||
struct winsize winsize;
|
||||
int tty_fd;
|
||||
} PtyProcess;
|
||||
} PtyProc;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/pty_process_unix.h.generated.h"
|
||||
# include "os/pty_proc_unix.h.generated.h"
|
||||
#endif
|
@ -10,20 +10,20 @@
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/pty_conpty_win.h"
|
||||
#include "nvim/os/pty_process_win.h"
|
||||
#include "nvim/os/pty_proc_win.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/pty_process_win.c.generated.h"
|
||||
# include "os/pty_proc_win.c.generated.h"
|
||||
#endif
|
||||
|
||||
static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused)
|
||||
static void CALLBACK pty_proc_finish1(void *context, BOOLEAN unused)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
PtyProcess *ptyproc = (PtyProcess *)context;
|
||||
Process *proc = (Process *)ptyproc;
|
||||
PtyProc *ptyproc = (PtyProc *)context;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
|
||||
os_conpty_free(ptyproc->conpty);
|
||||
// NB: pty_process_finish1() is called on a separate thread,
|
||||
// NB: pty_proc_finish1() is called on a separate thread,
|
||||
// but the timer only works properly if it's started by the main thread.
|
||||
loop_schedule_fast(proc->loop, event_create(start_wait_eof_timer, ptyproc));
|
||||
}
|
||||
@ -31,7 +31,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused)
|
||||
static void start_wait_eof_timer(void **argv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
PtyProcess *ptyproc = (PtyProcess *)argv[0];
|
||||
PtyProc *ptyproc = (PtyProc *)argv[0];
|
||||
|
||||
if (ptyproc->finish_wait != NULL) {
|
||||
uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200);
|
||||
@ -39,15 +39,15 @@ static void start_wait_eof_timer(void **argv)
|
||||
}
|
||||
|
||||
/// @returns zero on success, or negative error code.
|
||||
int pty_process_spawn(PtyProcess *ptyproc)
|
||||
int pty_proc_spawn(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
Process *proc = (Process *)ptyproc;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
int status = 0;
|
||||
conpty_t *conpty_object = NULL;
|
||||
char *in_name = NULL;
|
||||
char *out_name = NULL;
|
||||
HANDLE process_handle = NULL;
|
||||
HANDLE proc_handle = NULL;
|
||||
uv_connect_t *in_req = NULL;
|
||||
uv_connect_t *out_req = NULL;
|
||||
wchar_t *cmd_line = NULL;
|
||||
@ -69,7 +69,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
||||
uv_pipe_connect(in_req,
|
||||
&proc->in.uv.pipe,
|
||||
in_name,
|
||||
pty_process_connect_cb);
|
||||
pty_proc_connect_cb);
|
||||
}
|
||||
|
||||
if (!proc->out.s.closed) {
|
||||
@ -77,7 +77,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
||||
uv_pipe_connect(out_req,
|
||||
&proc->out.s.uv.pipe,
|
||||
out_name,
|
||||
pty_process_connect_cb);
|
||||
pty_proc_connect_cb);
|
||||
}
|
||||
|
||||
if (proc->cwd != NULL) {
|
||||
@ -105,7 +105,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
||||
}
|
||||
|
||||
if (!os_conpty_spawn(conpty_object,
|
||||
&process_handle,
|
||||
&proc_handle,
|
||||
NULL,
|
||||
cmd_line,
|
||||
cwd,
|
||||
@ -114,42 +114,42 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
||||
status = (int)GetLastError();
|
||||
goto cleanup;
|
||||
}
|
||||
proc->pid = (int)GetProcessId(process_handle);
|
||||
proc->pid = (int)GetProcessId(proc_handle);
|
||||
|
||||
uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer);
|
||||
ptyproc->wait_eof_timer.data = (void *)ptyproc;
|
||||
if (!RegisterWaitForSingleObject(&ptyproc->finish_wait,
|
||||
process_handle,
|
||||
pty_process_finish1,
|
||||
proc_handle,
|
||||
pty_proc_finish1,
|
||||
ptyproc,
|
||||
INFINITE,
|
||||
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
// Wait until pty_process_connect_cb is called.
|
||||
// Wait until pty_proc_connect_cb is called.
|
||||
while ((in_req != NULL && in_req->handle != NULL)
|
||||
|| (out_req != NULL && out_req->handle != NULL)) {
|
||||
uv_run(&proc->loop->uv, UV_RUN_ONCE);
|
||||
}
|
||||
|
||||
ptyproc->conpty = conpty_object;
|
||||
ptyproc->process_handle = process_handle;
|
||||
ptyproc->proc_handle = proc_handle;
|
||||
conpty_object = NULL;
|
||||
process_handle = NULL;
|
||||
proc_handle = NULL;
|
||||
|
||||
cleanup:
|
||||
if (status) {
|
||||
// In the case of an error of MultiByteToWideChar or CreateProcessW.
|
||||
ELOG("pty_process_spawn(%s): %s: error code: %d",
|
||||
ELOG("pty_proc_spawn(%s): %s: error code: %d",
|
||||
proc->argv[0], emsg, status);
|
||||
status = os_translate_sys_error(status);
|
||||
}
|
||||
os_conpty_free(conpty_object);
|
||||
xfree(in_name);
|
||||
xfree(out_name);
|
||||
if (process_handle != NULL) {
|
||||
CloseHandle(process_handle);
|
||||
if (proc_handle != NULL) {
|
||||
CloseHandle(proc_handle);
|
||||
}
|
||||
xfree(in_req);
|
||||
xfree(out_req);
|
||||
@ -159,32 +159,32 @@ cleanup:
|
||||
return status;
|
||||
}
|
||||
|
||||
const char *pty_process_tty_name(PtyProcess *ptyproc)
|
||||
const char *pty_proc_tty_name(PtyProc *ptyproc)
|
||||
{
|
||||
return "?";
|
||||
}
|
||||
|
||||
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
|
||||
void pty_proc_resize(PtyProc *ptyproc, uint16_t width, uint16_t height)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
os_conpty_set_size(ptyproc->conpty, width, height);
|
||||
}
|
||||
|
||||
void pty_process_close(PtyProcess *ptyproc)
|
||||
void pty_proc_close(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
Process *proc = (Process *)ptyproc;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
|
||||
pty_process_close_master(ptyproc);
|
||||
pty_proc_close_master(ptyproc);
|
||||
|
||||
if (ptyproc->finish_wait != NULL) {
|
||||
UnregisterWaitEx(ptyproc->finish_wait, NULL);
|
||||
ptyproc->finish_wait = NULL;
|
||||
uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL);
|
||||
}
|
||||
if (ptyproc->process_handle != NULL) {
|
||||
CloseHandle(ptyproc->process_handle);
|
||||
ptyproc->process_handle = NULL;
|
||||
if (ptyproc->proc_handle != NULL) {
|
||||
CloseHandle(ptyproc->proc_handle);
|
||||
ptyproc->proc_handle = NULL;
|
||||
}
|
||||
|
||||
if (proc->internal_close_cb) {
|
||||
@ -192,17 +192,17 @@ void pty_process_close(PtyProcess *ptyproc)
|
||||
}
|
||||
}
|
||||
|
||||
void pty_process_close_master(PtyProcess *ptyproc)
|
||||
void pty_proc_close_master(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
}
|
||||
|
||||
void pty_process_teardown(Loop *loop)
|
||||
void pty_proc_teardown(Loop *loop)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
}
|
||||
|
||||
static void pty_process_connect_cb(uv_connect_t *req, int status)
|
||||
static void pty_proc_connect_cb(uv_connect_t *req, int status)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
assert(status == 0);
|
||||
@ -212,23 +212,23 @@ static void pty_process_connect_cb(uv_connect_t *req, int status)
|
||||
static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
PtyProcess *ptyproc = wait_eof_timer->data;
|
||||
Process *proc = (Process *)ptyproc;
|
||||
PtyProc *ptyproc = wait_eof_timer->data;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
|
||||
assert(ptyproc->finish_wait != NULL);
|
||||
if (proc->out.s.closed || proc->out.did_eof || !uv_is_readable(proc->out.s.uvstream)) {
|
||||
uv_timer_stop(&ptyproc->wait_eof_timer);
|
||||
pty_process_finish2(ptyproc);
|
||||
pty_proc_finish2(ptyproc);
|
||||
}
|
||||
}
|
||||
|
||||
static void pty_process_finish2(PtyProcess *ptyproc)
|
||||
static void pty_proc_finish2(PtyProc *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
Process *proc = (Process *)ptyproc;
|
||||
Proc *proc = (Proc *)ptyproc;
|
||||
|
||||
DWORD exit_code = 0;
|
||||
GetExitCodeProcess(ptyproc->process_handle, &exit_code);
|
||||
GetExitCodeProcess(ptyproc->proc_handle, &exit_code);
|
||||
proc->status = proc->exit_signal ? 128 + proc->exit_signal : (int)exit_code;
|
||||
|
||||
proc->internal_exit_cb(proc);
|
||||
@ -427,14 +427,14 @@ cleanup:
|
||||
return rc;
|
||||
}
|
||||
|
||||
PtyProcess pty_process_init(Loop *loop, void *data)
|
||||
PtyProc pty_proc_init(Loop *loop, void *data)
|
||||
{
|
||||
PtyProcess rv;
|
||||
rv.process = process_init(loop, kProcessTypePty, data);
|
||||
PtyProc rv;
|
||||
rv.proc = proc_init(loop, kProcTypePty, data);
|
||||
rv.width = 80;
|
||||
rv.height = 24;
|
||||
rv.conpty = NULL;
|
||||
rv.finish_wait = NULL;
|
||||
rv.process_handle = NULL;
|
||||
rv.proc_handle = NULL;
|
||||
return rv;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
// IWYU pragma: private, include "nvim/os/pty_process.h"
|
||||
// IWYU pragma: private, include "nvim/os/pty_proc.h"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/lib/queue_defs.h"
|
||||
#include "nvim/os/pty_conpty_win.h"
|
||||
|
||||
typedef struct pty_process {
|
||||
Process process;
|
||||
Proc proc;
|
||||
uint16_t width, height;
|
||||
conpty_t *conpty;
|
||||
HANDLE finish_wait;
|
||||
HANDLE process_handle;
|
||||
HANDLE proc_handle;
|
||||
uv_timer_t wait_eof_timer;
|
||||
} PtyProcess;
|
||||
} PtyProc;
|
||||
|
||||
// Structure used by build_cmd_line()
|
||||
typedef struct arg_node {
|
||||
@ -23,5 +23,5 @@ typedef struct arg_node {
|
||||
} ArgNode;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "os/pty_process_win.h.generated.h"
|
||||
# include "os/pty_proc_win.h.generated.h"
|
||||
#endif
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef MSWIN
|
||||
# include "nvim/os/pty_process_win.h"
|
||||
#else
|
||||
# include "nvim/os/pty_process_unix.h"
|
||||
#endif
|
@ -14,10 +14,10 @@
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/event/defs.h"
|
||||
#include "nvim/event/libuv_process.h"
|
||||
#include "nvim/event/libuv_proc.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/process.h"
|
||||
#include "nvim/event/proc.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/event/stream.h"
|
||||
#include "nvim/event/wstream.h"
|
||||
@ -872,12 +872,12 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
|
||||
char prog[MAXPATHL];
|
||||
xstrlcpy(prog, argv[0], MAXPATHL);
|
||||
|
||||
LibuvProcess uvproc = libuv_process_init(&main_loop, &buf);
|
||||
Process *proc = &uvproc.process;
|
||||
LibuvProc uvproc = libuv_proc_init(&main_loop, &buf);
|
||||
Proc *proc = &uvproc.proc;
|
||||
MultiQueue *events = multiqueue_new_child(main_loop.events);
|
||||
proc->events = events;
|
||||
proc->argv = argv;
|
||||
int status = process_spawn(proc, has_input, true, true);
|
||||
int status = proc_spawn(proc, has_input, true, true);
|
||||
if (status) {
|
||||
loop_poll_events(&main_loop, 0);
|
||||
// Failed, probably 'shell' is not executable.
|
||||
@ -910,7 +910,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
|
||||
|
||||
if (!wstream_write(&proc->in, input_buffer)) {
|
||||
// couldn't write, stop the process and tell the user about it
|
||||
process_stop(proc);
|
||||
proc_stop(proc);
|
||||
return -1;
|
||||
}
|
||||
// close the input stream after everything is written
|
||||
@ -927,7 +927,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
|
||||
msg_no_more = true;
|
||||
lines_left = -1;
|
||||
}
|
||||
int exitcode = process_wait(proc, -1, NULL);
|
||||
int exitcode = proc_wait(proc, -1, NULL);
|
||||
if (!got_int && out_data_decide_throttle(0)) {
|
||||
// Last chunk of output was skipped; display it now.
|
||||
out_data_ring(NULL, SIZE_MAX);
|
||||
|
@ -950,8 +950,8 @@ void time_msg(const char *mesg, const proftime_T *start)
|
||||
/// Initializes the `time_fd` stream for the --startuptime report.
|
||||
///
|
||||
/// @param fname startuptime report file path
|
||||
/// @param process_name name of the current Nvim process to write in the report.
|
||||
void time_init(const char *fname, const char *process_name)
|
||||
/// @param proc_name name of the current Nvim process to write in the report.
|
||||
void time_init(const char *fname, const char *proc_name)
|
||||
{
|
||||
const size_t bufsize = 8192; // Big enough for the entire --startuptime report.
|
||||
time_fd = fopen(fname, "a");
|
||||
@ -972,7 +972,7 @@ void time_init(const char *fname, const char *process_name)
|
||||
semsg("time_init: setvbuf failed: %d %s", r, uv_err_name(r));
|
||||
return;
|
||||
}
|
||||
fprintf(time_fd, "--- Startup times for process: %s ---\n", process_name);
|
||||
fprintf(time_fd, "--- Startup times for process: %s ---\n", proc_name);
|
||||
}
|
||||
|
||||
/// Flushes the startuptimes to disk for the current process
|
||||
|
@ -6875,7 +6875,8 @@ bool set_ref_in_quickfix(int copyID)
|
||||
// In a location list window and none of the other windows is
|
||||
// referring to this location list. Mark the location list
|
||||
// context as still in use.
|
||||
if (mark_quickfix_ctx(win->w_llist_ref, copyID)) {
|
||||
if (mark_quickfix_ctx(win->w_llist_ref, copyID)
|
||||
|| mark_quickfix_user_data(win->w_llist_ref, copyID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ Debugging tests
|
||||
DBG 2022-06-15T18:37:45.227 T57.58016.0/c UI: stop
|
||||
INF 2022-06-15T18:37:45.227 T57.58016.0/c os_exit:595: Nvim exit: 0
|
||||
DBG 2022-06-15T18:37:45.229 T57.58016.0 read_cb:118: closing Stream (0x7fd5d700ea18): EOF (end of file)
|
||||
INF 2022-06-15T18:37:45.229 T57.58016.0 on_process_exit:400: exited: pid=58017 status=0 stoptime=0
|
||||
INF 2022-06-15T18:37:45.229 T57.58016.0 on_proc_exit:400: exited: pid=58017 status=0 stoptime=0
|
||||
```
|
||||
- You can set `$GDB` to [run functional tests under gdbserver](https://github.com/neovim/neovim/pull/1527):
|
||||
|
||||
|
@ -114,6 +114,7 @@ function Session:request(method, ...)
|
||||
return true, result
|
||||
end
|
||||
|
||||
--- Runs the event loop.
|
||||
function Session:run(request_cb, notification_cb, setup_cb, timeout)
|
||||
local function on_request(method, args, response)
|
||||
coroutine_exec(request_cb, method, args, function(status, result, flag)
|
||||
|
@ -1,6 +1,6 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local clear = n.clear
|
||||
local feed_command = n.feed_command
|
||||
|
@ -1,6 +1,6 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
local uv = vim.uv
|
||||
|
||||
local clear, command, testprg = n.clear, n.command, n.testprg
|
||||
@ -199,7 +199,7 @@ end)
|
||||
|
||||
describe('autocmd TextChangedT', function()
|
||||
clear()
|
||||
local screen = tt.screen_setup()
|
||||
local screen = tt.setup_screen()
|
||||
|
||||
it('works', function()
|
||||
command('autocmd TextChangedT * ++once let g:called = 1')
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local clear = n.clear
|
||||
local eq = t.eq
|
||||
|
@ -1,6 +1,6 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local assert_log = t.assert_log
|
||||
local clear = n.clear
|
||||
|
@ -157,5 +157,28 @@ describe('vim.ui', function()
|
||||
exec_lua [[local _, err = vim.ui.open('foo') ; return err]]
|
||||
)
|
||||
end)
|
||||
|
||||
it('opt.cmd #29490', function()
|
||||
t.matches(
|
||||
'ENOENT: no such file or directory',
|
||||
t.pcall_err(exec_lua, function()
|
||||
vim.ui.open('foo', { cmd = { 'non-existent-tool' } })
|
||||
end)
|
||||
)
|
||||
|
||||
eq(
|
||||
{
|
||||
code = 0,
|
||||
signal = 0,
|
||||
stderr = '',
|
||||
stdout = 'arg1=arg1;arg2=https://example.com;',
|
||||
},
|
||||
exec_lua(function(cmd_)
|
||||
local cmd, err = vim.ui.open('https://example.com', { cmd = cmd_ })
|
||||
assert(cmd and not err)
|
||||
return cmd:wait()
|
||||
end, { n.testprg('printargs-test'), 'arg1' })
|
||||
)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
local clear, eq, api = n.clear, t.eq, n.api
|
||||
local feed = n.feed
|
||||
local feed_data = tt.feed_data
|
||||
@ -17,7 +17,7 @@ describe(':terminal altscreen', function()
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = tt.screen_setup()
|
||||
screen = tt.setup_screen()
|
||||
feed_data({
|
||||
'line1',
|
||||
'line2',
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
local ok = t.ok
|
||||
|
||||
if t.skip(t.is_os('win')) then
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local assert_alive = n.assert_alive
|
||||
local feed, clear = n.feed, n.clear
|
||||
@ -29,7 +29,7 @@ describe(':terminal buffer', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
command('set modifiable swapfile undolevels=20')
|
||||
screen = tt.screen_setup()
|
||||
screen = tt.setup_screen()
|
||||
end)
|
||||
|
||||
it('terminal-mode forces various options', function()
|
||||
@ -574,7 +574,7 @@ if is_os('win') then
|
||||
feed_command('set modifiable swapfile undolevels=20')
|
||||
poke_eventloop()
|
||||
local cmd = { 'cmd.exe', '/K', 'PROMPT=$g$s' }
|
||||
screen = tt.screen_setup(nil, cmd)
|
||||
screen = tt.setup_screen(nil, cmd)
|
||||
end)
|
||||
|
||||
it('"put" operator sends data normally', function()
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local feed, clear = n.feed, n.clear
|
||||
local testprg, command = n.testprg, n.command
|
||||
@ -18,7 +18,7 @@ describe(':terminal cursor', function()
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = tt.screen_setup()
|
||||
screen = tt.setup_screen()
|
||||
end)
|
||||
|
||||
it('moves the screen cursor when focused', function()
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local feed, clear = n.feed, n.clear
|
||||
local api = n.api
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
local clear, eq, eval = n.clear, t.eq, n.eval
|
||||
local feed, api, command = n.feed, n.api, n.command
|
||||
local feed_data = tt.feed_data
|
||||
@ -14,7 +14,7 @@ describe(':terminal mouse', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
api.nvim_set_option_value('statusline', '==========', {})
|
||||
screen = tt.screen_setup()
|
||||
screen = tt.setup_screen()
|
||||
command('highlight StatusLine NONE')
|
||||
command('highlight StatusLineNC NONE')
|
||||
command('highlight StatusLineTerm NONE')
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local clear, eq = n.clear, t.eq
|
||||
local feed, testprg = n.feed, n.testprg
|
||||
@ -22,7 +22,7 @@ describe(':terminal scrollback', function()
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = tt.screen_setup(nil, nil, 30)
|
||||
screen = tt.setup_screen(nil, nil, 30)
|
||||
end)
|
||||
|
||||
describe('when the limit is exceeded', function()
|
||||
@ -399,9 +399,9 @@ describe("'scrollback' option", function()
|
||||
it('set to 0 behaves as 1', function()
|
||||
local screen
|
||||
if is_os('win') then
|
||||
screen = tt.screen_setup(nil, { 'cmd.exe' }, 30)
|
||||
screen = tt.setup_screen(nil, { 'cmd.exe' }, 30)
|
||||
else
|
||||
screen = tt.screen_setup(nil, { 'sh' }, 30)
|
||||
screen = tt.setup_screen(nil, { 'sh' }, 30)
|
||||
end
|
||||
|
||||
api.nvim_set_option_value('scrollback', 0, {})
|
||||
@ -416,10 +416,10 @@ describe("'scrollback' option", function()
|
||||
local screen
|
||||
if is_os('win') then
|
||||
command([[let $PROMPT='$$']])
|
||||
screen = tt.screen_setup(nil, { 'cmd.exe' }, 30)
|
||||
screen = tt.setup_screen(nil, { 'cmd.exe' }, 30)
|
||||
else
|
||||
command('let $PS1 = "$"')
|
||||
screen = tt.screen_setup(nil, { 'sh' }, 30)
|
||||
screen = tt.setup_screen(nil, { 'sh' }, 30)
|
||||
end
|
||||
|
||||
api.nvim_set_option_value('scrollback', 200, {})
|
||||
@ -480,8 +480,8 @@ describe("'scrollback' option", function()
|
||||
end)
|
||||
|
||||
it('deletes extra lines immediately', function()
|
||||
-- Scrollback is 10 on screen_setup
|
||||
local screen = tt.screen_setup(nil, nil, 30)
|
||||
-- Scrollback is 10 on setup_screen
|
||||
local screen = tt.setup_screen(nil, nil, 30)
|
||||
local lines = {}
|
||||
for i = 1, 30 do
|
||||
table.insert(lines, 'line' .. tostring(i))
|
||||
|
@ -7,7 +7,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local eq = t.eq
|
||||
local feed_data = tt.feed_data
|
||||
@ -2111,7 +2111,7 @@ describe('TUI', function()
|
||||
finally(function()
|
||||
os.remove('testF')
|
||||
end)
|
||||
local screen = tt.screen_setup(
|
||||
local screen = tt.setup_screen(
|
||||
0,
|
||||
('"%s" -u NONE -i NONE --cmd "set noswapfile noshowcmd noruler" --cmd "normal iabc" > /dev/null 2>&1 && cat testF && rm testF'):format(
|
||||
nvim_prog
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
local feed_data = tt.feed_data
|
||||
local feed, clear = n.feed, n.clear
|
||||
local poke_eventloop = n.poke_eventloop
|
||||
@ -37,7 +37,7 @@ describe(':terminal window', function()
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = tt.screen_setup()
|
||||
screen = tt.setup_screen()
|
||||
end)
|
||||
|
||||
it('sets topline correctly #8556', function()
|
||||
@ -198,7 +198,7 @@ describe(':terminal with multigrid', function()
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = tt.screen_setup(0, nil, 50, nil, { ext_multigrid = true })
|
||||
screen = tt.setup_screen(0, nil, 50, nil, { ext_multigrid = true })
|
||||
end)
|
||||
|
||||
it('resizes to requested size', function()
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
local assert_alive = n.assert_alive
|
||||
local clear = n.clear
|
||||
local feed = n.feed
|
||||
@ -22,7 +22,7 @@ describe(':terminal', function()
|
||||
-- set the statusline to a constant value because of variables like pid
|
||||
-- and current directory and to improve visibility of splits
|
||||
api.nvim_set_option_value('statusline', '==========', {})
|
||||
screen = tt.screen_setup(3)
|
||||
screen = tt.setup_screen(3)
|
||||
command('highlight StatusLine NONE')
|
||||
command('highlight StatusLineNC NONE')
|
||||
command('highlight StatusLineTerm NONE')
|
||||
|
@ -250,6 +250,8 @@ function M.set_method_error(err)
|
||||
method_error = err
|
||||
end
|
||||
|
||||
--- Runs the event loop of the given session.
|
||||
---
|
||||
--- @param lsession test.Session
|
||||
--- @param request_cb function?
|
||||
--- @param notification_cb function?
|
||||
@ -296,6 +298,7 @@ function M.run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
|
||||
return lsession.eof_err
|
||||
end
|
||||
|
||||
--- Runs the event loop of the current global session.
|
||||
function M.run(request_cb, notification_cb, setup_cb, timeout)
|
||||
assert(session)
|
||||
return M.run_session(session, request_cb, notification_cb, setup_cb, timeout)
|
||||
|
@ -1,6 +1,13 @@
|
||||
-- To test tui/input.c, this module spawns `nvim` inside :terminal and sends
|
||||
-- bytes via jobsend(). Note: the functional/testutil.lua test-session methods
|
||||
-- operate on the _host_ session, _not_ the child session.
|
||||
-- Functions to test :terminal and the Nvim TUI.
|
||||
-- Starts a child process in a `:terminal` and sends bytes to the child via nvim_chan_send().
|
||||
-- Note: the global functional/testutil.lua test-session is _host_ session, _not_
|
||||
-- the child session.
|
||||
--
|
||||
-- - Use `setup_screen()` to test `:terminal` behavior with an arbitrary command.
|
||||
-- - Use `setup_child_nvim()` to test the Nvim TUI.
|
||||
-- - NOTE: Only use this if your test actually needs the full lifecycle/capabilities of the
|
||||
-- builtin Nvim TUI. Most tests should just use `Screen.new()` directly, or plain old API calls.
|
||||
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
@ -9,18 +16,20 @@ local exec_lua = n.exec_lua
|
||||
local api = n.api
|
||||
local nvim_prog = n.nvim_prog
|
||||
|
||||
local function feed_data(data)
|
||||
local M = {}
|
||||
|
||||
function M.feed_data(data)
|
||||
if type(data) == 'table' then
|
||||
data = table.concat(data, '\n')
|
||||
end
|
||||
exec_lua('vim.api.nvim_chan_send(vim.b.terminal_job_id, ...)', data)
|
||||
end
|
||||
|
||||
local function feed_termcode(data)
|
||||
feed_data('\027' .. data)
|
||||
function M.feed_termcode(data)
|
||||
M.feed_data('\027' .. data)
|
||||
end
|
||||
|
||||
local function make_lua_executor(session)
|
||||
function M.make_lua_executor(session)
|
||||
return function(code, ...)
|
||||
local status, rv = session:request('nvim_exec_lua', code, { ... })
|
||||
if not status then
|
||||
@ -34,60 +43,68 @@ end
|
||||
-- some t for controlling the terminal. the codes were taken from
|
||||
-- infocmp xterm-256color which is less what libvterm understands
|
||||
-- civis/cnorm
|
||||
local function hide_cursor()
|
||||
feed_termcode('[?25l')
|
||||
function M.hide_cursor()
|
||||
M.feed_termcode('[?25l')
|
||||
end
|
||||
local function show_cursor()
|
||||
feed_termcode('[?25h')
|
||||
function M.show_cursor()
|
||||
M.feed_termcode('[?25h')
|
||||
end
|
||||
-- smcup/rmcup
|
||||
local function enter_altscreen()
|
||||
feed_termcode('[?1049h')
|
||||
function M.enter_altscreen()
|
||||
M.feed_termcode('[?1049h')
|
||||
end
|
||||
local function exit_altscreen()
|
||||
feed_termcode('[?1049l')
|
||||
function M.exit_altscreen()
|
||||
M.feed_termcode('[?1049l')
|
||||
end
|
||||
-- character attributes
|
||||
local function set_fg(num)
|
||||
feed_termcode('[38;5;' .. num .. 'm')
|
||||
function M.set_fg(num)
|
||||
M.feed_termcode('[38;5;' .. num .. 'm')
|
||||
end
|
||||
local function set_bg(num)
|
||||
feed_termcode('[48;5;' .. num .. 'm')
|
||||
function M.set_bg(num)
|
||||
M.feed_termcode('[48;5;' .. num .. 'm')
|
||||
end
|
||||
local function set_bold()
|
||||
feed_termcode('[1m')
|
||||
function M.set_bold()
|
||||
M.feed_termcode('[1m')
|
||||
end
|
||||
local function set_italic()
|
||||
feed_termcode('[3m')
|
||||
function M.set_italic()
|
||||
M.feed_termcode('[3m')
|
||||
end
|
||||
local function set_underline()
|
||||
feed_termcode('[4m')
|
||||
function M.set_underline()
|
||||
M.feed_termcode('[4m')
|
||||
end
|
||||
local function set_underdouble()
|
||||
feed_termcode('[4:2m')
|
||||
function M.set_underdouble()
|
||||
M.feed_termcode('[4:2m')
|
||||
end
|
||||
local function set_undercurl()
|
||||
feed_termcode('[4:3m')
|
||||
function M.set_undercurl()
|
||||
M.feed_termcode('[4:3m')
|
||||
end
|
||||
local function set_strikethrough()
|
||||
feed_termcode('[9m')
|
||||
function M.set_strikethrough()
|
||||
M.feed_termcode('[9m')
|
||||
end
|
||||
local function clear_attrs()
|
||||
feed_termcode('[0;10m')
|
||||
function M.clear_attrs()
|
||||
M.feed_termcode('[0;10m')
|
||||
end
|
||||
-- mouse
|
||||
local function enable_mouse()
|
||||
feed_termcode('[?1002h')
|
||||
function M.enable_mouse()
|
||||
M.feed_termcode('[?1002h')
|
||||
end
|
||||
local function disable_mouse()
|
||||
feed_termcode('[?1002l')
|
||||
function M.disable_mouse()
|
||||
M.feed_termcode('[?1002l')
|
||||
end
|
||||
|
||||
local default_command = { testprg('tty-test') }
|
||||
|
||||
local function screen_setup(extra_rows, command, cols, env, screen_opts)
|
||||
--- Runs `cmd` in a :terminal, and returns a `Screen` object.
|
||||
---
|
||||
---@param extra_rows? integer Extra rows to add to the default screen.
|
||||
---@param cmd? string|string[] Command to run in the terminal (default: `{ 'tty-test' }`)
|
||||
---@param cols? integer Create screen with this many columns (default: 50)
|
||||
---@param env? table Environment set on the `cmd` job.
|
||||
---@param screen_opts? table Options for `Screen.new()`.
|
||||
---@return test.functional.ui.screen # Screen attached to the global (not child) Nvim session.
|
||||
function M.setup_screen(extra_rows, cmd, cols, env, screen_opts)
|
||||
extra_rows = extra_rows and extra_rows or 0
|
||||
command = command and command or default_command
|
||||
cmd = cmd and cmd or default_command
|
||||
cols = cols and cols or 50
|
||||
|
||||
api.nvim_command('highlight TermCursor cterm=reverse')
|
||||
@ -120,7 +137,7 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts)
|
||||
screen:attach(screen_opts or { rgb = false })
|
||||
|
||||
api.nvim_command('enew')
|
||||
api.nvim_call_function('termopen', { command, env and { env = env } or nil })
|
||||
api.nvim_call_function('termopen', { cmd, env and { env = env } or nil })
|
||||
api.nvim_input('<CR>')
|
||||
local vim_errmsg = api.nvim_eval('v:errmsg')
|
||||
if vim_errmsg and '' ~= vim_errmsg then
|
||||
@ -133,7 +150,7 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts)
|
||||
|
||||
-- tty-test puts the terminal into raw mode and echoes input. Tests work by
|
||||
-- feeding termcodes to control the display and asserting by screen:expect.
|
||||
if command == default_command and screen_opts == nil then
|
||||
if cmd == default_command and screen_opts == nil then
|
||||
-- Wait for "tty ready" to be printed before each test or the terminal may
|
||||
-- still be in canonical mode (will echo characters for example).
|
||||
local empty_line = (' '):rep(cols)
|
||||
@ -160,37 +177,24 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts)
|
||||
return screen
|
||||
end
|
||||
|
||||
local function setup_child_nvim(args, opts)
|
||||
--- Spawns Nvim with `args` in a :terminal, and returns a `Screen` object.
|
||||
---
|
||||
--- @note Only use this if you actually need the full lifecycle/capabilities of the builtin Nvim
|
||||
--- TUI. Most tests should just use `Screen.new()` directly, or plain old API calls.
|
||||
---
|
||||
---@param args? string[] Args passed to child Nvim.
|
||||
---@param opts? table Options
|
||||
---@return test.functional.ui.screen # Screen attached to the global (not child) Nvim session.
|
||||
function M.setup_child_nvim(args, opts)
|
||||
opts = opts or {}
|
||||
local argv = { nvim_prog, unpack(args) }
|
||||
local argv = { nvim_prog, unpack(args or {}) }
|
||||
|
||||
local env = opts.env or {}
|
||||
if not env.VIMRUNTIME then
|
||||
env.VIMRUNTIME = os.getenv('VIMRUNTIME')
|
||||
end
|
||||
|
||||
return screen_setup(opts.extra_rows, argv, opts.cols, env)
|
||||
return M.setup_screen(opts.extra_rows, argv, opts.cols, env)
|
||||
end
|
||||
|
||||
return {
|
||||
feed_data = feed_data,
|
||||
feed_termcode = feed_termcode,
|
||||
make_lua_executor = make_lua_executor,
|
||||
hide_cursor = hide_cursor,
|
||||
show_cursor = show_cursor,
|
||||
enter_altscreen = enter_altscreen,
|
||||
exit_altscreen = exit_altscreen,
|
||||
set_fg = set_fg,
|
||||
set_bg = set_bg,
|
||||
set_bold = set_bold,
|
||||
set_italic = set_italic,
|
||||
set_underline = set_underline,
|
||||
set_underdouble = set_underdouble,
|
||||
set_undercurl = set_undercurl,
|
||||
set_strikethrough = set_strikethrough,
|
||||
clear_attrs = clear_attrs,
|
||||
enable_mouse = enable_mouse,
|
||||
disable_mouse = disable_mouse,
|
||||
screen_setup = screen_setup,
|
||||
setup_child_nvim = setup_child_nvim,
|
||||
}
|
||||
return M
|
@ -8,6 +8,7 @@ local exec_lua = n.exec_lua
|
||||
local pcall_err = t.pcall_err
|
||||
local matches = t.matches
|
||||
local insert = n.insert
|
||||
local NIL = vim.NIL
|
||||
|
||||
before_each(clear)
|
||||
|
||||
@ -15,10 +16,12 @@ describe('treesitter language API', function()
|
||||
-- error tests not requiring a parser library
|
||||
it('handles missing language', function()
|
||||
eq(
|
||||
".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
|
||||
'.../treesitter.lua:0: Parser not found.',
|
||||
pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")
|
||||
)
|
||||
|
||||
eq(NIL, exec_lua("return vim.treesitter._get_parser(0, 'borklang')"))
|
||||
|
||||
-- actual message depends on platform
|
||||
matches(
|
||||
"Failed to load parser for language 'borklang': uv_dlopen: .+",
|
||||
@ -105,9 +108,10 @@ describe('treesitter language API', function()
|
||||
command('set filetype=borklang')
|
||||
-- Should throw an error when filetype changes to borklang
|
||||
eq(
|
||||
".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
|
||||
'.../treesitter.lua:0: Parser not found.',
|
||||
pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0, 'borklang')")
|
||||
)
|
||||
eq(NIL, exec_lua("return vim.treesitter._get_parser(0, 'borklang')"))
|
||||
end
|
||||
)
|
||||
|
||||
|
@ -135,9 +135,7 @@ void ui_refresh(void)
|
||||
insert(test_text)
|
||||
|
||||
eq(
|
||||
'.../treesitter.lua:0: There is no parser available for buffer 1 and one'
|
||||
.. ' could not be created because lang could not be determined. Either'
|
||||
.. ' pass lang or set the buffer filetype',
|
||||
'.../treesitter.lua:0: Parser not found.',
|
||||
pcall_err(exec_lua, 'vim.treesitter.get_parser(0)')
|
||||
)
|
||||
|
||||
|
@ -1078,6 +1078,44 @@ describe('CursorLine and CursorLineNr highlights', function()
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_cursorline_screenline_resize()
|
||||
it("'cursorlineopt' screenline is updated on window resize", function()
|
||||
local screen = Screen.new(75, 8)
|
||||
screen:attach()
|
||||
exec([[
|
||||
50vnew
|
||||
call setline(1, repeat('xyz ', 30))
|
||||
setlocal number cursorline cursorlineopt=screenline
|
||||
normal! $
|
||||
]])
|
||||
screen:expect([[
|
||||
{8: 1 }xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xy│ |
|
||||
{8: }z xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz │{1:~ }|
|
||||
{8: }{21:xyz xyz xyz xyz xyz xyz xyz^ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|*3
|
||||
{3:[No Name] [+] }{2:[No Name] }|
|
||||
|
|
||||
]])
|
||||
command('vertical resize -4')
|
||||
screen:expect([[
|
||||
{8: 1 }xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xy│ |
|
||||
{8: }z xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz │{1:~ }|
|
||||
{8: }{21:xyz xyz xyz xyz xyz xyz xyz xyz xyz^ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|*3
|
||||
{3:[No Name] [+] }{2:[No Name] }|
|
||||
|
|
||||
]])
|
||||
command('set cpoptions+=n')
|
||||
screen:expect([[
|
||||
{8: 1 }xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xy│ |
|
||||
z xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz │{1:~ }|
|
||||
{21:xyz xyz xyz xyz xyz xyz xyz xyz^ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|*3
|
||||
{3:[No Name] [+] }{2:[No Name] }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_cursorline_after_yank()
|
||||
it('always updated. vim-patch:8.1.0849', function()
|
||||
local screen = Screen.new(50, 5)
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local clear, insert = n.clear, n.insert
|
||||
local command = n.command
|
||||
|
@ -803,6 +803,73 @@ void testFunction () {
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('setup a diff with 2 files and set linematch:30', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=linematch:30<cr>')
|
||||
local f1 = [[
|
||||
start
|
||||
?a
|
||||
]]
|
||||
local f2 = [[
|
||||
start
|
||||
!b
|
||||
!a
|
||||
!c
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }^start │{7: }{8: 1 }start |
|
||||
{7: }{8: 2 }{22:!b }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:!a }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 }{4: }{27:!c}{4: }│{7: }{8: 2 }{4: }{27:?a}{4: }|
|
||||
{7: }{8: 5 } │{7: }{8: 3 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:e |
|
||||
]])
|
||||
end)
|
||||
it('display results with ignore white', function()
|
||||
feed(':set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }^start │{7: }{8: 1 }start |
|
||||
{7: }{8: 2 }{22:!b }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{27:!}{4:a }│{7: }{8: 2 }{4: }{27:?}{4:a }|
|
||||
{7: }{8: 4 }{22: !c }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 5 } │{7: }{8: 3 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('a diff that would result in multiple groups before grouping optimization', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=linematch:30<cr>')
|
||||
@ -1084,6 +1151,436 @@ something
|
||||
end
|
||||
)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
abbcabbcdefghijklmnop
|
||||
]]
|
||||
local f2 = [[
|
||||
abca?bc
|
||||
dfgh?ijl
|
||||
mnop?
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('when the entire hunk is compared, cross-line', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^abca}{27:?}{4:bc }│{7: }{8: 1 }{4:a}{27:b}{4:bca}{27:b}{4:bcd}{27:e}{4:fghij}{27:k}{4:lmnop }|
|
||||
{7: }{8: 2 }{4:dfgh}{27:?}{4:ijl }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{4:mnop}{27:?}{4: }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('when the single line is compared, cross-line', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:30<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^abca}{27:?}{4:bc }│{7: }{8: 1 }{4:a}{27:b}{4:bca}{27:b}{4:bc}{27:defghijklmnop}{4: }|
|
||||
{7: }{8: 2 }{22:dfgh?ijl }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:mnop? }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:30 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('when the diff hunk and the single line are too long to run chardiff', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:10<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^ab}{27:ca?bc}{4: }│{7: }{8: 1 }{4:ab}{27:bcabbcdefghijklmnop}{4: }|
|
||||
{7: }{8: 2 }{22:dfgh?ijl }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:mnop? }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:10 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with wordmatch enabled', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
wA w1 wB w1 w2 wC w3 w4
|
||||
]]
|
||||
local f2 = [[
|
||||
w1 w2
|
||||
w2 w3
|
||||
w4 w5
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
|
||||
describe('when the entire hunk is compared, cross-line', function()
|
||||
it('display results', function()
|
||||
feed(':set diffopt+=worddiff:30<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA w1 wB }{4:w1 w2}{27: wC}{4: w3}{27: }{4:w4 }|
|
||||
{7: }{8: 2 }{27:w2}{4: w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{4:w4}{27: w5}{4: }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=worddiff:30 |
|
||||
]])
|
||||
end)
|
||||
it('display results, with ignore white', function()
|
||||
feed(':set diffopt+=worddiff:20<cr>:set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA}{4: }{27:w1}{4: }{27:wB}{4: w1 w2 }{27:wC}{4: w3 w4 }|
|
||||
{7: }{8: 2 }{27:w2}{4: w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{4:w4 }{27:w5}{4: }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('when the single line is compared, cross-line', function()
|
||||
it('display results', function()
|
||||
feed(':set diffopt+=worddiff:20<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA w1 wB }{4:w1 w2}{27: wC w3 w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=worddiff:20 |
|
||||
]])
|
||||
end)
|
||||
it('display results, with ignore white', function()
|
||||
feed(':set diffopt+=worddiff:15<cr>:set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w1 w2 }│{7: }{8: 1 }{27:wA}{4: }{27:w1}{4: }{27:wB}{4: w1 w2 }{27:wC}{4: }{27:w3}{4: }{27:w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('when the diff hunk and the single line are too long to run chardiff', function()
|
||||
it('display results', function()
|
||||
feed(':set diffopt+=worddiff:10<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w}{27:1 w2}{4: }│{7: }{8: 1 }{4:w}{27:A w1 wB w1 w2 wC w3 w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=worddiff:10 |
|
||||
]])
|
||||
end)
|
||||
it('display results, with ignore white', function()
|
||||
feed(':set diffopt+=worddiff:10<cr>:set diffopt+=iwhiteall<cr>')
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^w}{27:1 w2}{4: }│{7: }{8: 1 }{4:w}{27:A w1 wB w1 w2 wC w3 w4}{4: }|
|
||||
{7: }{8: 2 }{22:w2 w3 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 3 }{22:w4 w5 }│{7: }{8: }{23:--------------------------------------------}|
|
||||
{7: }{8: 4 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled, with and without ignore white', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
ababcabcdabcde
|
||||
]]
|
||||
local f2 = [[
|
||||
abc abcd abcde abcdef
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('normal comparison, including whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^ab}{27:c }{4:abc}{27:d }{4:abcd}{27:e }{4:abcde}{27:f}{4: }│{7: }{8: 1 }{4:ababcabcdabcde }|
|
||||
{7: }{8: 2 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
describe('ignore whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>:set diffopt+=iwhiteall<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^ab}{27:c}{4: abc}{27:d}{4: abcd}{27:e}{4: abcde}{27:f}{4: }│{7: }{8: 1 }{4:ababcabcdabcde }|
|
||||
{7: }{8: 2 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=iwhiteall |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled, with different UTF-8 character', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
aaaहaaa
|
||||
]]
|
||||
local f2 = [[
|
||||
aaaसaaa
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('normal comparison, including whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7: }{8: 1 }{4:^aaa}{27:स}{4:a}{27:a}{4:a }│{7: }{8: 1 }{4:aaa}{27:ह}{4:a}{27:a}{4:a }|
|
||||
{7: }{8: 2 } │{7: }{8: 2 } |
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe('show a diff with charmatch enabled, with same UTF-8 character', function()
|
||||
before_each(function()
|
||||
local f1 = [[
|
||||
aaaहaaa
|
||||
]]
|
||||
local f2 = [[
|
||||
aaaहaaa
|
||||
]]
|
||||
write_file(fname, f1, false)
|
||||
write_file(fname_2, f2, false)
|
||||
reread()
|
||||
end)
|
||||
describe('normal comparison, including whitespace', function()
|
||||
before_each(function()
|
||||
feed(':set diffopt+=chardiff:100<cr>')
|
||||
end)
|
||||
it('display results', function()
|
||||
screen:expect([[
|
||||
{7:+ }{8: 1 }{13:^+-- 2 lines: aaaहaaa······················}│{7:+ }{8: 1 }{13:+-- 2 lines: aaaहaaa·······················}|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{1:~ }│{1:~ }|
|
||||
{3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }|
|
||||
:set diffopt+=chardiff:100 |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('regressions', function()
|
||||
|
@ -1,7 +1,7 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local tt = require('test.functional.terminal.testutil')
|
||||
local tt = require('test.functional.testterm')
|
||||
|
||||
local assert_alive = n.assert_alive
|
||||
local mkdir, write_file, rmdir = t.mkdir, t.write_file, n.rmdir
|
||||
|
@ -262,14 +262,34 @@ func Test_cursorline_callback()
|
||||
|
||||
call timer_start(300, 'Func')
|
||||
END
|
||||
call writefile(lines, 'Xcul_timer')
|
||||
call writefile(lines, 'Xcul_timer', 'D')
|
||||
|
||||
let buf = RunVimInTerminal('-S Xcul_timer', #{rows: 8})
|
||||
call TermWait(buf, 310)
|
||||
call VerifyScreenDump(buf, 'Test_cursorline_callback_1', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
call delete('Xcul_timer')
|
||||
endfunc
|
||||
|
||||
func Test_cursorline_screenline_resize()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
50vnew
|
||||
call setline(1, repeat('xyz ', 30))
|
||||
setlocal number cursorline cursorlineopt=screenline
|
||||
normal! $
|
||||
END
|
||||
call writefile(lines, 'Xcul_screenline_resize', 'D')
|
||||
|
||||
let buf = RunVimInTerminal('-S Xcul_screenline_resize', #{rows: 8})
|
||||
call VerifyScreenDump(buf, 'Test_cursorline_screenline_resize_1', {})
|
||||
call term_sendkeys(buf, ":vertical resize -4\<CR>")
|
||||
call VerifyScreenDump(buf, 'Test_cursorline_screenline_resize_2', {})
|
||||
call term_sendkeys(buf, ":set cpoptions+=n\<CR>")
|
||||
call VerifyScreenDump(buf, 'Test_cursorline_screenline_resize_3', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_cursorline_screenline_update()
|
||||
@ -280,7 +300,7 @@ func Test_cursorline_screenline_update()
|
||||
set cursorline cursorlineopt=screenline
|
||||
inoremap <F2> <Cmd>call cursor(1, 1)<CR>
|
||||
END
|
||||
call writefile(lines, 'Xcul_screenline')
|
||||
call writefile(lines, 'Xcul_screenline', 'D')
|
||||
|
||||
let buf = RunVimInTerminal('-S Xcul_screenline', #{rows: 8})
|
||||
call term_sendkeys(buf, "A")
|
||||
@ -290,7 +310,17 @@ func Test_cursorline_screenline_update()
|
||||
call term_sendkeys(buf, "\<Esc>")
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
call delete('Xcul_screenline')
|
||||
endfunc
|
||||
|
||||
func Test_cursorline_screenline_zero_width()
|
||||
CheckOption foldcolumn
|
||||
|
||||
set cursorline culopt=screenline winminwidth=1 foldcolumn=1
|
||||
" This used to crash Vim
|
||||
1vnew | redraw
|
||||
|
||||
bwipe!
|
||||
set cursorline& culopt& winminwidth& foldcolumn&
|
||||
endfunc
|
||||
|
||||
func Test_cursorline_cursorbind_horizontal_scroll()
|
||||
|
@ -1150,15 +1150,14 @@ func Test_cfg_file()
|
||||
unlet g:filetype_cfg
|
||||
|
||||
" RAPID cfg
|
||||
let ext = 'cfg'
|
||||
for i in ['EIO', 'MMC', 'MOC', 'PROC', 'SIO', 'SYS']
|
||||
for ext in ['cfg', 'Cfg', 'CFG']
|
||||
call writefile([i .. ':CFG'], 'cfgfile.' .. ext)
|
||||
execute "split cfgfile." .. ext
|
||||
call assert_equal('rapid', &filetype)
|
||||
bwipe!
|
||||
call delete('cfgfile.' .. ext)
|
||||
" check different case of file extension
|
||||
let ext = substitute(ext, '\(\l\)', '\u\1', '')
|
||||
endfor
|
||||
endfor
|
||||
|
||||
" clean up
|
||||
|
@ -4071,11 +4071,23 @@ func Test_ll_window_ctx()
|
||||
enew | only
|
||||
endfunc
|
||||
|
||||
" Similar to the problem above, but for user data.
|
||||
func Test_ll_window_user_data()
|
||||
call setloclist(0, [#{bufnr: bufnr(), user_data: {}}])
|
||||
lopen
|
||||
wincmd t
|
||||
close
|
||||
call test_garbagecollect_now()
|
||||
call feedkeys("\<CR>", 'tx')
|
||||
call test_garbagecollect_now()
|
||||
%bwipe!
|
||||
endfunc
|
||||
|
||||
" The following test used to crash vim
|
||||
func Test_lfile_crash()
|
||||
sp Xtest
|
||||
au QuickFixCmdPre * bw
|
||||
call assert_fails('lfile', 'E40')
|
||||
call assert_fails('lfile', 'E40:')
|
||||
au! QuickFixCmdPre
|
||||
endfunc
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user