Update runtime files

This commit is contained in:
Bram Moolenaar
2022-08-15 18:51:32 +01:00
parent 5a4fff4d94
commit e1f3fd1d02
24 changed files with 2998 additions and 1186 deletions

View File

@ -1,184 +1,95 @@
" Language: ConTeXt typesetting engine
" Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com>
" Latest Revision: 2016 Oct 21
vim9script
let s:keepcpo= &cpo
set cpo&vim
# Language: ConTeXt typesetting engine
# Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com>
# Former Maintainers: Nikolai Weibull <now@bitwi.se>
# Latest Revision: 2022 Aug 12
" Helper functions {{{
function! s:context_echo(message, mode)
redraw
echo "\r"
execute 'echohl' a:mode
echomsg '[ConTeXt]' a:message
echohl None
endf
# Typesetting {{{
import autoload './typeset.vim'
function! s:sh()
return has('win32') || has('win64') || has('win16') || has('win95')
\ ? ['cmd.exe', '/C']
\ : ['/bin/sh', '-c']
endfunction
export def ConTeXtCmd(path: string): list<string>
return ['mtxrun', '--script', 'context', '--nonstopmode', '--autogenerate', path]
enddef
" For backward compatibility
if exists('*win_getid')
export def Typeset(bufname: string, env = {}, Cmd = ConTeXtCmd): bool
return typeset.TypesetBuffer(bufname, Cmd, env, 'ConTeXt')
enddef
function! s:win_getid()
return win_getid()
endf
export def JobStatus()
typeset.JobStatus('ConTeXt')
enddef
function! s:win_id2win(winid)
return win_id2win(a:winid)
endf
export def StopJobs()
typeset.StopJobs('ConTeXt')
enddef
else
export def Log(bufname: string)
execute 'edit' typeset.LogPath(bufname)
enddef
# }}}
function! s:win_getid()
return winnr()
endf
# Completion {{{
def BinarySearch(base: string, keywords: list<string>): list<string>
const pat = '^' .. base
const len = len(keywords)
var res = []
var lft = 0
var rgt = len
function! s:win_id2win(winnr)
return a:winnr
endf
endif
" }}}
" ConTeXt jobs {{{
if has('job')
let g:context_jobs = []
" Print the status of ConTeXt jobs
function! context#job_status()
let l:jobs = filter(g:context_jobs, 'job_status(v:val) == "run"')
let l:n = len(l:jobs)
call s:context_echo(
\ 'There '.(l:n == 1 ? 'is' : 'are').' '.(l:n == 0 ? 'no' : l:n)
\ .' job'.(l:n == 1 ? '' : 's').' running'
\ .(l:n == 0 ? '.' : ' (' . join(l:jobs, ', ').').'),
\ 'ModeMsg')
endfunction
" Stop all ConTeXt jobs
function! context#stop_jobs()
let l:jobs = filter(g:context_jobs, 'job_status(v:val) == "run"')
for job in l:jobs
call job_stop(job)
endfor
sleep 1
let l:tmp = []
for job in l:jobs
if job_status(job) == "run"
call add(l:tmp, job)
endif
endfor
let g:context_jobs = l:tmp
if empty(g:context_jobs)
call s:context_echo('Done. No jobs running.', 'ModeMsg')
# Find the leftmost index matching base
while lft < rgt
var i = (lft + rgt) / 2
if keywords[i] < base
lft = i + 1
else
call s:context_echo('There are still some jobs running. Please try again.', 'WarningMsg')
rgt = i
endif
endfunction
endwhile
function! context#callback(path, job, status)
if index(g:context_jobs, a:job) != -1 && job_status(a:job) != 'run' " just in case
call remove(g:context_jobs, index(g:context_jobs, a:job))
while lft < len && keywords[lft] =~ pat
add(res, keywords[lft])
lft += 1
endwhile
return res
enddef
var isMetaPostBlock = false
var MP_KEYWORDS: list<string> = []
var CTX_KEYWORDS: list<string> = []
# Complete only MetaPost keywords in MetaPost blocks, and complete only
# ConTeXt keywords otherwise.
export def Complete(findstart: number, base: string): any
if findstart == 1
if len(synstack(line("."), 1)) > 0 && synIDattr(synstack(line("."), 1)[0], "name") ==# 'contextMPGraphic'
isMetaPostBlock = true
return match(getline('.'), '\S\+\%' .. col('.') .. 'c')
endif
call s:callback(a:path, a:job, a:status)
endfunction
function! context#close_cb(channel)
call job_status(ch_getjob(a:channel)) " Trigger exit_cb's callback for faster feedback
endfunction
function! s:typeset(path)
call add(g:context_jobs,
\ job_start(add(s:sh(), context#command() . ' ' . shellescape(fnamemodify(a:path, ":t"))), {
\ 'close_cb' : 'context#close_cb',
\ 'exit_cb' : function(get(b:, 'context_callback', get(g:, 'context_callback', 'context#callback')),
\ [a:path]),
\ 'in_io' : 'null'
\ }))
endfunction
else " No jobs
function! context#job_status()
call s:context_echo('Not implemented', 'WarningMsg')
endfunction!
function! context#stop_jobs()
call s:context_echo('Not implemented', 'WarningMsg')
endfunction
function! context#callback(path, job, status)
call s:callback(a:path, a:job, a:status)
endfunction
function! s:typeset(path)
execute '!' . context#command() . ' ' . shellescape(fnamemodify(a:path, ":t"))
call call(get(b:, 'context_callback', get(g:, 'context_callback', 'context#callback')),
\ [a:path, 0, v:shell_error])
endfunction
endif " has('job')
function! s:callback(path, job, status) abort
if a:status < 0 " Assume the job was terminated
return
# Complete only \commands starting with a backslash
isMetaPostBlock = false
var pos = match(getline('.'), '\\\zs\S\+\%' .. col('.') .. 'c')
return (pos == -1) ? -3 : pos
endif
" Get info about the current window
let l:winid = s:win_getid() " Save window id
let l:efm = &l:errorformat " Save local errorformat
let l:cwd = fnamemodify(getcwd(), ":p") " Save local working directory
" Set errorformat to parse ConTeXt errors
execute 'setl efm=' . escape(b:context_errorformat, ' ')
try " Set cwd to expand error file correctly
execute 'lcd' fnameescape(fnamemodify(a:path, ':h'))
catch /.*/
execute 'setl efm=' . escape(l:efm, ' ')
throw v:exception
endtry
try
execute 'cgetfile' fnameescape(fnamemodify(a:path, ':r') . '.log')
botright cwindow
finally " Restore cwd and errorformat
execute s:win_id2win(l:winid) . 'wincmd w'
execute 'lcd ' . fnameescape(l:cwd)
execute 'setl efm=' . escape(l:efm, ' ')
endtry
if a:status == 0
call s:context_echo('Success!', 'ModeMsg')
else
call s:context_echo('There are errors. ', 'ErrorMsg')
if isMetaPostBlock
if empty(MP_KEYWORDS)
MP_KEYWORDS = sort(syntaxcomplete#OmniSyntaxList(['mf\w\+', 'mp\w\+']))
endif
return BinarySearch(base, MP_KEYWORDS)
endif
endfunction
function! context#command()
return get(b:, 'context_mtxrun', get(g:, 'context_mtxrun', 'mtxrun'))
\ . ' --script context --autogenerate --nonstopmode'
\ . ' --synctex=' . (get(b:, 'context_synctex', get(g:, 'context_synctex', 0)) ? '1' : '0')
\ . ' ' . get(b:, 'context_extra_options', get(g:, 'context_extra_options', ''))
endfunction
if empty(CTX_KEYWORDS)
CTX_KEYWORDS = sort(syntaxcomplete#OmniSyntaxList([
'context\w\+', 'texAleph', 'texEtex', 'texLuatex', 'texOmega',
'texPdftex', 'texTex', 'texXeTeX'
]))
endif
return BinarySearch(base, CTX_KEYWORDS)
enddef
# }}}
" Accepts an optional path (useful for big projects, when the file you are
" editing is not the project's root document). If no argument is given, uses
" the path of the current buffer.
function! context#typeset(...) abort
let l:path = fnamemodify(strlen(a:000[0]) > 0 ? a:1 : expand("%"), ":p")
let l:cwd = fnamemodify(getcwd(), ":p") " Save local working directory
call s:context_echo('Typesetting...', 'ModeMsg')
execute 'lcd' fnameescape(fnamemodify(l:path, ":h"))
try
call s:typeset(l:path)
finally " Restore local working directory
execute 'lcd ' . fnameescape(l:cwd)
endtry
endfunction!
"}}}
let &cpo = s:keepcpo
unlet s:keepcpo
" vim: sw=2 fdm=marker
# vim: sw=2 fdm=marker

View File

@ -0,0 +1,233 @@
vim9script
# Language: Generic TeX typesetting engine
# Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com>
# Latest Revision: 2022 Aug 12
# Constants and helpers {{{
const SLASH = !exists("+shellslash") || &shellslash ? '/' : '\'
def Echo(msg: string, mode: string, label: string)
redraw
echo "\r"
execute 'echohl' mode
echomsg printf('[%s] %s', label, msg)
echohl None
enddef
def EchoMsg(msg: string, label = 'Notice')
Echo(msg, 'ModeMsg', label)
enddef
def EchoWarn(msg: string, label = 'Warning')
Echo(msg, 'WarningMsg', label)
enddef
def EchoErr(msg: string, label = 'Error')
Echo(msg, 'ErrorMsg', label)
enddef
# }}}
# Track jobs {{{
var running_jobs = {} # Dictionary of job IDs of jobs currently executing
def AddJob(label: string, j: job)
if !has_key(running_jobs, label)
running_jobs[label] = []
endif
add(running_jobs[label], j)
enddef
def RemoveJob(label: string, j: job)
if has_key(running_jobs, label) && index(running_jobs[label], j) != -1
remove(running_jobs[label], index(running_jobs[label], j))
endif
enddef
def GetRunningJobs(label: string): list<job>
return has_key(running_jobs, label) ? running_jobs[label] : []
enddef
# }}}
# Callbacks {{{
def ProcessOutput(qfid: number, wd: string, efm: string, ch: channel, msg: string)
# Make sure the quickfix list still exists
if getqflist({'id': qfid}).id != qfid
EchoErr("Quickfix list not found, stopping the job")
call job_stop(ch_getjob(ch))
return
endif
# Make sure the working directory is correct
silent execute "lcd" wd
setqflist([], 'a', {'id': qfid, 'lines': [msg], 'efm': efm})
silent lcd -
enddef
def CloseCb(ch: channel)
job_status(ch_getjob(ch)) # Trigger exit_cb's callback
enddef
def ExitCb(label: string, jobid: job, exitStatus: number)
RemoveJob(label, jobid)
if exitStatus == 0
botright cwindow
EchoMsg('Success!', label)
elseif exitStatus < 0
EchoWarn('Job terminated', label)
else
botright copen
wincmd p
EchoWarn('There are errors.', label)
endif
enddef
# }}}
# Create a new empty quickfix list at the end of the stack and return its id {{{
def NewQuickfixList(path: string): number
if setqflist([], ' ', {'nr': '$', 'title': path}) == -1
return -1
endif
return getqflist({'nr': '$', 'id': 0}).id
enddef
# }}}
# Public interface {{{
# When a TeX document is split into several source files, each source file
# may contain a "magic line" specifiying the "root" file, e.g.:
#
# % !TEX root = main.tex
#
# Using this line, Vim can know which file to typeset even if the current
# buffer is different from main.tex.
#
# This function searches for the magic line in the first ten lines of the
# given buffer, and returns the full path of the root document.
#
# NOTE: the value of "% !TEX root" *must* be a relative path.
export def FindRootDocument(bufname: string = bufname("%")): string
const bufnr = bufnr(bufname)
if !bufexists(bufnr)
return bufname
endif
var rootpath = fnamemodify(bufname(bufnr), ':p')
# Search for magic line `% !TEX root = ...` in the first ten lines
const header = getbufline(bufnr, 1, 10)
const idx = match(header, '^\s*%\s\+!TEX\s\+root\s*=\s*\S')
if idx > -1
const main = matchstr(header[idx], '!TEX\s\+root\s*=\s*\zs.*$')
rootpath = simplify(fnamemodify(rootpath, ":h") .. SLASH .. main)
endif
return rootpath
enddef
export def LogPath(bufname: string): string
const logfile = FindRootDocument(bufname)
return fnamemodify(logfile, ":r") .. ".log"
enddef
# Typeset the specified path
#
# Parameters:
# label: a descriptive string used in messages to identify the kind of job
# Cmd: a function that takes the path of a document and returns the typesetting command
# path: the path of the document to be typeset. To avoid ambiguities, pass a *full* path.
# efm: the error format string to parse the output of the command.
# env: environment variables for the process (passed to job_start())
#
# Returns:
# true if the job is started successfully;
# false otherwise.
export def Typeset(
label: string,
Cmd: func(string): list<string>,
path: string,
efm: string,
env: dict<string> = {}
): bool
var fp = fnamemodify(path, ":p")
var wd = fnamemodify(fp, ":h")
var qfid = NewQuickfixList(fp)
if qfid == -1
EchoErr('Could not create quickfix list', label)
return false
endif
if !filereadable(fp)
EchoErr(printf('File not readable: %s', fp), label)
return false
endif
var jobid = job_start(Cmd(path), {
env: env,
cwd: wd,
in_io: "null",
callback: (c, m) => ProcessOutput(qfid, wd, efm, c, m),
close_cb: CloseCb,
exit_cb: (j, e) => ExitCb(label, j, e),
})
if job_status(jobid) ==# "fail"
EchoErr("Failed to start job", label)
return false
endif
AddJob(label, jobid)
EchoMsg('Typesetting...', label)
return true
enddef
export def JobStatus(label: string)
EchoMsg('Jobs still running: ' .. string(len(GetRunningJobs(label))), label)
enddef
export def StopJobs(label: string)
for job in GetRunningJobs(label)
job_stop(job)
endfor
EchoMsg('Done.', label)
enddef
# Typeset the specified buffer
#
# Parameters:
# name: a buffer's name. this may be empty to indicate the current buffer.
# cmd: a function that takes the path of a document and returns the typesetting command
# label: a descriptive string used in messages to identify the kind of job
# env: environment variables for the process (passed to job_start())
#
# Returns:
# true if the job is started successfully;
# false otherwise.
export def TypesetBuffer(
name: string,
Cmd: func(string): list<string>,
env = {},
label = 'Typeset'
): bool
const bufname = bufname(name)
if empty(bufname)
EchoErr('Please save the buffer first.', label)
return false
endif
const efm = getbufvar(bufnr(bufname), "&efm")
const rootpath = FindRootDocument(bufname)
return Typeset('ConTeXt', Cmd, rootpath, efm, env)
enddef
# }}}
# vim: sw=2 fdm=marker