diff options
95 files changed, 2128 insertions, 5290 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 42eb50ac43..111e8b76d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,14 @@ include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) find_package(LuaJit REQUIRED) include_directories(SYSTEM ${LUAJIT_INCLUDE_DIRS}) +set(LIBUNIBILIUM_USE_STATIC ON) +find_package(LibUnibilium REQUIRED) +include_directories(SYSTEM ${LIBUNIBILIUM_INCLUDE_DIRS}) + +set(LIBTERMKEY_USE_STATIC ON) +find_package(LibTermkey REQUIRED) +include_directories(SYSTEM ${LIBTERMEY_INCLUDE_DIRS}) + find_package(LibIntl) if(LibIntl_FOUND) include_directories(SYSTEM ${LibIntl_INCLUDE_DIRS}) @@ -2776,7 +2776,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # TODO(unknown): figure out if they're using default arguments in fn proto. # Check if people are using the verboten C basic types. - match = Search(r'\b(short|long(?! +double)|long long)\b', line) + match = Search(r'\b(short|long long)\b', line) if match: error(filename, linenum, 'runtime/int', 4, 'Use int16_t/int64_t/etc, rather than the C type %s' diff --git a/cmake/FindLibTermkey.cmake b/cmake/FindLibTermkey.cmake index 533f168fe9..e5ec132f85 100644 --- a/cmake/FindLibTermkey.cmake +++ b/cmake/FindLibTermkey.cmake @@ -8,7 +8,7 @@ find_package(PkgConfig) if(NOT LIBTERMKEY_USE_BUNDLED) find_package(PkgConfig) if (PKG_CONFIG_FOUND) - pkg_check_modules(PC_LIBTERMKEY QUIET libtermkey) + pkg_check_modules(PC_LIBTERMKEY QUIET termkey) endif() else() set(PC_LIBTERMKEY_INCLUDEDIR) diff --git a/cmake/FindLibTickit.cmake b/cmake/FindLibTickit.cmake deleted file mode 100644 index c20bf4f74f..0000000000 --- a/cmake/FindLibTickit.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# - Try to find libtickit -# Once done this will define -# LIBTICKIT_FOUND - System has libtickit -# LIBTICKIT_INCLUDE_DIRS - The libtickit include directories -# LIBTICKIT_LIBRARIES - The libraries needed to use libtickit - -find_package(PkgConfig) -if(NOT LIBTICKIT_USE_BUNDLED) - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(PC_LIBTICKIT QUIET libtickit) - endif() -else() - set(PC_LIBTICKIT_INCLUDEDIR) - set(PC_LIBTICKIT_INCLUDE_DIRS) - set(PC_LIBTICKIT_LIBDIR) - set(PC_LIBTICKIT_LIBRARY_DIRS) - set(LIMIT_SEARCH NO_DEFAULT_PATH) -endif() - -set(LIBTICKIT_DEFINITIONS ${PC_LIBTICKIT_CFLAGS_OTHER}) - -find_path(LIBTICKIT_INCLUDE_DIR tickit.h - PATHS ${PC_LIBTICKIT_INCLUDEDIR} ${PC_LIBTICKIT_INCLUDE_DIRS} - ${LIMIT_SEARCH}) - -# If we're asked to use static linkage, add libuv.a as a preferred library name. -if(LIBTICKIT_USE_STATIC) - list(APPEND LIBTICKIT_NAMES - "${CMAKE_STATIC_LIBRARY_PREFIX}tickit${CMAKE_STATIC_LIBRARY_SUFFIX}") -endif() - -list(APPEND LIBTICKIT_NAMES tickit) - -find_library(LIBTICKIT_LIBRARY NAMES ${LIBTICKIT_NAMES} - HINTS ${PC_LIBTICKIT_LIBDIR} ${PC_LIBTICKIT_LIBRARY_DIRS} - ${LIMIT_SEARCH}) - -set(LIBTICKIT_LIBRARIES ${LIBTICKIT_LIBRARY}) -set(LIBTICKIT_INCLUDE_DIRS ${LIBTICKIT_INCLUDE_DIR}) - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LIBTICKIT_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(LibTickit DEFAULT_MSG - LIBTICKIT_LIBRARY LIBTICKIT_INCLUDE_DIR) - -mark_as_advanced(LIBTICKIT_INCLUDE_DIR LIBTICKIT_LIBRARY) diff --git a/cmake/FindLibUnibilium.cmake b/cmake/FindLibUnibilium.cmake index aace9a40d5..6aafd07c90 100644 --- a/cmake/FindLibUnibilium.cmake +++ b/cmake/FindLibUnibilium.cmake @@ -8,7 +8,7 @@ find_package(PkgConfig) if(NOT LIBUNIBILIUM_USE_BUNDLED) find_package(PkgConfig) if (PKG_CONFIG_FOUND) - pkg_check_modules(PC_LIBUNIBILIUM QUIET libunibilium) + pkg_check_modules(PC_LIBUNIBILIUM QUIET unibilium) endif() else() set(PC_LIBUNIBILIUM_INCLUDEDIR) diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index c620c96e61..1ee5c78adf 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -23,7 +23,6 @@ check_include_files(locale.h HAVE_LOCALE_H) check_include_files(pwd.h HAVE_PWD_H) check_include_files(strings.h HAVE_STRINGS_H) check_include_files(stropts.h HAVE_STROPTS_H) -check_include_files(sys/ioctl.h HAVE_SYS_IOCTL_H) check_include_files(sys/param.h HAVE_SYS_PARAM_H) check_include_files(sys/time.h HAVE_SYS_TIME_H) check_include_files(sys/wait.h HAVE_SYS_WAIT_H) @@ -33,9 +32,6 @@ if(NOT HAVE_SYS_WAIT_H AND UNIX) endif() check_include_files(sys/utsname.h HAVE_SYS_UTSNAME_H) check_include_files(utime.h HAVE_UTIME_H) -check_include_files(termcap.h HAVE_TERMCAP_H) -check_include_files(termios.h HAVE_TERMIOS_H) -check_include_files(termio.h HAVE_TERMIO_H) check_include_files(unistd.h HAVE_UNISTD_H) check_include_files(utime.h HAVE_UTIME_H) diff --git a/config/config.h.in b/config/config.h.in index 0a2aa4786e..9e6f3d8e13 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -53,15 +53,10 @@ #cmakedefine HAVE_STRINGS_H #cmakedefine HAVE_STRNCASECMP #cmakedefine HAVE_STROPTS_H -#cmakedefine HAVE_SYS_IOCTL_H #cmakedefine HAVE_SYS_PARAM_H #cmakedefine HAVE_SYS_TIME_H #cmakedefine HAVE_SYS_UTSNAME_H #cmakedefine HAVE_SYS_WAIT_H -#cmakedefine HAVE_TERMCAP_H -#cmakedefine HAVE_TERMIOS_H -#cmakedefine HAVE_TERMIO_H -#define HAVE_TGETENT 1 #cmakedefine HAVE_UNISTD_H #define HAVE_UP_BC_PC 1 #cmakedefine HAVE_UTIME @@ -70,8 +65,6 @@ #cmakedefine HAVE_WORKING_LIBINTL #define RETSIGTYPE void #define SIGRETURN return -#define TERMINFO 1 -#define TGETENT_ZERO_ERR 0 #define TIME_WITH_SYS_TIME 1 #cmakedefine UNIX #define USEMAN_S 1 @@ -79,9 +72,3 @@ #define FEAT_BROWSE #define FEAT_CSCOPE #define FEAT_MOUSE -#define FEAT_MOUSE_NET -#define FEAT_MOUSE_SGR -#define FEAT_MOUSE_TTY -#define FEAT_MOUSE_URXVT -#define FEAT_MOUSE_XTERM -#define FEAT_TERMRESPONSE diff --git a/runtime/autoload/spellfile.vim b/runtime/autoload/spellfile.vim index e7fd1d8b17..8024f40bb0 100644 --- a/runtime/autoload/spellfile.vim +++ b/runtime/autoload/spellfile.vim @@ -198,7 +198,7 @@ endfunc function! spellfile#WritableSpellDir() if has("unix") " For Unix always use the $HOME/.vim directory - return $HOME . "/.vim/spell" + return $HOME . "/.nvim/spell" endif for dir in split(&rtp, ',') if filewritable(dir) == 2 diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 85384519e6..6a9e4f025d 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -296,6 +296,9 @@ Name triggered by ~ |WinLeave| before leaving a window |TabEnter| after entering another tab page |TabLeave| before leaving a tab page +|TabNew| when creating a new tab page +|TabNewEntered| after entering a new tab page +|TabClosed| after closing a tab page |CmdwinEnter| after entering the command-line window |CmdwinLeave| before leaving the command-line window @@ -859,6 +862,15 @@ TabEnter Just after entering a tab page. |tab-page| TabLeave Just before leaving a tab page. |tab-page| A WinLeave event will have been triggered first. + *TabNew* +TabNew When creating a new tab page. |tab-page| + After WinEnter and before TabEnter. + *TabNewEntered* +TabNewEntered After entering a new tab page. |tab-page| + After BufEnter. + *TabClosed* +TabClosed After closing a tab page. <afile> can be used + for the tab page number. *TermChanged* TermChanged After the value of 'term' has changed. Useful for re-loading the syntax file to update the diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 3b63de503e..21f3e61944 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -648,9 +648,6 @@ The flags that you can use for the substitute commands: compiled without the |+insert_expand| feature} CTRL-Y to scroll the screen down {not in Vi, not available when compiled without the |+insert_expand| feature} - If the 'edcompatible' option is on, Vim remembers the [c] flag and - toggles it each time you use it, but resets it when you give a new - search pattern. {not in Vi: highlighting of the match, other responses than 'y' or 'n'} [e] When the search pattern fails, do not issue an error message and, in @@ -665,11 +662,9 @@ The flags that you can use for the substitute commands: {not in Vi} [g] Replace all occurrences in the line. Without this argument, - replacement occurs only for the first occurrence in each line. If - the 'edcompatible' option is on, Vim remembers this flag and toggles - it each time you use it, but resets it when you give a new search - pattern. If the 'gdefault' option is on, this flag is on by default - and the [g] argument switches it off. + replacement occurs only for the first occurrence in each line. If the + 'gdefault' option is on, this flag is on by default and the [g] + argument switches it off. [i] Ignore case for the pattern. The 'ignorecase' and 'smartcase' options are not used. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 456cd5dff6..c4d84c36c3 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2959,9 +2959,12 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()* feedkeys({string} [, {mode}]) *feedkeys()* Characters in {string} are queued for processing as if they - come from a mapping or were typed by the user. They are added - to the end of the typeahead buffer, thus if a mapping is still - being executed these characters come after them. + come from a mapping or were typed by the user. + By default the string is added to the end of the typeahead + buffer, thus if a mapping is still being executed the + characters come after them. Use the 'i' flag to insert before + other characters, they will be executed next, before any + characters from a mapping. The function does not wait for processing of keys contained in {string}. To include special keys into {string}, use double-quotes @@ -2975,6 +2978,7 @@ feedkeys({string} [, {mode}]) *feedkeys()* 't' Handle keys as if typed; otherwise they are handled as if coming from a mapping. This matters for undo, opening folds, etc. + 'i' Insert the string instead of appending (see above). Return value is always 0. filereadable({file}) *filereadable()* diff --git a/runtime/doc/manpages/de/vim-de.1 b/runtime/doc/manpages/de/vim-de.1 index 52a97a0856..d958372012 100644 --- a/runtime/doc/manpages/de/vim-de.1 +++ b/runtime/doc/manpages/de/vim-de.1 @@ -156,6 +156,7 @@ sehr ähnlich zu Vi, selbst wenn eine VimRC\-Datei existiert. \-d Startet im diff\-Modus. Es sollten zwei, drei oder vier Dateinamen als Parameter übergeben werden. \fBVim\fP öffnet sie alle und zeigt die +Unterschiede an. .TP \-d {Gerät} Öffnet das {Gerät}, um es als Terminal zu nutzen. Nur für AmigaOS. Beispiel: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 928a98066f..790903a847 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2415,12 +2415,7 @@ A jump table for the options with a short description can be found at |Q_op|. both width and height of windows is affected *'ed'* *'edcompatible'* *'noed'* *'noedcompatible'* -'edcompatible' 'ed' boolean (default off) - global - Makes the 'g' and 'c' flags of the ":substitute" command to be - toggled each time the flag is given. See |complex-change|. See - also 'gdefault' option. - Switching this option on is discouraged! +'edcompatible' 'ed' Removed. {Nvim} *'encoding'* *'enc'* *E543* 'encoding' 'enc' string (default: "latin1" or value from $LANG) diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 3b3214456b..0e4418ba6a 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -674,7 +674,6 @@ Short explanation of each option: *option-list* 'directory' 'dir' list of directory names for the swap file 'display' 'dy' list of flags for how to display text 'eadirection' 'ead' in which direction 'equalalways' works -'edcompatible' 'ed' toggle flags of ":substitute" command 'encoding' 'enc' encoding used internally 'endofline' 'eol' write <EOL> for last line in file 'equalalways' 'ea' windows are automatically made the same size diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 3c7ad9fe28..dd3a031020 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -251,6 +251,9 @@ When switching to another tab page the order is: WinEnter BufEnter +When entering a new tab page (|:tabnew|), TabNew is triggered before TabEnter +and after WinEnter. + ============================================================================== 4. Setting 'tabline' *setting-tabline* diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 073de3876a..b062ea2d30 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1228,8 +1228,6 @@ call append("$", "secure\tsafer working with script files in the current directo call <SID>BinOptionG("secure", &secure) call append("$", "gdefault\tuse the 'g' flag for \":substitute\"") call <SID>BinOptionG("gd", &gd) -call append("$", "edcompatible\t'g' and 'c' flags of \":substitute\" toggle") -call <SID>BinOptionG("ed", &ed) if exists("+opendevice") call append("$", "opendevice\tallow reading/writing devices") call <SID>BinOptionG("odev", &odev) diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 5614a3f2f6..c5aacec565 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -32,17 +32,17 @@ syn keyword vimOption contained ai ambw ari aw backupext beval brk buflisted cdp syn keyword vimOption contained akm anti arshape awa backupskip bex browsedir buftype cedit cindent clipboard cole complete crb cscopetagorder csto cwh dg dip eadirection ek equalprg ex fdi fen fileencoding fk foldcolumn foldmethod formatoptions gd go guifont guitabtooltip hid hkp iconstring imc imsearch inde insertmode iskeyword keymodel laststatus lisp lpl magic maxfuncdepth menuitems mm modifiable mousem mps number opendevice paste pex pmbcs printencoding prompt rdt report rlc ruler scb scs sessionoptions shellquote shiftround showfulltag sidescrolloff smarttab sp spf srr startofline suffixes switchbuf ta tagrelative tbis textauto timeout tm ts ttybuiltin tx undolevels vbs viewdir wak weirdinvert wic wildmenu winfixheight wiv wop writebackup syn keyword vimOption contained al antialias autochdir background balloondelay bexpr bk bs casemap cf cink cmdheight colorcolumn completefunc copyindent cscopeverbose csverb debug dict dir eb enc errorbells expandtab fdl fenc fileencodings fkmap foldenable foldminlines formatprg gdefault gp guifontset helpfile hidden hl ignorecase imcmdline imsf indentexpr is isp keywordprg lazyredraw lispwords ls makeef maxmapdepth mfd mmd modified mousemodel msm numberwidth operatorfunc pastetoggle pexpr pmbfn printexpr pt re restorescreen rnu rulerformat scr sect sft shellredir shiftwidth showmatch siso smc spc spl ss statusline suffixesadd sws tabline tags tbs textmode timeoutlen to tsl uc undoreload vdir viewoptions warn wfh wig wildmode winfixwidth wiw wrap writedelay syn keyword vimOption contained aleph ar autoindent backspace ballooneval bg bkc bsdir cb cfu cinkeys cmdwinheight columns completeopt cot cscopepathcomp cspc cuc deco dictionary directory ed encoding errorfile exrc fdls fencs fileformat flp foldexpr foldnestmax fp gfm grepformat guifontwide helpheight highlight hlg im imd imstatusfunc indentkeys isf isprint km lbr list lsp makeprg maxmem mh mmp more mouses mzq nuw opfunc patchexpr pfn popt printfont pumheight readonly revins ro runtimepath scroll sections sh shellslash shm showmode sj smd spell splitbelow ssl stl sw sxe tabpagemax tagstack tenc textwidth title toolbar tsr ttym udf updatecount ve viminfo wb wfw wildchar wildoptions winheight wm wrapmargin ws -syn keyword vimOption contained allowrevins arab autoread backup balloonexpr bh bl bsk cc ch cino cmp com concealcursor cp cscopeprg csprg cul def diff display edcompatible endofline errorformat fcl fdm fex fileformats fml foldignore foldopen fs gfn grepprg guiheadroom helplang history hls imactivatefunc imdisable inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev patchmode ph preserveindent printheader pvh redrawtime ri rs sb scrollbind secure shcf shelltemp shortmess showtabline slm spellcapcheck splitright ssop stmp swapfile sxq tabstop tal term titlelen toolbariconsize ttimeout ttymouse udir updatetime verbose virtualedit wc wh wildcharm wim winminheight wmh wrapscan ww +syn keyword vimOption contained allowrevins arab autoread backup balloonexpr bh bl bsk cc ch cino cmp com concealcursor cp cscopeprg csprg cul def diff display endofline errorformat fcl fdm fex fileformats fml foldignore foldopen fs gfn grepprg guiheadroom helplang history hls imactivatefunc imdisable inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev patchmode ph preserveindent printheader pvh redrawtime ri rs sb scrollbind secure shcf shelltemp shortmess showtabline slm spellcapcheck splitright ssop stmp swapfile sxq tabstop tal term titlelen toolbariconsize ttimeout ttymouse udir updatetime verbose virtualedit wc wh wildcharm wim winminheight wmh wrapscan ww syn keyword vimOption contained altkeymap arabic autowrite backupcopy bdir bin bomb bt ccv charconvert cinoptions cms comments conceallevel cpo cscopequickfix csqf cursorbind define diffexpr dy ef eol esckeys fcs fdn ff fileignorecase fmr foldlevel foldtext fsync gfs gtl guioptions hf hk hlsearch imactivatekey imi include inex isi js kp linebreak lm lz matchpairs maxmemtot mkspellmem mod mousef mouset nf pa path pheader previewheight printmbcharset pvw regexpengine rightleft rtp sbo scrolljump sel shell shq sm so spellfile spr st sts swapsync syn tag tb termbidi tgst titleold top ttimeoutlen ttyscroll ul ur unnamedclip unc verbosefile visualbell " vimOptions: These are the turn-off setting variants {{{2 -syn keyword vimOption contained noacd noallowrevins noantialias noarabic noarshape noautoread noaw noballooneval nobinary nobk nobuflisted nocin noconfirm nocopyindent nocscoperelative nocsre nocuc nocursorcolumn nodelcombine nodigraph noed noendofline noerrorbells noex nofen nofk nogd nohid nohkmap nohkp nohlsearch noicon noim noimcmdline noimdisable noinf noinsertmode nojoinspaces nolazyredraw nolinebreak nolist nolpl noma nomagic noml nomodeline nomodified nomousef nomousehide nonumber noopendevice nopi nopreviewwindow nopvw norelativenumber norestorescreen nori norl noro noru nosb noscb noscs nosft noshelltemp noshowfulltag noshowmode nosm nosmartindent nosmd nosol nosplitbelow nospr nossl nostartofline noswapfile nota notagrelative notbi notbs noterse notextmode notgst notimeout noto notr nottybuiltin notx noundofile novisualbell nowarn noweirdinvert nowfw nowildignorecase nowinfixheight nowiv nowrap nowrite nowritebackup -syn keyword vimOption contained noai noaltkeymap noar noarabicshape noautochdir noautowrite noawa nobeval nobl nocf nocindent nocp nocscopetag nocst nocul nocursorline nodg noea noedcompatible noeol noesckeys noexpandtab nofic nofkmap nogdefault nohidden nohkmapp nohls noic noignorecase noimc noimd noincsearch noinfercase nois nojs nolbr nolisp noloadplugins nolz nomacatsui nomh nomod nomodifiable nomore nomousefocus nonu noodev nopaste nopreserveindent noprompt noreadonly noremap norevins norightleft nornu nors noruler nosc noscrollbind nosecure noshellslash noshiftround noshowcmd noshowmatch nosi nosmartcase nosmarttab nospell nosplitright nosr nosta nostmp noswf notagbsearch notagstack notbidi notermbidi notextauto notildeop notitle notop nottimeout noudf novb nowa nowb nowfh nowic nowildmenu nowinfixwidth nowmnu nowrapscan nowriteany nows +syn keyword vimOption contained noacd noallowrevins noantialias noarabic noarshape noautoread noaw noballooneval nobinary nobk nobuflisted nocin noconfirm nocopyindent nocscoperelative nocsre nocuc nocursorcolumn nodelcombine nodigraph noendofline noerrorbells noex nofen nofk nogd nohid nohkmap nohkp nohlsearch noicon noim noimcmdline noimdisable noinf noinsertmode nojoinspaces nolazyredraw nolinebreak nolist nolpl noma nomagic noml nomodeline nomodified nomousef nomousehide nonumber noopendevice nopi nopreviewwindow nopvw norelativenumber norestorescreen nori norl noro noru nosb noscb noscs nosft noshelltemp noshowfulltag noshowmode nosm nosmartindent nosmd nosol nosplitbelow nospr nossl nostartofline noswapfile nota notagrelative notbi notbs noterse notextmode notgst notimeout noto notr nottybuiltin notx noundofile novisualbell nowarn noweirdinvert nowfw nowildignorecase nowinfixheight nowiv nowrap nowrite nowritebackup +syn keyword vimOption contained noai noaltkeymap noar noarabicshape noautochdir noautowrite noawa nobeval nobl nocf nocindent nocp nocscopetag nocst nocul nocursorline nodg noea noeol noesckeys noexpandtab nofic nofkmap nogdefault nohidden nohkmapp nohls noic noignorecase noimc noimd noincsearch noinfercase nois nojs nolbr nolisp noloadplugins nolz nomacatsui nomh nomod nomodifiable nomore nomousefocus nonu noodev nopaste nopreserveindent noprompt noreadonly noremap norevins norightleft nornu nors noruler nosc noscrollbind nosecure noshellslash noshiftround noshowcmd noshowmatch nosi nosmartcase nosmarttab nospell nosplitright nosr nosta nostmp noswf notagbsearch notagstack notbidi notermbidi notextauto notildeop notitle notop nottimeout noudf novb nowa nowb nowfh nowic nowildmenu nowinfixwidth nowmnu nowrapscan nowriteany nows syn keyword vimOption contained noakm noanti noarab noari noautoindent noautowriteall nobackup nobin nobomb noci nocompatible nocrb nocscopeverbose nocsverb nocursorbind nodeco nodiff noeb noek noequalalways noet noexrc nofileignorecase nofoldenable noguipty nohk " vimOptions: These are the invertible variants {{{2 -syn keyword vimOption contained invacd invallowrevins invantialias invarabic invarshape invautoread invaw invballooneval invbinary invbk invbuflisted invcin invconfirm invcopyindent invcscoperelative invcsre invcuc invcursorcolumn invdelcombine invdigraph inved invendofline inverrorbells invex invfen invfk invgd invhid invhkmap invhkp invhlsearch invicon invim invimcmdline invimdisable invinf invinsertmode invjoinspaces invlazyredraw invlinebreak invlist invlpl invma invmagic invml invmodeline invmodified invmousef invmousehide invnumber invopendevice invpi invpreviewwindow invpvw invrelativenumber invrestorescreen invri invrl invro invru invsb invscb invscs invsft invshelltemp invshowfulltag invshowmode invsm invsmartindent invsmd invsol invsplitbelow invspr invssl invstartofline invswapfile invta invtagrelative invtbi invtbs invterse invtextmode invtgst invtimeout invto invtr invttybuiltin invtx invundofile invvisualbell invwarn invweirdinvert invwfw invwildignorecase invwinfixheight invwiv invwrap invwrite invwritebackup -syn keyword vimOption contained invai invaltkeymap invar invarabicshape invautochdir invautowrite invawa invbeval invbl invcf invcindent invcp invcscopetag invcst invcul invcursorline invdg invea invedcompatible inveol invesckeys invexpandtab invfic invfkmap invgdefault invhidden invhkmapp invhls invic invignorecase invimc invimd invincsearch invinfercase invis invjs invlbr invlisp invloadplugins invlz invmacatsui invmh invmod invmodifiable invmore invmousefocus invnu invodev invpaste invpreserveindent invprompt invreadonly invremap invrevins invrightleft invrnu invrs invruler invsc invscrollbind invsecure invshellslash invshiftround invshowcmd invshowmatch invsi invsmartcase invsmarttab invspell invsplitright invsr invsta invstmp invswf invtagbsearch invtagstack invtbidi invtermbidi invtextauto invtildeop invtitle invtop invttimeout invudf invvb invwa invwb invwfh invwic invwildmenu invwinfixwidth invwmnu invwrapscan invwriteany invws +syn keyword vimOption contained invacd invallowrevins invantialias invarabic invarshape invautoread invaw invballooneval invbinary invbk invbuflisted invcin invconfirm invcopyindent invcscoperelative invcsre invcuc invcursorcolumn invdelcombine invdigraph invendofline inverrorbells invex invfen invfk invgd invhid invhkmap invhkp invhlsearch invicon invim invimcmdline invimdisable invinf invinsertmode invjoinspaces invlazyredraw invlinebreak invlist invlpl invma invmagic invml invmodeline invmodified invmousef invmousehide invnumber invopendevice invpi invpreviewwindow invpvw invrelativenumber invrestorescreen invri invrl invro invru invsb invscb invscs invsft invshelltemp invshowfulltag invshowmode invsm invsmartindent invsmd invsol invsplitbelow invspr invssl invstartofline invswapfile invta invtagrelative invtbi invtbs invterse invtextmode invtgst invtimeout invto invtr invttybuiltin invtx invundofile invvisualbell invwarn invweirdinvert invwfw invwildignorecase invwinfixheight invwiv invwrap invwrite invwritebackup +syn keyword vimOption contained invai invaltkeymap invar invarabicshape invautochdir invautowrite invawa invbeval invbl invcf invcindent invcp invcscopetag invcst invcul invcursorline invdg invea inveol invesckeys invexpandtab invfic invfkmap invgdefault invhidden invhkmapp invhls invic invignorecase invimc invimd invincsearch invinfercase invis invjs invlbr invlisp invloadplugins invlz invmacatsui invmh invmod invmodifiable invmore invmousefocus invnu invodev invpaste invpreserveindent invprompt invreadonly invremap invrevins invrightleft invrnu invrs invruler invsc invscrollbind invsecure invshellslash invshiftround invshowcmd invshowmatch invsi invsmartcase invsmarttab invspell invsplitright invsr invsta invstmp invswf invtagbsearch invtagstack invtbidi invtermbidi invtextauto invtildeop invtitle invtop invttimeout invudf invvb invwa invwb invwfh invwic invwildmenu invwinfixwidth invwmnu invwrapscan invwriteany invws syn keyword vimOption contained invakm invanti invarab invari invautoindent invautowriteall invbackup invbin invbomb invci invcompatible invcrb invcscopeverbose invcsverb invcursorbind invdeco invdiff inveb invek invequalalways invet invexrc invfileignorecase invfoldenable invguipty invhk " termcap codes (which can also be set) {{{2 @@ -62,7 +62,7 @@ syn keyword vimErrSetting contained hardtabs ht w1200 w300 w9600 " AutoCmd Events {{{2 syn case ignore -syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre Cmd-event CmdwinEnter CmdwinLeave ColorScheme CompleteDone CursorHold CursorHoldI CursorMoved CursorMovedI EncodingChanged FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave JobActivity MenuPopup QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SessionLoadPost ShellCmdPost ShellFilterPost SourceCmd SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabEnter TabLeave TermChanged TermResponse TextChanged TextChangedI User UserGettingBored VimEnter VimLeave VimLeavePre VimResized WinEnter WinLeave +syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre Cmd-event CmdwinEnter CmdwinLeave ColorScheme CompleteDone CursorHold CursorHoldI CursorMoved CursorMovedI EncodingChanged FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave JobActivity MenuPopup QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SessionLoadPost ShellCmdPost ShellFilterPost SourceCmd SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabEnter TabLeave TabNew TabNewEntered TabClosed TermChanged TermResponse TextChanged TextChangedI User UserGettingBored VimEnter VimLeave VimLeavePre VimResized WinEnter WinLeave " Highlight commonly used Groupnames {{{2 syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index d31a062a68..9ff9b4cf6f 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -287,7 +287,7 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, { String m = { .data=(char *)name, - .size=min(name_len, ]]..max_fname_len..[[) + .size=MIN(name_len, ]]..max_fname_len..[[) }; MsgpackRpcRequestHandler rv = map_get(String, MsgpackRpcRequestHandler)(methods, m); diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 6fc5022d48..1c2dad6094 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -26,13 +26,16 @@ file(MAKE_DIRECTORY ${GENERATED_DIR}/os) file(MAKE_DIRECTORY ${GENERATED_DIR}/api) file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private) file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc) +file(MAKE_DIRECTORY ${GENERATED_DIR}/tui) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc) +file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui) -file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c) +file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c + tui/*.c) file(GLOB_RECURSE NEOVIM_HEADERS *.h) foreach(sfile ${NEOVIM_SOURCES}) @@ -60,9 +63,6 @@ set(CONV_SOURCES fold.c getchar.c if_cscope.c - indent.c - keymap.c - mark.c mbyte.c memline.c menu.c @@ -71,7 +71,6 @@ set(CONV_SOURCES move.c normal.c ops.c - os_unix.c path.c popupmnu.c quickfix.c @@ -81,7 +80,6 @@ set(CONV_SOURCES spell.c syntax.c tag.c - ui.c window.c) foreach(sfile ${CONV_SOURCES}) @@ -164,19 +162,6 @@ if (LibIntl_FOUND) list(APPEND NVIM_LINK_LIBRARIES ${LibIntl_LIBRARY}) endif() -check_library_exists(curses tgetent "" HAVE_LIBCURSES) -if (HAVE_LIBCURSES) - list(APPEND NVIM_LINK_LIBRARIES curses) -else() - check_library_exists(tinfo tgetent "" HAVE_LIBTINFO) - if (HAVE_LIBTINFO) - list(APPEND NVIM_LINK_LIBRARIES tinfo) - else() - find_package(Curses REQUIRED) - list(APPEND NVIM_LINK_LIBRARIES ${CURSES_LIBRARY}) - endif() -endif() - if(Iconv_LIBRARIES) list(APPEND NVIM_LINK_LIBRARIES ${Iconv_LIBRARIES}) endif() @@ -186,8 +171,12 @@ list(APPEND NVIM_LINK_LIBRARIES ${LIBUV_LIBRARIES} ${MSGPACK_LIBRARIES} ${LUAJIT_LIBRARIES} + ${LIBTICKIT_LIBRARIES} + ${LIBTERMKEY_LIBRARIES} + ${LIBUNIBILIUM_LIBRARIES} m - ${CMAKE_THREAD_LIBS_INIT}) + ${CMAKE_THREAD_LIBS_INIT} + ) add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 750b151d10..39ca0756f3 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -65,6 +65,7 @@ bool try_end(Error *err) } } else if (did_throw) { api_set_error(err, Exception, "%s", current_exception->value); + discard_current_exception(); } return err->set; @@ -643,10 +644,8 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup) switch (obj->v_type) { case VAR_STRING: - if (obj->vval.v_string != NULL) { - rv.type = kObjectTypeString; - rv.data.string = cstr_to_string((char *) obj->vval.v_string); - } + rv.type = kObjectTypeString; + rv.data.string = cstr_to_string((char *) obj->vval.v_string); break; case VAR_NUMBER: diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b7b2f7630c..33020c5b8c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -57,6 +57,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) FUNC_ATTR_DEFERRED { bool remap = true; + bool insert = false; bool typed = false; if (keys.size == 0) { @@ -68,6 +69,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) case 'n': remap = false; break; case 'm': remap = true; break; case 't': typed = true; break; + case 'i': insert = true; break; } } @@ -80,7 +82,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) keys_esc = keys.data; } ins_typebuf((char_u *)keys_esc, (remap ? REMAP_YES : REMAP_NONE), - typebuf.tb_len, !typed, false); + insert ? 0 : typebuf.tb_len, !typed, false); if (escape_csi) { free(keys_esc); @@ -362,20 +364,16 @@ void vim_report_error(String str) ArrayOf(Buffer) vim_get_buffers(void) { Array rv = ARRAY_DICT_INIT; - buf_T *b = firstbuf; - while (b) { + FOR_ALL_BUFFERS(b) { rv.size++; - b = b->b_next; } rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; - b = firstbuf; - while (b) { + FOR_ALL_BUFFERS(b) { rv.items[i++] = BUFFER_OBJ(b->handle); - b = b->b_next; } return rv; @@ -469,20 +467,16 @@ void vim_set_current_window(Window window, Error *err) ArrayOf(Tabpage) vim_get_tabpages(void) { Array rv = ARRAY_DICT_INIT; - tabpage_T *tp = first_tabpage; - while (tp) { + FOR_ALL_TABS(tp) { rv.size++; - tp = tp->tp_next; } rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; - tp = first_tabpage; - while (tp) { + FOR_ALL_TABS(tp) { rv.items[i++] = TABPAGE_OBJ(tp->handle); - tp = tp->tp_next; } return rv; @@ -584,7 +578,7 @@ Array vim_get_api_info(uint64_t channel_id) /// `emsg` instead of `msg` to print each line) static void write_msg(String message, bool to_err) { - static int out_pos = 0, err_pos = 0; + static size_t out_pos = 0, err_pos = 0; static char out_line_buf[LINE_BUFFER_SIZE], err_line_buf[LINE_BUFFER_SIZE]; #define PUSH_CHAR(i, pos, line_buf, msg) \ diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3a378ea360..4db2b4ef6b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4337,10 +4337,8 @@ void buf_addsign( if (lnum == sign->lnum && id == sign->id) { sign->typenr = typenr; return; - } - else if (id < 0 /* keep signs sorted by lnum */ - && lnum < sign->lnum) - { + } else if ((lnum == sign->lnum && id != sign->id) + || (id < 0 && lnum < sign->lnum)) { // attempt to keep signs sorted by lnum insert_sign(buf, prev, sign, id, lnum, typenr); return; } diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 6fb5ebf3e4..b8fcd2de09 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1849,25 +1849,6 @@ int hex2nr(int c) return c - '0'; } -#if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK) - -/// Convert two hex characters to a byte. -/// Return -1 if one of the characters is not hex. -/// -/// @param p -/// -/// @return The two hex characters converted to a byte or -1 if one of the -/// character is not hex. -int hexhex2nr(char_u *p) -{ - if (!vim_isxdigit(p[0]) || !vim_isxdigit(p[1])) { - return -1; - } - return (hex2nr(p[0]) << 4) + hex2nr(p[1]); -} - -#endif // if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK) - /// Return true if "str" starts with a backslash that should be removed. /// For WIN32 this is only done when the character after the /// backslash is not a normal file name character. diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 35c00c4f23..63b0eaf4f6 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1582,7 +1582,7 @@ static int diff_cmp(char_u *s1, char_u *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { - return MB_STRICMP(s1, s2); + return mb_stricmp(s1, s2); } // Ignore white space changes and possibly ignore case. diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 243468b680..cb6bfc9cc9 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1532,7 +1532,9 @@ static int getexactdigraph(int char1, int char2, int meta_char) if (convert_setup(&vc, (char_u *)"utf-8", p_enc) == OK) { vc.vc_fail = true; - to = string_convert(&vc, buf, &i); + assert(i >= 0); + size_t len = (size_t)i; + to = string_convert(&vc, buf, &len); if (to != NULL) { retval = (*mb_ptr2char)(to); diff --git a/src/nvim/edit.c b/src/nvim/edit.c index fa4e4b2835..7913a37d2c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6594,9 +6594,10 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) for (s = line + curwin->w_cursor.col; s > line; --s) if (!vim_iswordc(s[-1])) break; + assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX); if (s + (p - look) <= line + curwin->w_cursor.col && (icase - ? MB_STRNICMP(s, look, p - look) + ? mb_strnicmp(s, look, (size_t)(p - look)) : STRNCMP(s, look, p - look)) == 0) match = TRUE; } else @@ -6605,10 +6606,11 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) { line = get_cursor_pos_ptr(); + assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX); if ((curwin->w_cursor.col == (colnr_T)(p - look) || !vim_iswordc(line[-(p - look) - 1])) && (icase - ? MB_STRNICMP(line - (p - look), look, p - look) + ? mb_strnicmp(line - (p - look), look, (size_t)(p - look)) : STRNCMP(line - (p - look), look, p - look)) == 0) match = TRUE; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4bd4d89ef..b06c0961c5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3615,7 +3615,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) s1 = get_tv_string_buf(rettv, buf1); s2 = get_tv_string_buf(&var2, buf2); if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2); + i = ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2); else i = 0; n1 = FALSE; @@ -4955,7 +4955,7 @@ tv_equal ( case VAR_STRING: s1 = get_tv_string_buf(tv1, buf1); s2 = get_tv_string_buf(tv2, buf2); - return (ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0; + return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; } EMSG2(_(e_intern2), "tv_equal()"); @@ -9955,14 +9955,8 @@ static void f_has(typval_T *argvars, typval_T *rettv) #endif "tag_binary", "tag_old_static", -#ifdef TERMINFO - "terminfo", -#endif "termresponse", "textobjects", -#ifdef HAVE_TGETENT - "tgetent", -#endif "title", "user-commands", /* was accidentally included in 5.4 */ "user_commands", @@ -14424,13 +14418,10 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv) if (argvars[2].v_type != VAR_UNKNOWN) { mode = get_tv_string_buf(&argvars[2], modebuf); modec = TOLOWER_ASC(mode[0]); - if (modec != 't' && modec != 'c' && modec != 'g') + if (modec != 'c' && modec != 'g') modec = 0; /* replace invalid with current */ } else { - if (abstract_ui || t_colors > 1) - modec = 'c'; - else - modec = 't'; + modec = 'c'; } @@ -18080,21 +18071,6 @@ static int function_exists(char_u *name) return n; } -char_u *get_expanded_name(char_u *name, int check) -{ - char_u *nm = name; - char_u *p; - - p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL); - - if (p != NULL && *nm == NUL) - if (!check || translated_function_exists(p)) - return p; - - free(p); - return NULL; -} - /// Return TRUE if "name" looks like a builtin function name: starts with a /// lower case letter and doesn't contain AUTOLOAD_CHAR. /// "len" is the length of "name", or -1 for NUL terminated. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 3143363af6..ad49153a1e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1232,9 +1232,6 @@ do_shell ( * avoid having to type return below. */ msg_putchar('\r'); /* put cursor at start of line */ - if (!autocmd_busy) { - stoptermcap(); - } msg_putchar('\n'); /* may shift screen one line up */ /* warning message before calling the shell */ @@ -1292,8 +1289,6 @@ do_shell ( wait_return(msg_silent == 0); no_wait_return = save_nwr; } - - starttermcap(); /* start termcap if not done by wait_return() */ } /* display any error messages now */ @@ -3615,16 +3610,13 @@ void do_sub(exarg_T *eap) /* * Find trailing options. When '&' is used, keep old options. */ - if (*cmd == '&') + if (*cmd == '&') { ++cmd; - else { - if (!p_ed) { - if (p_gd) /* default is global on */ - do_all = TRUE; - else - do_all = FALSE; - do_ask = FALSE; - } + } else { + // default is global on + do_all = p_gd ? TRUE : FALSE; + + do_ask = FALSE; do_error = TRUE; do_print = FALSE; do_count = false; @@ -3632,10 +3624,8 @@ void do_sub(exarg_T *eap) do_ic = 0; } while (*cmd) { - /* - * Note that 'g' and 'c' are always inverted, also when p_ed is off. - * 'r' is never inverted. - */ + // Note that 'g' and 'c' are always inverted. + // 'r' is never inverted. if (*cmd == 'g') do_all = !do_all; else if (*cmd == 'c') @@ -5267,8 +5257,7 @@ void fix_help_buffer(void) else { /* Do the conversion. If it fails * use the unconverted text. */ - cp = string_convert(&vc, IObuff, - NULL); + cp = string_convert(&vc, IObuff, NULL); if (cp == NULL) cp = IObuff; } diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index aade4d736d..98a2b2814d 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2483,7 +2483,7 @@ return { { command='winpos', flags=bit.bor(EXTRA, TRLBAR, CMDWIN), - func='ex_winpos', + func='ex_ni', }, { command='wnext', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 7de47cb296..9bcbfa2f26 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -155,10 +155,6 @@ void do_debug(char_u *cmd) #define CMD_INTERRUPT 6 - /* Make sure we are in raw mode and start termcap mode. Might have side - * effects... */ - starttermcap(); - ++RedrawingDisabled; /* don't redisplay the window */ ++no_wait_return; /* don't wait for return */ did_emsg = FALSE; /* don't use error from debugged stuff */ diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9b48398d96..bf9b5c16d7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -146,10 +146,6 @@ struct dbg_stuff { # define gui_mch_find_dialog ex_ni # define gui_mch_replace_dialog ex_ni # define ex_helpfind ex_ni -#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN) -#else -# define ex_winpos ex_ni -#endif static int did_lcd; /* whether ":lcd" was produced for a session */ #ifndef HAVE_WORKING_LIBINTL # define ex_language ex_ni @@ -5332,10 +5328,12 @@ void tabpage_close_other(tabpage_T *tp, int forceit) int done = 0; win_T *wp; int h = tabline_height(); + char_u prev_idx[NUMBUFLEN]; /* Limit to 1000 windows, autocommands may add a window while we close * one. OK, so I'm paranoid... */ while (++done < 1000) { + sprintf((char *)prev_idx, "%i", tabpage_index(tp)); wp = tp->tp_firstwin; ex_win_close(forceit, wp, tp); @@ -5344,6 +5342,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit) if (!valid_tabpage(tp) || tp->tp_firstwin == wp) break; } + apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, FALSE, curbuf); redraw_tabline = TRUE; if (h != tabline_height()) @@ -5397,13 +5396,11 @@ static void ex_stop(exarg_T *eap) windgoto((int)Rows - 1, 0); out_char('\n'); out_flush(); - stoptermcap(); out_flush(); /* needed for SUN to restore xterm buffer */ mch_restore_title(3); /* restore window titles */ ui_suspend(); /* call machine specific function */ maketitle(); resettitle(); /* force updating the title */ - starttermcap(); scroll_start(); /* scroll screen before redrawing */ redraw_later_clear(); shell_resized(); /* may have resized window */ @@ -5721,7 +5718,9 @@ void ex_splitview(exarg_T *eap) if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 ? 0 : (int)eap->line2 + 1) != FAIL) { + apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf); do_exedit(eap, old_curwin); + apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf); /* set the alternate buffer for the window we came from */ if (curwin != old_curwin @@ -6438,7 +6437,7 @@ static void ex_winsize(exarg_T *eap) p = arg; h = getdigits_int(&arg); if (*p != NUL && *arg == NUL) - screen_resize(w, h, TRUE); + screen_resize(w, h); else EMSG(_("E465: :winsize requires two number arguments")); } @@ -6473,35 +6472,6 @@ static void ex_wincmd(exarg_T *eap) } } -#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN) -/* - * ":winpos". - */ -static void ex_winpos(exarg_T *eap) -{ - int x, y; - char_u *arg = eap->arg; - char_u *p; - - if (*arg == NUL) { - EMSG(_("E188: Obtaining window position not implemented for this platform")); - } else { - x = getdigits_int(&arg); - arg = skipwhite(arg); - p = arg; - y = getdigits_int(&arg); - if (*p == NUL || *arg != NUL) { - EMSG(_("E466: :winpos requires two number arguments")); - return; - } -# ifdef HAVE_TGETENT - if (*T_CWP) - term_set_winpos(x, y); -# endif - } -} -#endif - /* * Handle command that work like operators: ":delete", ":yank", ":>" and ":<". */ diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a4e8fccc23..6a3bd16feb 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3697,6 +3697,7 @@ ExpandFromContext ( {EXPAND_EVENTS, get_event_name, TRUE, TRUE}, {EXPAND_AUGROUP, get_augroup_name, TRUE, TRUE}, {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE}, + {EXPAND_SIGN, get_sign_name, TRUE, TRUE}, {EXPAND_PROFILE, get_profile_name, TRUE, TRUE}, #ifdef HAVE_WORKING_LIBINTL {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE}, @@ -4617,35 +4618,6 @@ int del_history_idx(int histype, int idx) return TRUE; } - -/* - * Very specific function to remove the value in ":set key=val" from the - * history. - */ -void remove_key_from_history(void) -{ - char_u *p; - int i; - - i = hisidx[HIST_CMD]; - if (i < 0) - return; - p = history[HIST_CMD][i].hisstr; - if (p != NULL) - for (; *p; ++p) - if (STRNCMP(p, "key", 3) == 0 && !isalpha(p[3])) { - p = vim_strchr(p + 3, '='); - if (p == NULL) - break; - ++p; - for (i = 0; p[i] && !vim_iswhite(p[i]); ++i) - if (p[i] == '\\' && p[i + 1]) - ++i; - STRMOVE(p, p + i); - --p; - } -} - /* * Get indices "num1,num2" that specify a range within a list (not a range of * text lines in a buffer!) from a string. Used for ":history" and ":clist". diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 2667d13b78..cd27567bd8 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -10,6 +10,7 @@ * fileio.c: read from and write to a file */ +#include <assert.h> #include <errno.h> #include <stdbool.h> #include <string.h> @@ -1234,7 +1235,6 @@ retry: if (size <= 0) break; - # ifdef USE_ICONV if (iconv_fd != (iconv_t)-1) { /* @@ -1293,16 +1293,6 @@ retry: } # endif -# ifdef MACOS_CONVERT - if (fio_flags & FIO_MACROMAN) { - /* - * Conversion from Apple MacRoman char encoding to UTF-8 or - * latin1. This is in os_mac_conv.c. - */ - if (macroman2enc(ptr, &size, real_size) == FAIL) - goto rewind_retry; - } else -# endif if (fio_flags != 0) { int u8c; char_u *dest; @@ -1812,7 +1802,6 @@ failed: * Switch on raw mode now and clear the screen. */ if (read_stdin) { - starttermcap(); screenclear(); } @@ -4029,38 +4018,6 @@ static int buf_write_bytes(struct bw_info *ip) } } - -# ifdef MACOS_CONVERT - else if (flags & FIO_MACROMAN) { - /* - * Convert UTF-8 or latin1 to Apple MacRoman. - */ - char_u *from; - size_t fromlen; - - if (ip->bw_restlen > 0) { - /* Need to concatenate the remainder of the previous call and - * the bytes of the current call. Use the end of the - * conversion buffer for this. */ - fromlen = len + ip->bw_restlen; - from = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; - memmove(from, ip->bw_rest, (size_t)ip->bw_restlen); - memmove(from + ip->bw_restlen, buf, (size_t)len); - } else { - from = buf; - fromlen = len; - } - - if (enc2macroman(from, fromlen, - ip->bw_conv_buf, &len, ip->bw_conv_buflen, - ip->bw_rest, &ip->bw_restlen) == FAIL) { - ip->bw_conv_error = TRUE; - return FAIL; - } - buf = ip->bw_conv_buf; - } -# endif - # ifdef USE_ICONV if (ip->bw_iconv_fd != (iconv_t)-1) { const char *from; @@ -5245,8 +5202,11 @@ static struct event_name { {"StdinReadPre", EVENT_STDINREADPRE}, {"SwapExists", EVENT_SWAPEXISTS}, {"Syntax", EVENT_SYNTAX}, + {"TabClosed", EVENT_TABCLOSED}, {"TabEnter", EVENT_TABENTER}, {"TabLeave", EVENT_TABLEAVE}, + {"TabNew", EVENT_TABNEW}, + {"TabNewEntered", EVENT_TABNEWENTERED}, {"TermChanged", EVENT_TERMCHANGED}, {"TermResponse", EVENT_TERMRESPONSE}, {"TextChanged", EVENT_TEXTCHANGED}, @@ -5811,10 +5771,13 @@ void do_autocmd(char_u *arg, int forceit) nested, cmd, forceit, group) == FAIL) break; } else { - while (*arg && !vim_iswhite(*arg)) - if (do_autocmd_event(event_name2nr(arg, &arg), pat, - nested, cmd, forceit, group) == FAIL) + while (*arg && !vim_iswhite(*arg)) { + event_T event = event_name2nr(arg, &arg); + assert(event < NUM_EVENTS); + if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) { break; + } + } } if (need_free) @@ -6467,12 +6430,6 @@ int has_cmdundefined(void) return first_autopat[(int)EVENT_CMDUNDEFINED] != NULL; } -/// @returns true when there is an FuncUndefined autocommand defined. -int has_funcundefined(void) -{ - return first_autopat[(int)EVENT_FUNCUNDEFINED] != NULL; -} - static int apply_autocmds_group ( event_T event, @@ -6635,7 +6592,8 @@ apply_autocmds_group ( || event == EVENT_QUICKFIXCMDPRE || event == EVENT_COLORSCHEME || event == EVENT_QUICKFIXCMDPOST - || event == EVENT_JOBACTIVITY) + || event == EVENT_JOBACTIVITY + || event == EVENT_TABCLOSED) fname = vim_strsave(fname); else fname = FullName_save(fname, FALSE); diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index 29bd1307f6..7a9e2adae1 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -93,8 +93,11 @@ typedef enum auto_event { EVENT_SPELLFILEMISSING, /* spell file missing */ EVENT_CURSORMOVED, /* cursor was moved */ EVENT_CURSORMOVEDI, /* cursor was moved in Insert mode */ + EVENT_TABCLOSED, /* a tab has closed */ EVENT_TABLEAVE, /* before leaving a tab page */ EVENT_TABENTER, /* after entering a tab page */ + EVENT_TABNEW, /* when creating a new tab */ + EVENT_TABNEWENTERED, /* after entering a new tab */ EVENT_SHELLCMDPOST, /* after ":!cmd" */ EVENT_SHELLFILTERPOST, /* after ":1,2!cmd", ":w !cmd", ":r !cmd". */ EVENT_TEXTCHANGED, /* text was modified */ diff --git a/src/nvim/garray.c b/src/nvim/garray.c index c3a3426e87..31a79db209 100644 --- a/src/nvim/garray.c +++ b/src/nvim/garray.c @@ -198,23 +198,3 @@ void ga_append(garray_T *gap, char c) { GA_APPEND(char, gap, c); } - -#if defined(UNIX) || defined(WIN3264) - -/// Append the text in "gap" below the cursor line and clear "gap". -/// -/// @param gap -void append_ga_line(garray_T *gap) -{ - // Remove trailing CR. - if (!GA_EMPTY(gap) - && !curbuf->b_p_bin - && (((char_u *)gap->ga_data)[gap->ga_len - 1] == CAR)) { - gap->ga_len--; - } - ga_append(gap, NUL); - ml_append(curwin->w_cursor.lnum++, gap->ga_data, 0, FALSE); - gap->ga_len = 0; -} - -#endif // if defined(UNIX) || defined(WIN3264) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index d97c983133..b8a145483f 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1203,7 +1203,6 @@ void save_typeahead(tasave_T *tp) readbuf1.bh_first.b_next = NULL; tp->save_readbuf2 = readbuf2; readbuf2.bh_first.b_next = NULL; - tp->save_inputbuf = input_buffer_save(); } /* @@ -1224,7 +1223,6 @@ void restore_typeahead(tasave_T *tp) readbuf1 = tp->save_readbuf1; free_buff(&readbuf2); readbuf2 = tp->save_readbuf2; - input_buffer_restore(tp->save_inputbuf); } /* @@ -1546,22 +1544,6 @@ int vpeekc(void) } /* - * Like vpeekc(), but don't allow mapping. Do allow checking for terminal - * codes. - */ -int vpeekc_nomap(void) -{ - int c; - - ++no_mapping; - ++allow_keys; - c = vpeekc(); - --no_mapping; - --allow_keys; - return c; -} - -/* * Check if any character is available, also half an escape sequence. * Trick: when no typeahead found, but there is something in the typeahead * buffer, it must be an ESC that is recognized as the start of a key code. @@ -1929,8 +1911,6 @@ static int vgetorpeek(int advance) if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { - int save_keylen = keylen; - /* * When no matching mapping found or found a * non-matching mapping that matches at least what the @@ -1947,25 +1927,7 @@ static int vgetorpeek(int advance) || (p_remap && typebuf.tb_noremap[ typebuf.tb_off] == RM_YES)) && !timedout) { - keylen = check_termcode(max_mlen + 1, - NULL, 0, NULL); - - /* If no termcode matched but 'pastetoggle' - * matched partially it's like an incomplete key - * sequence. */ - if (keylen == 0 && save_keylen == KEYLEN_PART_KEY) - keylen = KEYLEN_PART_KEY; - - /* - * When getting a partial match, but the last - * characters were not typed, don't wait for a - * typed character to complete the termcode. - * This helps a lot when a ":normal" command ends - * in an ESC. - */ - if (keylen < 0 - && typebuf.tb_len == typebuf.tb_maplen) - keylen = 0; + keylen = 0; } else keylen = 0; if (keylen == 0) { /* no matching terminal code */ @@ -2515,29 +2477,27 @@ fix_input_buffer ( int script /* TRUE when reading from a script */ ) { - if (abstract_ui) { - // Should not escape K_SPECIAL/CSI while in embedded mode because vim key - // codes keys are processed in input.c/input_enqueue. + if (!using_script()) { + // Should not escape K_SPECIAL/CSI reading input from the user because vim + // key codes keys are processed in input.c/input_enqueue. buf[len] = NUL; return len; } + // Reading from script, need to process special bytes int i; char_u *p = buf; - /* - * Two characters are special: NUL and K_SPECIAL. - * When compiled With the GUI CSI is also special. - * Replace NUL by K_SPECIAL KS_ZERO KE_FILLER - * Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER - * Replace CSI by K_SPECIAL KS_EXTRA KE_CSI - * Don't replace K_SPECIAL when reading a script file. - */ + // Two characters are special: NUL and K_SPECIAL. + // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER + // Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER + // Replace CSI by K_SPECIAL KS_EXTRA KE_CSI + // Don't replace K_SPECIAL when reading a script file. for (i = len; --i >= 0; ++p) { if (p[0] == NUL || (p[0] == K_SPECIAL && !script - && (i < 2 || p[1] != KS_EXTRA || is_user_input(p[2])))) { + && (i < 2 || p[1] != KS_EXTRA))) { memmove(p + 3, p + 1, (size_t)i); p[2] = K_THIRD(p[0]); p[1] = K_SECOND(p[0]); @@ -2546,7 +2506,7 @@ fix_input_buffer ( len += 2; } } - *p = NUL; /* add trailing NUL */ + *p = NUL; // add trailing NUL return len; } @@ -3772,11 +3732,6 @@ eval_map_expr ( return res; } -static bool is_user_input(int k) -{ - return k != (int)KE_EVENT && k != (int)KE_CURSORHOLD; -} - /* * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result * can be put in the typeahead buffer. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 1aa90777fa..86c28e823a 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -904,13 +904,10 @@ EXTERN char_u *use_viminfo INIT(= NULL); /* name of viminfo file to use */ EXTERN FILE *scriptin[NSCRIPT]; /* streams to read script from */ EXTERN int curscript INIT(= 0); /* index in scriptin[] */ EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */ -EXTERN int read_cmd_fd INIT(= 0); /* fd to read commands from */ /* volatile because it is used in signal handler catch_sigint(). */ EXTERN volatile int got_int INIT(= FALSE); /* set to TRUE when interrupt signal occurred */ -EXTERN int termcap_active INIT(= FALSE); /* set by starttermcap() */ -EXTERN int cur_tmode INIT(= TMODE_COOK); /* input terminal mode */ EXTERN int bangredo INIT(= FALSE); /* set to TRUE with ! command */ EXTERN int searchcmdlen; /* length of previous search cmd */ EXTERN int reg_do_extmatch INIT(= 0); /* Used when compiling regexp: @@ -1237,14 +1234,8 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ EXTERN int ignored; EXTERN char *ignoredp; -/* Temporarily moved these static variables to assist in migrating from - * os_unix.c */ -EXTERN int curr_tmode INIT(= TMODE_COOK); /* contains current terminal mode */ - // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); -// Using the "abstract_ui" termcap -EXTERN bool abstract_ui INIT(= false); /// Used to track the status of external functions. /// Currently only used for iconv(). diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index fb04f4407c..cc6aa57419 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -96,7 +96,7 @@ * Sets the current position at the start of line "page_line". * If margin is TRUE start in the left margin (for header and line number). * - * int mch_print_text_out(char_u *p, int len); + * int mch_print_text_out(char_u *p, size_t len); * Output one character of text p[len] at the current position. * Return TRUE if there is no room for another character in the same line. * @@ -495,7 +495,6 @@ static void prt_header(prt_settings_T *psettings, int pagenum, linenr_T lnum) int page_line; char_u *tbuf; char_u *p; - int l; /* Also use the space for the line number. */ if (prt_use_number()) @@ -542,9 +541,9 @@ static void prt_header(prt_settings_T *psettings, int pagenum, linenr_T lnum) page_line = 0 - prt_header_height(); mch_print_start_line(TRUE, page_line); for (p = tbuf; *p != NUL; ) { - if (mch_print_text_out(p, - (l = (*mb_ptr2len)(p)) - )) { + int l = (*mb_ptr2len)(p); + assert(l >= 0); + if (mch_print_text_out(p, (size_t)l)) { ++page_line; if (page_line >= 0) /* out of room in header */ break; @@ -617,10 +616,7 @@ void ex_hardcopy(exarg_T *eap) eap->forceit) == FAIL) return; - if (t_colors > 1) - settings.modec = 'c'; - else - settings.modec = 't'; + settings.modec = 'c'; if (!syntax_present(curwin)) settings.do_syntax = FALSE; @@ -887,7 +883,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T ppos->ff = TRUE; need_break = 1; } else { - need_break = mch_print_text_out(line + col, outputlen); + need_break = mch_print_text_out(line + col, (size_t)outputlen); if (has_mbyte) print_pos += (*mb_ptr2cells)(line + col); else @@ -2122,19 +2118,25 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) props = enc_canon_props(p_encoding); if (!(props & ENC_8BIT) && ((*p_pmcs != NUL) || !(props & ENC_UNICODE))) { p_mbenc_first = NULL; + int effective_cmap; for (cmap = 0; cmap < (int)ARRAY_SIZE(prt_ps_mbfonts); cmap++) if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap], - &p_mbenc)) { - if (p_mbenc_first == NULL) + &p_mbenc)) { + if (p_mbenc_first == NULL) { p_mbenc_first = p_mbenc; - if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap], - &p_mbchar)) + effective_cmap = cmap; + } + if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap], &p_mbchar)) break; } /* Use first encoding matched if no charset matched */ - if (p_mbchar == NULL && p_mbenc_first != NULL) + if (p_mbchar == NULL && p_mbenc_first != NULL) { p_mbenc = p_mbenc_first; + cmap = effective_cmap; + } + + assert(p_mbenc == NULL || cmap < (int)ARRAY_SIZE(prt_ps_mbfonts)); } prt_out_mbyte = (p_mbenc != NULL); @@ -2868,7 +2870,7 @@ void mch_print_start_line(int margin, int page_line) prt_half_width = FALSE; } -int mch_print_text_out(char_u *p, int len) +int mch_print_text_out(char_u *p, size_t len) { int need_break; char_u ch; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 075acc6c13..5711207933 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -1,3 +1,4 @@ +#include <assert.h> #include <inttypes.h> #include <stdbool.h> @@ -198,7 +199,8 @@ int set_indent(int size, int flags) // characters and allocate accordingly. We will fill the rest with spaces // after the if (!curbuf->b_p_et) below. if (orig_char_len != -1) { - newline = xmalloc(orig_char_len + size - ind_done + line_len); + assert(orig_char_len + size - ind_done + line_len >= 0); + newline = xmalloc((size_t)(orig_char_len + size - ind_done + line_len)); todo = size - ind_done; // Set total length of indent in characters, which may have been @@ -219,7 +221,8 @@ int set_indent(int size, int flags) } } else { todo = size; - newline = xmalloc(ind_len + line_len); + assert(ind_len + line_len >= 0); + newline = xmalloc((size_t)(ind_len + line_len)); s = newline; } @@ -384,7 +387,8 @@ int copy_indent(int size, char_u *src) // Allocate memory for the result: the copied indent, new indent // and the rest of the line. line_len = (int)STRLEN(get_cursor_line_ptr()) + 1; - line = xmalloc(ind_len + line_len); + assert(ind_len + line_len >= 0); + line = xmalloc((size_t)(ind_len + line_len)); p = line; } } @@ -449,7 +453,7 @@ int get_number_indent(linenr_T lnum) */ int get_breakindent_win(win_T *wp, char_u *line) { static int prev_indent = 0; /* cached indent value */ - static int prev_ts = 0L; /* cached tabstop value */ + static long prev_ts = 0; /* cached tabstop value */ static char_u *prev_line = NULL; /* cached pointer to line */ static int prev_tick = 0; // changedtick of cached value int bri = 0; diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 251926c01a..67f342fd37 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -3,6 +3,7 @@ * special key codes. */ +#include <assert.h> #include <inttypes.h> #include "nvim/vim.h" @@ -457,7 +458,7 @@ char_u *get_special_key_name(int c, int modifiers) if (IS_SPECIAL(c)) { string[idx++] = 't'; string[idx++] = '_'; - string[idx++] = KEY2TERMCAP0(c); + string[idx++] = (char_u)KEY2TERMCAP0(c); string[idx++] = KEY2TERMCAP1(c); } /* Not a special key, only modifiers, output directly */ @@ -465,7 +466,7 @@ char_u *get_special_key_name(int c, int modifiers) if (has_mbyte && (*mb_char2len)(c) > 1) idx += (*mb_char2bytes)(c, string + idx); else if (vim_isprintc(c)) - string[idx++] = c; + string[idx++] = (char_u)c; else { s = transchar(c); while (*s) @@ -496,7 +497,7 @@ trans_special ( { int modifiers = 0; int key; - int dlen = 0; + unsigned int dlen = 0; key = find_special_key(srcp, &modifiers, keycode, FALSE); if (key == 0) @@ -506,19 +507,22 @@ trans_special ( if (modifiers != 0) { dst[dlen++] = K_SPECIAL; dst[dlen++] = KS_MODIFIER; - dst[dlen++] = modifiers; + dst[dlen++] = (char_u)modifiers; } if (IS_SPECIAL(key)) { dst[dlen++] = K_SPECIAL; - dst[dlen++] = KEY2TERMCAP0(key); + dst[dlen++] = (char_u)KEY2TERMCAP0(key); dst[dlen++] = KEY2TERMCAP1(key); } else if (has_mbyte && !keycode) - dlen += (*mb_char2bytes)(key, dst + dlen); - else if (keycode) - dlen = (int)(add_char2buf(key, dst + dlen) - dst); + dlen += (unsigned int)(*mb_char2bytes)(key, dst + dlen); + else if (keycode) { + char_u *after = add_char2buf(key, dst + dlen); + assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); + dlen = (unsigned int)(after - dst); + } else - dst[dlen++] = key; + dst[dlen++] = (char_u)key; return dlen; } @@ -752,26 +756,3 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) } return 0; /* Shouldn't get here */ } - -/* - * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on - * the given information about which mouse button is down, and whether the - * mouse was clicked, dragged or released. - */ -int -get_pseudo_mouse_code ( - int button, /* eg MOUSE_LEFT */ - int is_click, - int is_drag -) -{ - int i; - - for (i = 0; mouse_table[i].pseudo_code; i++) - if (button == mouse_table[i].button - && is_click == mouse_table[i].is_click - && is_drag == mouse_table[i].is_drag) { - return mouse_table[i].pseudo_code; - } - return (int)KE_IGNORE; /* not recognized, ignore it */ -} diff --git a/src/nvim/main.c b/src/nvim/main.c index 6e24806c56..f2891f0979 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -95,7 +95,12 @@ typedef struct { char_u *use_ef; /* 'errorfile' from -q argument */ int want_full_screen; - bool stdout_isatty; /* is stdout a terminal? */ + bool input_isatty; // stdin is a terminal + bool output_isatty; // stdout is a terminal + bool err_isatty; // stderr is a terminal + bool headless; // Dont try to start an user interface + // or read/write to stdio(unless + // embedding) char_u *term; /* specified terminal name */ int no_swap_file; /* "-n" argument used */ int use_debug_break_level; @@ -197,9 +202,7 @@ int main(int argc, char **argv) early_init(); - /* - * Check if we have an interactive window. - */ + // Check if we have an interactive window. check_and_set_isatty(¶ms); /* @@ -242,34 +245,20 @@ int main(int argc, char **argv) printf(_("%d files to edit\n"), GARGCOUNT); if (params.want_full_screen && !silent_mode) { - if (embedded_mode) { - // embedded mode implies abstract_ui - termcapinit((uint8_t *)"abstract_ui"); - } else { - // set terminal name and get terminal capabilities (will set full_screen) - // Do some initialization of the screen - termcapinit(params.term); - } + termcapinit((uint8_t *)"abstract_ui"); screen_start(); /* don't know where cursor is now */ TIME_MSG("Termcap init"); } event_init(); - - if (abstract_ui) { - full_screen = true; - t_colors = 256; - T_CCO = (uint8_t *)"256"; - } else { - // Print a warning if stdout is not a terminal TODO(tarruda): Remove this - // check once the new terminal UI is implemented - check_tty(¶ms); - } + full_screen = true; + t_colors = 256; + T_CCO = (uint8_t *)"256"; + check_tty(¶ms); /* * Set the default values for the options that use Rows and Columns. */ - ui_get_shellsize(); /* inits Rows and Columns */ win_init_size(); /* Set the 'diff' option now, so that it can be checked for in a .vimrc * file. There is no buffer yet though. */ @@ -382,25 +371,22 @@ int main(int argc, char **argv) newline_on_exit = TRUE; #endif - /* - * When done something that is not allowed or error message call - * wait_return. This must be done before starttermcap(), because it may - * switch to another screen. It must be done after settmode(TMODE_RAW), - * because we want to react on a single key stroke. - * Call settmode and starttermcap here, so the T_KS and T_TI may be - * defined by termcapinit and redefined in .exrc. - */ - settmode(TMODE_RAW); - TIME_MSG("setting raw mode"); + if (!params.headless && (params.output_isatty || params.err_isatty)) { + if (params.input_isatty && (need_wait_return || msg_didany)) { + // Since at this point there's no UI instance running yet, error messages + // would have been printed to stdout. Before starting (which can result + // in a alternate screen buffer being shown) we need confirmation that + // the user has seen the messages and that is done with a call to + // wait_return. For that to work, stdin must be openend temporarily. + input_start_stdin(); + wait_return(TRUE); + TIME_MSG("waiting for return"); + input_stop_stdin(); + } - if (need_wait_return || msg_didany) { - wait_return(TRUE); - TIME_MSG("waiting for return"); + ui_builtin_start(); } - starttermcap(); // start termcap if not done by wait_return() - TIME_MSG("start termcap"); - may_req_ambiguous_char_width(); setmouse(); // may start using the mouse if (scroll_region) { @@ -480,10 +466,6 @@ int main(int argc, char **argv) no_wait_return = FALSE; starting = 0; - /* Requesting the termresponse is postponed until here, so that a "-c q" - * argument doesn't make it appear in the shell Vim was started from. */ - may_req_termresponse(); - /* start in insert mode */ if (p_im) need_start_insertmode = TRUE; @@ -965,14 +947,14 @@ static void command_line_scan(mparm_T *parmp) c = argv[0][argv_idx++]; switch (c) { case NUL: /* "vim -" read from stdin */ - /* "ex -" silent mode */ - if (exmode_active) + if (exmode_active) { + // "ex -" silent mode silent_mode = TRUE; - else { - if (parmp->edit_type != EDIT_NONE) + } else { + if (parmp->edit_type != EDIT_NONE) { mainerr(ME_TOO_MANY_ARGS, argv[0]); + } parmp->edit_type = EDIT_STDIN; - read_cmd_fd = 2; /* read from stderr instead of stdin */ } argv_idx = -1; /* skip to next argument */ break; @@ -1004,8 +986,11 @@ static void command_line_scan(mparm_T *parmp) } mch_exit(0); + } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) { + parmp->headless = true; } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) { embedded_mode = true; + parmp->headless = true; } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { #if !defined(UNIX) parmp->literal = TRUE; @@ -1418,7 +1403,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv) memset(paramp, 0, sizeof(*paramp)); paramp->argc = argc; paramp->argv = argv; - paramp->want_full_screen = TRUE; + paramp->headless = false; + paramp->want_full_screen = true; paramp->use_debug_break_level = -1; paramp->window_count = -1; } @@ -1439,15 +1425,13 @@ static void init_startuptime(mparm_T *paramp) starttime = time(NULL); } -/* - * Check if we have an interactive window. - */ static void check_and_set_isatty(mparm_T *paramp) { - paramp->stdout_isatty = os_isatty(STDOUT_FILENO); + paramp->input_isatty = os_isatty(fileno(stdin)); + paramp->output_isatty = os_isatty(fileno(stdout)); + paramp->err_isatty = os_isatty(fileno(stderr)); TIME_MSG("window checked"); } - /* * Get filename from command line, given that there is one. */ @@ -1532,28 +1516,43 @@ static void handle_tag(char_u *tagname) } } -/* - * Print a warning if stdout is not a terminal. - * When starting in Ex mode and commands come from a file, set Silent mode. - */ +// Print a warning if stdout is not a terminal. +// When starting in Ex mode and commands come from a file, set Silent mode. static void check_tty(mparm_T *parmp) { + if (parmp->headless) { + return; + } + // is active input a terminal? - bool input_isatty = os_isatty(read_cmd_fd); if (exmode_active) { - if (!input_isatty) - silent_mode = TRUE; - } else if (parmp->want_full_screen && (!parmp->stdout_isatty || !input_isatty) - ) { - if (!parmp->stdout_isatty) + if (!parmp->input_isatty) { + silent_mode = true; + } + } else if (parmp->want_full_screen && (!parmp->err_isatty + && (!parmp->output_isatty || !parmp->input_isatty))) { + + if (!parmp->output_isatty) { mch_errmsg(_("Vim: Warning: Output is not to a terminal\n")); - if (!input_isatty) + } + + if (!parmp->input_isatty) { mch_errmsg(_("Vim: Warning: Input is not from a terminal\n")); + } + out_flush(); - if (scriptin[0] == NULL) + + if (scriptin[0] == NULL) { os_delay(2000L, true); + } + TIME_MSG("Warning delay"); } + + if (parmp->edit_type != EDIT_STDIN && !parmp->input_isatty) { + // read commands from directly from stdin + input_start_stdin(); + } } /* @@ -1573,13 +1572,6 @@ static void read_stdin(void) msg_didany = i; TIME_MSG("reading stdin"); check_swap_exists_action(); - /* - * Close stdin and dup it from stderr. Required for GPM to work - * properly, and for running external commands. - * Is there any other system that cannot do this? - */ - close(0); - ignored = dup(2); } /* @@ -2104,6 +2096,7 @@ static void usage(void) mch_msg(_(" -i <nviminfo> Use <nviminfo> instead of .nviminfo\n")); mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n")); mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); + mch_msg(_(" --headless Don't start a user interface\n")); mch_msg(_(" --version Print version information and exit\n")); mch_msg(_(" -h | --help Print this help message and exit\n")); diff --git a/src/nvim/map.c b/src/nvim/map.c index 3f485cb952..31fe8a01ea 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -100,7 +100,7 @@ static inline khint_t String_hash(String s) static inline bool String_eq(String a, String b) { - return strncmp(a.data, b.data, min(a.size, b.size)) == 0; + return strncmp(a.data, b.data, MIN(a.size, b.size)) == 0; } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 5853b535cd..ddbe4e76cb 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -604,7 +604,8 @@ static char_u *mark_line(pos_T *mp, int lead_len) if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count) return vim_strsave((char_u *)"-invalid-"); - s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (int)Columns); + assert(Columns >= 0 && (size_t)Columns <= SIZE_MAX); + s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (size_t)Columns); /* Truncate the line to fit it in the window */ len = 0; @@ -1033,10 +1034,11 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) if (posp->lnum == lnum && posp->col >= mincol) \ { \ posp->lnum += lnum_amount; \ + assert(col_amount > INT_MIN && col_amount <= INT_MAX); \ if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \ posp->col = 0; \ else \ - posp->col += col_amount; \ + posp->col += (colnr_T)col_amount; \ } \ } @@ -1329,7 +1331,7 @@ int removable(char_u *name) copy_option_part(&p, part, 51, ", "); if (part[0] == 'r') { n = STRLEN(part + 1); - if (MB_STRNICMP(part + 1, name, n) == 0) { + if (mb_strnicmp(part + 1, name, n) == 0) { retval = TRUE; break; } @@ -1499,12 +1501,13 @@ void copy_viminfo_marks(vir_T *virp, FILE *fp_out, int count, int eof, int flags if (load_marks) { if (line[1] != NUL) { int64_t lnum_64; - unsigned u; + unsigned int u; sscanf((char *)line + 2, "%" SCNd64 "%u", &lnum_64, &u); // safely downcast to linenr_T (long); remove when linenr_T refactored assert(lnum_64 <= LONG_MAX); pos.lnum = (linenr_T)lnum_64; - pos.col = u; + assert(u <= INT_MAX); + pos.col = (colnr_T)u; switch (line[1]) { case '"': curbuf->b_last_cursor = pos; break; case '^': curbuf->b_last_insert = pos; break; diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index bc8f768724..5c89e9d8eb 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -143,7 +143,7 @@ static char utf8len_tab[256] = /* * Like utf8len_tab above, but using a zero for illegal lead bytes. */ -static char utf8len_tab_zero[256] = +static uint8_t utf8len_tab_zero[256] = { 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,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, @@ -1375,7 +1375,7 @@ static int dbcs_ptr2char(const char_u *p) */ int utf_ptr2char(const char_u *p) { - int len; + uint8_t len; if (p[0] < 0x80) /* be quick for ASCII */ return p[0]; @@ -1427,12 +1427,12 @@ int utf_ptr2char(const char_u *p) */ static int utf_safe_read_char_adv(char_u **s, size_t *n) { - int c, k; + int c; if (*n == 0) /* end of buffer */ return 0; - k = utf8len_tab_zero[**s]; + uint8_t k = utf8len_tab_zero[**s]; if (k == 1) { /* ASCII character or NUL */ @@ -1440,7 +1440,7 @@ static int utf_safe_read_char_adv(char_u **s, size_t *n) return *(*s)++; } - if ((size_t)k <= *n) { + if (k <= *n) { /* We have a multibyte sequence and it isn't truncated by buffer * limits so utf_ptr2char() is safe to use. Or the first byte is * illegal (k=0), and it's also safe to use utf_ptr2char(). */ @@ -2839,6 +2839,16 @@ int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) return 0; } +/* We need to call mb_stricmp() even when we aren't dealing with a multi-byte + * encoding because mb_stricmp() takes care of all ascii and non-ascii + * encodings, including characters with umlauts in latin1, etc., while + * STRICMP() only handles the system locale version, which often does not + * handle non-ascii properly. */ +int mb_stricmp(char_u *s1, char_u *s2) +{ + return mb_strnicmp(s1, s2, MAXCOL); +} + /* * "g8": show bytes of the UTF-8 char under the cursor. Doesn't matter what * 'encoding' has been set to. @@ -3504,7 +3514,8 @@ void * my_iconv_open(char_u *to, char_u *from) * Returns the converted string in allocated memory. NULL for an error. * If resultlenp is not NULL, sets it to the result length in bytes. */ -static char_u * iconv_string(vimconv_T *vcp, char_u *str, int slen, int *unconvlenp, int *resultlenp) +static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, + size_t *unconvlenp, size_t *resultlenp) { const char *from; size_t fromlen; @@ -3534,8 +3545,7 @@ static char_u * iconv_string(vimconv_T *vcp, char_u *str, int slen, int *unconvl tolen = len - done - 2; /* Avoid a warning for systems with a wrong iconv() prototype by * casting the second argument to void *. */ - if (iconv(vcp->vc_fd, (void *)&from, &fromlen, &to, &tolen) - != (size_t)-1) { + if (iconv(vcp->vc_fd, (void *)&from, &fromlen, &to, &tolen) != SIZE_MAX) { /* Finished, append a NUL. */ *to = NUL; break; @@ -3547,7 +3557,7 @@ static char_u * iconv_string(vimconv_T *vcp, char_u *str, int slen, int *unconvl && (ICONV_ERRNO == ICONV_EINVAL || ICONV_ERRNO == EINVAL)) { /* Handle an incomplete sequence at the end. */ *to = NUL; - *unconvlenp = (int)fromlen; + *unconvlenp = fromlen; break; } /* Check both ICONV_EILSEQ and EILSEQ, because the dynamically loaded @@ -3581,7 +3591,7 @@ static char_u * iconv_string(vimconv_T *vcp, char_u *str, int slen, int *unconvl } if (resultlenp != NULL && result != NULL) - *resultlenp = (int)(to - (char *)result); + *resultlenp = (size_t)(to - (char *)result); return result; } @@ -3802,7 +3812,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, * Illegal chars are often changed to "?", unless vcp->vc_fail is set. * When something goes wrong, NULL is returned and "*lenp" is unchanged. */ -char_u * string_convert(vimconv_T *vcp, char_u *ptr, int *lenp) +char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) { return string_convert_ext(vcp, ptr, lenp, NULL); } @@ -3812,18 +3822,17 @@ char_u * string_convert(vimconv_T *vcp, char_u *ptr, int *lenp) * an incomplete sequence at the end it is not converted and "*unconvlenp" is * set to the number of remaining bytes. */ -char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, - int *unconvlenp) +char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, + size_t *lenp, size_t *unconvlenp) { char_u *retval = NULL; char_u *d; - int len; - int i; int l; int c; + size_t len; if (lenp == NULL) - len = (int)STRLEN(ptr); + len = STRLEN(ptr); else len = *lenp; if (len == 0) @@ -3833,7 +3842,7 @@ char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, case CONV_TO_UTF8: /* latin1 to utf-8 conversion */ retval = xmalloc(len * 2 + 1); d = retval; - for (i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { c = ptr[i]; if (c < 0x80) *d++ = c; @@ -3844,13 +3853,13 @@ char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, } *d = NUL; if (lenp != NULL) - *lenp = (int)(d - retval); + *lenp = (size_t)(d - retval); break; case CONV_9_TO_UTF8: /* latin9 to utf-8 conversion */ retval = xmalloc(len * 3 + 1); d = retval; - for (i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { c = ptr[i]; switch (c) { case 0xa4: c = 0x20ac; break; /* euro */ @@ -3866,19 +3875,19 @@ char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, } *d = NUL; if (lenp != NULL) - *lenp = (int)(d - retval); + *lenp = (size_t)(d - retval); break; case CONV_TO_LATIN1: /* utf-8 to latin1 conversion */ case CONV_TO_LATIN9: /* utf-8 to latin9 conversion */ retval = xmalloc(len + 1); d = retval; - for (i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { l = utf_ptr2len_len(ptr + i, len - i); if (l == 0) *d++ = NUL; else if (l == 1) { - int l_w = utf8len_tab_zero[ptr[i]]; + uint8_t l_w = utf8len_tab_zero[ptr[i]]; if (l_w == 0) { /* Illegal utf-8 byte cannot be converted */ @@ -3929,30 +3938,8 @@ char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, } *d = NUL; if (lenp != NULL) - *lenp = (int)(d - retval); - break; - -# ifdef MACOS_CONVERT - case CONV_MAC_LATIN1: - retval = mac_string_convert(ptr, len, lenp, vcp->vc_fail, - 'm', 'l', unconvlenp); - break; - - case CONV_LATIN1_MAC: - retval = mac_string_convert(ptr, len, lenp, vcp->vc_fail, - 'l', 'm', unconvlenp); - break; - - case CONV_MAC_UTF8: - retval = mac_string_convert(ptr, len, lenp, vcp->vc_fail, - 'm', 'u', unconvlenp); - break; - - case CONV_UTF8_MAC: - retval = mac_string_convert(ptr, len, lenp, vcp->vc_fail, - 'u', 'm', unconvlenp); + *lenp = (size_t)(d - retval); break; -# endif # ifdef USE_ICONV case CONV_ICONV: /* conversion with output_conv.vc_fd */ diff --git a/src/nvim/message.c b/src/nvim/message.c index 87b0253d70..825b8b2550 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -40,6 +40,7 @@ #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/term.h" +#include "nvim/ui.h" #include "nvim/mouse.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -915,15 +916,6 @@ void wait_return(int redraw) State = oldState; /* restore State before set_shellsize */ setmouse(); msg_check(); - -#if defined(UNIX) - /* - * When switching screens, we need to output an extra newline on exit. - */ - if (swapping_screen() && !termcap_active) - newline_on_exit = TRUE; -#endif - need_wait_return = FALSE; did_wait_return = TRUE; emsg_on_display = FALSE; /* can delete error message now */ @@ -936,11 +928,9 @@ void wait_return(int redraw) } if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */ - starttermcap(); /* start termcap before redrawing */ shell_resized(); } else if (!skip_redraw && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) { - starttermcap(); /* start termcap before redrawing */ redraw_later(VALID); } } @@ -979,17 +969,6 @@ void set_keep_msg(char_u *s, int attr) } /* - * If there currently is a message being displayed, set "keep_msg" to it, so - * that it will be displayed again after redraw. - */ -void set_keep_msg_from_hist(void) -{ - if (keep_msg == NULL && last_msg_hist != NULL && msg_scrolled == 0 - && (State & NORMAL)) - set_keep_msg(last_msg_hist->msg, last_msg_hist->attr); -} - -/* * Prepare for outputting characters in the command line. */ void msg_start(void) @@ -1780,19 +1759,6 @@ static void msg_scroll_up(void) { /* scrolling up always works */ screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL); - - if (!can_clear((char_u *)" ")) { - /* Scrolling up doesn't result in the right background. Set the - * background here. It's not efficient, but avoids that we have to do - * it all over the code. */ - screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); - - /* Also clear the last char of the last but one line if it was not - * cleared before to avoid a scroll-up. */ - if (ScreenAttrs[LineOffset[Rows - 2] + Columns - 1] == (sattr_T)-1) - screen_fill((int)Rows - 2, (int)Rows - 1, - (int)Columns - 1, (int)Columns, ' ', ' ', 0); - } } /* @@ -1975,19 +1941,11 @@ static void t_puts(int *t_col, char_u *t_s, char_u *s, int attr) } } -/* - * Returns TRUE when messages should be printed with mch_errmsg(). - * This is used when there is no valid screen, so we can see error messages. - * If termcap is not active, we may be writing in an alternate console - * window, cursor positioning may not work correctly (window size may be - * different, e.g. for Win32 console) or we just don't know where the - * cursor is. - */ +// Returns TRUE when messages should be printed to stdout/stderr, which +// happens when no UIs are attached and nvim is not being embedded int msg_use_printf(void) { - return !msg_check_screen() - || (swapping_screen() && !termcap_active) - ; + return !embedded_mode && !ui_active(); } /* @@ -2379,24 +2337,6 @@ void repeat_message(void) } /* - * msg_check_screen - check if the screen is initialized. - * Also check msg_row and msg_col, if they are too big it may cause a crash. - * While starting the GUI the terminal codes will be set for the GUI, but the - * output goes to the terminal. Don't use the terminal codes then. - */ -static int msg_check_screen(void) -{ - if (!full_screen || !screen_valid(FALSE)) - return FALSE; - - if (msg_row >= Rows) - msg_row = Rows - 1; - if (msg_col >= Columns) - msg_col = Columns - 1; - return TRUE; -} - -/* * Clear from current message position to end of screen. * Skip this when ":silent" was used, no need to clear for redirection. */ diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 383e2bf6a5..a7e471625b 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2395,11 +2395,6 @@ int get_keystroke(void) } else if (len > 0) ++waited; /* keep track of the waiting time */ - /* Incomplete termcode and not timed out yet: get more characters */ - if ((n = check_termcode(1, buf, buflen, &len)) < 0 - && (!p_ttimeout || waited * 100L < (p_ttm < 0 ? p_tm : p_ttm))) - continue; - if (n == KEYLEN_REMOVED) { /* key code removed */ if (must_redraw != 0 && !need_wait_return && (State & CMDLINE) == 0) { /* Redrawing was postponed, do it now. */ @@ -3301,28 +3296,6 @@ home_replace_save ( return dst; } -void prepare_to_exit(void) -{ -#if defined(SIGHUP) && defined(SIG_IGN) - /* Ignore SIGHUP, because a dropped connection causes a read error, which - * makes Vim exit and then handling SIGHUP causes various reentrance - * problems. */ - signal(SIGHUP, SIG_IGN); -#endif - - { - windgoto((int)Rows - 1, 0); - - /* - * Switch terminal mode back now, so messages end up on the "normal" - * screen (if there are two screens). - */ - settmode(TMODE_COOK); - stoptermcap(); - out_flush(); - } -} - /* * Preserve files and exit. * When called IObuff must contain a message. @@ -3340,9 +3313,6 @@ void preserve_exit(void) } really_exiting = true; - - prepare_to_exit(); - out_str(IObuff); screen_start(); // don't know where cursor is now out_flush(); diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 9f67bd1760..84a1732a6a 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -451,12 +451,6 @@ void setmouse(void) if (*p_mouse == NUL) return; - /* don't switch mouse on when not in raw mode (Ex mode) */ - if (!abstract_ui && cur_tmode != TMODE_RAW) { - mch_setmouse(false); - return; - } - if (VIsual_active) checkfor = MOUSE_VISUAL; else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE) diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 920274f850..58c181e4de 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -45,6 +45,7 @@ typedef struct { typedef struct { uint64_t id; + size_t refcount; size_t pending_requests; PMap(cstr_t) *subscribed_events; bool is_job, closed; @@ -102,9 +103,7 @@ void channel_init(void) channel_from_stdio(); } - if (abstract_ui) { - remote_ui_init(); - } + remote_ui_init(); } /// Teardown the module @@ -125,11 +124,13 @@ void channel_teardown(void) /// stdin/stdout. stderr is forwarded to the editor error stream. /// /// @param argv The argument vector for the process. [consumed] -/// @return The channel id +/// @return The channel id (> 0), on success. +/// 0, on error. uint64_t channel_from_job(char **argv) { Channel *channel = register_channel(); channel->is_job = true; + incref(channel); // job channels are only closed by the exit_cb int status; channel->data.job = job_start(argv, @@ -142,6 +143,10 @@ uint64_t channel_from_job(char **argv) &status); if (status <= 0) { + if (status == 0) { // Two decrefs needed if status == 0. + decref(channel); // Only one needed if status < 0, + } // because exit_cb will do the second one. + decref(channel); return 0; } @@ -169,13 +174,6 @@ void channel_from_stream(uv_stream_t *stream) channel->data.streams.uv = stream; } -bool channel_exists(uint64_t id) -{ - Channel *channel; - return (channel = pmap_get(uint64_t)(channels, id)) != NULL - && !channel->closed; -} - /// Sends event/arguments to channel /// /// @param id The channel id. If 0, the event will be sent to all @@ -233,6 +231,7 @@ Object channel_send_call(uint64_t id, return NIL; } + incref(channel); uint64_t request_id = channel->next_request_id++; // Send the msgpack-rpc request send_request(channel, request_id, method_name, args); @@ -248,18 +247,15 @@ Object channel_send_call(uint64_t id, if (frame.errored) { api_set_error(err, Exception, "%s", frame.result.data.string.data); api_free_object(frame.result); - return NIL; - } - - if (!kv_size(channel->call_stack) && channel->closed) { - free_channel(channel); } if (!channel->pending_requests) { send_delayed_notifications(); } - return frame.result; + decref(channel); + + return frame.errored ? NIL : frame.result; } /// Subscribes to event broadcasts @@ -320,6 +316,7 @@ bool channel_close(uint64_t id) static void channel_from_stdio(void) { Channel *channel = register_channel(); + incref(channel); // stdio channels are only closed on exit channel->is_job = false; // read stream channel->data.streams.read = rstream_new(parse_msgpack, @@ -354,23 +351,18 @@ static void job_err(RStream *rstream, void *data, bool eof) static void job_exit(Job *job, void *data) { - Channel *channel = data; - // ensure the channel is flagged as closed so channel_send_call frees it - // later - channel->closed = true; - if (!kv_size(channel->call_stack)) { - free_channel(channel); - } + decref(data); } static void parse_msgpack(RStream *rstream, void *data, bool eof) { Channel *channel = data; + incref(channel); if (eof) { close_channel(channel); call_set_error(channel, "Channel was closed by the client"); - return; + goto end; } size_t count = rstream_pending(rstream); @@ -408,7 +400,7 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) } msgpack_unpacked_destroy(&unpacked); // Bail out from this event loop iteration - return; + goto end; } handle_request(channel, &unpacked.data); @@ -417,6 +409,7 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) if (result == MSGPACK_UNPACK_NOMEM_ERROR) { OUT_STR(e_outofmem); out_char('\n'); + decref(channel); preserve_exit(); } @@ -431,6 +424,9 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) "This error can also happen when deserializing " "an object with high level of nesting"); } + +end: + decref(channel); } static void handle_request(Channel *channel, msgpack_object *request) @@ -450,7 +446,7 @@ static void handle_request(Channel *channel, msgpack_object *request) &out_buffer))) { char buf[256]; snprintf(buf, sizeof(buf), - "Channel %" PRIu64 " sent an invalid message, closing.", + "Channel %" PRIu64 " sent an invalid message, closed.", channel->id); call_set_error(channel, buf); } @@ -477,6 +473,7 @@ static void handle_request(Channel *channel, msgpack_object *request) event_data->handler = handler; event_data->args = args; event_data->request_id = request_id; + incref(channel); event_push((Event) { .handler = on_request_event, .data = event_data @@ -502,6 +499,7 @@ static void on_request_event(Event event) &out_buffer)); // All arguments were freed already, but we still need to free the array free(args.items); + decref(channel); kmp_free(RequestEventPool, request_event_pool, e); } @@ -628,6 +626,7 @@ static void close_channel(Channel *channel) } channel->closed = true; + if (channel->is_job) { if (channel->data.job) { job_stop(channel->data.job); @@ -638,24 +637,26 @@ static void close_channel(Channel *channel) uv_handle_t *handle = (uv_handle_t *)channel->data.streams.uv; if (handle) { uv_close(handle, close_cb); - free_channel(channel); } else { - event_push((Event) { .handler = on_stdio_close }, false); + event_push((Event) { .handler = on_stdio_close, .data = channel }, false); } } + + decref(channel); } static void on_stdio_close(Event e) { - mch_exit(0); + decref(e.data); + + if (!exiting) { + mch_exit(0); + } } static void free_channel(Channel *channel) { - if (abstract_ui) { - remote_ui_disconnect(channel->id); - } - + remote_ui_disconnect(channel->id); pmap_del(uint64_t)(channels, channel->id); msgpack_unpacker_free(channel->unpacker); @@ -679,6 +680,7 @@ static void close_cb(uv_handle_t *handle) static Channel *register_channel(void) { Channel *rv = xmalloc(sizeof(Channel)); + rv->refcount = 1; rv->closed = false; rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); rv->id = next_id++; @@ -787,6 +789,18 @@ static void send_delayed_notifications(void) } } +static void incref(Channel *channel) +{ + channel->refcount++; +} + +static void decref(Channel *channel) +{ + if (!(--channel->refcount)) { + free_channel(channel); + } +} + #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL #define REQ "[request] " #define RES "[response] " diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index a702d4f256..355176aa5f 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -86,8 +86,8 @@ bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) FUNC_ATTR_NONNULL_ALL { - *arg = obj->via.dec; - return obj->type == MSGPACK_OBJECT_DOUBLE; + *arg = obj->via.f64; + return obj->type == MSGPACK_OBJECT_FLOAT; } bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) @@ -120,7 +120,7 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) arg->type = kObjectTypeInteger; return msgpack_rpc_to_integer(obj, &arg->data.integer); - case MSGPACK_OBJECT_DOUBLE: + case MSGPACK_OBJECT_FLOAT: arg->type = kObjectTypeFloat; return msgpack_rpc_to_float(obj, &arg->data.floating); @@ -295,22 +295,6 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) } } -/// Finishes the msgpack-rpc call with an error message. -/// -/// @param msg The error message -/// @param res A packer that contains the response -void msgpack_rpc_error(char *msg, msgpack_packer *res) - FUNC_ATTR_NONNULL_ALL -{ - size_t len = strlen(msg); - - // error message - msgpack_pack_bin(res, len); - msgpack_pack_bin_body(res, msg, len); - // Nil result - msgpack_pack_nil(res); -} - /// Handler executed when an invalid method name is passed Object msgpack_rpc_handle_missing_method(uint64_t channel_id, uint64_t request_id, diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e147280723..8c7deaa243 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1834,7 +1834,6 @@ do_mouse ( bool fixindent /* PUT_FIXINDENT if fixing indent necessary */ ) { - static bool do_always = false; /* ignore 'mouse' setting next time */ static bool got_click = false; /* got a click some time back */ int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */ @@ -1859,23 +1858,6 @@ do_mouse ( save_cursor = curwin->w_cursor; - // When "abstract_ui" is active, always recognize mouse events, otherwise: - // - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'. - // - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'. - // - For command line and insert mode 'mouse' is checked before calling - // do_mouse(). - if (!abstract_ui) { - if (do_always) - do_always = false; - else { - if (VIsual_active) { - if (!mouse_has(MOUSE_VISUAL)) - return false; - } else if (State == NORMAL && !mouse_has(MOUSE_NORMAL)) - return false; - } - } - for (;; ) { which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag); if (is_drag) { @@ -1996,7 +1978,6 @@ do_mouse ( stuffcharReadbuff('y'); stuffcharReadbuff(K_MIDDLEMOUSE); } - do_always = true; /* ignore 'mouse' setting next time */ return false; } /* diff --git a/src/nvim/option.c b/src/nvim/option.c index 182834c4f3..bf7d0d0bf3 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2059,12 +2059,6 @@ void set_init_1(void) /* Parse default for 'fillchars'. */ (void)set_chars_option(&p_fcs); - -# ifdef MACOS_CONVERT - /* Moved to os_mac_conv.c to avoid dependency problems. */ - mac_lang_init(); -# endif - /* enc_locale() will try to find the encoding of the current locale. */ p = enc_locale(); if (p != NULL) { @@ -4226,23 +4220,6 @@ did_set_string_option ( } - - -#if defined(FEAT_MOUSE_TTY) && defined(UNIX) - /* 'ttymouse' */ - else if (varp == &p_ttym) { - /* Switch the mouse off before changing the escape sequences used for - * that. */ - mch_setmouse(FALSE); - if (opt_strings_flags(p_ttym, p_ttym_values, &ttym_flags, FALSE) != OK) - errmsg = e_invarg; - else - check_mouse_termcode(); - if (termcap_active) - setmouse(); /* may switch it on again */ - } -#endif - /* 'selection' */ else if (varp == &p_sel) { if (*p_sel == NUL @@ -4945,11 +4922,16 @@ set_bool_option ( if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; + // Ensure that compatible can not be enabled if ((int *)varp == &p_cp && p_cp == TRUE) { - /* Ensure that compatible can not be enabled */ p_cp = FALSE; return e_unsupportedoption; } + // Ensure that edcompatible can not be enabled + else if ((int *)varp == &p_ed && p_ed == TRUE) { + p_ed = FALSE; + return e_unsupportedoption; + } /* 'undofile' */ else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { /* Only take action when the option was set. When reset we do not @@ -5434,8 +5416,7 @@ set_num_option ( curbuf->b_p_iminsert = B_IMODE_NONE; } p_iminsert = curbuf->b_p_iminsert; - if (termcap_active) /* don't do this in the alternate screen */ - showmode(); + showmode(); /* Show/unshow value of 'keymap' in status lines. */ status_redraw_curbuf(); } else if (pp == &p_window) { @@ -5554,12 +5535,11 @@ set_num_option ( */ if (old_Rows != Rows || old_Columns != Columns) { /* Changing the screen size is not allowed while updating the screen. */ - if (updating_screen) + if (updating_screen) { *pp = old_value; - else if (full_screen - ) - screen_resize((int)Columns, (int)Rows, TRUE); - else { + } else if (full_screen) { + screen_resize((int)Columns, (int)Rows); + } else { /* Postpone the resizing; check the size and cmdline position for * messages. */ check_shellsize(); @@ -5965,27 +5945,6 @@ set_option_value ( return NULL; } -/* - * Get the terminal code for a terminal option. - * Returns NULL when not found. - */ -char_u *get_term_code(char_u *tname) -{ - int opt_idx; - char_u *varp; - - if (tname[0] != 't' || tname[1] != '_' || - tname[2] == NUL || tname[3] == NUL) - return NULL; - if ((opt_idx = findoption(tname)) >= 0) { - varp = get_varp(&(options[opt_idx])); - if (varp != NULL) - varp = *(char_u **)(varp); - return varp; - } - return find_termcode(tname + 2); -} - char_u *get_highlight_default(void) { int i; @@ -6409,7 +6368,6 @@ void clear_termoptions(void) */ mch_setmouse(FALSE); /* switch mouse off */ mch_restore_title(3); /* restore window titles */ - stoptermcap(); /* stop termcap mode */ free_termoptions(); } @@ -7827,17 +7785,6 @@ int option_was_set(char_u *name) } /* - * Reset the flag indicating option "name" was set. - */ -void reset_option_was_set(char_u *name) -{ - int idx = findoption(name); - - if (idx >= 0) - options[idx].flags &= ~P_WAS_SET; -} - -/* * fill_breakat_flags() -- called when 'breakat' changes value. */ static void fill_breakat_flags(void) diff --git a/src/nvim/os/event.c b/src/nvim/os/event.c index 34560610bd..9a15bf92d0 100644 --- a/src/nvim/os/event.c +++ b/src/nvim/os/event.c @@ -54,7 +54,6 @@ void event_init(void) wstream_init(); // Initialize input events input_init(); - input_start(); // Timer to wake the event loop if a timeout argument is passed to // `event_poll` // Signals @@ -73,12 +72,13 @@ void event_teardown(void) return; } + process_events_from(immediate_events); + process_events_from(deferred_events); + input_stop_stdin(); channel_teardown(); job_teardown(); server_teardown(); signal_teardown(); - input_stop(); - input_teardown(); // this last `uv_run` will return after all handles are stopped, it will // also take care of finishing any uv_close calls made by other *_teardown // functions. diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 2ae4558f3d..00efa28161 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -19,7 +19,6 @@ #include "nvim/fileio.h" #include "nvim/ex_cmds2.h" #include "nvim/getchar.h" -#include "nvim/term.h" #include "nvim/main.h" #include "nvim/misc1.h" @@ -32,9 +31,9 @@ typedef enum { kInputEof } InbufPollResult; -static RStream *read_stream; -static RBuffer *read_buffer, *input_buffer; -static bool eof = false, started_reading = false; +static RStream *read_stream = NULL; +static RBuffer *read_buffer = NULL, *input_buffer = NULL; +static bool eof = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/input.c.generated.h" @@ -45,43 +44,29 @@ static bool eof = false, started_reading = false; void input_init(void) { input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN); - - if (abstract_ui) { - return; - } - - read_buffer = rbuffer_new(READ_BUFFER_SIZE); - read_stream = rstream_new(read_cb, read_buffer, NULL); - rstream_set_file(read_stream, read_cmd_fd); -} - -void input_teardown(void) -{ - if (abstract_ui) { - return; - } - - rstream_free(read_stream); } -// Listen for input -void input_start(void) +void input_start_stdin(void) { - if (abstract_ui) { + if (read_stream) { return; } + read_buffer = rbuffer_new(READ_BUFFER_SIZE); + read_stream = rstream_new(read_cb, read_buffer, NULL); + rstream_set_file(read_stream, fileno(stdin)); rstream_start(read_stream); } -// Stop listening for input -void input_stop(void) +void input_stop_stdin(void) { - if (abstract_ui) { + if (!read_stream) { return; } rstream_stop(read_stream); + rstream_free(read_stream); + read_stream = NULL; } // Low level input function. @@ -141,11 +126,9 @@ bool os_char_avail(void) } // Check for CTRL-C typed by reading all available characters. -// In cooked mode we should get SIGINT, no need to check. void os_breakcheck(void) { - if (curr_tmode == TMODE_RAW) - input_poll(0); + event_poll(0); } /// Test whether a file descriptor refers to a terminal. @@ -157,51 +140,42 @@ bool os_isatty(int fd) return uv_guess_handle(fd) == UV_TTY; } -/// Return the contents of the input buffer and make it empty. The returned -/// pointer must be passed to `input_buffer_restore()` later. -String input_buffer_save(void) -{ - size_t inbuf_size = rbuffer_pending(input_buffer); - String rv = { - .data = xmemdup(rbuffer_read_ptr(input_buffer), inbuf_size), - .size = inbuf_size - }; - rbuffer_consumed(input_buffer, inbuf_size); - return rv; -} - -/// Restore the contents of the input buffer and free `str` -void input_buffer_restore(String str) -{ - rbuffer_consumed(input_buffer, rbuffer_pending(input_buffer)); - rbuffer_write(input_buffer, str.data, str.size); - free(str.data); -} - size_t input_enqueue(String keys) { char *ptr = keys.data, *end = ptr + keys.size; while (rbuffer_available(input_buffer) >= 6 && ptr < end) { uint8_t buf[6] = {0}; - unsigned int new_size = trans_special((uint8_t **)&ptr, buf, false); - - if (!new_size) { - if (*ptr == '<') { - // Invalid key sequence, skip until the next '>' or until *end - do { - ptr++; - } while (ptr < end && *ptr != '>'); + unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true); + + if (new_size) { + new_size = handle_mouse_event(&ptr, buf, new_size); + rbuffer_write(input_buffer, (char *)buf, new_size); + continue; + } + + if (*ptr == '<') { + // Invalid key sequence, skip until the next '>' or until *end + do { ptr++; - continue; - } - // copy the character unmodified - *buf = (uint8_t)*ptr++; - new_size = 1; + } while (ptr < end && *ptr != '>'); + ptr++; + continue; } - new_size = handle_mouse_event(&ptr, buf, new_size); - rbuffer_write(input_buffer, (char *)buf, new_size); + // copy the character, escaping CSI and K_SPECIAL + if ((uint8_t)*ptr == CSI) { + rbuffer_write(input_buffer, (char *)&(uint8_t){K_SPECIAL}, 1); + rbuffer_write(input_buffer, (char *)&(uint8_t){KS_EXTRA}, 1); + rbuffer_write(input_buffer, (char *)&(uint8_t){KE_CSI}, 1); + } else if ((uint8_t)*ptr == K_SPECIAL) { + rbuffer_write(input_buffer, (char *)&(uint8_t){K_SPECIAL}, 1); + rbuffer_write(input_buffer, (char *)&(uint8_t){KS_SPECIAL}, 1); + rbuffer_write(input_buffer, (char *)&(uint8_t){KE_FILLER}, 1); + } else { + rbuffer_write(input_buffer, ptr, 1); + } + ptr++; } size_t rv = (size_t)(ptr - keys.data); @@ -215,15 +189,19 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, unsigned int bufsize) { int mouse_code = 0; + int type = 0; if (bufsize == 3) { mouse_code = buf[2]; + type = buf[1]; } else if (bufsize == 6) { // prefixed with K_SPECIAL KS_MODIFIER mod mouse_code = buf[5]; + type = buf[4]; } - if (!((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE) + if (type != KS_EXTRA + || !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE) || (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT))) { return bufsize; } @@ -298,7 +276,7 @@ static bool input_poll(int ms) prof_inchar_enter(); } - event_poll_until(ms, input_ready()); + event_poll_until(ms, input_ready() || eof); if (do_profiling == PROF_YES && ms) { prof_inchar_exit(); @@ -307,96 +285,31 @@ static bool input_poll(int ms) return input_ready(); } +void input_done(void) +{ + eof = true; +} + // This is a replacement for the old `WaitForChar` function in os_unix.c static InbufPollResult inbuf_poll(int ms) { - if (typebuf_was_filled || rbuffer_pending(input_buffer)) { + if (input_ready() || input_poll(ms)) { return kInputAvail; } - if (input_poll(ms)) { - return eof && rstream_pending(read_stream) == 0 ? - kInputEof : - kInputAvail; - } - - return kInputNone; -} - -static void stderr_switch(void) -{ - int mode = cur_tmode; - // We probably set the wrong file descriptor to raw mode. Switch back to - // cooked mode - settmode(TMODE_COOK); - // Stop the idle handle - rstream_stop(read_stream); - // Use stderr for stdin, also works for shell commands. - read_cmd_fd = 2; - // Initialize and start the input stream - rstream_set_file(read_stream, read_cmd_fd); - rstream_start(read_stream); - // Set the mode back to what it was - settmode(mode); + return eof ? kInputEof : kInputNone; } static void read_cb(RStream *rstream, void *data, bool at_eof) { if (at_eof) { - if (!started_reading - && rstream_is_regular_file(rstream) - && os_isatty(STDERR_FILENO)) { - // Read error. Since stderr is a tty we switch to reading from it. This - // is for handling for cases like "foo | xargs vim" because xargs - // redirects stdin from /dev/null. Previously, this was done in ui.c - stderr_switch(); - } else { - eof = true; - } - } - - convert_input(); - process_interrupts(); - started_reading = true; -} - -static void convert_input(void) -{ - if (abstract_ui || !rbuffer_available(input_buffer)) { - // No input buffer space - return; + eof = true; } - bool convert = input_conv.vc_type != CONV_NONE; - // Set unconverted data/length - char *data = rbuffer_read_ptr(read_buffer); - size_t data_length = rbuffer_pending(read_buffer); - size_t converted_length = data_length; - - if (convert) { - // Perform input conversion according to `input_conv` - size_t unconverted_length = 0; - data = (char *)string_convert_ext(&input_conv, - (uint8_t *)data, - (int *)&converted_length, - (int *)&unconverted_length); - data_length -= unconverted_length; - } - - // The conversion code will be gone eventually, for now assume `input_buffer` - // always has space for the converted data(it's many times the size of - // `read_buffer`, so it's hard to imagine a scenario where the converted data - // doesn't fit) - assert(converted_length <= rbuffer_available(input_buffer)); - // Write processed data to input buffer. - (void)rbuffer_write(input_buffer, data, converted_length); - // Adjust raw buffer pointers - rbuffer_consumed(read_buffer, data_length); - - if (convert) { - // data points to memory allocated by `string_convert_ext`, free it. - free(data); - } + char *buf = rbuffer_read_ptr(read_buffer); + size_t buf_size = rbuffer_pending(read_buffer); + (void)rbuffer_write(input_buffer, buf, buf_size); + rbuffer_consumed(read_buffer, buf_size); } static void process_interrupts(void) @@ -440,9 +353,8 @@ static int push_event_key(uint8_t *buf, int maxlen) static bool input_ready(void) { return typebuf_was_filled || // API call filled typeahead - rbuffer_pending(input_buffer) > 0 || // Stdin input - event_has_deferred() || // Events must be processed - (!abstract_ui && eof); // Stdin closed + rbuffer_pending(input_buffer) > 0 || // Input buffer filled + event_has_deferred(); // Events must be processed } // Exit because of an input read error. diff --git a/src/nvim/os/rstream.c b/src/nvim/os/rstream.c index e36a0213c8..a46e7d6f3c 100644 --- a/src/nvim/os/rstream.c +++ b/src/nvim/os/rstream.c @@ -271,15 +271,6 @@ void rstream_set_file(RStream *rstream, uv_file file) rstream->free_handle = true; } -/// Tests if the stream is backed by a regular file -/// -/// @param rstream The `RStream` instance -/// @return True if the underlying file descriptor represents a regular file -bool rstream_is_regular_file(RStream *rstream) -{ - return rstream->file_type == UV_FILE; -} - /// Starts watching for events from a `RStream` instance. /// /// @param rstream The `RStream` instance diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index d074ace884..df21bed446 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -20,7 +20,7 @@ KMEMPOOL_INIT(SignalEventPool, int, SignalEventFreer) kmempool_t(SignalEventPool) *signal_event_pool = NULL; -static uv_signal_t sint, spipe, shup, squit, sterm, swinch; +static uv_signal_t spipe, shup, squit, sterm; #ifdef SIGPWR static uv_signal_t spwr; #endif @@ -30,24 +30,18 @@ static bool rejecting_deadly; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/signal.c.generated.h" #endif + void signal_init(void) { signal_event_pool = kmp_init(SignalEventPool); - uv_signal_init(uv_default_loop(), &sint); uv_signal_init(uv_default_loop(), &spipe); uv_signal_init(uv_default_loop(), &shup); uv_signal_init(uv_default_loop(), &squit); uv_signal_init(uv_default_loop(), &sterm); - uv_signal_init(uv_default_loop(), &swinch); - uv_signal_start(&sint, signal_cb, SIGINT); uv_signal_start(&spipe, signal_cb, SIGPIPE); uv_signal_start(&shup, signal_cb, SIGHUP); uv_signal_start(&squit, signal_cb, SIGQUIT); uv_signal_start(&sterm, signal_cb, SIGTERM); - if (!abstract_ui) { - // TODO(tarruda): There must be an API function for resizing window - uv_signal_start(&swinch, signal_cb, SIGWINCH); - } #ifdef SIGPWR uv_signal_init(uv_default_loop(), &spwr); uv_signal_start(&spwr, signal_cb, SIGPWR); @@ -57,12 +51,10 @@ void signal_init(void) void signal_teardown(void) { signal_stop(); - uv_close((uv_handle_t *)&sint, NULL); uv_close((uv_handle_t *)&spipe, NULL); uv_close((uv_handle_t *)&shup, NULL); uv_close((uv_handle_t *)&squit, NULL); uv_close((uv_handle_t *)&sterm, NULL); - uv_close((uv_handle_t *)&swinch, NULL); #ifdef SIGPWR uv_close((uv_handle_t *)&spwr, NULL); #endif @@ -70,12 +62,10 @@ void signal_teardown(void) void signal_stop(void) { - uv_signal_stop(&sint); uv_signal_stop(&spipe); uv_signal_stop(&shup); uv_signal_stop(&squit); uv_signal_stop(&sterm); - uv_signal_stop(&swinch); #ifdef SIGPWR uv_signal_stop(&spwr); #endif @@ -94,16 +84,12 @@ void signal_accept_deadly(void) static char * signal_name(int signum) { switch (signum) { - case SIGINT: - return "SIGINT"; #ifdef SIGPWR case SIGPWR: return "SIGPWR"; #endif case SIGPIPE: return "SIGPIPE"; - case SIGWINCH: - return "SIGWINCH"; case SIGTERM: return "SIGTERM"; case SIGQUIT: @@ -148,9 +134,6 @@ static void on_signal_event(Event event) kmp_free(SignalEventPool, signal_event_pool, event.data); switch (signum) { - case SIGINT: - got_int = true; - break; #ifdef SIGPWR case SIGPWR: // Signal of a power failure(eg batteries low), flush the swap files to @@ -161,9 +144,6 @@ static void on_signal_event(Event event) case SIGPIPE: // Ignore break; - case SIGWINCH: - shell_resized(); - break; case SIGTERM: case SIGQUIT: case SIGHUP: diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index d674db951f..bfdf5969ff 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -56,18 +56,10 @@ #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/defs.h" -#if defined(HAVE_SYS_IOCTL_H) -# include <sys/ioctl.h> -#endif - #ifdef HAVE_STROPTS_H # include <stropts.h> #endif -#if defined(HAVE_TERMIOS_H) -# include <termios.h> -#endif - #ifdef HAVE_SELINUX # include <selinux/selinux.h> static int selinux_enabled = -1; @@ -82,53 +74,6 @@ static int did_set_title = FALSE; static char_u *oldicon = NULL; static int did_set_icon = FALSE; -/* - * If the machine has job control, use it to suspend the program, - * otherwise fake it by starting a new shell. - */ -void mch_suspend(void) -{ -#if defined(SIGTSTP) - out_flush(); /* needed to make cursor visible on some systems */ - settmode(TMODE_COOK); - out_flush(); /* needed to disable mouse on some systems */ - - // Note: compiler defines _REENTRANT when given -pthread flag. -# if defined(_REENTRANT) && defined(SIGCONT) - sigcont_received = FALSE; -# endif - uv_kill(0, SIGTSTP); // send ourselves a STOP signal -# if defined(_REENTRANT) && defined(SIGCONT) - /* - * Wait for the SIGCONT signal to be handled. It generally happens - * immediately, but somehow not all the time. Do not call pause() - * because there would be race condition which would hang Vim if - * signal happened in between the test of sigcont_received and the - * call to pause(). If signal is not yet received, call sleep(0) - * to just yield CPU. Signal should then be received. If somehow - * it's still not received, sleep 1, 2, 3 ms. Don't bother waiting - * further if signal is not received after 1+2+3+4 ms (not expected - * to happen). - */ - { - long wait_time; - for (wait_time = 0; !sigcont_received && wait_time <= 3L; wait_time++) - /* Loop is not entered most of the time */ - os_delay(wait_time, false); - } -# endif - - /* - * Set oldtitle to NULL, so the current title is obtained again. - */ - free(oldtitle); - oldtitle = NULL; - settmode(TMODE_RAW); - need_check_timestamps = TRUE; - did_check_timestamps = FALSE; -#endif -} - static int get_x11_title(int test_only) { return FALSE; @@ -161,7 +106,6 @@ int mch_can_restore_icon(void) */ void mch_settitle(char_u *title, char_u *icon) { - int type = 0; static int recursive = 0; if (T_NAME == NULL) /* no terminal name (yet) */ @@ -175,39 +119,13 @@ void mch_settitle(char_u *title, char_u *icon) return; ++recursive; - /* - * if the window ID and the display is known, we may use X11 calls - */ - - /* - * Note: if "t_ts" is set, title is set with escape sequence rather - * than x11 calls, because the x11 calls don't always work - */ - if ((type || *T_TS != NUL || abstract_ui) && title != NULL) { - if (oldtitle == NULL - ) /* first call but not in GUI, save title */ - (void)get_x11_title(FALSE); - - if (abstract_ui) { - ui_set_title((char *)title); - } else if (*T_TS != NUL) /* it's OK if t_fs is empty */ - term_settitle(title); + if (title != NULL) { + ui_set_title((char *)title); did_set_title = TRUE; } - if ((type || *T_CIS != NUL || abstract_ui) && icon != NULL) { - if (oldicon == NULL - ) /* first call, save icon */ - get_x11_icon(FALSE); - - if (abstract_ui) { - ui_set_icon((char *)icon); - } else if (*T_CIS != NUL) { - out_str(T_CIS); /* set icon start */ - out_str_nf(icon); - out_str(T_CIE); /* set icon end */ - out_flush(); - } + if (icon != NULL) { + ui_set_icon((char *)icon); did_set_icon = TRUE; } --recursive; @@ -246,17 +164,6 @@ int vim_is_xterm(char_u *name) } /* - * Return TRUE if "name" appears to be that of a terminal - * known to support the xterm-style mouse protocol. - * Relies on term_is_xterm having been set to its correct value. - */ -int use_xterm_like_mouse(char_u *name) -{ - return name != NULL - && (term_is_xterm || STRNICMP(name, "screen", 6) == 0); -} - -/* * Return non-zero when using an xterm mouse, according to 'ttymouse'. * Return 1 for "xterm". * Return 2 for "xterm2". @@ -481,7 +388,6 @@ void mch_exit(int r) exiting = TRUE; { - settmode(TMODE_COOK); mch_restore_title(3); /* restore xterm title and icon name */ /* * When t_ti is not empty but it doesn't cause swapping terminal @@ -492,9 +398,7 @@ void mch_exit(int r) if (swapping_screen() && !newline_on_exit) exit_scroll(); - /* Stop termcap: May need to check for T_CRV response, which - * requires RAW mode. */ - stoptermcap(); + ui_builtin_stop(); /* * A newline is only required after a message in the alternate screen. @@ -511,10 +415,6 @@ void mch_exit(int r) out_flush(); ml_close_all(TRUE); /* remove all memfiles */ -#ifdef MACOS_CONVERT - mac_conv_cleanup(); -#endif - event_teardown(); #ifdef EXITFREE @@ -524,143 +424,6 @@ void mch_exit(int r) exit(r); } -void mch_settmode(int tmode) -{ - static int first = TRUE; - -#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || \ - defined(HAVE_TERMIOS_H)) - /* - * for "new" tty systems - */ -# ifdef HAVE_TERMIOS_H - static struct termios told; - struct termios tnew; -# else - static struct termio told; - struct termio tnew; -# endif - - if (first) { - first = FALSE; -# if defined(HAVE_TERMIOS_H) - tcgetattr(read_cmd_fd, &told); -# else - ioctl(read_cmd_fd, TCGETA, &told); -# endif - } - - tnew = told; - if (tmode == TMODE_RAW) { - /* - * ~ICRNL enables typing ^V^M - */ - tnew.c_iflag &= ~ICRNL; - tnew.c_lflag &= ~(ICANON | ECHO | ISIG | ECHOE -# if defined(IEXTEN) - | IEXTEN /* IEXTEN enables typing ^V on SOLARIS */ -# endif - ); -# ifdef ONLCR /* don't map NL -> CR NL, we do it ourselves */ - tnew.c_oflag &= ~ONLCR; -# endif - tnew.c_cc[VMIN] = 1; /* return after 1 char */ - tnew.c_cc[VTIME] = 0; /* don't wait */ - } else if (tmode == TMODE_SLEEP) - tnew.c_lflag &= ~(ECHO); - -# if defined(HAVE_TERMIOS_H) - { - int n = 10; - - /* A signal may cause tcsetattr() to fail (e.g., SIGCONT). Retry a - * few times. */ - while (tcsetattr(read_cmd_fd, TCSANOW, &tnew) == -1 - && errno == EINTR && n > 0) - --n; - } -# else - ioctl(read_cmd_fd, TCSETA, &tnew); -# endif - -#else - - /* - * for "old" tty systems - */ -# ifndef TIOCSETN -# define TIOCSETN TIOCSETP /* for hpux 9.0 */ -# endif - static struct sgttyb ttybold; - struct sgttyb ttybnew; - - if (first) { - first = FALSE; - ioctl(read_cmd_fd, TIOCGETP, &ttybold); - } - - ttybnew = ttybold; - if (tmode == TMODE_RAW) { - ttybnew.sg_flags &= ~(CRMOD | ECHO); - ttybnew.sg_flags |= RAW; - } else if (tmode == TMODE_SLEEP) - ttybnew.sg_flags &= ~(ECHO); - ioctl(read_cmd_fd, TIOCSETN, &ttybnew); -#endif - curr_tmode = tmode; -} - -/* - * Try to get the code for "t_kb" from the stty setting - * - * Even if termcap claims a backspace key, the user's setting *should* - * prevail. stty knows more about reality than termcap does, and if - * somebody's usual erase key is DEL (which, for most BSD users, it will - * be), they're going to get really annoyed if their erase key starts - * doing forward deletes for no reason. (Eric Fischer) - */ -void get_stty(void) -{ - char_u buf[2]; - char_u *p; - -#if defined(ECHOE) && defined(ICANON) && (defined(HAVE_TERMIO_H) || \ - defined(HAVE_TERMIOS_H)) - /* for "new" tty systems */ -# ifdef HAVE_TERMIOS_H - struct termios keys; -# else - struct termio keys; -# endif - -# if defined(HAVE_TERMIOS_H) - if (tcgetattr(read_cmd_fd, &keys) != -1) -# else - if (ioctl(read_cmd_fd, TCGETA, &keys) != -1) -# endif - { - buf[0] = keys.c_cc[VERASE]; - intr_char = keys.c_cc[VINTR]; -#else - /* for "old" tty systems */ - struct sgttyb keys; - - if (ioctl(read_cmd_fd, TIOCGETP, &keys) != -1) { - buf[0] = keys.sg_erase; - intr_char = keys.sg_kill; -#endif - buf[1] = NUL; - add_termcode((char_u *)"kb", buf, FALSE); - - /* - * If <BS> and <DEL> are now the same, redefine <DEL>. - */ - p = find_termcode((char_u *)"kD"); - if (p != NULL && p[0] == buf[0] && p[1] == buf[1]) - do_fixdel(NULL); - } -} - /* * Set mouse clicks on or off. */ @@ -712,173 +475,6 @@ void mch_setmouse(int on) } -/// Sets the mouse termcode, depending on the 'term' and 'ttymouse' options. -void check_mouse_termcode(void) -{ - xterm_conflict_mouse = false; - - if (use_xterm_mouse() - && use_xterm_mouse() != 3 - ) { - set_mouse_termcode(KS_MOUSE, (char_u *)(term_is_8bit(T_NAME) - ? "\233M" - : "\033[M")); - if (*p_mouse != NUL) { - /* force mouse off and maybe on to send possibly new mouse - * activation sequence to the xterm, with(out) drag tracing. */ - mch_setmouse(FALSE); - setmouse(); - } - } else - del_mouse_termcode(KS_MOUSE); - - - /* There is no conflict, but one may type "ESC }" from Insert mode. Don't - * define it in the GUI or when using an xterm. */ - if (!use_xterm_mouse() - ) - set_mouse_termcode(KS_NETTERM_MOUSE, - (char_u *)"\033}"); - else - del_mouse_termcode(KS_NETTERM_MOUSE); - - // Conflicts with xterm mouse: "\033[" and "\033[M". - // Also conflicts with the xterm termresponse, skip this if it was requested - // already. - if (!use_xterm_mouse()) { - set_mouse_termcode(KS_DEC_MOUSE, (char_u *)(term_is_8bit(T_NAME) - ? "\233" : "\033[")); - xterm_conflict_mouse = true; - } - else { - del_mouse_termcode(KS_DEC_MOUSE); - } - /* same as the dec mouse */ - if (use_xterm_mouse() == 3 && !did_request_esc_sequence()) { - set_mouse_termcode(KS_URXVT_MOUSE, - (char_u *)(term_is_8bit(T_NAME) ? "\233" : "\033[")); - if (*p_mouse != NUL) { - mch_setmouse(false); - setmouse(); - } - resume_get_esc_sequence(); - } else { - del_mouse_termcode(KS_URXVT_MOUSE); - } - // There is no conflict with xterm mouse. - if (use_xterm_mouse() == 4) { - set_mouse_termcode(KS_SGR_MOUSE, (char_u *)(term_is_8bit(T_NAME) - ? "\233<" - : "\033[<")); - - if (*p_mouse != NUL) { - mch_setmouse(FALSE); - setmouse(); - } - } else { - del_mouse_termcode(KS_SGR_MOUSE); - } -} - -/* - * Try to get the current window size: - * 1. with an ioctl(), most accurate method - * 2. from the environment variables LINES and COLUMNS - * 3. from the termcap - * 4. keep using the old values - * Return OK when size could be determined, FAIL otherwise. - */ -int mch_get_shellsize(void) -{ - long rows = 0; - long columns = 0; - char_u *p; - - /* - * 1. try using an ioctl. It is the most accurate method. - * - * Try using TIOCGWINSZ first, some systems that have it also define - * TIOCGSIZE but don't have a struct ttysize. - */ -# ifdef TIOCGWINSZ - { - struct winsize ws; - int fd = 1; - - /* When stdout is not a tty, use stdin for the ioctl(). */ - if (!isatty(fd) && isatty(read_cmd_fd)) - fd = read_cmd_fd; - if (ioctl(fd, TIOCGWINSZ, &ws) == 0) { - columns = ws.ws_col; - rows = ws.ws_row; - } - } -# else /* TIOCGWINSZ */ -# ifdef TIOCGSIZE - { - struct ttysize ts; - int fd = 1; - - /* When stdout is not a tty, use stdin for the ioctl(). */ - if (!isatty(fd) && isatty(read_cmd_fd)) - fd = read_cmd_fd; - if (ioctl(fd, TIOCGSIZE, &ts) == 0) { - columns = ts.ts_cols; - rows = ts.ts_lines; - } - } -# endif /* TIOCGSIZE */ -# endif /* TIOCGWINSZ */ - - /* - * 2. get size from environment - * When being POSIX compliant ('|' flag in 'cpoptions') this overrules - * the ioctl() values! - */ - if (columns == 0 || rows == 0 || vim_strchr(p_cpo, CPO_TSIZE) != NULL) { - if ((p = (char_u *)os_getenv("LINES"))) - rows = atoi((char *)p); - if ((p = (char_u *)os_getenv("COLUMNS"))) - columns = atoi((char *)p); - } - -#ifdef HAVE_TGETENT - /* - * 3. try reading "co" and "li" entries from termcap - */ - if (columns == 0 || rows == 0) - getlinecol(&columns, &rows); -#endif - - /* - * 4. If everything fails, use the old values - */ - if (columns <= 0 || rows <= 0) - return FAIL; - - Rows = rows; - Columns = columns; - limit_screen_size(); - return OK; -} - -/* - * Try to set the window size to Rows and Columns. - */ -void mch_set_shellsize(void) -{ - if (*T_CWS) { - /* - * NOTE: if you get an error here that term_set_winsize() is - * undefined, check the output of configure. It could probably not - * find a ncurses, termcap or termlib library. - */ - term_set_winsize((int)Rows, (int)Columns); - out_flush(); - screen_start(); /* don't know where cursor is now */ - } -} - /* * mch_expand_wildcards() - this code does wild-card pattern matching using * the shell @@ -1134,7 +730,10 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, { redraw_later_clear(); /* probably messed up screen */ msg_putchar('\n'); /* clear bottom line quickly */ - cmdline_row = Rows - 1; /* continue on last line */ +#if SIZEOF_LONG > SIZEOF_INT + assert(Rows <= (long)INT_MAX + 1); +#endif + cmdline_row = (int)(Rows - 1); /* continue on last line */ MSG(_(e_wildexpand)); msg_start(); /* don't overwrite this message */ } @@ -1262,7 +861,7 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, goto notfound; } *num_file = i; - *file = (char_u **)xmalloc(sizeof(char_u *) * i); + *file = xmalloc(sizeof(char_u *) * (size_t)i); /* * Isolate the individual file names. diff --git a/src/nvim/path.c b/src/nvim/path.c index e5f78440c2..93aa5eed3d 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -297,7 +297,7 @@ int vim_fnamecmp(char_u *x, char_u *y) return vim_fnamencmp(x, y, MAXPATHL); #else if (p_fic) - return MB_STRICMP(x, y); + return mb_stricmp(x, y); return STRCMP(x, y); #endif } @@ -327,7 +327,7 @@ int vim_fnamencmp(char_u *x, char_u *y, size_t len) return cx - cy; #else if (p_fic) - return MB_STRNICMP(x, y, len); + return mb_strnicmp(x, y, len); return STRNCMP(x, y, len); #endif } @@ -578,16 +578,6 @@ unix_expandpath ( if (*path_end != NUL) backslash_halve(buf + len + 1); if (os_file_exists(buf)) { /* add existing file */ -#ifdef MACOS_CONVERT - size_t precomp_len = STRLEN(buf)+1; - char_u *precomp_buf = - mac_precompose_path(buf, precomp_len, &precomp_len); - - if (precomp_buf) { - memmove(buf, precomp_buf, precomp_len); - free(precomp_buf); - } -#endif addfile(gap, buf, flags); } } diff --git a/src/nvim/pos.h b/src/nvim/pos.h index 7cfb52b283..7071df51e8 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -4,8 +4,8 @@ typedef long linenr_T; // line number type typedef int colnr_T; // column number type -#define MAXLNUM (0x7fffffffL) // maximum (invalid) line number -#define MAXCOL (0x7fffffffL) // maximum column number, 31 bits +#define MAXLNUM 0x7fffffff // maximum (invalid) line number +#define MAXCOL 0x7fffffff // maximum column number, 31 bits /* * position in file or buffer diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 4af09915d5..8b7033b64b 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6226,8 +6226,10 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) if (!ireg_ic) result = STRNCMP(s1, s2, *n); - else - result = MB_STRNICMP(s1, s2, *n); + else { + assert(*n >= 0); + result = mb_strnicmp(s1, s2, (size_t)*n); + } /* if it failed and it's utf8 and we want to combineignore: */ if (result != 0 && enc_utf8 && ireg_icombine) { diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index b082903282..99e9c3afec 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -2887,6 +2887,7 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size) if (stackp < stack) \ { \ st_error(postfix, end, p); \ + free(stack); \ return NULL; \ } @@ -3316,13 +3317,17 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size) } e = POP(); - if (stackp != stack) - EMSG_RET_NULL(_( - "E875: (NFA regexp) (While converting from postfix to NFA), too many states left on stack")); + if (stackp != stack) { + free(stack); + EMSG_RET_NULL(_("E875: (NFA regexp) (While converting from postfix to NFA)," + "too many states left on stack")); + } - if (istate >= nstate) - EMSG_RET_NULL(_( - "E876: (NFA regexp) Not enough space to store the whole NFA ")); + if (istate >= nstate) { + free(stack); + EMSG_RET_NULL(_("E876: (NFA regexp) " + "Not enough space to store the whole NFA ")); + } matchstate = &state_ptr[istate++]; /* the match state */ matchstate->c = NFA_MATCH; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7e7a7c1148..80a87e9f7b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -232,119 +232,6 @@ void redraw_buf_later(buf_T *buf, int type) } /* - * Redraw as soon as possible. When the command line is not scrolled redraw - * right away and restore what was on the command line. - * Return a code indicating what happened. - */ -int redraw_asap(int type) -{ - int rows; - int r; - int ret = 0; - schar_T *screenline; /* copy from ScreenLines[] */ - sattr_T *screenattr; /* copy from ScreenAttrs[] */ - int i; - u8char_T *screenlineUC = NULL; /* copy from ScreenLinesUC[] */ - u8char_T *screenlineC[MAX_MCO]; /* copy from ScreenLinesC[][] */ - schar_T *screenline2 = NULL; /* copy from ScreenLines2[] */ - const bool l_enc_utf8 = enc_utf8; - const int l_enc_dbcs = enc_dbcs; - const long l_p_mco = p_mco; - - redraw_later(type); - if (msg_scrolled || (State != NORMAL && State != NORMAL_BUSY)) - return ret; - - /* Allocate space to save the text displayed in the command line area. */ - rows = Rows - cmdline_row; - screenline = xmalloc((size_t)(rows * Columns * sizeof(schar_T))); - screenattr = xmalloc((size_t)(rows * Columns * sizeof(sattr_T))); - - if (l_enc_utf8) { - screenlineUC = xmalloc((size_t)(rows * Columns * sizeof(u8char_T))); - - for (i = 0; i < l_p_mco; ++i) { - screenlineC[i] = xmalloc((size_t)(rows * Columns * sizeof(u8char_T))); - } - } - if (l_enc_dbcs == DBCS_JPNU) { - screenline2 = xmalloc((size_t)(rows * Columns * sizeof(schar_T))); - } - - /* Save the text displayed in the command line area. */ - for (r = 0; r < rows; ++r) { - memmove(screenline + r * Columns, - ScreenLines + LineOffset[cmdline_row + r], - (size_t)Columns * sizeof(schar_T)); - memmove(screenattr + r * Columns, - ScreenAttrs + LineOffset[cmdline_row + r], - (size_t)Columns * sizeof(sattr_T)); - if (l_enc_utf8) { - memmove(screenlineUC + r * Columns, - ScreenLinesUC + LineOffset[cmdline_row + r], - (size_t)Columns * sizeof(u8char_T)); - for (i = 0; i < l_p_mco; ++i) - memmove(screenlineC[i] + r * Columns, - ScreenLinesC[r] + LineOffset[cmdline_row + r], - (size_t)Columns * sizeof(u8char_T)); - } - if (l_enc_dbcs == DBCS_JPNU) - memmove(screenline2 + r * Columns, - ScreenLines2 + LineOffset[cmdline_row + r], - (size_t)Columns * sizeof(schar_T)); - } - - update_screen(0); - ret = 3; - - if (must_redraw == 0) { - int off = (int)(current_ScreenLine - ScreenLines); - - /* Restore the text displayed in the command line area. */ - for (r = 0; r < rows; ++r) { - memmove(current_ScreenLine, - screenline + r * Columns, - (size_t)Columns * sizeof(schar_T)); - memmove(ScreenAttrs + off, - screenattr + r * Columns, - (size_t)Columns * sizeof(sattr_T)); - if (l_enc_utf8) { - memmove(ScreenLinesUC + off, - screenlineUC + r * Columns, - (size_t)Columns * sizeof(u8char_T)); - for (i = 0; i < l_p_mco; ++i) - memmove(ScreenLinesC[i] + off, - screenlineC[i] + r * Columns, - (size_t)Columns * sizeof(u8char_T)); - } - if (l_enc_dbcs == DBCS_JPNU) - memmove(ScreenLines2 + off, - screenline2 + r * Columns, - (size_t)Columns * sizeof(schar_T)); - SCREEN_LINE(cmdline_row + r, 0, Columns, Columns, FALSE); - } - ret = 4; - } - - free(screenline); - free(screenattr); - if (l_enc_utf8) { - free(screenlineUC); - for (i = 0; i < l_p_mco; ++i) - free(screenlineC[i]); - } - if (l_enc_dbcs == DBCS_JPNU) - free(screenline2); - - /* Show the intro message when appropriate. */ - maybe_intro_message(); - - setcursor(); - - return ret; -} - -/* * Changed something in the current window, at buffer line "lnum", that * requires that line and possibly other lines to be redrawn. * Used when entering/leaving Insert mode with the cursor on a folded line. @@ -4472,23 +4359,6 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, int rl if (char_cells == 2) ScreenLines[off_to + 1] = ScreenLines[off_from + 1]; -#if defined(FEAT_GUI) || defined(UNIX) - /* The bold trick makes a single column of pixels appear in the - * next character. When a bold character is removed, the next - * character should be redrawn too. This happens for our own GUI - * and for some xterms. */ - if ( -# ifdef UNIX - term_is_xterm -# endif - ) { - hl = ScreenAttrs[off_to]; - if (hl > HL_ALL) - hl = syn_attr2attr(hl); - if (hl & HL_BOLD) - redraw_next = TRUE; - } -#endif ScreenAttrs[off_to] = ScreenAttrs[off_from]; /* For simplicity set the attributes of second half of a * double-wide character equal to the first half. */ @@ -5456,24 +5326,6 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) if (need_redraw || force_redraw_this ) { -#if defined(FEAT_GUI) || defined(UNIX) - /* The bold trick makes a single row of pixels appear in the next - * character. When a bold character is removed, the next - * character should be redrawn too. This happens for our own GUI - * and for some xterms. */ - if (need_redraw && ScreenLines[off] != ' ' && ( -# ifdef UNIX - term_is_xterm -# endif - )) { - int n = ScreenAttrs[off]; - - if (n > HL_ALL) - n = syn_attr2attr(n); - if (n & HL_BOLD) - force_redraw_next = TRUE; - } -#endif /* When at the end of the text and overwriting a two-cell * character with a one-cell character, need to clear the next * cell. Also when overwriting the left halve of a two-cell char @@ -5837,157 +5689,26 @@ next_search_hl_pos( static void screen_start_highlight(int attr) { - attrentry_T *aep = NULL; - screen_attr = attr; if (full_screen) { - if (abstract_ui) { - char buf[20]; - sprintf(buf, "\033|%dh", attr); - OUT_STR(buf); - } else { - if (attr > HL_ALL) { /* special HL attr. */ - if (t_colors > 1) - aep = syn_cterm_attr2entry(attr); - else - aep = syn_term_attr2entry(attr); - if (aep == NULL) /* did ":syntax clear" */ - attr = 0; - else - attr = aep->ae_attr; - } - if ((attr & HL_BOLD) && T_MD != NULL) /* bold */ - out_str(T_MD); - else if (aep != NULL && t_colors > 1 && aep->ae_u.cterm.fg_color - && cterm_normal_fg_bold) - /* If the Normal FG color has BOLD attribute and the new HL - * has a FG color defined, clear BOLD. */ - out_str(T_ME); - if ((attr & HL_STANDOUT) && T_SO != NULL) /* standout */ - out_str(T_SO); - if ((attr & (HL_UNDERLINE | HL_UNDERCURL)) && T_US != NULL) - /* underline or undercurl */ - out_str(T_US); - if ((attr & HL_ITALIC) && T_CZH != NULL) /* italic */ - out_str(T_CZH); - if ((attr & HL_INVERSE) && T_MR != NULL) /* inverse (reverse) */ - out_str(T_MR); - - /* - * Output the color or start string after bold etc., in case the - * bold etc. override the color setting. - */ - if (aep != NULL) { - if (t_colors > 1) { - if (aep->ae_u.cterm.fg_color) - term_fg_color(aep->ae_u.cterm.fg_color - 1); - if (aep->ae_u.cterm.bg_color) - term_bg_color(aep->ae_u.cterm.bg_color - 1); - } else { - if (aep->ae_u.term.start != NULL) - out_str(aep->ae_u.term.start); - } - } - } + char buf[20]; + sprintf(buf, "\033|%dh", attr); + OUT_STR(buf); } } void screen_stop_highlight(void) { - int do_ME = FALSE; /* output T_ME code */ - if (screen_attr != 0) { - if (abstract_ui) { - // Handled in ui.c - char buf[20]; - sprintf(buf, "\033|%dH", screen_attr); - OUT_STR(buf); - } else { - if (screen_attr > HL_ALL) { /* special HL attr. */ - attrentry_T *aep; - - if (t_colors > 1) { - /* - * Assume that t_me restores the original colors! - */ - aep = syn_cterm_attr2entry(screen_attr); - if (aep != NULL && (aep->ae_u.cterm.fg_color - || aep->ae_u.cterm.bg_color)) - do_ME = TRUE; - } else { - aep = syn_term_attr2entry(screen_attr); - if (aep != NULL && aep->ae_u.term.stop != NULL) { - if (STRCMP(aep->ae_u.term.stop, T_ME) == 0) - do_ME = TRUE; - else - out_str(aep->ae_u.term.stop); - } - } - if (aep == NULL) /* did ":syntax clear" */ - screen_attr = 0; - else - screen_attr = aep->ae_attr; - } - - /* - * Often all ending-codes are equal to T_ME. Avoid outputting the - * same sequence several times. - */ - if (screen_attr & HL_STANDOUT) { - if (STRCMP(T_SE, T_ME) == 0) - do_ME = TRUE; - else - out_str(T_SE); - } - if (screen_attr & (HL_UNDERLINE | HL_UNDERCURL)) { - if (STRCMP(T_UE, T_ME) == 0) - do_ME = TRUE; - else - out_str(T_UE); - } - if (screen_attr & HL_ITALIC) { - if (STRCMP(T_CZR, T_ME) == 0) - do_ME = TRUE; - else - out_str(T_CZR); - } - if (do_ME || (screen_attr & (HL_BOLD | HL_INVERSE))) - out_str(T_ME); - - if (t_colors > 1) { - /* set Normal cterm colors */ - if (cterm_normal_fg_color != 0) - term_fg_color(cterm_normal_fg_color - 1); - if (cterm_normal_bg_color != 0) - term_bg_color(cterm_normal_bg_color - 1); - if (cterm_normal_fg_bold) - out_str(T_MD); - } - } + // Handled in ui.c + char buf[20]; + sprintf(buf, "\033|%dH", screen_attr); + OUT_STR(buf); } screen_attr = 0; } /* - * Reset the colors for a cterm. Used when leaving Vim. - * The machine specific code may override this again. - */ -void reset_cterm_colors(void) -{ - if (!abstract_ui && t_colors > 1) { - /* set Normal cterm colors */ - if (cterm_normal_fg_color > 0 || cterm_normal_bg_color > 0) { - out_str(T_OP); - screen_attr = -1; - } - if (cterm_normal_fg_bold) { - out_str(T_ME); - screen_attr = -1; - } - } -} - -/* * Put character ScreenLines["off"] on the screen at position "row" and "col", * using the attributes from ScreenAttrs["off"]. */ @@ -6138,7 +5859,6 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, int end_off; int did_delete; int c; - int norm_term; #if defined(FEAT_GUI) || defined(UNIX) int force_next = FALSE; #endif @@ -6153,7 +5873,6 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, return; /* it's a "normal" terminal when not in a GUI or cterm */ - norm_term = (!abstract_ui && t_colors <= 1); for (row = start_row; row < end_row; ++row) { if (has_mbyte ) { @@ -6174,11 +5893,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, did_delete = FALSE; if (c2 == ' ' && end_col == Columns - && can_clear(T_CE) - && (attr == 0 - || (norm_term - && attr <= HL_ALL - && ((attr & ~(HL_BOLD | HL_ITALIC)) == 0)))) { + && attr == 0) { /* * check if we really need to clear something */ @@ -6227,24 +5942,6 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, || force_next #endif ) { -#if defined(FEAT_GUI) || defined(UNIX) - /* The bold trick may make a single row of pixels appear in - * the next character. When a bold character is removed, the - * next character should be redrawn too. This happens for our - * own GUI and for some xterms. */ - if ( -# ifdef UNIX - term_is_xterm -# endif - ) { - if (ScreenLines[off] != ' ' - && (ScreenAttrs[off] > HL_ALL - || ScreenAttrs[off] & HL_BOLD)) - force_next = TRUE; - else - force_next = FALSE; - } -#endif ScreenLines[off] = c; if (enc_utf8) { if (c >= 0x80) { @@ -6584,10 +6281,6 @@ static void screenclear2(void) return; } - if (!abstract_ui) { - screen_attr = -1; /* force setting the Normal colors */ - } - screen_stop_highlight(); /* don't want highlighting here */ @@ -6597,17 +6290,9 @@ static void screenclear2(void) LineWraps[i] = FALSE; } - if (can_clear(T_CL)) { - out_str(T_CL); /* clear the display */ - clear_cmdline = FALSE; - mode_displayed = FALSE; - } else { - /* can't clear the screen, mark all chars with invalid attributes */ - for (i = 0; i < Rows; ++i) - lineinvalid(LineOffset[i], (int)Columns); - clear_cmdline = TRUE; - } - + out_str(T_CL); /* clear the display */ + clear_cmdline = FALSE; + mode_displayed = FALSE; screen_cleared = TRUE; /* can use contents of ScreenLines now */ win_rest_invalid(firstwin); @@ -6637,15 +6322,6 @@ static void lineclear(unsigned off, int width) } /* - * Mark one line in ScreenLines invalid by setting the attributes to an - * invalid value. - */ -static void lineinvalid(unsigned off, int width) -{ - (void)memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T)); -} - -/* * Copy part of a Screenline for vertically split window "wp". */ static void linecopy(int to, int from, win_T *wp) @@ -6672,16 +6348,6 @@ static void linecopy(int to, int from, win_T *wp) } /* - * Return TRUE if clearing with term string "p" would work. - * It can't work when the string is empty or it won't set the right background. - */ -int can_clear(char_u *p) -{ - return abstract_ui || (*p != NUL && (t_colors <= 1 - || cterm_normal_bg_color == 0 || *T_UT != NUL)); -} - -/* * Reset cursor position. Use whenever cursor was moved because of outputting * something directly to the screen (shell commands) or a terminal control * code. @@ -7167,7 +6833,7 @@ screen_ins_lines ( int cursor_row; int type; int result_empty; - int can_ce = can_clear(T_CE); + int can_ce = true; /* * FAIL if @@ -7207,7 +6873,7 @@ screen_ins_lines ( result_empty = (row + line_count >= end); if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL) type = USE_REDRAW; - else if (can_clear(T_CD) && result_empty) + else if (result_empty) type = USE_T_CD; else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL)) type = USE_T_CAL; @@ -7260,10 +6926,7 @@ screen_ins_lines ( while ((j -= line_count) >= row) linecopy(j + line_count, j, wp); j += line_count; - if (can_clear((char_u *)" ")) - lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); - else - lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width); + lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); LineWraps[j] = FALSE; } else { j = end - 1 - i; @@ -7274,10 +6937,7 @@ screen_ins_lines ( } LineOffset[j + line_count] = temp; LineWraps[j + line_count] = FALSE; - if (can_clear((char_u *)" ")) - lineclear(temp, (int)Columns); - else - lineinvalid(temp, (int)Columns); + lineclear(temp, (int)Columns); } } @@ -7364,7 +7024,7 @@ screen_del_lines ( * We can delete lines only when 'db' flag not set or when 'ce' option * available. */ - can_delete = (*T_DB == NUL || can_clear(T_CE)); + can_delete = true; /* * There are six ways to delete lines: @@ -7380,7 +7040,7 @@ screen_del_lines ( */ if (wp != NULL && wp->w_width != Columns && *T_CSV == NUL) type = USE_REDRAW; - else if (can_clear(T_CD) && result_empty) + else if (result_empty) type = USE_T_CD; else if (row == 0 && ( /* On the Amiga, somehow '\n' on the last line doesn't always scroll @@ -7390,9 +7050,7 @@ screen_del_lines ( type = USE_NL; else if (*T_CDL != NUL && line_count > 1 && can_delete) type = USE_T_CDL; - else if (can_clear(T_CE) && result_empty - && (wp == NULL || wp->w_width == Columns) - ) + else if (result_empty && (wp == NULL || wp->w_width == Columns)) type = USE_T_CE; else if (*T_DL != NUL && can_delete) type = USE_T_DL; @@ -7424,10 +7082,7 @@ screen_del_lines ( while ((j += line_count) <= end - 1) linecopy(j - line_count, j, wp); j -= line_count; - if (can_clear((char_u *)" ")) - lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); - else - lineinvalid(LineOffset[j] + wp->w_wincol, wp->w_width); + lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); LineWraps[j] = FALSE; } else { /* whole width, moving the line pointers is faster */ @@ -7439,10 +7094,7 @@ screen_del_lines ( } LineOffset[j - line_count] = temp; LineWraps[j - line_count] = FALSE; - if (can_clear((char_u *)" ")) - lineclear(temp, (int)Columns); - else - lineinvalid(temp, (int)Columns); + lineclear(temp, (int)Columns); } } @@ -8153,7 +7805,7 @@ int screen_screenrow(void) * If 'mustset' is FALSE, we may try to get the real window size and if * it fails use 'width' and 'height'. */ -void screen_resize(int width, int height, int mustset) +void screen_resize(int width, int height) { static int busy = FALSE; @@ -8182,24 +7834,15 @@ void screen_resize(int width, int height, int mustset) ++busy; - // TODO(tarruda): "mustset" is still used in the old tests, which don't use - // "abstract_ui" yet. This will change when a new TUI is merged. - if (abstract_ui || mustset || (ui_get_shellsize() == FAIL && height != 0)) { - Rows = height; - Columns = width; - } + Rows = height; + Columns = width; check_shellsize(); height = Rows; width = Columns; - - if (abstract_ui) { - // Clear the output buffer to ensure UIs don't receive redraw command meant - // for invalid screen sizes. - out_buf_clear(); - ui_resize(width, height); - } else { - mch_set_shellsize(); - } + // Clear the output buffer to ensure UIs don't receive redraw command meant + // for invalid screen sizes. + out_buf_clear(); + ui_resize(width, height); /* The window layout used to be adjusted here, but it now happens in * screenalloc() (also invoked from screenclear()). That is because the diff --git a/src/nvim/search.c b/src/nvim/search.c index 055d2db445..095d7484a5 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -9,6 +9,7 @@ * search.c: code for normal mode searching commands */ +#include <assert.h> #include <errno.h> #include <inttypes.h> #include <stdbool.h> @@ -1258,11 +1259,12 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * ignored because we are interested in the next line -- Acevedo */ if ((compl_cont_status & CONT_ADDING) && !(compl_cont_status & CONT_SOL)) { - if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0) + if ((p_ic ? mb_stricmp(p, pat) : STRCMP(p, pat)) == 0) return OK; } else if (*p != NUL) { /* ignore empty lines */ /* expanding lines or words */ - if ((p_ic ? MB_STRNICMP(p, pat, compl_length) + assert(compl_length >= 0); + if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length) : STRNCMP(p, pat, compl_length)) == 0) return OK; } @@ -4234,8 +4236,10 @@ search_line: ) { /* compare the first "len" chars from "ptr" */ startp = skipwhite(p); - if (p_ic) - matched = !MB_STRNICMP(startp, ptr, len); + if (p_ic) { + assert(len >= 0); + matched = !mb_strnicmp(startp, ptr, (size_t)len); + } else matched = !STRNCMP(startp, ptr, len); if (matched && define_matched && whole diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index e5e29768af..46b8d47c00 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -351,40 +351,3 @@ bool sha256_self_test(void) } return failures == false; } - -/// Fill "header[header_len]" with random_data. -/// Also "salt[salt_len]" when "salt" is not NULL. -/// -/// @param header -/// @param header_len -/// @param salt -/// @param salt_len -void sha2_seed(char_u *restrict header, size_t header_len, - char_u *restrict salt, size_t salt_len) -{ - static char_u random_data[1000]; - char_u sha256sum[SHA256_SUM_SIZE]; - context_sha256_T ctx; - - unsigned int seed = (unsigned int) os_hrtime(); - - size_t i; - for (i = 0; i < sizeof(random_data) - 1; i++) { - random_data[i] = (char_u) ((os_hrtime() ^ (uint64_t)rand_r(&seed)) & 0xff); - } - sha256_start(&ctx); - sha256_update(&ctx, random_data, sizeof(random_data)); - sha256_finish(&ctx, sha256sum); - - // put first block into header. - for (i = 0; i < header_len; i++) { - header[i] = sha256sum[i % sizeof(sha256sum)]; - } - - // put remaining block into salt. - if (salt != NULL) { - for (i = 0; i < salt_len; i++) { - salt[i] = sha256sum[(i + header_len) % sizeof(sha256sum)]; - } - } -} diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 1d13a6abad..f66560f772 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -335,7 +335,7 @@ # include <time.h> // for time_t #endif -#define MAXWLEN 250 // Assume max. word len is this many bytes. +#define MAXWLEN 254 // Assume max. word len is this many bytes. // Some places assume a word length fits in a // byte, thus it can't be above 255. @@ -13082,8 +13082,9 @@ spell_dump_compl ( // proper case later. This isn't exactly right when // length changes for multi-byte characters with // ignore case... + assert(depth >= 0); if (depth <= patlen - && MB_STRNICMP(word, pat, depth) != 0) + && mb_strnicmp(word, pat, (size_t)depth) != 0) --depth; } } @@ -13154,7 +13155,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int d ml_append(lnum, p, (colnr_T)0, FALSE); } else if (((dumpflags & DUMPFLAG_ICASE) - ? MB_STRNICMP(p, pat, STRLEN(pat)) == 0 + ? mb_strnicmp(p, pat, STRLEN(pat)) == 0 : STRNCMP(p, pat, STRLEN(pat)) == 0) && ins_compl_add_infercase(p, (int)STRLEN(p), p_ic, NULL, *dir, 0) == OK) diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 20fa35ed1c..2a0014c6c1 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -301,17 +301,6 @@ void del_trailing_spaces(char_u *ptr) } /* - * Like strncpy(), but always terminate the result with one NUL. - * "to" must be "len + 1" long! - */ -void vim_strncpy(char_u *restrict to, const char_u *restrict from, size_t len) - FUNC_ATTR_NONNULL_ALL -{ - STRNCPY(to, from, len); - to[len] = NUL; -} - -/* * Like strcat(), but make sure the result fits in "tosize" bytes and is * always NUL terminated. */ diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index c88088f25f..c94e6e437b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -49,40 +49,32 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" -/* - * Structure that stores information about a highlight group. - * The ID of a highlight group is also called group ID. It is the index in - * the highlight_ga array PLUS ONE. - */ +// Structure that stores information about a highlight group. +// The ID of a highlight group is also called group ID. It is the index in +// the highlight_ga array PLUS ONE. struct hl_group { - char_u *sg_name; /* highlight group name */ - char_u *sg_name_u; /* uppercase of sg_name */ - /* for normal terminals */ - int sg_term; /* "term=" highlighting attributes */ - char_u *sg_start; /* terminal string for start highl */ - char_u *sg_stop; /* terminal string for stop highl */ - int sg_term_attr; /* Screen attr for term mode */ - /* for color terminals */ - int sg_cterm; /* "cterm=" highlighting attr */ - int sg_cterm_bold; /* bold attr was set for light color */ - int sg_cterm_fg; /* terminal fg color number + 1 */ - int sg_cterm_bg; /* terminal bg color number + 1 */ - int sg_cterm_attr; /* Screen attr for color term mode */ - /* Store the sp color name for the GUI or synIDattr() */ - int sg_gui; /* "gui=" highlighting attributes */ + char_u *sg_name; // highlight group name + char_u *sg_name_u; // uppercase of sg_name + int sg_attr; // Screen attr + int sg_link; // link to this highlight group ID + int sg_set; // combination of SG_* flags + scid_T sg_scriptID; // script in which the group was last set + // for terminal UIs + int sg_cterm; // "cterm=" highlighting attr + int sg_cterm_fg; // terminal fg color number + 1 + int sg_cterm_bg; // terminal bg color number + 1 + int sg_cterm_bold; // bold attr was set for light color + // for RGB UIs + int sg_gui; // "gui=" highlighting attributes RgbValue sg_rgb_fg; // RGB foreground color RgbValue sg_rgb_bg; // RGB background color uint8_t *sg_rgb_fg_name; // RGB foreground color name uint8_t *sg_rgb_bg_name; // RGB background color name - int sg_link; /* link to this highlight group ID */ - int sg_set; /* combination of SG_* flags */ - scid_T sg_scriptID; /* script in which the group was last set */ }; -#define SG_TERM 1 /* term has been set */ -#define SG_CTERM 2 /* cterm has been set */ -#define SG_GUI 4 /* gui has been set */ -#define SG_LINK 8 /* link has been set */ +#define SG_CTERM 2 // cterm has been set +#define SG_GUI 4 // gui has been set +#define SG_LINK 8 // link has been set // highlight groups for 'highlight' option static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; @@ -221,7 +213,7 @@ struct name_list { /* * An attribute number is the index in attr_table plus ATTR_OFF. */ -#define ATTR_OFF (HL_ALL + 1) +#define ATTR_OFF 1 static char *(spo_name_tab[SPO_COUNT]) = @@ -1399,7 +1391,7 @@ static int syn_stack_equal(synstate_T *sp) || six->matches[j] == NULL) break; if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic - ? MB_STRICMP(bsx->matches[j], + ? mb_stricmp(bsx->matches[j], six->matches[j]) != 0 : STRCMP(bsx->matches[j], six->matches[j]) != 0) break; @@ -6054,7 +6046,6 @@ do_highlight ( ) { char_u *name_end; - char_u *p; char_u *linep; char_u *key_start; char_u *arg_start; @@ -6242,7 +6233,7 @@ do_highlight ( if (STRCMP(key, "NONE") == 0) { if (!init || HL_TABLE()[idx].sg_set == 0) { if (!init) - HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI; + HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI; highlight_clear(idx); } continue; @@ -6312,20 +6303,14 @@ do_highlight ( } if (error) break; - if (*key == 'T') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM)) { - if (!init) - HL_TABLE()[idx].sg_set |= SG_TERM; - HL_TABLE()[idx].sg_term = attr; - } - } else if (*key == 'C') { + if (*key == 'C') { if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { if (!init) HL_TABLE()[idx].sg_set |= SG_CTERM; HL_TABLE()[idx].sg_cterm = attr; HL_TABLE()[idx].sg_cterm_bold = FALSE; } - } else { + } else if (*key == 'G') { if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) HL_TABLE()[idx].sg_set |= SG_GUI; @@ -6438,27 +6423,17 @@ do_highlight ( } color &= 7; /* truncate to 8 colors */ } else if (t_colors == 16 || t_colors == 88 || t_colors == 256) { - /* - * Guess: if the termcap entry ends in 'm', it is - * probably an xterm-like terminal. Use the changed - * order for colors. - */ - if (*T_CAF != NUL) - p = T_CAF; - else - p = T_CSF; - if (abstract_ui || (*p != NUL && *(p + STRLEN(p) - 1) == 'm')) - switch (t_colors) { - case 16: - color = color_numbers_8[i]; - break; - case 88: - color = color_numbers_88[i]; - break; - case 256: - color = color_numbers_256[i]; - break; - } + switch (t_colors) { + case 16: + color = color_numbers_8[i]; + break; + case 88: + color = color_numbers_88[i]; + break; + case 256: + color = color_numbers_256[i]; + break; + } } } } @@ -6471,8 +6446,6 @@ do_highlight ( cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); { must_redraw = CLEAR; - if (termcap_active && color >= 0) - term_fg_color(color); } } } else { @@ -6482,8 +6455,6 @@ do_highlight ( { must_redraw = CLEAR; if (color >= 0) { - if (termcap_active) - term_bg_color(color); if (t_colors < 16) i = (color == 0 || color == 4); else @@ -6536,73 +6507,9 @@ do_highlight ( normal_bg = HL_TABLE()[idx].sg_rgb_bg; } } else if (STRCMP(key, "GUISP") == 0) { - // Ignored + // Ignored for now } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) { - char_u buf[100]; - char_u *tname; - - if (!init) - HL_TABLE()[idx].sg_set |= SG_TERM; - - /* - * The "start" and "stop" arguments can be a literal escape - * sequence, or a comma separated list of terminal codes. - */ - if (STRNCMP(arg, "t_", 2) == 0) { - off = 0; - buf[0] = 0; - while (arg[off] != NUL) { - /* Isolate one termcap name */ - for (len = 0; arg[off + len] && - arg[off + len] != ','; ++len) - ; - tname = vim_strnsave(arg + off, len); - /* lookup the escape sequence for the item */ - p = get_term_code(tname); - free(tname); - if (p == NULL) /* ignore non-existing things */ - p = (char_u *)""; - - /* Append it to the already found stuff */ - if ((int)(STRLEN(buf) + STRLEN(p)) >= 99) { - EMSG2(_("E422: terminal code too long: %s"), arg); - error = TRUE; - break; - } - STRCAT(buf, p); - - /* Advance to the next item */ - off += len; - if (arg[off] == ',') /* another one follows */ - ++off; - } - } else { - /* - * Copy characters from arg[] to buf[], translating <> codes. - */ - for (p = arg, off = 0; off < 100 - 6 && *p; ) { - len = (int)trans_special(&p, buf + off, FALSE); - if (len > 0) /* recognized special char */ - off += len; - else /* copy as normal char */ - buf[off++] = *p++; - } - buf[off] = NUL; - } - if (error) - break; - - if (STRCMP(buf, "NONE") == 0) /* resetting the value */ - p = NULL; - else - p = vim_strsave(buf); - if (key[2] == 'A') { - free(HL_TABLE()[idx].sg_start); - HL_TABLE()[idx].sg_start = p; - } else { - free(HL_TABLE()[idx].sg_stop); - HL_TABLE()[idx].sg_stop = p; - } + // Ignored for now } else { EMSG2(_("E423: Illegal argument: %s"), key_start); error = TRUE; @@ -6628,12 +6535,9 @@ do_highlight ( syn_unadd_group(); else { if (is_normal_group) { - HL_TABLE()[idx].sg_term_attr = 0; - HL_TABLE()[idx].sg_cterm_attr = 0; - if (abstract_ui) { - // If the normal group has changed, it is simpler to refresh every UI - ui_refresh(); - } + HL_TABLE()[idx].sg_attr = 0; + // If the normal group has changed, it is simpler to refresh every UI + ui_refresh(); } else set_hl_attr(idx); HL_TABLE()[idx].sg_scriptID = current_SID; @@ -6678,10 +6582,11 @@ void restore_cterm_colors(void) */ static int hl_has_settings(int idx, int check_link) { - return HL_TABLE()[idx].sg_term_attr != 0 - || HL_TABLE()[idx].sg_cterm_attr != 0 + return HL_TABLE()[idx].sg_attr != 0 || HL_TABLE()[idx].sg_cterm_fg != 0 || HL_TABLE()[idx].sg_cterm_bg != 0 + || HL_TABLE()[idx].sg_rgb_fg_name != NULL + || HL_TABLE()[idx].sg_rgb_bg_name != NULL || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)); } @@ -6690,17 +6595,11 @@ static int hl_has_settings(int idx, int check_link) */ static void highlight_clear(int idx) { - HL_TABLE()[idx].sg_term = 0; - free(HL_TABLE()[idx].sg_start); - HL_TABLE()[idx].sg_start = NULL; - free(HL_TABLE()[idx].sg_stop); - HL_TABLE()[idx].sg_stop = NULL; - HL_TABLE()[idx].sg_term_attr = 0; + HL_TABLE()[idx].sg_attr = 0; HL_TABLE()[idx].sg_cterm = 0; HL_TABLE()[idx].sg_cterm_bold = FALSE; HL_TABLE()[idx].sg_cterm_fg = 0; HL_TABLE()[idx].sg_cterm_bg = 0; - HL_TABLE()[idx].sg_cterm_attr = 0; HL_TABLE()[idx].sg_gui = 0; HL_TABLE()[idx].sg_rgb_fg = -1; HL_TABLE()[idx].sg_rgb_bg = -1; @@ -6720,23 +6619,20 @@ static void highlight_clear(int idx) * Note that this table is used by ALL buffers. This is required because the * GUI can redraw at any time for any buffer. */ -static garray_T term_attr_table = GA_EMPTY_INIT_VALUE; - -#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx] - -static garray_T cterm_attr_table = GA_EMPTY_INIT_VALUE; +static garray_T attr_table = GA_EMPTY_INIT_VALUE; -#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx] +#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] /* * Return the attr number for a set of colors and font. - * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table + * Add a new entry to the term_attr_table, attr_table or gui_attr_table * if the combination is new. * Return 0 for error. */ -static int get_attr_entry(garray_T *table, attrentry_T *aep) +static int get_attr_entry(attrentry_T *aep) { + garray_T *table = &attr_table; attrentry_T *taep; static int recursive = FALSE; @@ -6751,31 +6647,14 @@ static int get_attr_entry(garray_T *table, attrentry_T *aep) */ for (int i = 0; i < table->ga_len; ++i) { taep = &(((attrentry_T *)table->ga_data)[i]); - if ( aep->ae_attr == taep->ae_attr - && ( - (table == &term_attr_table - && (aep->ae_u.term.start == NULL) - == (taep->ae_u.term.start == NULL) - && (aep->ae_u.term.start == NULL - || STRCMP(aep->ae_u.term.start, - taep->ae_u.term.start) == 0) - && (aep->ae_u.term.stop == NULL) - == (taep->ae_u.term.stop == NULL) - && (aep->ae_u.term.stop == NULL - || STRCMP(aep->ae_u.term.stop, - taep->ae_u.term.stop) == 0)) - || (table == &cterm_attr_table - && aep->ae_u.cterm.fg_color - == taep->ae_u.cterm.fg_color - && aep->ae_u.cterm.bg_color - == taep->ae_u.cterm.bg_color - && aep->fg_color - == taep->fg_color - && aep->bg_color - == taep->bg_color) - )) - + if (aep->cterm_ae_attr == taep->cterm_ae_attr + && aep->cterm_fg_color == taep->cterm_fg_color + && aep->cterm_bg_color == taep->cterm_bg_color + && aep->rgb_ae_attr == taep->rgb_ae_attr + && aep->rgb_fg_color == taep->rgb_fg_color + && aep->rgb_bg_color == taep->rgb_bg_color) { return i + ATTR_OFF; + } } if (table->ga_len + ATTR_OFF > MAX_TYPENR) { @@ -6801,156 +6680,83 @@ static int get_attr_entry(garray_T *table, attrentry_T *aep) recursive = FALSE; } - /* - * This is a new combination of colors and font, add an entry. - */ + + // This is a new combination of colors and font, add an entry. taep = GA_APPEND_VIA_PTR(attrentry_T, table); memset(taep, 0, sizeof(*taep)); - taep->ae_attr = aep->ae_attr; - if (table == &term_attr_table) { - if (aep->ae_u.term.start == NULL) - taep->ae_u.term.start = NULL; - else - taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start); - if (aep->ae_u.term.stop == NULL) - taep->ae_u.term.stop = NULL; - else - taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop); - } else if (table == &cterm_attr_table) { - taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color; - taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color; - taep->fg_color = aep->fg_color; - taep->bg_color = aep->bg_color; - } + taep->cterm_ae_attr = aep->cterm_ae_attr; + taep->cterm_fg_color = aep->cterm_fg_color; + taep->cterm_bg_color = aep->cterm_bg_color; + taep->rgb_ae_attr = aep->rgb_ae_attr; + taep->rgb_fg_color = aep->rgb_fg_color; + taep->rgb_bg_color = aep->rgb_bg_color; return table->ga_len - 1 + ATTR_OFF; } -/* - * Clear all highlight tables. - */ +// Clear all highlight tables. void clear_hl_tables(void) { - attrentry_T *taep; - - for (int i = 0; i < term_attr_table.ga_len; ++i) { - taep = &(((attrentry_T *)term_attr_table.ga_data)[i]); - free(taep->ae_u.term.start); - free(taep->ae_u.term.stop); - } - ga_clear(&term_attr_table); - ga_clear(&cterm_attr_table); + ga_clear(&attr_table); } -/* - * Combine special attributes (e.g., for spelling) with other attributes - * (e.g., for syntax highlighting). - * "prim_attr" overrules "char_attr". - * This creates a new group when required. - * Since we expect there to be few spelling mistakes we don't cache the - * result. - * Return the resulting attributes. - */ +// Combine special attributes (e.g., for spelling) with other attributes +// (e.g., for syntax highlighting). +// "prim_attr" overrules "char_attr". +// This creates a new group when required. +// Since we expect there to be few spelling mistakes we don't cache the +// result. +// Return the resulting attributes. int hl_combine_attr(int char_attr, int prim_attr) { attrentry_T *char_aep = NULL; attrentry_T *spell_aep; attrentry_T new_en; - if (char_attr == 0) + if (char_attr == 0) { return prim_attr; - if (char_attr <= HL_ALL && prim_attr <= HL_ALL) - return char_attr | prim_attr; - - if (abstract_ui || t_colors > 1) { - if (char_attr > HL_ALL) - char_aep = syn_cterm_attr2entry(char_attr); - if (char_aep != NULL) - new_en = *char_aep; - else { - memset(&new_en, 0, sizeof(new_en)); - if (char_attr <= HL_ALL) - new_en.ae_attr = char_attr; - } - - if (prim_attr <= HL_ALL) - new_en.ae_attr |= prim_attr; - else { - spell_aep = syn_cterm_attr2entry(prim_attr); - if (spell_aep != NULL) { - new_en.ae_attr |= spell_aep->ae_attr; - if (spell_aep->ae_u.cterm.fg_color > 0) - new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color; - if (spell_aep->ae_u.cterm.bg_color > 0) - new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color; - if (spell_aep->fg_color >= 0) - new_en.fg_color = spell_aep->fg_color; - if (spell_aep->bg_color >= 0) - new_en.bg_color = spell_aep->bg_color; - } - } - return get_attr_entry(&cterm_attr_table, &new_en); } - if (char_attr > HL_ALL) - char_aep = syn_term_attr2entry(char_attr); - if (char_aep != NULL) + // Find the entry for char_attr + char_aep = syn_cterm_attr2entry(char_attr); + + if (char_aep != NULL) { + // Copy all attributes from char_aep to the new entry new_en = *char_aep; - else { + } else { memset(&new_en, 0, sizeof(new_en)); - if (char_attr <= HL_ALL) - new_en.ae_attr = char_attr; } - if (prim_attr <= HL_ALL) - new_en.ae_attr |= prim_attr; - else { - spell_aep = syn_term_attr2entry(prim_attr); - if (spell_aep != NULL) { - new_en.ae_attr |= spell_aep->ae_attr; - if (spell_aep->ae_u.term.start != NULL) { - new_en.ae_u.term.start = spell_aep->ae_u.term.start; - new_en.ae_u.term.stop = spell_aep->ae_u.term.stop; - } - } - } - return get_attr_entry(&term_attr_table, &new_en); -} - - -/* - * Get the highlight attributes (HL_BOLD etc.) from an attribute nr. - * Only to be used when "attr" > HL_ALL. - */ -int syn_attr2attr(int attr) -{ - attrentry_T *aep; + spell_aep = syn_cterm_attr2entry(prim_attr); + if (spell_aep != NULL) { + new_en.cterm_ae_attr |= spell_aep->cterm_ae_attr; + new_en.rgb_ae_attr |= spell_aep->rgb_ae_attr; - if (abstract_ui || t_colors > 1) - aep = syn_cterm_attr2entry(attr); - else - aep = syn_term_attr2entry(attr); + if (spell_aep->cterm_fg_color > 0) { + new_en.cterm_fg_color = spell_aep->cterm_fg_color; + } - if (aep == NULL) /* highlighting not set */ - return 0; - return aep->ae_attr; -} + if (spell_aep->cterm_bg_color > 0) { + new_en.cterm_bg_color = spell_aep->cterm_bg_color; + } + if (spell_aep->rgb_fg_color >= 0) { + new_en.rgb_fg_color = spell_aep->rgb_fg_color; + } -attrentry_T *syn_term_attr2entry(int attr) -{ - attr -= ATTR_OFF; - if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */ - return NULL; - return &(TERM_ATTR_ENTRY(attr)); + if (spell_aep->rgb_bg_color >= 0) { + new_en.rgb_bg_color = spell_aep->rgb_bg_color; + } + } + return get_attr_entry(&new_en); } attrentry_T *syn_cterm_attr2entry(int attr) { attr -= ATTR_OFF; - if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */ + if (attr >= attr_table.ga_len) /* did ":syntax clear" */ return NULL; - return &(CTERM_ATTR_ENTRY(attr)); + return &(ATTR_ENTRY(attr)); } #define LIST_ATTR 1 @@ -6965,13 +6771,6 @@ static void highlight_list_one(int id) sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */ didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_term, NULL, "term"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_start, "start"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_stop, "stop"); - - didh = highlight_list_arg(id, didh, LIST_ATTR, sgp->sg_cterm, NULL, "cterm"); didh = highlight_list_arg(id, didh, LIST_INT, sgp->sg_cterm_fg, NULL, "ctermfg"); @@ -7049,7 +6848,7 @@ char_u * highlight_has_attr ( int id, int flag, - int modec /* 'g' for GUI, 'c' for cterm, 't' for term */ + int modec // 'g' for GUI, 'c' for cterm ) { int attr; @@ -7057,12 +6856,11 @@ highlight_has_attr ( if (id <= 0 || id > highlight_ga.ga_len) return NULL; - if (modec == 'g') + if (modec == 'g') { attr = HL_TABLE()[id - 1].sg_gui; - else if (modec == 'c') + } else { attr = HL_TABLE()[id - 1].sg_cterm; - else - attr = HL_TABLE()[id - 1].sg_term; + } if (attr & flag) return (char_u *)"1"; @@ -7179,37 +6977,16 @@ set_hl_attr ( if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) return; - /* - * For the term mode: If there are other than "normal" highlighting - * attributes, need to allocate an attr number. - */ - if (sgp->sg_start == NULL && sgp->sg_stop == NULL) - sgp->sg_term_attr = sgp->sg_term; - else { - at_en.ae_attr = sgp->sg_term; - at_en.ae_u.term.start = sgp->sg_start; - at_en.ae_u.term.stop = sgp->sg_stop; - sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en); - } - - /* - * For the color term mode: If there are other than "normal" - * highlighting attributes, need to allocate an attr number. - */ - if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 - && sgp->sg_rgb_fg == -1 && sgp->sg_rgb_bg == -1) { - sgp->sg_cterm_attr = sgp->sg_cterm; - } else { - at_en.ae_attr = abstract_ui ? sgp->sg_gui : sgp->sg_cterm; - at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg; - at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg; - // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is - // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name - // before setting attr_entry->{f,g}g_color to a other than -1 - at_en.fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; - at_en.bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en); - } + at_en.cterm_ae_attr = sgp->sg_cterm; + at_en.cterm_fg_color = sgp->sg_cterm_fg; + at_en.cterm_bg_color = sgp->sg_cterm_bg; + at_en.rgb_ae_attr = sgp->sg_gui; + // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is + // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name + // before setting attr_entry->{f,g}g_color to a other than -1 + at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; + at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; + sgp->sg_attr = get_attr_entry(&at_en); } /* @@ -7348,18 +7125,11 @@ static void syn_unadd_group(void) */ int syn_id2attr(int hl_id) { - int attr; struct hl_group *sgp; hl_id = syn_get_final_id(hl_id); sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */ - - if (abstract_ui || t_colors > 1) - attr = sgp->sg_cterm_attr; - else - attr = sgp->sg_term_attr; - - return attr; + return sgp->sg_attr; } @@ -7446,11 +7216,12 @@ int highlight_changed(void) * bold-underlined. */ attr = 0; + bool colon = false; for (; *p && *p != ','; ++p) { /* parse upto comma */ if (vim_iswhite(*p)) /* ignore white space */ continue; - if (attr > HL_ALL) /* Combination with ':' is not allowed. */ + if (colon) /* Combination with ':' is not allowed. */ return FAIL; switch (*p) { @@ -7472,6 +7243,7 @@ int highlight_changed(void) case ':': ++p; /* highlight group name */ if (attr || *p == NUL) /* no combinations */ return FAIL; + colon = true; end = vim_strchr(p, ','); if (end == NULL) end = p + STRLEN(p); @@ -7506,7 +7278,6 @@ int highlight_changed(void) hlcnt = highlight_ga.ga_len; if (id_S == 0) { /* Make sure id_S is always valid to simplify code below */ memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group)); - HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S]; id_S = hlcnt + 10; } for (int i = 0; i < 9; i++) { @@ -7521,7 +7292,6 @@ int highlight_changed(void) highlight_user[i] = syn_id2attr(id); if (id_SNC == 0) { memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group)); - hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC]; hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC]; hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC]; } else @@ -7531,20 +7301,26 @@ int highlight_changed(void) hlt[hlcnt + i].sg_link = 0; /* Apply difference between UserX and HLF_S to HLF_SNC */ - hlt[hlcnt + i].sg_term ^= - hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term; - if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start) - hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start; - if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop) - hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop; - hlt[hlcnt + i].sg_cterm ^= - hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; - if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) + hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; + + if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) { hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; - if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) + } + + if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) { hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; - hlt[hlcnt + i].sg_gui ^= - hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; + } + + hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; + + if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) { + hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg; + } + + if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) { + hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; + } + highlight_ga.ga_len = hlcnt + i + 1; set_hl_attr(hlcnt + i); /* At long last we can apply */ highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1); diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index 93088180b9..af2ac719c6 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -16,7 +16,6 @@ #define HL_UNDERLINE 0x08 #define HL_UNDERCURL 0x10 #define HL_STANDOUT 0x20 -#define HL_ALL 0x3f #define HL_CONTAINED 0x01 /* not used on toplevel */ #define HL_TRANSP 0x02 /* has no highlighting */ diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index abf7ea5a7d..67cf672ef2 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -67,23 +67,11 @@ struct syn_state { * may have made the state invalid */ }; -/* - * Structure shared between syntax.c, screen.c and gui_x11.c. - */ +// Structure shared between syntax.c, screen.c typedef struct attr_entry { - short ae_attr; /* HL_BOLD, etc. */ - RgbValue fg_color, bg_color; - union { - struct { - char_u *start; /* start escape sequence */ - char_u *stop; /* stop escape sequence */ - } term; - struct { - /* These colors need to be > 8 bits to hold 256. */ - uint16_t fg_color; /* foreground color number */ - uint16_t bg_color; /* background color number */ - } cterm; - } ae_u; + short rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. + RgbValue rgb_fg_color, rgb_bg_color; + int cterm_fg_color, cterm_bg_color; } attrentry_T; #endif // NVIM_SYNTAX_DEFS_H diff --git a/src/nvim/tag.c b/src/nvim/tag.c index bab594a27d..5c3d7cc30b 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1641,7 +1641,8 @@ parse_line: /* No match yet and are at the end of the binary search. */ break; } else if (state == TS_SKIP_BACK) { - if (MB_STRNICMP(tagp.tagname, orgpat.head, cmplen) != 0) + assert(cmplen >= 0); + if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) state = TS_STEP_FORWARD; else /* Have to skip back more. Restore the curr_offset @@ -1649,7 +1650,8 @@ parse_line: search_info.curr_offset = search_info.curr_offset_used; continue; } else if (state == TS_STEP_FORWARD) { - if (MB_STRNICMP(tagp.tagname, orgpat.head, cmplen) != 0) { + assert(cmplen >= 0); + if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) { if ((off_t)ftell(fp) > search_info.match_offset) break; /* past last match */ else @@ -1657,7 +1659,8 @@ parse_line: } } else /* skip this match if it can't match */ - if (MB_STRNICMP(tagp.tagname, orgpat.head, cmplen) != 0) + assert(cmplen >= 0); + if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) continue; /* @@ -1691,7 +1694,8 @@ parse_line: match = FALSE; else { if (orgpat.regmatch.rm_ic) { - match = (MB_STRNICMP(tagp.tagname, orgpat.pat, cmplen) == 0); + assert(cmplen >= 0); + match = mb_strnicmp(tagp.tagname, orgpat.pat, (size_t)cmplen) == 0; if (match) match_no_ic = (STRNCMP(tagp.tagname, orgpat.pat, cmplen) == 0); diff --git a/src/nvim/term.c b/src/nvim/term.c index 14c087cb60..a16c1a59ca 100644 --- a/src/nvim/term.c +++ b/src/nvim/term.c @@ -58,20 +58,6 @@ #include "nvim/os/time.h" #include "nvim/os/input.h" -#ifdef HAVE_TGETENT -# ifdef HAVE_TERMIOS_H -# include <termios.h> /* seems to be required for some Linux */ -# endif -# ifdef HAVE_TERMCAP_H -# include <termcap.h> -# endif - -/* - * A few linux systems define outfuntype in termcap.h to be used as the third - * argument for tputs(). - */ -# define TPUTSFUNCAST (int (*)(int)) -#endif #undef tgetstr @@ -101,62 +87,7 @@ struct builtin_term { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "term.c.generated.h" #endif -#if defined(FEAT_GUI) \ - || (defined(FEAT_MOUSE) && (!defined(UNIX) || defined(FEAT_MOUSE_XTERM) \ - || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE))) -static int get_bytes_from_buf(char_u *, char_u *, int); -#endif - -#ifdef HAVE_TGETENT -static char_u *tgetent_error(char_u *, char_u *); - -/* - * Here is our own prototype for tgetstr(), any prototypes from the include - * files have been disabled by the define at the start of this file. - */ -char *tgetstr(char *, char **); - -/* Change this to "if 1" to debug what happens with termresponse. */ -# define LOG_TR(msg) -/* Request Terminal Version status: */ -# define CRV_GET 1 /* send T_CRV when switched to RAW mode */ -# define CRV_SENT 2 /* did send T_CRV, waiting for answer */ -# define CRV_GOT 3 /* received T_CRV response */ -static int crv_status = CRV_GET; -/* Request Cursor position report: */ -# define U7_GET 1 /* send T_U7 when switched to RAW mode */ -# define U7_SENT 2 /* did send T_U7, waiting for answer */ -# define U7_GOT 3 /* received T_U7 response */ -static int u7_status = U7_GET; -/* - * Don't declare these variables if termcap.h contains them. - * Autoconf checks if these variables should be declared extern (not all - * systems have them). - * Some versions define ospeed to be speed_t, but that is incompatible with - * BSD, where ospeed is short and speed_t is long. - */ -# ifndef HAVE_OSPEED -# ifdef OSPEED_EXTERN -extern short ospeed; -# else -short ospeed; -# endif -# endif -# ifndef HAVE_UP_BC_PC -# ifdef UP_BC_PC_EXTERN -extern char *UP, *BC, PC; -# else -char *UP, *BC, PC; -# endif -# endif - -# define TGETSTR(s, p) vim_tgetstr((s), (p)) -# define TGETENT(b, t) tgetent((char *)(b), (char *)(t)) -#endif /* HAVE_TGETENT */ - -static int xt_index_in = 0; -static int xt_index_out = 0; static bool detected_8bit = false; // detected 8-bit terminal @@ -190,771 +121,6 @@ static struct builtin_term builtin_termcaps[] = {(int)KS_CM, "\033|%p1%d;%p2%dM"}, // there are no key sequences here, for "abstract_ui" vim key codes are // parsed directly in input_enqueue() - - -#ifndef NO_BUILTIN_TCAPS - -# if defined(ALL_BUILTIN_TCAPS) - /* - * Amiga console window, default for Amiga - */ - {(int)KS_NAME, "amiga"}, - {(int)KS_CE, "\033[K"}, - {(int)KS_CD, "\033[J"}, - {(int)KS_AL, "\033[L"}, -# ifdef TERMINFO - {(int)KS_CAL, "\033[%p1%dL"}, -# else - {(int)KS_CAL, "\033[%dL"}, -# endif - {(int)KS_DL, "\033[M"}, -# ifdef TERMINFO - {(int)KS_CDL, "\033[%p1%dM"}, -# else - {(int)KS_CDL, "\033[%dM"}, -# endif - {(int)KS_CL, "\014"}, - {(int)KS_VI, "\033[0 p"}, - {(int)KS_VE, "\033[1 p"}, - {(int)KS_ME, "\033[0m"}, - {(int)KS_MR, "\033[7m"}, - {(int)KS_MD, "\033[1m"}, - {(int)KS_SE, "\033[0m"}, - {(int)KS_SO, "\033[33m"}, - {(int)KS_US, "\033[4m"}, - {(int)KS_UE, "\033[0m"}, - {(int)KS_CZH, "\033[3m"}, - {(int)KS_CZR, "\033[0m"}, - {(int)KS_MS, "y"}, - {(int)KS_UT, "y"}, /* guessed */ - {(int)KS_LE, "\b"}, -# ifdef TERMINFO - {(int)KS_CM, "\033[%i%p1%d;%p2%dH"}, -# else - {(int)KS_CM, "\033[%i%d;%dH"}, -# endif -# ifdef TERMINFO - {(int)KS_CRI, "\033[%p1%dC"}, -# else - {(int)KS_CRI, "\033[%dC"}, -# endif - {K_UP, "\233A"}, - {K_DOWN, "\233B"}, - {K_LEFT, "\233D"}, - {K_RIGHT, "\233C"}, - {K_S_UP, "\233T"}, - {K_S_DOWN, "\233S"}, - {K_S_LEFT, "\233 A"}, - {K_S_RIGHT, "\233 @"}, - {K_S_TAB, "\233Z"}, - {K_F1, "\233\060~"}, /* some compilers don't dig "\2330" */ - {K_F2, "\233\061~"}, - {K_F3, "\233\062~"}, - {K_F4, "\233\063~"}, - {K_F5, "\233\064~"}, - {K_F6, "\233\065~"}, - {K_F7, "\233\066~"}, - {K_F8, "\233\067~"}, - {K_F9, "\233\070~"}, - {K_F10, "\233\071~"}, - {K_S_F1, "\233\061\060~"}, - {K_S_F2, "\233\061\061~"}, - {K_S_F3, "\233\061\062~"}, - {K_S_F4, "\233\061\063~"}, - {K_S_F5, "\233\061\064~"}, - {K_S_F6, "\233\061\065~"}, - {K_S_F7, "\233\061\066~"}, - {K_S_F8, "\233\061\067~"}, - {K_S_F9, "\233\061\070~"}, - {K_S_F10, "\233\061\071~"}, - {K_HELP, "\233?~"}, - {K_INS, "\233\064\060~"}, /* 101 key keyboard */ - {K_PAGEUP, "\233\064\061~"}, /* 101 key keyboard */ - {K_PAGEDOWN, "\233\064\062~"}, /* 101 key keyboard */ - {K_HOME, "\233\064\064~"}, /* 101 key keyboard */ - {K_END, "\233\064\065~"}, /* 101 key keyboard */ - - {BT_EXTRA_KEYS, ""}, - {TERMCAP2KEY('#', '2'), "\233\065\064~"}, /* shifted home key */ - {TERMCAP2KEY('#', '3'), "\233\065\060~"}, /* shifted insert key */ - {TERMCAP2KEY('*', '7'), "\233\065\065~"}, /* shifted end key */ -# endif - -# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) - /* - * standard ANSI terminal, default for unix - */ - {(int)KS_NAME, "ansi"}, - {(int)KS_CE, "\033[K"}, - {(int)KS_AL, "\033[L"}, -# ifdef TERMINFO - {(int)KS_CAL, "\033[%p1%dL"}, -# else - {(int)KS_CAL, "\033[%dL"}, -# endif - {(int)KS_DL, "\033[M"}, -# ifdef TERMINFO - {(int)KS_CDL, "\033[%p1%dM"}, -# else - {(int)KS_CDL, "\033[%dM"}, -# endif - {(int)KS_CL, "\033[H\033[2J"}, - {(int)KS_ME, "\033[0m"}, - {(int)KS_MR, "\033[7m"}, - {(int)KS_MS, "y"}, - {(int)KS_UT, "y"}, /* guessed */ - {(int)KS_LE, "\b"}, -# ifdef TERMINFO - {(int)KS_CM, "\033[%i%p1%d;%p2%dH"}, -# else - {(int)KS_CM, "\033[%i%d;%dH"}, -# endif -# ifdef TERMINFO - {(int)KS_CRI, "\033[%p1%dC"}, -# else - {(int)KS_CRI, "\033[%dC"}, -# endif -# endif - -# if defined(ALL_BUILTIN_TCAPS) - /* - * These codes are valid when nansi.sys or equivalent has been installed. - * Function keys on a PC are preceded with a NUL. These are converted into - * K_NUL '\316' in os_inchar(), because we cannot handle NULs in key codes. - * CTRL-arrow is used instead of SHIFT-arrow. - */ - {(int)KS_NAME, "pcansi"}, - {(int)KS_DL, "\033[M"}, - {(int)KS_AL, "\033[L"}, - {(int)KS_CE, "\033[K"}, - {(int)KS_CL, "\033[2J"}, - {(int)KS_ME, "\033[0m"}, - {(int)KS_MR, "\033[5m"}, /* reverse: black on lightgrey */ - {(int)KS_MD, "\033[1m"}, /* bold: white text */ - {(int)KS_SE, "\033[0m"}, /* standout end */ - {(int)KS_SO, "\033[31m"}, /* standout: white on blue */ - {(int)KS_CZH, "\033[34;43m"}, /* italic mode: blue text on yellow */ - {(int)KS_CZR, "\033[0m"}, /* italic mode end */ - {(int)KS_US, "\033[36;41m"}, /* underscore mode: cyan text on red */ - {(int)KS_UE, "\033[0m"}, /* underscore mode end */ - {(int)KS_CCO, "8"}, /* allow 8 colors */ -# ifdef TERMINFO - {(int)KS_CAB, "\033[4%p1%dm"}, /* set background color */ - {(int)KS_CAF, "\033[3%p1%dm"}, /* set foreground color */ -# else - {(int)KS_CAB, "\033[4%dm"}, /* set background color */ - {(int)KS_CAF, "\033[3%dm"}, /* set foreground color */ -# endif - {(int)KS_OP, "\033[0m"}, /* reset colors */ - {(int)KS_MS, "y"}, - {(int)KS_UT, "y"}, /* guessed */ - {(int)KS_LE, "\b"}, -# ifdef TERMINFO - {(int)KS_CM, "\033[%i%p1%d;%p2%dH"}, -# else - {(int)KS_CM, "\033[%i%d;%dH"}, -# endif -# ifdef TERMINFO - {(int)KS_CRI, "\033[%p1%dC"}, -# else - {(int)KS_CRI, "\033[%dC"}, -# endif - {K_UP, "\316H"}, - {K_DOWN, "\316P"}, - {K_LEFT, "\316K"}, - {K_RIGHT, "\316M"}, - {K_S_LEFT, "\316s"}, - {K_S_RIGHT, "\316t"}, - {K_F1, "\316;"}, - {K_F2, "\316<"}, - {K_F3, "\316="}, - {K_F4, "\316>"}, - {K_F5, "\316?"}, - {K_F6, "\316@"}, - {K_F7, "\316A"}, - {K_F8, "\316B"}, - {K_F9, "\316C"}, - {K_F10, "\316D"}, - {K_F11, "\316\205"}, /* guessed */ - {K_F12, "\316\206"}, /* guessed */ - {K_S_F1, "\316T"}, - {K_S_F2, "\316U"}, - {K_S_F3, "\316V"}, - {K_S_F4, "\316W"}, - {K_S_F5, "\316X"}, - {K_S_F6, "\316Y"}, - {K_S_F7, "\316Z"}, - {K_S_F8, "\316["}, - {K_S_F9, "\316\\"}, - {K_S_F10, "\316]"}, - {K_S_F11, "\316\207"}, /* guessed */ - {K_S_F12, "\316\210"}, /* guessed */ - {K_INS, "\316R"}, - {K_DEL, "\316S"}, - {K_HOME, "\316G"}, - {K_END, "\316O"}, - {K_PAGEDOWN, "\316Q"}, - {K_PAGEUP, "\316I"}, -# endif - - -# if defined(WIN3264) || defined(ALL_BUILTIN_TCAPS) - /* - * These codes are valid for the Win32 Console . The entries that start with - * ESC | are translated into console calls in os_win32.c. The function keys - * are also translated in os_win32.c. - */ - {(int)KS_NAME, "win32"}, - {(int)KS_CE, "\033|K"}, /* clear to end of line */ - {(int)KS_AL, "\033|L"}, /* add new blank line */ -# ifdef TERMINFO - {(int)KS_CAL, "\033|%p1%dL"}, /* add number of new blank lines */ -# else - {(int)KS_CAL, "\033|%dL"}, /* add number of new blank lines */ -# endif - {(int)KS_DL, "\033|M"}, /* delete line */ -# ifdef TERMINFO - {(int)KS_CDL, "\033|%p1%dM"}, /* delete number of lines */ -# else - {(int)KS_CDL, "\033|%dM"}, /* delete number of lines */ -# endif - {(int)KS_CL, "\033|J"}, /* clear screen */ - {(int)KS_CD, "\033|j"}, /* clear to end of display */ - {(int)KS_VI, "\033|v"}, /* cursor invisible */ - {(int)KS_VE, "\033|V"}, /* cursor visible */ - - {(int)KS_ME, "\033|0m"}, /* normal */ - {(int)KS_MR, "\033|112m"}, /* reverse: black on lightgray */ - {(int)KS_MD, "\033|15m"}, /* bold: white on black */ - {(int)KS_SO, "\033|31m"}, /* standout: white on blue */ - {(int)KS_SE, "\033|0m"}, /* standout end */ - {(int)KS_CZH, "\033|225m"}, /* italic: blue text on yellow */ - {(int)KS_CZR, "\033|0m"}, /* italic end */ - {(int)KS_US, "\033|67m"}, /* underscore: cyan text on red */ - {(int)KS_UE, "\033|0m"}, /* underscore end */ - {(int)KS_CCO, "16"}, /* allow 16 colors */ -# ifdef TERMINFO - {(int)KS_CAB, "\033|%p1%db"}, /* set background color */ - {(int)KS_CAF, "\033|%p1%df"}, /* set foreground color */ -# else - {(int)KS_CAB, "\033|%db"}, /* set background color */ - {(int)KS_CAF, "\033|%df"}, /* set foreground color */ -# endif - - {(int)KS_MS, "y"}, /* save to move cur in reverse mode */ - {(int)KS_UT, "y"}, - {(int)KS_LE, "\b"}, -# ifdef TERMINFO - {(int)KS_CM, "\033|%i%p1%d;%p2%dH"}, /* cursor motion */ -# else - {(int)KS_CM, "\033|%i%d;%dH"}, /* cursor motion */ -# endif - {(int)KS_VB, "\033|B"}, /* visual bell */ - {(int)KS_TI, "\033|S"}, /* put terminal in termcap mode */ - {(int)KS_TE, "\033|E"}, /* out of termcap mode */ -# ifdef TERMINFO - {(int)KS_CS, "\033|%i%p1%d;%p2%dr"}, /* scroll region */ -# else - {(int)KS_CS, "\033|%i%d;%dr"}, /* scroll region */ -# endif - - {K_UP, "\316H"}, - {K_DOWN, "\316P"}, - {K_LEFT, "\316K"}, - {K_RIGHT, "\316M"}, - {K_S_UP, "\316\304"}, - {K_S_DOWN, "\316\317"}, - {K_S_LEFT, "\316\311"}, - {K_C_LEFT, "\316s"}, - {K_S_RIGHT, "\316\313"}, - {K_C_RIGHT, "\316t"}, - {K_S_TAB, "\316\017"}, - {K_F1, "\316;"}, - {K_F2, "\316<"}, - {K_F3, "\316="}, - {K_F4, "\316>"}, - {K_F5, "\316?"}, - {K_F6, "\316@"}, - {K_F7, "\316A"}, - {K_F8, "\316B"}, - {K_F9, "\316C"}, - {K_F10, "\316D"}, - {K_F11, "\316\205"}, - {K_F12, "\316\206"}, - {K_S_F1, "\316T"}, - {K_S_F2, "\316U"}, - {K_S_F3, "\316V"}, - {K_S_F4, "\316W"}, - {K_S_F5, "\316X"}, - {K_S_F6, "\316Y"}, - {K_S_F7, "\316Z"}, - {K_S_F8, "\316["}, - {K_S_F9, "\316\\"}, - {K_S_F10, "\316]"}, - {K_S_F11, "\316\207"}, - {K_S_F12, "\316\210"}, - {K_INS, "\316R"}, - {K_DEL, "\316S"}, - {K_HOME, "\316G"}, - {K_S_HOME, "\316\302"}, - {K_C_HOME, "\316w"}, - {K_END, "\316O"}, - {K_S_END, "\316\315"}, - {K_C_END, "\316u"}, - {K_PAGEDOWN, "\316Q"}, - {K_PAGEUP, "\316I"}, - {K_KPLUS, "\316N"}, - {K_KMINUS, "\316J"}, - {K_KMULTIPLY, "\316\067"}, - {K_K0, "\316\332"}, - {K_K1, "\316\336"}, - {K_K2, "\316\342"}, - {K_K3, "\316\346"}, - {K_K4, "\316\352"}, - {K_K5, "\316\356"}, - {K_K6, "\316\362"}, - {K_K7, "\316\366"}, - {K_K8, "\316\372"}, - {K_K9, "\316\376"}, -# endif - -# if defined(ALL_BUILTIN_TCAPS) - /* - * VT320 is working as an ANSI terminal compatible DEC terminal. - * (it covers VT1x0, VT2x0 and VT3x0 up to VT320 on VMS as well) - * Note: K_F1...K_F5 are for internal use, should not be defined. - * TODO:- rewrite ESC[ codes to CSI - * - keyboard languages (CSI ? 26 n) - */ - {(int)KS_NAME, "vt320"}, - {(int)KS_CE, "\033[K"}, - {(int)KS_AL, "\033[L"}, -# ifdef TERMINFO - {(int)KS_CAL, "\033[%p1%dL"}, -# else - {(int)KS_CAL, "\033[%dL"}, -# endif - {(int)KS_DL, "\033[M"}, -# ifdef TERMINFO - {(int)KS_CDL, "\033[%p1%dM"}, -# else - {(int)KS_CDL, "\033[%dM"}, -# endif - {(int)KS_CL, "\033[H\033[2J"}, - {(int)KS_CD, "\033[J"}, - {(int)KS_CCO, "8"}, /* allow 8 colors */ - {(int)KS_ME, "\033[0m"}, - {(int)KS_MR, "\033[7m"}, - {(int)KS_MD, "\033[1m"}, /* bold mode */ - {(int)KS_SE, "\033[22m"}, /* normal mode */ - {(int)KS_UE, "\033[24m"}, /* exit underscore mode */ - {(int)KS_US, "\033[4m"}, /* underscore mode */ - {(int)KS_CZH, "\033[34;43m"}, /* italic mode: blue text on yellow */ - {(int)KS_CZR, "\033[0m"}, /* italic mode end */ - {(int)KS_CAB, "\033[4%dm"}, /* set background color (ANSI) */ - {(int)KS_CAF, "\033[3%dm"}, /* set foreground color (ANSI) */ - {(int)KS_CSB, "\033[102;%dm"}, /* set screen background color */ - {(int)KS_CSF, "\033[101;%dm"}, /* set screen foreground color */ - {(int)KS_MS, "y"}, - {(int)KS_UT, "y"}, - {(int)KS_LE, "\b"}, -# ifdef TERMINFO - {(int)KS_CM, "\033[%i%p1%d;%p2%dH"}, -# else - {(int)KS_CM, "\033[%i%d;%dH"}, -# endif -# ifdef TERMINFO - {(int)KS_CRI, "\033[%p1%dC"}, -# else - {(int)KS_CRI, "\033[%dC"}, -# endif - {K_UP, "\033[A"}, - {K_DOWN, "\033[B"}, - {K_RIGHT, "\033[C"}, - {K_LEFT, "\033[D"}, - {K_F1, "\033[11~"}, - {K_F2, "\033[12~"}, - {K_F3, "\033[13~"}, - {K_F4, "\033[14~"}, - {K_F5, "\033[15~"}, - {K_F6, "\033[17~"}, - {K_F7, "\033[18~"}, - {K_F8, "\033[19~"}, - {K_F9, "\033[20~"}, - {K_F10, "\033[21~"}, - {K_F11, "\033[23~"}, - {K_F12, "\033[24~"}, - {K_F13, "\033[25~"}, - {K_F14, "\033[26~"}, - {K_F15, "\033[28~"}, /* Help */ - {K_F16, "\033[29~"}, /* Select */ - {K_F17, "\033[31~"}, - {K_F18, "\033[32~"}, - {K_F19, "\033[33~"}, - {K_F20, "\033[34~"}, - {K_INS, "\033[2~"}, - {K_DEL, "\033[3~"}, - {K_HOME, "\033[1~"}, - {K_END, "\033[4~"}, - {K_PAGEUP, "\033[5~"}, - {K_PAGEDOWN, "\033[6~"}, - {K_KPLUS, "\033Ok"}, /* keypad plus */ - {K_KMINUS, "\033Om"}, /* keypad minus */ - {K_KDIVIDE, "\033Oo"}, /* keypad / */ - {K_KMULTIPLY, "\033Oj"}, /* keypad * */ - {K_KENTER, "\033OM"}, /* keypad Enter */ - {K_BS, "\x7f"}, /* for some reason 0177 doesn't work */ -# endif - -# if defined(ALL_BUILTIN_TCAPS) - /* - * Ordinary vt52 - */ - {(int)KS_NAME, "vt52"}, - {(int)KS_CE, "\033K"}, - {(int)KS_CD, "\033J"}, - {(int)KS_CM, "\033Y%+ %+ "}, - {(int)KS_LE, "\b"}, - {(int)KS_AL, "\033T"}, - {(int)KS_DL, "\033U"}, - {(int)KS_CL, "\033H\033J"}, - {(int)KS_ME, "\033SO"}, - {(int)KS_MR, "\033S2"}, - {(int)KS_MS, "y"}, -# endif - -# if defined(UNIX) || defined(ALL_BUILTIN_TCAPS) || defined(SOME_BUILTIN_TCAPS) - {(int)KS_NAME, "xterm"}, - {(int)KS_CE, "\033[K"}, - {(int)KS_AL, "\033[L"}, -# ifdef TERMINFO - {(int)KS_CAL, "\033[%p1%dL"}, -# else - {(int)KS_CAL, "\033[%dL"}, -# endif - {(int)KS_DL, "\033[M"}, -# ifdef TERMINFO - {(int)KS_CDL, "\033[%p1%dM"}, -# else - {(int)KS_CDL, "\033[%dM"}, -# endif -# ifdef TERMINFO - {(int)KS_CS, "\033[%i%p1%d;%p2%dr"}, -# else - {(int)KS_CS, "\033[%i%d;%dr"}, -# endif - {(int)KS_CL, "\033[H\033[2J"}, - {(int)KS_CD, "\033[J"}, - {(int)KS_ME, "\033[m"}, - {(int)KS_MR, "\033[7m"}, - {(int)KS_MD, "\033[1m"}, - {(int)KS_UE, "\033[m"}, - {(int)KS_US, "\033[4m"}, - {(int)KS_MS, "y"}, - {(int)KS_UT, "y"}, - {(int)KS_LE, "\b"}, -# ifdef TERMINFO - {(int)KS_CM, "\033[%i%p1%d;%p2%dH"}, -# else - {(int)KS_CM, "\033[%i%d;%dH"}, -# endif - {(int)KS_SR, "\033M"}, -# ifdef TERMINFO - {(int)KS_CRI, "\033[%p1%dC"}, -# else - {(int)KS_CRI, "\033[%dC"}, -# endif - {(int)KS_KS, "\033[?1h\033="}, - {(int)KS_KE, "\033[?1l\033>"}, - {(int)KS_CIS, "\033]1;"}, - {(int)KS_CIE, "\007"}, - {(int)KS_TS, "\033]2;"}, - {(int)KS_FS, "\007"}, -# ifdef TERMINFO - {(int)KS_CWS, "\033[8;%p1%d;%p2%dt"}, - {(int)KS_CWP, "\033[3;%p1%d;%p2%dt"}, -# else - {(int)KS_CWS, "\033[8;%d;%dt"}, - {(int)KS_CWP, "\033[3;%d;%dt"}, -# endif - {(int)KS_CRV, "\033[>c"}, - {(int)KS_U7, "\033[6n"}, - - {K_UP, "\033O*A"}, - {K_DOWN, "\033O*B"}, - {K_RIGHT, "\033O*C"}, - {K_LEFT, "\033O*D"}, - /* An extra set of cursor keys for vt100 mode */ - {K_XUP, "\033[1;*A"}, - {K_XDOWN, "\033[1;*B"}, - {K_XRIGHT, "\033[1;*C"}, - {K_XLEFT, "\033[1;*D"}, - /* An extra set of function keys for vt100 mode */ - {K_XF1, "\033O*P"}, - {K_XF2, "\033O*Q"}, - {K_XF3, "\033O*R"}, - {K_XF4, "\033O*S"}, - {K_F1, "\033[11;*~"}, - {K_F2, "\033[12;*~"}, - {K_F3, "\033[13;*~"}, - {K_F4, "\033[14;*~"}, - {K_F5, "\033[15;*~"}, - {K_F6, "\033[17;*~"}, - {K_F7, "\033[18;*~"}, - {K_F8, "\033[19;*~"}, - {K_F9, "\033[20;*~"}, - {K_F10, "\033[21;*~"}, - {K_F11, "\033[23;*~"}, - {K_F12, "\033[24;*~"}, - {K_S_TAB, "\033[Z"}, - {K_HELP, "\033[28;*~"}, - {K_UNDO, "\033[26;*~"}, - {K_INS, "\033[2;*~"}, - {K_HOME, "\033[1;*H"}, - /* {K_S_HOME, "\033O2H"}, */ - /* {K_C_HOME, "\033O5H"}, */ - {K_KHOME, "\033[1;*~"}, - {K_XHOME, "\033O*H"}, /* other Home */ - {K_ZHOME, "\033[7;*~"}, /* other Home */ - {K_END, "\033[1;*F"}, - /* {K_S_END, "\033O2F"}, */ - /* {K_C_END, "\033O5F"}, */ - {K_KEND, "\033[4;*~"}, - {K_XEND, "\033O*F"}, /* other End */ - {K_ZEND, "\033[8;*~"}, - {K_PAGEUP, "\033[5;*~"}, - {K_PAGEDOWN, "\033[6;*~"}, - {K_KPLUS, "\033O*k"}, /* keypad plus */ - {K_KMINUS, "\033O*m"}, /* keypad minus */ - {K_KDIVIDE, "\033O*o"}, /* keypad / */ - {K_KMULTIPLY, "\033O*j"}, /* keypad * */ - {K_KENTER, "\033O*M"}, /* keypad Enter */ - {K_KPOINT, "\033O*n"}, /* keypad . */ - {K_KDEL, "\033[3;*~"}, /* keypad Del */ - - {BT_EXTRA_KEYS, ""}, - {TERMCAP2KEY('k', '0'), "\033[10;*~"}, /* F0 */ - {TERMCAP2KEY('F', '3'), "\033[25;*~"}, /* F13 */ - /* F14 and F15 are missing, because they send the same codes as the undo - * and help key, although they don't work on all keyboards. */ - {TERMCAP2KEY('F', '6'), "\033[29;*~"}, /* F16 */ - {TERMCAP2KEY('F', '7'), "\033[31;*~"}, /* F17 */ - {TERMCAP2KEY('F', '8'), "\033[32;*~"}, /* F18 */ - {TERMCAP2KEY('F', '9'), "\033[33;*~"}, /* F19 */ - {TERMCAP2KEY('F', 'A'), "\033[34;*~"}, /* F20 */ - - {TERMCAP2KEY('F', 'B'), "\033[42;*~"}, /* F21 */ - {TERMCAP2KEY('F', 'C'), "\033[43;*~"}, /* F22 */ - {TERMCAP2KEY('F', 'D'), "\033[44;*~"}, /* F23 */ - {TERMCAP2KEY('F', 'E'), "\033[45;*~"}, /* F24 */ - {TERMCAP2KEY('F', 'F'), "\033[46;*~"}, /* F25 */ - {TERMCAP2KEY('F', 'G'), "\033[47;*~"}, /* F26 */ - {TERMCAP2KEY('F', 'H'), "\033[48;*~"}, /* F27 */ - {TERMCAP2KEY('F', 'I'), "\033[49;*~"}, /* F28 */ - {TERMCAP2KEY('F', 'J'), "\033[50;*~"}, /* F29 */ - {TERMCAP2KEY('F', 'K'), "\033[51;*~"}, /* F30 */ - - {TERMCAP2KEY('F', 'L'), "\033[52;*~"}, /* F31 */ - {TERMCAP2KEY('F', 'M'), "\033[53;*~"}, /* F32 */ - {TERMCAP2KEY('F', 'N'), "\033[54;*~"}, /* F33 */ - {TERMCAP2KEY('F', 'O'), "\033[55;*~"}, /* F34 */ - {TERMCAP2KEY('F', 'P'), "\033[56;*~"}, /* F35 */ - {TERMCAP2KEY('F', 'Q'), "\033[57;*~"}, /* F36 */ - {TERMCAP2KEY('F', 'R'), "\033[58;*~"}, /* F37 */ -# endif - -# if defined(DEBUG) || defined(ALL_BUILTIN_TCAPS) - /* - * for debugging - */ - {(int)KS_NAME, "debug"}, - {(int)KS_CE, "[CE]"}, - {(int)KS_CD, "[CD]"}, - {(int)KS_AL, "[AL]"}, -# ifdef TERMINFO - {(int)KS_CAL, "[CAL%p1%d]"}, -# else - {(int)KS_CAL, "[CAL%d]"}, -# endif - {(int)KS_DL, "[DL]"}, -# ifdef TERMINFO - {(int)KS_CDL, "[CDL%p1%d]"}, -# else - {(int)KS_CDL, "[CDL%d]"}, -# endif -# ifdef TERMINFO - {(int)KS_CS, "[%p1%dCS%p2%d]"}, -# else - {(int)KS_CS, "[%dCS%d]"}, -# endif -# ifdef TERMINFO - {(int)KS_CSV, "[%p1%dCSV%p2%d]"}, -# else - {(int)KS_CSV, "[%dCSV%d]"}, -# endif -# ifdef TERMINFO - {(int)KS_CAB, "[CAB%p1%d]"}, - {(int)KS_CAF, "[CAF%p1%d]"}, - {(int)KS_CSB, "[CSB%p1%d]"}, - {(int)KS_CSF, "[CSF%p1%d]"}, -# else - {(int)KS_CAB, "[CAB%d]"}, - {(int)KS_CAF, "[CAF%d]"}, - {(int)KS_CSB, "[CSB%d]"}, - {(int)KS_CSF, "[CSF%d]"}, -# endif - {(int)KS_OP, "[OP]"}, - {(int)KS_LE, "[LE]"}, - {(int)KS_CL, "[CL]"}, - {(int)KS_VI, "[VI]"}, - {(int)KS_VE, "[VE]"}, - {(int)KS_VS, "[VS]"}, - {(int)KS_ME, "[ME]"}, - {(int)KS_MR, "[MR]"}, - {(int)KS_MB, "[MB]"}, - {(int)KS_MD, "[MD]"}, - {(int)KS_SE, "[SE]"}, - {(int)KS_SO, "[SO]"}, - {(int)KS_UE, "[UE]"}, - {(int)KS_US, "[US]"}, - {(int)KS_UCE, "[UCE]"}, - {(int)KS_UCS, "[UCS]"}, - {(int)KS_MS, "[MS]"}, - {(int)KS_UT, "[UT]"}, -# ifdef TERMINFO - {(int)KS_CM, "[%p1%dCM%p2%d]"}, -# else - {(int)KS_CM, "[%dCM%d]"}, -# endif - {(int)KS_SR, "[SR]"}, -# ifdef TERMINFO - {(int)KS_CRI, "[CRI%p1%d]"}, -# else - {(int)KS_CRI, "[CRI%d]"}, -# endif - {(int)KS_VB, "[VB]"}, - {(int)KS_KS, "[KS]"}, - {(int)KS_KE, "[KE]"}, - {(int)KS_TI, "[TI]"}, - {(int)KS_TE, "[TE]"}, - {(int)KS_CIS, "[CIS]"}, - {(int)KS_CIE, "[CIE]"}, - {(int)KS_TS, "[TS]"}, - {(int)KS_FS, "[FS]"}, -# ifdef TERMINFO - {(int)KS_CWS, "[%p1%dCWS%p2%d]"}, - {(int)KS_CWP, "[%p1%dCWP%p2%d]"}, -# else - {(int)KS_CWS, "[%dCWS%d]"}, - {(int)KS_CWP, "[%dCWP%d]"}, -# endif - {(int)KS_CRV, "[CRV]"}, - {(int)KS_U7, "[U7]"}, - {K_UP, "[KU]"}, - {K_DOWN, "[KD]"}, - {K_LEFT, "[KL]"}, - {K_RIGHT, "[KR]"}, - {K_XUP, "[xKU]"}, - {K_XDOWN, "[xKD]"}, - {K_XLEFT, "[xKL]"}, - {K_XRIGHT, "[xKR]"}, - {K_S_UP, "[S-KU]"}, - {K_S_DOWN, "[S-KD]"}, - {K_S_LEFT, "[S-KL]"}, - {K_C_LEFT, "[C-KL]"}, - {K_S_RIGHT, "[S-KR]"}, - {K_C_RIGHT, "[C-KR]"}, - {K_F1, "[F1]"}, - {K_XF1, "[xF1]"}, - {K_F2, "[F2]"}, - {K_XF2, "[xF2]"}, - {K_F3, "[F3]"}, - {K_XF3, "[xF3]"}, - {K_F4, "[F4]"}, - {K_XF4, "[xF4]"}, - {K_F5, "[F5]"}, - {K_F6, "[F6]"}, - {K_F7, "[F7]"}, - {K_F8, "[F8]"}, - {K_F9, "[F9]"}, - {K_F10, "[F10]"}, - {K_F11, "[F11]"}, - {K_F12, "[F12]"}, - {K_S_F1, "[S-F1]"}, - {K_S_XF1, "[S-xF1]"}, - {K_S_F2, "[S-F2]"}, - {K_S_XF2, "[S-xF2]"}, - {K_S_F3, "[S-F3]"}, - {K_S_XF3, "[S-xF3]"}, - {K_S_F4, "[S-F4]"}, - {K_S_XF4, "[S-xF4]"}, - {K_S_F5, "[S-F5]"}, - {K_S_F6, "[S-F6]"}, - {K_S_F7, "[S-F7]"}, - {K_S_F8, "[S-F8]"}, - {K_S_F9, "[S-F9]"}, - {K_S_F10, "[S-F10]"}, - {K_S_F11, "[S-F11]"}, - {K_S_F12, "[S-F12]"}, - {K_HELP, "[HELP]"}, - {K_UNDO, "[UNDO]"}, - {K_BS, "[BS]"}, - {K_INS, "[INS]"}, - {K_KINS, "[KINS]"}, - {K_DEL, "[DEL]"}, - {K_KDEL, "[KDEL]"}, - {K_HOME, "[HOME]"}, - {K_S_HOME, "[C-HOME]"}, - {K_C_HOME, "[C-HOME]"}, - {K_KHOME, "[KHOME]"}, - {K_XHOME, "[XHOME]"}, - {K_ZHOME, "[ZHOME]"}, - {K_END, "[END]"}, - {K_S_END, "[C-END]"}, - {K_C_END, "[C-END]"}, - {K_KEND, "[KEND]"}, - {K_XEND, "[XEND]"}, - {K_ZEND, "[ZEND]"}, - {K_PAGEUP, "[PAGEUP]"}, - {K_PAGEDOWN, "[PAGEDOWN]"}, - {K_KPAGEUP, "[KPAGEUP]"}, - {K_KPAGEDOWN, "[KPAGEDOWN]"}, - {K_MOUSE, "[MOUSE]"}, - {K_KPLUS, "[KPLUS]"}, - {K_KMINUS, "[KMINUS]"}, - {K_KDIVIDE, "[KDIVIDE]"}, - {K_KMULTIPLY, "[KMULTIPLY]"}, - {K_KENTER, "[KENTER]"}, - {K_KPOINT, "[KPOINT]"}, - {K_K0, "[K0]"}, - {K_K1, "[K1]"}, - {K_K2, "[K2]"}, - {K_K3, "[K3]"}, - {K_K4, "[K4]"}, - {K_K5, "[K5]"}, - {K_K6, "[K6]"}, - {K_K7, "[K7]"}, - {K_K8, "[K8]"}, - {K_K9, "[K9]"}, -# endif - -#endif /* NO_BUILTIN_TCAPS */ - - /* - * The most minimal terminal: only clear screen and cursor positioning - * Always included. - */ - {(int)KS_NAME, "dumb"}, - {(int)KS_CL, "\014"}, -#ifdef TERMINFO - {(int)KS_CM, "\033[%i%p1%d;%p2%dH"}, -#else - {(int)KS_CM, "\033[%i%d;%dH"}, -#endif - - /* - * end marker - */ {(int)KS_NAME, NULL} }; /* end of builtin_termcaps */ @@ -995,10 +161,6 @@ void term_init(void) setbuf(stdout, NULL); out_flush(); - -#ifdef MACOS_CONVERT - mac_conv_init(); -#endif } /* @@ -1009,8 +171,6 @@ void term_init(void) char_u *(term_strings[(int)KS_LAST + 1]); static bool need_gather = false; // need to fill termleader[] -static char_u termleader[256 + 1]; // for check_termcode() -static bool check_for_codes = false; // check for key code response static struct builtin_term *find_builtin_term(char_u *term) { @@ -1076,38 +236,6 @@ static void parse_builtin_tcap(char_u *term) } /* - * Set number of colors. - * Store it as a number in t_colors. - * Store it as a string in T_CCO (using nr_colors[]). - */ -static void set_color_count(int nr) -{ - char_u nr_colors[20]; /* string for number of colors */ - - t_colors = nr; - if (t_colors > 1) - sprintf((char *)nr_colors, "%d", t_colors); - else - *nr_colors = NUL; - set_string_option_direct((char_u *)"t_Co", -1, nr_colors, OPT_FREE, 0); -} - -#ifdef HAVE_TGETENT -static char *(key_names[]) = -{ - /* Do this one first, it may cause a screen redraw. */ - "Co", - "ku", "kd", "kr", "kl", - "#2", "#4", "%i", "*7", - "k1", "k2", "k3", "k4", "k5", "k6", - "k7", "k8", "k9", "k;", "F1", "F2", - "%1", "&8", "kb", "kI", "kD", "kh", - "@7", "kP", "kN", "K1", "K3", "K4", "K5", "kB", - NULL -}; -#endif - -/* * Set terminal options for terminal "term". * Return OK if terminal 'term' was found in a termcap, FAIL otherwise. * @@ -1115,11 +243,6 @@ static char *(key_names[]) = */ int set_termname(char_u *term) { -#ifdef HAVE_TGETENT - int builtin_first = p_tbi; - int try; - int termcap_cleared = FALSE; -#endif int width = 0, height = 0; char_u *error_msg = NULL; char_u *bs_p, *del_p; @@ -1128,17 +251,11 @@ int set_termname(char_u *term) if (silent_mode) return OK; - if (!STRCMP(term, "abstract_ui")) { - abstract_ui = true; - } - + term = (uint8_t *)"abstract_ui"; detected_8bit = false; // reset 8-bit detection if (term_is_builtin(term)) { term += 8; -#ifdef HAVE_TGETENT - builtin_first = 1; -#endif } /* @@ -1151,146 +268,15 @@ int set_termname(char_u *term) * 1. try external termcap * 2. try builtin termcap, if both fail default to a builtin terminal */ -#ifdef HAVE_TGETENT - for (try = builtin_first ? 0 : 1; try < 3; ++try) { - /* - * Use external termcap - */ - if (try == 1) { - char_u *p; - static char_u tstrbuf[TBUFSZ]; - int i; - char_u tbuf[TBUFSZ]; - char_u *tp; - static struct { - enum SpecialKey dest; /* index in term_strings[] */ - char *name; /* termcap name for string */ - } string_names[] = - { {KS_CE, "ce"}, {KS_AL, "al"}, {KS_CAL,"AL"}, - {KS_DL, "dl"}, {KS_CDL,"DL"}, {KS_CS, "cs"}, - {KS_CL, "cl"}, {KS_CD, "cd"}, - {KS_VI, "vi"}, {KS_VE, "ve"}, {KS_MB, "mb"}, - {KS_VS, "vs"}, {KS_ME, "me"}, {KS_MR, "mr"}, - {KS_MD, "md"}, {KS_SE, "se"}, {KS_SO, "so"}, - {KS_CZH,"ZH"}, {KS_CZR,"ZR"}, {KS_UE, "ue"}, - {KS_US, "us"}, {KS_UCE, "Ce"}, {KS_UCS, "Cs"}, - {KS_CM, "cm"}, {KS_SR, "sr"}, - {KS_CRI,"RI"}, {KS_VB, "vb"}, {KS_KS, "ks"}, - {KS_KE, "ke"}, {KS_TI, "ti"}, {KS_TE, "te"}, - {KS_BC, "bc"}, {KS_CSB,"Sb"}, {KS_CSF,"Sf"}, - {KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_LE, "le"}, - {KS_ND, "nd"}, {KS_OP, "op"}, {KS_CRV, "RV"}, - {KS_CIS, "IS"}, {KS_CIE, "IE"}, - {KS_TS, "ts"}, {KS_FS, "fs"}, - {KS_CWP, "WP"}, {KS_CWS, "WS"}, - {KS_CSI, "SI"}, {KS_CEI, "EI"}, - {KS_U7, "u7"}, - {(enum SpecialKey)0, NULL}}; - - /* - * If the external termcap does not have a matching entry, try the - * builtin ones. - */ - if ((error_msg = tgetent_error(tbuf, term)) == NULL) { - tp = tstrbuf; - if (!termcap_cleared) { - clear_termoptions(); /* clear old options */ - termcap_cleared = TRUE; - } - - /* get output strings */ - for (i = 0; string_names[i].name != NULL; ++i) { - if (term_str(string_names[i].dest) == NULL - || term_str(string_names[i].dest) == empty_option) - term_str(string_names[i].dest) = - TGETSTR(string_names[i].name, &tp); - } - - /* tgetflag() returns 1 if the flag is present, 0 if not and - * possibly -1 if the flag doesn't exist. */ - if ((T_MS == NULL || T_MS == empty_option) - && tgetflag("ms") > 0) - T_MS = (char_u *)"y"; - if ((T_XS == NULL || T_XS == empty_option) - && tgetflag("xs") > 0) - T_XS = (char_u *)"y"; - if ((T_DB == NULL || T_DB == empty_option) - && tgetflag("db") > 0) - T_DB = (char_u *)"y"; - if ((T_DA == NULL || T_DA == empty_option) - && tgetflag("da") > 0) - T_DA = (char_u *)"y"; - if ((T_UT == NULL || T_UT == empty_option) - && tgetflag("ut") > 0) - T_UT = (char_u *)"y"; - - - /* - * get key codes - */ - for (i = 0; key_names[i] != NULL; ++i) { - if (find_termcode((char_u *)key_names[i]) == NULL) { - p = TGETSTR(key_names[i], &tp); - /* if cursor-left == backspace, ignore it (televideo - * 925) */ - if (p != NULL - && (*p != Ctrl_H - || key_names[i][0] != 'k' - || key_names[i][1] != 'l')) - add_termcode((char_u *)key_names[i], p, FALSE); - } - } - - if (height == 0) - height = tgetnum("li"); - if (width == 0) - width = tgetnum("co"); - - /* - * Get number of colors (if not done already). - */ - if (term_str(KS_CCO) == NULL - || term_str(KS_CCO) == empty_option) - set_color_count(tgetnum("Co")); - - BC = (char *)TGETSTR("bc", &tp); - UP = (char *)TGETSTR("up", &tp); - p = TGETSTR("pc", &tp); - if (p) - PC = (char)*p; - } - } else /* try == 0 || try == 2 */ -#endif /* HAVE_TGETENT */ /* * Use builtin termcap */ { -#ifdef HAVE_TGETENT - /* - * If builtin termcap was already used, there is no need to search - * for the builtin termcap again, quit now. - */ - if (try == 2 && builtin_first && termcap_cleared) - break; -#endif /* * search for 'term' in builtin_termcaps[] */ struct builtin_term *termp = find_builtin_term(term); if (termp->bt_string == NULL) { /* did not find it */ -#ifdef HAVE_TGETENT - /* - * If try == 0, first try the external termcap. If that is not - * found we'll get back here with try == 2. - * If termcap_cleared is set we used the external termcap, - * don't complain about not finding the term in the builtin - * termcap. - */ - if (try == 0) /* try external one */ - continue; - if (termcap_cleared) /* found in external termcap */ - break; -#endif mch_errmsg("\r\n"); if (error_msg != NULL) { @@ -1304,11 +290,7 @@ int set_termname(char_u *term) for (termp = &(builtin_termcaps[0]); termp->bt_string != NULL; ++termp) { if (termp->bt_entry == (int)KS_NAME) { -#ifdef HAVE_TGETENT - mch_errmsg(" builtin_"); -#else mch_errmsg(" "); -#endif mch_errmsg(termp->bt_string); mch_errmsg("\r\n"); } @@ -1333,19 +315,9 @@ int set_termname(char_u *term) display_errors(); } out_flush(); -#ifdef HAVE_TGETENT - if (!termcap_cleared) { -#endif clear_termoptions(); /* clear old options */ -#ifdef HAVE_TGETENT - termcap_cleared = TRUE; - } -#endif parse_builtin_tcap(term); } -#ifdef HAVE_TGETENT -} -#endif /* * special: There is no info in the termcap about whether the cursor @@ -1358,16 +330,6 @@ int set_termname(char_u *term) else T_CCS = empty_option; -#ifdef UNIX - /* - * Any "stty" settings override the default for t_kb from the termcap. - * This is in os_unix.c, because it depends a lot on the version of unix that - * is being used. - * Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly. - */ - get_stty(); -#endif - /* * If the termcap has no entry for 'bs' and/or 'del' and the ioctl() also * didn't work, use the default CTRL-H @@ -1390,39 +352,10 @@ int set_termname(char_u *term) term_is_xterm = vim_is_xterm(term); #endif -# if defined(UNIX) - /* - * For Unix, set the 'ttymouse' option to the type of mouse to be used. - * The termcode for the mouse is added as a side effect in option.c. - */ - { - char_u *p = (char_u *)""; - if (use_xterm_like_mouse(term)) { - if (use_xterm_mouse()) - p = NULL; /* keep existing value, might be "xterm2" */ - else - p = (char_u *)"xterm"; - } - if (p != NULL) { - set_option_value((char_u *)"ttym", 0L, p, 0); - /* Reset the WAS_SET flag, 'ttymouse' can be set to "sgr" or - * "xterm2" in check_termcode(). */ - reset_option_was_set((char_u *)"ttym"); - } - if (p == NULL - ) - check_mouse_termcode(); /* set mouse termcode anyway */ - } -# else - set_mouse_termcode(KS_MOUSE, (char_u *)"\233M"); -# endif - ttest(TRUE); /* make sure we have a valid set of terminal codes */ full_screen = TRUE; /* we can use termcap codes from now on */ set_term_defaults(); /* use current values as defaults */ - LOG_TR("setting crv_status to CRV_GET"); - crv_status = CRV_GET; /* Get terminal version later */ /* * Initialize the terminal with the appropriate termcap codes. @@ -1431,7 +364,6 @@ int set_termname(char_u *term) * may redefine t_TI etc. */ if (starting != NO_SCREEN) { - starttermcap(); /* may change terminal mode */ setmouse(); /* may start using the mouse */ maketitle(); /* may display window title */ } @@ -1443,7 +375,7 @@ int set_termname(char_u *term) width = 80; height = 24; /* most terminals are 24 lines */ } - screen_resize(width, height, FALSE); /* may change Rows */ + screen_resize(width, height); // may change Rows if (starting != NO_SCREEN) { if (scroll_region) scroll_region_reset(); /* In case Rows changed */ @@ -1465,8 +397,6 @@ int set_termname(char_u *term) } } - may_req_termresponse(); - return OK; } @@ -1477,99 +407,6 @@ int set_termname(char_u *term) # define HMT_URXVT 16 # define HMT_SGR 32 -void -set_mouse_termcode ( - char_u n, /* KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE */ - char_u *s -) -{ - char_u name[2] = { n, KE_FILLER }; - - add_termcode(name, s, FALSE); -} - -# if (defined(UNIX) && defined(FEAT_MOUSE_TTY)) -void -del_mouse_termcode ( - char_u n /* KS_MOUSE, KS_NETTERM_MOUSE or KS_DEC_MOUSE */ -) -{ - char_u name[2] = { n, KE_FILLER }; - - del_termcode(name); -} -# endif - -#ifdef HAVE_TGETENT -/* - * Call tgetent() - * Return error message if it fails, NULL if it's OK. - */ -static char_u *tgetent_error(char_u *tbuf, char_u *term) -{ - int i = TGETENT(tbuf, term); - if (i < 0 /* -1 is always an error */ -# ifdef TGETENT_ZERO_ERR - || i == 0 /* sometimes zero is also an error */ -# endif - ) { - /* On FreeBSD tputs() gets a SEGV after a tgetent() which fails. Call - * tgetent() with the always existing "dumb" entry to avoid a crash or - * hang. */ - (void)TGETENT(tbuf, "dumb"); - - if (i < 0) -# ifdef TGETENT_ZERO_ERR - return (char_u *)_("E557: Cannot open termcap file"); - if (i == 0) -# endif -#ifdef TERMINFO - return (char_u *)_("E558: Terminal entry not found in terminfo"); -#else - return (char_u *)_("E559: Terminal entry not found in termcap"); -#endif - } - return NULL; -} - -/* - * Some versions of tgetstr() have been reported to return -1 instead of NULL. - * Fix that here. - */ -static char_u *vim_tgetstr(char *s, char_u **pp) -{ - char *p = tgetstr(s, (char **)pp); - if (p == (char *)-1) - p = NULL; - return (char_u *)p; -} -#endif /* HAVE_TGETENT */ - -#if defined(HAVE_TGETENT) && (defined(UNIX) || defined(MACOS_X)) -/* - * Get Columns and Rows from the termcap. Used after a window signal if the - * ioctl() fails. It doesn't make sense to call tgetent each time if the "co" - * and "li" entries never change. But on some systems this works. - * Errors while getting the entries are ignored. - */ -void -getlinecol ( - long *cp, /* pointer to columns */ - long *rp /* pointer to rows */ -) -{ - char_u tbuf[TBUFSZ]; - - if (T_NAME != NULL && *T_NAME != NUL && - tgetent_error(tbuf, T_NAME) == NULL) { - if (*cp == 0) - *cp = tgetnum("co"); - if (*rp == 0) - *rp = tgetnum("li"); - } -} -#endif /* defined(HAVE_TGETENT) && defined(UNIX) */ - /* * Get a string entry from the termcap and add it to the list of termcodes. * Used for <t_xx> special keys. @@ -1582,15 +419,6 @@ int add_termcap_entry(char_u *name, int force) char_u *term; int key; struct builtin_term *termp; -#ifdef HAVE_TGETENT - char_u *string; - int i; - int builtin_first; - char_u tbuf[TBUFSZ]; - char_u tstrbuf[TBUFSZ]; - char_u *tp = tstrbuf; - char_u *error_msg = NULL; -#endif /* * If the GUI is running or will start in a moment, we only support the keys @@ -1606,25 +434,8 @@ int add_termcap_entry(char_u *name, int force) if (term_is_builtin(term)) { /* name starts with "builtin_" */ term += 8; -#ifdef HAVE_TGETENT - builtin_first = TRUE; -#endif } -#ifdef HAVE_TGETENT - else - builtin_first = p_tbi; -#endif -#ifdef HAVE_TGETENT - /* - * We can get the entry from the builtin termcap and from the external one. - * If 'ttybuiltin' is on or the terminal name starts with "builtin_", try - * builtin termcap first. - * If 'ttybuiltin' is off, try external termcap first. - */ - for (i = 0; i < 2; ++i) { - if (!builtin_first == i) -#endif /* * Search in builtin termcap */ @@ -1642,29 +453,8 @@ int add_termcap_entry(char_u *name, int force) } } } -#ifdef HAVE_TGETENT - else { - /* - * Search in external termcap - */ - error_msg = tgetent_error(tbuf, term); - if (error_msg == NULL) { - string = TGETSTR((char *)name, &tp); - if (string != NULL && *string != NUL) { - add_termcode(name, string, FALSE); - return OK; - } - } - } -} -#endif if (sourcing_name == NULL) { -#ifdef HAVE_TGETENT - if (error_msg != NULL) - EMSG(error_msg); - else -#endif EMSG2(_("E436: No \"%s\" entry in termcap"), name); } return FAIL; @@ -1705,8 +495,6 @@ static char_u term_7to8bit(char_u *p) } -#if !defined(HAVE_TGETENT) - char_u *tltoa(unsigned long i) { static char_u buf[16]; @@ -1719,54 +507,6 @@ char_u *tltoa(unsigned long i) } while (i > 0 && p > buf); return p; } -#endif - -#ifndef HAVE_TGETENT - -/* - * minimal tgoto() implementation. - * no padding and we only parse for %i %d and %+char - */ - -static char *tgoto(char *cm, int x, int y) -{ - static char buf[30]; - - if (!cm) - return "OOPS"; - char *e = buf + 29; - for (char *s = buf; s < e && *cm; cm++) { - if (*cm != '%') { - *s++ = *cm; - continue; - } - switch (*++cm) { - case 'd': - char *p = (char *)tltoa((unsigned long)y); - y = x; - while (*p) - *s++ = *p++; - break; - case 'i': - x++; - y++; - break; - case '+': - *s++ = (char)(*++cm + y); - y = x; - break; - case '%': - *s++ = *cm; - break; - default: - return "OOPS"; - } - } - *s = '\0'; - return buf; -} - -#endif /* HAVE_TGETENT */ /* * Set the terminal name and initialize the terminal options. @@ -1795,27 +535,13 @@ void termcapinit(char_u *name) set_termname(T_NAME != NULL ? T_NAME : term); } -/// Write s[len] to the screen. -void term_write(char_u *s, size_t len) -{ - (void) fwrite(s, len, 1, stdout); - -#ifdef UNIX - if (p_wd) { // Unix is too fast, slow down a bit more - assert(p_wd >= 0 - && (sizeof(long) <= sizeof(uint64_t) || p_wd <= UINT64_MAX)); - os_microdelay((uint64_t)p_wd); - } -#endif -} - /* * the number of calls to ui_write is reduced by using the buffer "out_buf" */ # define OUT_SIZE 2047 // Add one to allow term_write() in os_win32.c to append a NUL static char_u out_buf[OUT_SIZE + 1]; -static int out_pos = 0; /* number of chars in out_buf */ +static size_t out_pos = 0; /* number of chars in out_buf */ // Clear the output buffer void out_buf_clear(void) @@ -1828,7 +554,7 @@ void out_buf_clear(void) */ void out_flush(void) { - int len = out_pos; + size_t len = out_pos; out_pos = 0; ui_write(out_buf, len); } @@ -1913,12 +639,8 @@ void out_str(char_u *s) /* avoid terminal strings being split up */ if (out_pos > OUT_SIZE - 20) out_flush(); -#ifdef HAVE_TGETENT - tputs((char *)s, 1, TPUTSFUNCAST out_char_nf); -#else while (*s) out_char_nf(*s++); -#endif /* For testing we write one string at a time. */ if (p_wd) @@ -1931,102 +653,29 @@ void out_str(char_u *s) */ void term_windgoto(int row, int col) { - OUT_STR(tgoto((char *)T_CM, col, row)); + char buf[32]; + snprintf(buf, sizeof(buf), "\033|%d;%dM", row, col); + OUT_STR(buf); } void term_cursor_right(int i) { - OUT_STR(tgoto((char *)T_CRI, 0, i)); + abort(); } void term_append_lines(int line_count) { - OUT_STR(tgoto((char *)T_CAL, 0, line_count)); + char buf[32]; + snprintf(buf, sizeof(buf), "\033|%dI", line_count); + OUT_STR(buf); } void term_delete_lines(int line_count) { - OUT_STR(tgoto((char *)T_CDL, 0, line_count)); -} - -#if defined(HAVE_TGETENT) -void term_set_winpos(int x, int y) -{ - /* Can't handle a negative value here */ - if (x < 0) - x = 0; - if (y < 0) - y = 0; - OUT_STR(tgoto((char *)T_CWP, y, x)); -} - -void term_set_winsize(int width, int height) -{ - OUT_STR(tgoto((char *)T_CWS, height, width)); + char buf[32]; + snprintf(buf, sizeof(buf), "\033|%dD", line_count); + OUT_STR(buf); } -#endif - -void term_fg_color(int n) -{ - /* Use "AF" termcap entry if present, "Sf" entry otherwise */ - if (*T_CAF) - term_color(T_CAF, n); - else if (*T_CSF) - term_color(T_CSF, n); -} - -void term_bg_color(int n) -{ - /* Use "AB" termcap entry if present, "Sb" entry otherwise */ - if (*T_CAB) - term_color(T_CAB, n); - else if (*T_CSB) - term_color(T_CSB, n); -} - -static void term_color(char_u *s, int n) -{ - char buf[20]; - int i = 2; /* index in s[] just after <Esc>[ or CSI */ - - /* Special handling of 16 colors, because termcap can't handle it */ - /* Also accept "\e[3%dm" for TERMINFO, it is sometimes used */ - /* Also accept CSI instead of <Esc>[ */ - if (n >= 8 && t_colors >= 16 - && ((s[0] == ESC && s[1] == '[') || (s[0] == CSI && (i = 1) == 1)) - && s[i] != NUL - && (STRCMP(s + i + 1, "%p1%dm") == 0 - || STRCMP(s + i + 1, "%dm") == 0) - && (s[i] == '3' || s[i] == '4')) { - const char *fmt = -#ifdef TERMINFO - "%s%s%%p1%%dm"; -#else - "%s%s%%dm"; -#endif - sprintf(buf, - fmt, - i == 2 ? "\033[" : "\233", - s[i] == '3' ? (n >= 16 ? "38;5;" : "9") - : (n >= 16 ? "48;5;" : "10")); - OUT_STR(tgoto(buf, 0, n >= 16 ? n : n - 8)); - } else - OUT_STR(tgoto((char *)s, 0, n)); -} - -#if defined(UNIX) || defined(MACOS_X) -/* - * Generic function to set window title, using t_ts and t_fs. - */ -void term_settitle(char_u *title) -{ - /* t_ts takes one argument: column in status line */ - OUT_STR(tgoto((char *)T_TS, 0, 0)); /* set title start */ - out_str_nf(title); - out_str(T_FS); /* set title end */ - out_flush(); -} -#endif /* * Make sure we have a valid set or terminal options. @@ -2118,43 +767,6 @@ void ttest(int pairs) t_colors = atoi((char *)T_CCO); } -#if defined(FEAT_GUI) \ - || (defined(FEAT_MOUSE) && (!defined(UNIX) || defined(FEAT_MOUSE_XTERM) \ - || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE))) -/* - * Read the next num_bytes bytes from buf, and store them in bytes. Assume - * that buf has been through inchar(). Returns the actual number of bytes used - * from buf (between num_bytes and num_bytes*2), or -1 if not enough bytes were - * available. - */ -static int get_bytes_from_buf(char_u *buf, char_u *bytes, int num_bytes) -{ - int len = 0; - char_u c; - - for (int i = 0; i < num_bytes; i++) { - if ((c = buf[len++]) == NUL) - return -1; - if (c == K_SPECIAL) { - if (buf[len] == NUL || buf[len + 1] == NUL) /* cannot happen? */ - return -1; - if (buf[len++] == (int)KS_ZERO) - c = NUL; - /* else it should be KS_SPECIAL; when followed by KE_FILLER c is - * K_SPECIAL, or followed by KE_CSI and c must be CSI. */ - if (buf[len++] == (int)KE_CSI) - c = CSI; - } else if (c == CSI && buf[len] == KS_EXTRA - && buf[len + 1] == (int)KE_CSI) - /* CSI is stored as CSI KS_SPECIAL KE_CSI to avoid confusion with - * the start of a special key, see add_to_input_buf_csi(). */ - len += 2; - bytes[i] = c; - } - return len; -} -#endif - /* * Check if the new shell size is valid, correct it if it's too small or way * too big. @@ -2206,11 +818,7 @@ void win_new_shellsize(void) */ void shell_resized(void) { - if (abstract_ui) { - ui_refresh(); - } else { - screen_resize(0, 0, FALSE); - } + ui_refresh(); } /* @@ -2223,7 +831,6 @@ void shell_resized_check(void) long old_Columns = Columns; if (!exiting) { - (void)ui_get_shellsize(); check_shellsize(); if (old_Rows != Rows || old_Columns != Columns) shell_resized(); @@ -2231,232 +838,6 @@ void shell_resized_check(void) } /* - * Set the terminal to TMODE_RAW (for Normal mode) or TMODE_COOK (for external - * commands and Ex mode). - */ -void settmode(int tmode) -{ - if (abstract_ui) { - return; - } - - if (full_screen) { - /* - * When returning after calling a shell we want to really set the - * terminal to raw mode, even though we think it already is, because - * the shell program may have reset the terminal mode. - * When we think the terminal is normal, don't try to set it to - * normal again, because that causes problems (logout!) on some - * machines. - */ - if (tmode != TMODE_COOK || cur_tmode != TMODE_COOK) { - { - /* May need to check for T_CRV response and termcodes, it - * doesn't work in Cooked mode, an external program may get - * them. */ - if (tmode != TMODE_RAW && (crv_status == CRV_SENT - || u7_status == U7_SENT)) - (void)vpeekc_nomap(); - check_for_codes_from_term(); - } - if (tmode != TMODE_RAW) - mch_setmouse(FALSE); /* switch mouse off */ - out_flush(); - mch_settmode(tmode); /* machine specific function */ - cur_tmode = tmode; - if (tmode == TMODE_RAW) - setmouse(); /* may switch mouse on */ - out_flush(); - } - may_req_termresponse(); - } -} - -void starttermcap(void) -{ - if (full_screen && !termcap_active) { - out_str(T_TI); /* start termcap mode */ - out_str(T_KS); /* start "keypad transmit" mode */ - out_flush(); - termcap_active = TRUE; - screen_start(); /* don't know where cursor is now */ - if (!abstract_ui) { - may_req_termresponse(); - /* Immediately check for a response. If t_Co changes, we don't - * want to redraw with wrong colors first. */ - if (crv_status == CRV_SENT) { - check_for_codes_from_term(); - } - } - } -} - -void stoptermcap(void) -{ - screen_stop_highlight(); - reset_cterm_colors(); - if (termcap_active) { - if (!abstract_ui) { - /* May need to discard T_CRV or T_U7 response. */ - if (crv_status == CRV_SENT || u7_status == U7_SENT) { -# ifdef UNIX - /* Give the terminal a chance to respond. */ - os_delay(100L, false); -# endif -# ifdef TCIFLUSH - /* Discard data received but not read. */ - if (exiting) - tcflush(fileno(stdin), TCIFLUSH); -# endif - } - /* Check for termcodes first, otherwise an external program may - * get them. */ - check_for_codes_from_term(); - } - out_str(T_KE); /* stop "keypad transmit" mode */ - out_flush(); - termcap_active = FALSE; - cursor_on(); /* just in case it is still off */ - out_str(T_TE); /* stop termcap mode */ - screen_start(); /* don't know where cursor is now */ - out_flush(); - } -} - -#if defined(UNIX) -/// Returns true when the xterm version was requested or anything else that -/// would send an ESC sequence back to Vim. -/// -/// If not sent yet, prevent it from being sent soon. -/// Used to check whether it is OK to enable checking for DEC mouse codes, -/// which conflict with may xterm ESC sequences. -bool did_request_esc_sequence(void) -{ - if (crv_status == CRV_GET) { - crv_status = 0; - } - if (u7_status == U7_GET) { - u7_status = 0; - } - return crv_status == CRV_SENT || u7_status == U7_SENT - || xt_index_out > xt_index_in; -} - -/// If requesting the version was disabled in did_request_esc_sequence(), -/// enable it again. -void resume_get_esc_sequence(void) -{ - if (crv_status == 0) { - crv_status = CRV_GET; - } - if (u7_status == 0) { - u7_status = U7_GET; - } -} -#endif - -/* - * Request version string (for xterm) when needed. - * Only do this after switching to raw mode, otherwise the result will be - * echoed. - * Only do this after startup has finished, to avoid that the response comes - * while executing "-c !cmd" or even after "-c quit". - * Only do this after termcap mode has been started, otherwise the codes for - * the cursor keys may be wrong. - * Only do this when 'esckeys' is on, otherwise the response causes trouble in - * Insert mode. - * On Unix only do it when both output and input are a tty (avoid writing - * request to terminal while reading from a file). - * Do not do this when a mouse is being detected that starts with the same ESC - * sequence as the termresponse. - * The result is caught in check_termcode(). - */ -void may_req_termresponse(void) -{ - if (crv_status == CRV_GET - && cur_tmode == TMODE_RAW - && starting == 0 - && termcap_active - && p_ek -# ifdef UNIX - && isatty(1) - && isatty(read_cmd_fd) - && !xterm_conflict_mouse -# endif - && *T_CRV != NUL) { - LOG_TR("Sending CRV"); - out_str(T_CRV); - crv_status = CRV_SENT; - /* check for the characters now, otherwise they might be eaten by - * get_keystroke() */ - out_flush(); - (void)vpeekc_nomap(); - } -} - -/* - * Check how the terminal treats ambiguous character width (UAX #11). - * First, we move the cursor to (1, 0) and print a test ambiguous character - * \u25bd (WHITE DOWN-POINTING TRIANGLE) and query current cursor position. - * If the terminal treats \u25bd as single width, the position is (1, 1), - * or if it is treated as double width, that will be (1, 2). - * This function has the side effect that changes cursor position, so - * it must be called immediately after entering termcap mode. - */ -void may_req_ambiguous_char_width(void) -{ - if (u7_status == U7_GET - && cur_tmode == TMODE_RAW - && termcap_active - && p_ek -# ifdef UNIX - && isatty(1) - && isatty(read_cmd_fd) -# endif - && *T_U7 != NUL - && !option_was_set((char_u *)"ambiwidth")) { - char_u buf[16]; - - LOG_TR("Sending U7 request"); - /* Do this in the second row. In the first row the returned sequence - * may be CSI 1;2R, which is the same as <S-F3>. */ - term_windgoto(1, 0); - buf[mb_char2bytes(0x25bd, buf)] = 0; - out_str(buf); - out_str(T_U7); - u7_status = U7_SENT; - out_flush(); - term_windgoto(1, 0); - out_str((char_u *)" "); - term_windgoto(0, 0); - /* check for the characters now, otherwise they might be eaten by - * get_keystroke() */ - out_flush(); - (void)vpeekc_nomap(); - } -} - -# ifdef DEBUG_TERMRESPONSE -static void log_tr(char *msg) { - static FILE *fd_tr = NULL; - static proftime_T start; - - if (fd_tr == NULL) { - fd_tr = fopen("termresponse.log", "w"); - profile_start(&start); - } - proftime_T now = start; - profile_end(&now); - fprintf(fd_tr, "%s: %s %s\n", - profile_msg(&now), - must_redraw == NOT_VALID ? "NV" - : must_redraw == CLEAR ? "CL" : " ", - msg); -} - -# endif - -/* * Return TRUE when saving and restoring the screen. */ int swapping_screen(void) @@ -2478,22 +859,12 @@ void scroll_start(void) } } -static int cursor_is_off = FALSE; - /* * Enable the cursor. */ void cursor_on(void) { - if (abstract_ui) { - ui_cursor_on(); - return; - } - - if (cursor_is_off) { - out_str(T_VE); - cursor_is_off = FALSE; - } + ui_cursor_on(); } /* @@ -2501,37 +872,7 @@ void cursor_on(void) */ void cursor_off(void) { - if (abstract_ui) { - ui_cursor_off(); - return; - } - - if (full_screen) { - if (!cursor_is_off) - out_str(T_VI); /* disable cursor */ - cursor_is_off = TRUE; - } -} - -/* - * Set cursor shape to match Insert mode. - */ -void term_cursor_shape(void) -{ - static int showing_insert_mode = MAYBE; - - if (!full_screen || *T_CSI == NUL || *T_CEI == NUL) - return; - - if (State & INSERT) { - if (showing_insert_mode != TRUE) - out_str(T_CSI); /* Insert mode cursor */ - showing_insert_mode = TRUE; - } else { - if (showing_insert_mode != FALSE) - out_str(T_CEI); /* non-Insert mode cursor */ - showing_insert_mode = FALSE; - } + ui_cursor_off(); } /* @@ -2542,11 +883,18 @@ void term_cursor_shape(void) */ void scroll_region_set(win_T *wp, int off) { - OUT_STR(tgoto((char *)T_CS, wp->w_winrow + wp->w_height - 1, - wp->w_winrow + off)); - if (*T_CSV != NUL && wp->w_width != Columns) - OUT_STR(tgoto((char *)T_CSV, wp->w_wincol + wp->w_width - 1, - wp->w_wincol)); + char buf[32]; + + snprintf(buf, sizeof(buf), "\033|%d;%dR", wp->w_winrow + wp->w_height - 1, + wp->w_winrow + off); + OUT_STR(buf); + + if (wp->w_width != Columns) { + snprintf(buf, sizeof(buf), "\033|%d;%dV", wp->w_wincol + wp->w_width - 1, + wp->w_wincol); + OUT_STR(buf); + } + screen_start(); /* don't know where cursor is now */ } @@ -2555,9 +903,13 @@ void scroll_region_set(win_T *wp, int off) */ void scroll_region_reset(void) { - OUT_STR(tgoto((char *)T_CS, (int)Rows - 1, 0)); - if (*T_CSV != NUL) - OUT_STR(tgoto((char *)T_CSV, (int)Columns - 1, 0)); + char buf[32]; + + snprintf(buf, sizeof(buf), "\033|%d;%dR", (int)Rows - 1, 0); + OUT_STR(buf); + snprintf(buf, sizeof(buf), "\033|%d;%dV", (int)Columns - 1, 0); + OUT_STR(buf); + screen_start(); /* don't know where cursor is now */ } @@ -2584,12 +936,6 @@ void clear_termcodes(void) termcodes = NULL; tc_max_len = 0; -#ifdef HAVE_TGETENT - BC = (char *)empty_option; - UP = (char *)empty_option; - PC = NUL; /* set pad character to NUL */ - ospeed = 0; -#endif need_gather = true; // need to fill termleader[] } @@ -2748,27 +1094,6 @@ static void del_termcode_idx(size_t idx) termcodes[i] = termcodes[i + 1]; } -/* - * Called when detected that the terminal sends 8-bit codes. - * Convert all 7-bit codes to their 8-bit equivalent. - */ -static void switch_to_8bit(void) -{ - /* Only need to do something when not already using 8-bit codes. */ - if (!term_is_8bit(T_NAME)) { - for (size_t i = 0; i < tc_len; ++i) { - char_u c = term_7to8bit(termcodes[i].code); - if (c != 0) { - STRMOVE(termcodes[i].code + 1, termcodes[i].code + 2); - termcodes[i].code[0] = c; - } - } - need_gather = true; // need to fill termleader[] - } - detected_8bit = true; - LOG_TR("Switching to 8 bit"); -} - static linenr_T orig_topline = 0; static int orig_topfill = 0; @@ -2788,847 +1113,6 @@ void set_mouse_topline(win_T *wp) } /* - * Check if typebuf.tb_buf[] contains a terminal key code. - * Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off - * + max_offset]. - * Return 0 for no match, -1 for partial match, > 0 for full match. - * Return KEYLEN_REMOVED when a key code was deleted. - * With a match, the match is removed, the replacement code is inserted in - * typebuf.tb_buf[] and the number of characters in typebuf.tb_buf[] is - * returned. - * When "buf" is not NULL, buf[bufsize] is used instead of typebuf.tb_buf[]. - * "buflen" is then the length of the string in buf[] and is updated for - * inserts and deletes. - */ -int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen) -{ - if (abstract_ui) { - // codes are parsed by input.c/input_enqueue - return 0; - } - - char_u *tp; - char_u *p; - int slen = 0; /* init for GCC */ - int modslen; - int len; - int retval = 0; - int offset; - char_u key_name[2]; - int modifiers; - int key; - int new_slen; - int extra; - char_u string[MAX_KEY_CODE_LEN + 1]; - int i, j; -# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \ - || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) - char_u bytes[6]; - int num_bytes; -# endif - int mouse_code = 0; /* init for GCC */ - int is_click, is_drag; - int wheel_code = 0; - int current_button; - static int held_button = MOUSE_RELEASE; - static int orig_num_clicks = 1; - static int orig_mouse_code = 0x0; - int cpo_koffset; - - cpo_koffset = (vim_strchr(p_cpo, CPO_KOFFSET) != NULL); - - /* - * Speed up the checks for terminal codes by gathering all first bytes - * used in termleader[]. Often this is just a single <Esc>. - */ - if (need_gather) - gather_termleader(); - - /* - * Check at several positions in typebuf.tb_buf[], to catch something like - * "x<Up>" that can be mapped. Stop at max_offset, because characters - * after that cannot be used for mapping, and with @r commands - * typebuf.tb_buf[] can become very long. - * This is used often, KEEP IT FAST! - */ - for (offset = 0; offset < max_offset; ++offset) { - if (buf == NULL) { - if (offset >= typebuf.tb_len) - break; - tp = typebuf.tb_buf + typebuf.tb_off + offset; - len = typebuf.tb_len - offset; /* length of the input */ - } else { - if (offset >= *buflen) - break; - tp = buf + offset; - len = *buflen - offset; - } - - /* - * Don't check characters after K_SPECIAL, those are already - * translated terminal chars (avoid translating ~@^Hx). - */ - if (*tp == K_SPECIAL) { - offset += 2; /* there are always 2 extra characters */ - continue; - } - - /* - * Skip this position if the character does not appear as the first - * character in term_strings. This speeds up a lot, since most - * termcodes start with the same character (ESC or CSI). - */ - i = *tp; - for (p = termleader; *p && *p != i; ++p) - ; - if (*p == NUL) - continue; - - /* - * Skip this position if p_ek is not set and tp[0] is an ESC and we - * are in Insert mode. - */ - if (*tp == ESC && !p_ek && (State & INSERT)) - continue; - - key_name[0] = NUL; /* no key name found yet */ - key_name[1] = NUL; /* no key name found yet */ - modifiers = 0; /* no modifiers yet */ - - size_t idx; - { - for (idx = 0; idx < tc_len; ++idx) { - /* - * Ignore the entry if we are not at the start of - * typebuf.tb_buf[] - * and there are not enough characters to make a match. - * But only when the 'K' flag is in 'cpoptions'. - */ - slen = termcodes[idx].len; - if (cpo_koffset && offset && len < slen) - continue; - if (STRNCMP(termcodes[idx].code, tp, - (size_t)(slen > len ? len : slen)) == 0) { - if (len < slen) /* got a partial sequence */ - return -1; /* need to get more chars */ - - /* - * When found a keypad key, check if there is another key - * that matches and use that one. This makes <Home> to be - * found instead of <kHome> when they produce the same - * key code. - */ - if (termcodes[idx].name[0] == 'K' - && VIM_ISDIGIT(termcodes[idx].name[1])) { - for (size_t j = idx + 1; j < tc_len; ++j) - if (termcodes[j].len == slen - && STRNCMP(termcodes[idx].code, - termcodes[j].code, slen) == 0) { - idx = j; - break; - } - } - - key_name[0] = termcodes[idx].name[0]; - key_name[1] = termcodes[idx].name[1]; - break; - } - - /* - * Check for code with modifier, like xterm uses: - * <Esc>[123;*X (modslen == slen - 3) - * Also <Esc>O*X and <M-O>*X (modslen == slen - 2). - * When there is a modifier the * matches a number. - * When there is no modifier the ;* or * is omitted. - */ - if (termcodes[idx].modlen > 0) { - modslen = termcodes[idx].modlen; - if (cpo_koffset && offset && len < modslen) - continue; - if (STRNCMP(termcodes[idx].code, tp, - (size_t)(modslen > len ? len : modslen)) == 0) { - int n; - - if (len <= modslen) /* got a partial sequence */ - return -1; /* need to get more chars */ - - if (tp[modslen] == termcodes[idx].code[slen - 1]) - slen = modslen + 1; /* no modifiers */ - else if (tp[modslen] != ';' && modslen == slen - 3) - continue; /* no match */ - else { - /* Skip over the digits, the final char must - * follow. */ - for (j = slen - 2; j < len && isdigit(tp[j]); ++j) - ; - ++j; - if (len < j) /* got a partial sequence */ - return -1; /* need to get more chars */ - if (tp[j - 1] != termcodes[idx].code[slen - 1]) - continue; /* no match */ - - /* Match! Convert modifier bits. */ - n = atoi((char *)tp + slen - 2) - 1; - if (n & 1) - modifiers |= MOD_MASK_SHIFT; - if (n & 2) - modifiers |= MOD_MASK_ALT; - if (n & 4) - modifiers |= MOD_MASK_CTRL; - if (n & 8) - modifiers |= MOD_MASK_META; - - slen = j; - } - key_name[0] = termcodes[idx].name[0]; - key_name[1] = termcodes[idx].name[1]; - break; - } - } - } - } - - if (key_name[0] == NUL - /* URXVT mouse uses <ESC>[#;#;#M, but we are matching <ESC>[ */ - || key_name[0] == KS_URXVT_MOUSE - || u7_status == U7_SENT - ) { - /* Check for some responses from terminal start with "<Esc>[" or - * CSI. - * - * - xterm version string: <Esc>[>{x};{vers};{y}c - * Also eat other possible responses to t_RV, rxvt returns - * "<Esc>[?1;2c". Also accept CSI instead of <Esc>[. - * mrxvt has been reported to have "+" in the version. Assume - * the escape sequence ends with a letter or one of "{|}~". - * - * - cursor position report: <Esc>[{row};{col}R - * The final byte is 'R'. now it is only used for checking for - * ambiguous-width character state. - */ - p = tp[0] == CSI ? tp + 1 : tp + 2; - if ((*T_CRV != NUL || *T_U7 != NUL) - && ((tp[0] == ESC && tp[1] == '[' && len >= 3) - || (tp[0] == CSI && len >= 2)) - && (VIM_ISDIGIT(*p) || *p == '>' || *p == '?')) { - int col; - int row_char = 0; - j = 0; - extra = 0; - for (i = 2 + (tp[0] != CSI); i < len - && !(tp[i] >= '{' && tp[i] <= '~') - && !ASCII_ISALPHA(tp[i]); ++i) - if (tp[i] == ';' && ++j == 1) { - extra = i + 1; - row_char = tp[i - 1]; - } - if (i == len) { - LOG_TR("Not enough characters for CRV"); - return -1; - } - if (extra > 0) { - col = atoi((char *)tp + extra); - } else { - col = 0; - } - - /* Eat it when it has 2 arguments and ends in 'R'. Also when - * u7_status is not "sent", it may be from a previous Vim that - * just exited. But not for <S-F3>, it sends something - * similar, check for row and column to make sense. */ - if (j == 1 && tp[i] == 'R' && row_char == '2' && col >= 2) { - char *aw = NULL; - - LOG_TR("Received U7 status"); - u7_status = U7_GOT; - did_cursorhold = TRUE; - if (col == 2) { - aw = "single"; - } else if (col == 3) { - aw = "double"; - } - - if (aw != NULL && STRCMP(aw, p_ambw) != 0) { - /* Setting the option causes a screen redraw. Do that - * right away if possible, keeping any messages. */ - set_option_value((char_u *)"ambw", 0L, (char_u *)aw, 0); -#ifdef DEBUG_TERMRESPONSE - { - char buf[100]; - int r = redraw_asap(CLEAR); - - sprintf(buf, "set 'ambiwidth', redraw_asap(): %d", - r); - log_tr(buf); - } -#else - redraw_asap(CLEAR); -#endif - } - key_name[0] = (int)KS_EXTRA; - key_name[1] = (int)KE_IGNORE; - slen = i + 1; - } else - /* eat it when at least one digit and ending in 'c' */ - if (*T_CRV != NUL && i > 2 + (tp[0] != CSI) && tp[i] == 'c') { - LOG_TR("Received CRV"); - crv_status = CRV_GOT; - did_cursorhold = TRUE; - - /* If this code starts with CSI, you can bet that the - * terminal uses 8-bit codes. */ - if (tp[0] == CSI) - switch_to_8bit(); - - /* rxvt sends its version number: "20703" is 2.7.3. - * Ignore it for when the user has set 'term' to xterm, - * even though it's an rxvt. */ - if (extra > 0) - extra = atoi((char *)tp + extra); - if (extra > 20000) - extra = 0; - - if (tp[1 + (tp[0] != CSI)] == '>' && j == 2) { - /* Only set 'ttymouse' automatically if it was not set - * by the user already. */ - if (!option_was_set((char_u *)"ttym")) { -# ifdef TTYM_SGR - if (extra >= 277) - set_option_value((char_u *)"ttym", 0L, - (char_u *)"sgr", 0); - else -# endif - /* if xterm version >= 95 use mouse dragging */ - if (extra >= 95) - set_option_value((char_u *)"ttym", 0L, - (char_u *)"xterm2", 0); - } - - /* if xterm version >= 141 try to get termcap codes */ - if (extra >= 141) { - LOG_TR("Enable checking for XT codes"); - check_for_codes = TRUE; - need_gather = true; - req_codes_from_term(); - } - } - set_vim_var_string(VV_TERMRESPONSE, tp, i + 1); - apply_autocmds(EVENT_TERMRESPONSE, - NULL, NULL, FALSE, curbuf); - key_name[0] = (int)KS_EXTRA; - key_name[1] = (int)KE_IGNORE; - slen = i + 1; - } - } - /* Check for '<Esc>P1+r<hex bytes><Esc>\'. A "0" instead of the - * "1" means an invalid request. */ - else if (check_for_codes - && ((tp[0] == ESC && tp[1] == 'P' && len >= 2) - || tp[0] == DCS)) { - j = 1 + (tp[0] != DCS); - for (i = j; i < len; ++i) - if ((tp[i] == ESC && tp[i + 1] == '\\' && i + 1 < len) - || tp[i] == STERM) { - if (i - j >= 3 && tp[j + 1] == '+' && tp[j + 2] == 'r') - got_code_from_term(tp + j, i); - key_name[0] = (int)KS_EXTRA; - key_name[1] = (int)KE_IGNORE; - slen = i + 1 + (tp[i] == ESC); - break; - } - - if (i == len) { - LOG_TR("not enough characters for XT"); - return -1; /* not enough characters */ - } - } - } - - if (key_name[0] == NUL) - continue; /* No match at this position, try next one */ - - /* We only get here when we have a complete termcode match */ - - /* - * If it is a mouse click, get the coordinates. - */ - if (key_name[0] == KS_MOUSE - || key_name[0] == KS_NETTERM_MOUSE - || key_name[0] == KS_DEC_MOUSE - || key_name[0] == KS_URXVT_MOUSE - || key_name[0] == KS_SGR_MOUSE - ) { - is_click = is_drag = FALSE; - -# if !defined(UNIX) || defined(FEAT_MOUSE_XTERM) || defined(FEAT_GUI) \ - || defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) - if (key_name[0] == (int)KS_MOUSE) { - /* - * For xterm we get "<t_mouse>scr", where - * s == encoded button state: - * 0x20 = left button down - * 0x21 = middle button down - * 0x22 = right button down - * 0x23 = any button release - * 0x60 = button 4 down (scroll wheel down) - * 0x61 = button 5 down (scroll wheel up) - * add 0x04 for SHIFT - * add 0x08 for ALT - * add 0x10 for CTRL - * add 0x20 for mouse drag (0x40 is drag with left button) - * c == column + ' ' + 1 == column + 33 - * r == row + ' ' + 1 == row + 33 - * - * The coordinates are passed on through global variables. - * Ugly, but this avoids trouble with mouse clicks at an - * unexpected moment and allows for mapping them. - */ - for (;; ) { - { - num_bytes = get_bytes_from_buf(tp + slen, bytes, 3); - if (num_bytes == -1) /* not enough coordinates */ - return -1; - mouse_code = bytes[0]; - mouse_col = bytes[1] - ' ' - 1; - mouse_row = bytes[2] - ' ' - 1; - } - slen += num_bytes; - - /* If the following bytes is also a mouse code and it has - * the same code, dump this one and get the next. This - * makes dragging a whole lot faster. */ - j = termcodes[idx].len; - if (STRNCMP(tp, tp + slen, (size_t)j) == 0 - && tp[slen + j] == mouse_code - && tp[slen + j + 1] != NUL - && tp[slen + j + 2] != NUL - ) - slen += j; - else - break; - } - } - - if (key_name[0] == KS_URXVT_MOUSE - || key_name[0] == KS_SGR_MOUSE) { - for (;; ) { - /* URXVT 1015 mouse reporting mode: - * Almost identical to xterm mouse mode, except the values - * are decimal instead of bytes. - * - * \033[%d;%d;%dM - * ^-- row - * ^----- column - * ^-------- code - * - * SGR 1006 mouse reporting mode: - * Almost identical to xterm mouse mode, except the values - * are decimal instead of bytes. - * - * \033[<%d;%d;%dM - * ^-- row - * ^----- column - * ^-------- code - * - * \033[<%d;%d;%dm : mouse release event - * ^-- row - * ^----- column - * ^-------- code - */ - p = tp + slen; - - mouse_code = getdigits_int(&p); - if (*p++ != ';') - return -1; - - /* when mouse reporting is SGR, add 32 to mouse code */ - if (key_name[0] == KS_SGR_MOUSE) - mouse_code += 32; - - mouse_col = getdigits_int(&p) - 1; - if (*p++ != ';') - return -1; - - mouse_row = getdigits_int(&p) - 1; - if (key_name[0] == KS_SGR_MOUSE && *p == 'm') - mouse_code |= MOUSE_RELEASE; - else if (*p != 'M') - return -1; - p++; - - slen += (int)(p - (tp + slen)); - - /* skip this one if next one has same code (like xterm - * case) */ - j = termcodes[idx].len; - if (STRNCMP(tp, tp + slen, (size_t)j) == 0) { - int slen2; - int cmd_complete = 0; - - /* check if the command is complete by looking for the - * 'M' */ - for (slen2 = slen; slen2 < len; slen2++) { - if (tp[slen2] == 'M' - || (key_name[0] == KS_SGR_MOUSE - && tp[slen2] == 'm')) { - cmd_complete = 1; - break; - } - } - p += j; - if (cmd_complete && getdigits_int(&p) == mouse_code) { - slen += j; /* skip the \033[ */ - continue; - } - } - break; - } - } - - if (key_name[0] == (int)KS_MOUSE - || key_name[0] == (int)KS_URXVT_MOUSE - || key_name[0] == KS_SGR_MOUSE - ) { - /* - * Handle mouse events. - * Recognize the xterm mouse wheel, but not in the GUI, the - * Linux console with GPM and the Win32 console - * (multi-clicks use >= 0x60). - */ - if (mouse_code >= MOUSEWHEEL_LOW - ) { - /* Keep the mouse_code before it's changed, so that we - * remember that it was a mouse wheel click. */ - wheel_code = mouse_code; - } else if (held_button == MOUSE_RELEASE - && (mouse_code == 0x23 || mouse_code == 0x24)) { - /* Apparently used by rxvt scroll wheel. */ - wheel_code = mouse_code - 0x23 + MOUSEWHEEL_LOW; - } - -# if defined(UNIX) && defined(FEAT_MOUSE_TTY) - else if (use_xterm_mouse() > 1) { - if (mouse_code & MOUSE_DRAG_XTERM) - mouse_code |= MOUSE_DRAG; - } -# endif - } -# endif /* !UNIX || FEAT_MOUSE_XTERM */ - if (key_name[0] == (int)KS_NETTERM_MOUSE) { - int mc, mr; - - /* expect a rather limited sequence like: balancing { - * \033}6,45\r - * '6' is the row, 45 is the column - */ - p = tp + slen; - mr = getdigits_int(&p); - if (*p++ != ',') - return -1; - mc = getdigits_int(&p); - if (*p++ != '\r') - return -1; - - mouse_col = mc - 1; - mouse_row = mr - 1; - mouse_code = MOUSE_LEFT; - slen += (int)(p - (tp + slen)); - } - if (key_name[0] == (int)KS_DEC_MOUSE) { - /* The DEC Locator Input Model - * Netterm delivers the code sequence: - * \033[2;4;24;80&w (left button down) - * \033[3;0;24;80&w (left button up) - * \033[6;1;24;80&w (right button down) - * \033[7;0;24;80&w (right button up) - * CSI Pe ; Pb ; Pr ; Pc ; Pp & w - * Pe is the event code - * Pb is the button code - * Pr is the row coordinate - * Pc is the column coordinate - * Pp is the third coordinate (page number) - * Pe, the event code indicates what event caused this report - * The following event codes are defined: - * 0 - request, the terminal received an explicit request - * for a locator report, but the locator is unavailable - * 1 - request, the terminal received an explicit request - * for a locator report - * 2 - left button down - * 3 - left button up - * 4 - middle button down - * 5 - middle button up - * 6 - right button down - * 7 - right button up - * 8 - fourth button down - * 9 - fourth button up - * 10 - locator outside filter rectangle - * Pb, the button code, ASCII decimal 0-15 indicating which - * buttons are down if any. The state of the four buttons - * on the locator correspond to the low four bits of the - * decimal value, - * "1" means button depressed - * 0 - no buttons down, - * 1 - right, - * 2 - middle, - * 4 - left, - * 8 - fourth - * Pr is the row coordinate of the locator position in the page, - * encoded as an ASCII decimal value. - * If Pr is omitted, the locator position is undefined - * (outside the terminal window for example). - * Pc is the column coordinate of the locator position in the - * page, encoded as an ASCII decimal value. - * If Pc is omitted, the locator position is undefined - * (outside the terminal window for example). - * Pp is the page coordinate of the locator position - * encoded as an ASCII decimal value. - * The page coordinate may be omitted if the locator is on - * page one (the default). We ignore it anyway. - */ - int Pe, Pb, Pr, Pc; - - p = tp + slen; - - /* get event status */ - Pe = getdigits_int(&p); - if (*p++ != ';') - return -1; - - /* get button status */ - Pb = getdigits_int(&p); - if (*p++ != ';') - return -1; - - /* get row status */ - Pr = getdigits_int(&p); - if (*p++ != ';') - return -1; - - /* get column status */ - Pc = getdigits_int(&p); - - /* the page parameter is optional */ - if (*p == ';') { - p++; - (void)getdigits_int(&p); - } - if (*p++ != '&') - return -1; - if (*p++ != 'w') - return -1; - - mouse_code = 0; - switch (Pe) { - case 0: return -1; /* position request while unavailable */ - case 1: /* a response to a locator position request includes - the status of all buttons */ - Pb &= 7; /* mask off and ignore fourth button */ - if (Pb & 4) - mouse_code = MOUSE_LEFT; - if (Pb & 2) - mouse_code = MOUSE_MIDDLE; - if (Pb & 1) - mouse_code = MOUSE_RIGHT; - if (Pb) { - held_button = mouse_code; - mouse_code |= MOUSE_DRAG; - } - is_drag = TRUE; - showmode(); - break; - case 2: mouse_code = MOUSE_LEFT; - break; - case 3: mouse_code = MOUSE_RELEASE | MOUSE_LEFT; - break; - case 4: mouse_code = MOUSE_MIDDLE; - break; - case 5: mouse_code = MOUSE_RELEASE | MOUSE_MIDDLE; - break; - case 6: mouse_code = MOUSE_RIGHT; - break; - case 7: mouse_code = MOUSE_RELEASE | MOUSE_RIGHT; - break; - case 8: return -1; /* fourth button down */ - case 9: return -1; /* fourth button up */ - case 10: return -1; /* mouse outside of filter rectangle */ - default: return -1; /* should never occur */ - } - - mouse_col = Pc - 1; - mouse_row = Pr - 1; - - slen += (int)(p - (tp + slen)); - } - - /* Interpret the mouse code */ - current_button = (mouse_code & MOUSE_CLICK_MASK); - if (current_button == MOUSE_RELEASE - && wheel_code == 0 - ) { - /* - * If we get a mouse drag or release event when - * there is no mouse button held down (held_button == - * MOUSE_RELEASE), produce a K_IGNORE below. - * (can happen when you hold down two buttons - * and then let them go, or click in the menu bar, but not - * on a menu, and drag into the text). - */ - if ((mouse_code & MOUSE_DRAG) == MOUSE_DRAG) - is_drag = TRUE; - current_button = held_button; - } else if (wheel_code == 0) { - { - static int orig_mouse_col = 0; - static int orig_mouse_row = 0; - - static uint64_t orig_mouse_time = 0; // time of previous mouse click - uint64_t mouse_time = os_hrtime(); // time of current mouse click - - // compute the time elapsed since the previous mouse click and - // convert it from ns to ms because p_mouset is stored as ms - long timediff = (long) (mouse_time - orig_mouse_time) / 1000000; - orig_mouse_time = mouse_time; - - if (mouse_code == orig_mouse_code - && timediff < p_mouset - && orig_num_clicks != 4 - && orig_mouse_col == mouse_col - && orig_mouse_row == mouse_row - && ((orig_topline == curwin->w_topline - && orig_topfill == curwin->w_topfill - ) - /* Double click in tab pages line also works - * when window contents changes. */ - || (mouse_row == 0 && firstwin->w_winrow > 0) - ) - ) - ++orig_num_clicks; - else - orig_num_clicks = 1; - orig_mouse_col = mouse_col; - orig_mouse_row = mouse_row; - orig_topline = curwin->w_topline; - orig_topfill = curwin->w_topfill; - } - is_click = TRUE; - orig_mouse_code = mouse_code; - } - if (!is_drag) - held_button = mouse_code & MOUSE_CLICK_MASK; - - /* - * Translate the actual mouse event into a pseudo mouse event. - * First work out what modifiers are to be used. - */ - if (orig_mouse_code & MOUSE_SHIFT) - modifiers |= MOD_MASK_SHIFT; - if (orig_mouse_code & MOUSE_CTRL) - modifiers |= MOD_MASK_CTRL; - if (orig_mouse_code & MOUSE_ALT) - modifiers |= MOD_MASK_ALT; - if (orig_num_clicks == 2) - modifiers |= MOD_MASK_2CLICK; - else if (orig_num_clicks == 3) - modifiers |= MOD_MASK_3CLICK; - else if (orig_num_clicks == 4) - modifiers |= MOD_MASK_4CLICK; - - /* Work out our pseudo mouse event */ - key_name[0] = (int)KS_EXTRA; - if (wheel_code != 0) { - if (wheel_code & MOUSE_CTRL) - modifiers |= MOD_MASK_CTRL; - if (wheel_code & MOUSE_ALT) - modifiers |= MOD_MASK_ALT; - key_name[1] = (wheel_code & 1) - ? (int)KE_MOUSEUP : (int)KE_MOUSEDOWN; - } else { - key_name[1] = (char_u)get_pseudo_mouse_code(current_button, - is_click, is_drag); - } - } - - - /* - * Change <xHome> to <Home>, <xUp> to <Up>, etc. - */ - key = handle_x_keys(TERMCAP2KEY(key_name[0], key_name[1])); - - /* - * Add any modifier codes to our string. - */ - new_slen = 0; /* Length of what will replace the termcode */ - if (modifiers != 0) { - /* Some keys have the modifier included. Need to handle that here - * to make mappings work. */ - key = simplify_key(key, &modifiers); - if (modifiers != 0) { - string[new_slen++] = K_SPECIAL; - string[new_slen++] = (int)KS_MODIFIER; - string[new_slen++] = (char_u)modifiers; - } - } - - /* Finally, add the special key code to our string */ - key_name[0] = (char_u)KEY2TERMCAP0(key); - key_name[1] = (char_u)KEY2TERMCAP1(key); - if (key_name[0] == KS_KEY) { - /* from ":set <M-b>=xx" */ - if (has_mbyte) - new_slen += (*mb_char2bytes)(key_name[1], string + new_slen); - else - string[new_slen++] = key_name[1]; - } else if (new_slen == 0 && key_name[0] == KS_EXTRA - && key_name[1] == KE_IGNORE) { - /* Do not put K_IGNORE into the buffer, do return KEYLEN_REMOVED - * to indicate what happened. */ - retval = KEYLEN_REMOVED; - } else { - string[new_slen++] = K_SPECIAL; - string[new_slen++] = key_name[0]; - string[new_slen++] = key_name[1]; - } - string[new_slen] = NUL; - extra = new_slen - slen; - if (buf == NULL) { - if (extra < 0) - /* remove matched chars, taking care of noremap */ - del_typebuf(-extra, offset); - else if (extra > 0) - /* insert the extra space we need */ - ins_typebuf(string + slen, REMAP_YES, offset, FALSE, FALSE); - - /* - * Careful: del_typebuf() and ins_typebuf() may have reallocated - * typebuf.tb_buf[]! - */ - memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, - (size_t)new_slen); - } else { - if (extra < 0) - /* remove matched characters */ - memmove(buf + offset, buf + offset - extra, - (size_t)(*buflen + offset + extra)); - else if (extra > 0) { - /* Insert the extra space we need. If there is insufficient - * space return -1. */ - if (*buflen + extra + new_slen >= bufsize) - return -1; - memmove(buf + offset + extra, buf + offset, - (size_t)(*buflen - offset)); - } - memmove(buf + offset, string, (size_t)new_slen); - *buflen = *buflen + extra + new_slen; - } - return retval == 0 ? (len + extra + offset) : retval; - } - - LOG_TR("normal character"); - - return 0; /* no match found */ -} - -/* * Replace any terminal code strings in from[] with the equivalent internal * vim representation. This is used for the "from" and "to" part of a * mapping, and the "to" part of a menu command. @@ -3833,32 +1317,6 @@ ssize_t find_term_bykeys(char_u *src) } /* - * Gather the first characters in the terminal key codes into a string. - * Used to speed up check_termcode(). - */ -static void gather_termleader(void) -{ - if (abstract_ui) { - return; - } - - int len = 0; - - if (check_for_codes) - termleader[len++] = DCS; /* the termcode response starts with DCS - in 8-bit mode */ - termleader[len] = NUL; - - for (size_t i = 0; i < tc_len; ++i) - if (vim_strchr(termleader, termcodes[i].code[0]) == NULL) { - termleader[len++] = termcodes[i].code[0]; - termleader[len] = NUL; - } - - need_gather = false; -} - -/* * Show all termcodes (for ":set termcap") * This code looks a lot like showoptions(), but is different. */ @@ -3977,171 +1435,6 @@ int show_one_termcode(char_u *name, char_u *code, int printit) } /* - * For Xterm >= 140 compiled with OPT_TCAP_QUERY: Obtain the actually used - * termcap codes from the terminal itself. - * We get them one by one to avoid a very long response string. - */ -static void req_codes_from_term(void) -{ - xt_index_out = 0; - xt_index_in = 0; - req_more_codes_from_term(); -} - -static void req_more_codes_from_term(void) -{ - char buf[11]; - int old_idx = xt_index_out; - - /* Don't do anything when going to exit. */ - if (exiting) - return; - - /* Send up to 10 more requests out than we received. Avoid sending too - * many, there can be a buffer overflow somewhere. */ - while (xt_index_out < xt_index_in + 10 && key_names[xt_index_out] != NULL) { -# ifdef DEBUG_TERMRESPONSE - char dbuf[100]; - - sprintf(dbuf, "Requesting XT %d: %s", - xt_index_out, key_names[xt_index_out]); - log_tr(dbuf); -# endif - sprintf(buf, "\033P+q%02x%02x\033\\", - key_names[xt_index_out][0], key_names[xt_index_out][1]); - out_str_nf((char_u *)buf); - ++xt_index_out; - } - - /* Send the codes out right away. */ - if (xt_index_out != old_idx) - out_flush(); -} - -/* - * Decode key code response from xterm: '<Esc>P1+r<name>=<string><Esc>\'. - * A "0" instead of the "1" indicates a code that isn't supported. - * Both <name> and <string> are encoded in hex. - * "code" points to the "0" or "1". - */ -static void got_code_from_term(char_u *code, int len) -{ -#define XT_LEN 100 - char_u name[3]; - char_u str[XT_LEN]; - ssize_t i; - int j = 0; - int c; - - /* A '1' means the code is supported, a '0' means it isn't. - * When half the length is > XT_LEN we can't use it. - * Our names are currently all 2 characters. */ - if (code[0] == '1' && code[7] == '=' && len / 2 < XT_LEN) { - /* Get the name from the response and find it in the table. */ - int byte = hexhex2nr(code + 3); - assert(byte != -1); - name[0] = (char_u)byte; - byte = hexhex2nr(code + 5); - assert(byte != -1); - name[1] = (char_u)byte; - name[2] = NUL; - for (i = 0; key_names[i] != NULL; ++i) { - if (STRCMP(key_names[i], name) == 0) { - assert(i <= INT_MAX); - xt_index_in = (int)i; - break; - } - } -# ifdef DEBUG_TERMRESPONSE - { - char buf[100]; - - sprintf(buf, "Received XT %d: %s", xt_index_in, (char *)name); - log_tr(buf); - } -# endif - if (key_names[i] != NULL) { - for (i = 8; (c = hexhex2nr(code + i)) >= 0; i += 2) - str[j++] = (char_u)c; - str[j] = NUL; - if (name[0] == 'C' && name[1] == 'o') { - /* Color count is not a key code. */ - int i = atoi((char *)str); - if (i != t_colors) { - /* Nr of colors changed, initialize highlighting and - * redraw everything. This causes a redraw, which usually - * clears the message. Try keeping the message if it - * might work. */ - set_keep_msg_from_hist(); - set_color_count(i); - init_highlight(TRUE, FALSE); -#ifdef DEBUG_TERMRESPONSE - { - char buf[100]; - int r = redraw_asap(CLEAR); - - sprintf(buf, "Received t_Co, redraw_asap(): %d", r); - log_tr(buf); - } -#else - redraw_asap(CLEAR); -#endif - } - } else { - /* First delete any existing entry with the same code. */ - i = find_term_bykeys(str); - if (i >= 0) - del_termcode_idx((size_t)i); - add_termcode(name, str, ATC_FROM_TERM); - } - } - } - - /* May request more codes now that we received one. */ - ++xt_index_in; - req_more_codes_from_term(); -} - -/* - * Check if there are any unanswered requests and deal with them. - * This is called before starting an external program or getting direct - * keyboard input. We don't want responses to be send to that program or - * handled as typed text. - */ -static void check_for_codes_from_term(void) -{ - int c; - - /* If no codes requested or all are answered, no need to wait. */ - if (xt_index_out == 0 || xt_index_out == xt_index_in) - return; - - /* Vgetc() will check for and handle any response. - * Keep calling vpeekc() until we don't get any responses. */ - ++no_mapping; - ++allow_keys; - for (;; ) { - c = vpeekc(); - if (c == NUL) /* nothing available */ - break; - - /* If a response is recognized it's replaced with K_IGNORE, must read - * it from the input stream. If there is no K_IGNORE we can't do - * anything, break here (there might be some responses further on, but - * we don't want to throw away any typed chars). */ - if (c != K_SPECIAL && c != K_IGNORE) - break; - c = vgetc(); - if (c != K_IGNORE) { - vungetc(c); - break; - } - } - --no_mapping; - --allow_keys; -} - -/* * Translate an internal mapping/abbreviation representation into the * corresponding external one recognized by :map/:abbrev commands; * respects the current B/k/< settings of 'cpoption'. diff --git a/src/nvim/tui/term_input.inl b/src/nvim/tui/term_input.inl new file mode 100644 index 0000000000..dba970e8f2 --- /dev/null +++ b/src/nvim/tui/term_input.inl @@ -0,0 +1,292 @@ +#include <termkey.h> + +#include "nvim/ascii.h" +#include "nvim/misc2.h" +#include "nvim/os/os.h" +#include "nvim/os/input.h" +#include "nvim/os/rstream.h" + +#define PASTETOGGLE_KEY "<f37>" + +struct term_input { + int in_fd; + TermKey *tk; + uv_tty_t input_handle; + uv_timer_t timer_handle; + RBuffer *read_buffer; + RStream *read_stream; +}; + +static void forward_simple_utf8(TermKeyKey *key) +{ + size_t len = 0; + char buf[64]; + char *ptr = key->utf8; + + while (*ptr) { + if (*ptr == '<') { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "<lt>"); + } else { + buf[len++] = *ptr; + } + ptr++; + } + + buf[len] = 0; + input_enqueue((String){.data = buf, .size = len}); +} + +static void forward_modified_utf8(TermKey *tk, TermKeyKey *key) +{ + size_t len; + char buf[64]; + + if (key->type == TERMKEY_TYPE_KEYSYM + && key->code.sym == TERMKEY_SYM_ESCAPE) { + len = (size_t)snprintf(buf, sizeof(buf), "<Esc>"); + } else { + len = termkey_strfkey(tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM); + } + + input_enqueue((String){.data = buf, .size = len}); +} + +static void forward_mouse_event(TermKey *tk, TermKeyKey *key) +{ + char buf[64]; + size_t len = 0; + int button, row, col; + TermKeyMouseEvent ev; + termkey_interpret_mouse(tk, key, &ev, &button, &row, &col); + + if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG) { + return; + } + + row--; col--; // Termkey uses 1-based coordinates + buf[len++] = '<'; + + if (key->modifiers & TERMKEY_KEYMOD_SHIFT) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "S-"); + } + + if (key->modifiers & TERMKEY_KEYMOD_CTRL) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "C-"); + } + + if (key->modifiers & TERMKEY_KEYMOD_ALT) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "A-"); + } + + if (button == 1) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Left"); + } else if (button == 2) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Middle"); + } else if (button == 3) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right"); + } + + if (ev == TERMKEY_MOUSE_PRESS) { + if (button == 4) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp"); + } else if (button == 5) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown"); + } else { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse"); + } + } else if (ev == TERMKEY_MOUSE_DRAG) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag"); + } + + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); + input_enqueue((String){.data = buf, .size = len}); +} + +static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force) +{ + return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key); +} + +static void timer_cb(uv_timer_t *handle); + +static int get_key_code_timeout(void) +{ + Integer ms = 0; + bool timeout = false; + // Check 'timeout' and 'ttimeout' to determine if we should send ESC + // after 'ttimeoutlen'. See :help 'ttimeout' for more information + Error err = ERROR_INIT; + timeout = vim_get_option(cstr_as_string("timeout"), &err).data.boolean; + if (!timeout) { + timeout = vim_get_option(cstr_as_string("ttimeout"), &err).data.boolean; + } + + if (timeout) { + ms = vim_get_option(cstr_as_string("ttimeoutlen"), &err).data.integer; + } + + return (int)ms; +} + +static void tk_getkeys(TermInput *input, bool force) +{ + TermKeyKey key; + TermKeyResult result; + + while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) { + if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) { + forward_simple_utf8(&key); + } else if (key.type == TERMKEY_TYPE_UNICODE || + key.type == TERMKEY_TYPE_FUNCTION || + key.type == TERMKEY_TYPE_KEYSYM) { + forward_modified_utf8(input->tk, &key); + } else if (key.type == TERMKEY_TYPE_MOUSE) { + forward_mouse_event(input->tk, &key); + } + } + + if (result != TERMKEY_RES_AGAIN) { + return; + } + + int ms = get_key_code_timeout(); + + if (ms > 0) { + // Stop the current timer if already running + uv_timer_stop(&input->timer_handle); + uv_timer_start(&input->timer_handle, timer_cb, (uint32_t)ms, 0); + } else { + tk_getkeys(input, true); + } +} + + +static void timer_cb(uv_timer_t *handle) +{ + tk_getkeys(handle->data, true); +} + +static bool handle_bracketed_paste(TermInput *input) +{ + char *ptr = rbuffer_read_ptr(input->read_buffer); + size_t len = rbuffer_pending(input->read_buffer); + if (len > 5 && (!strncmp(ptr, "\x1b[200~", 6) + || !strncmp(ptr, "\x1b[201~", 6))) { + bool enable = ptr[4] == '0'; + // Advance past the sequence + rbuffer_consumed(input->read_buffer, 6); + if (enable) { + // Get the current mode + int state = get_real_state(); + if (state & NORMAL) { + // Enter insert mode + input_enqueue(cstr_as_string("i")); + } else if (state & VISUAL) { + // Remove the selected text and enter insert mode + input_enqueue(cstr_as_string("c")); + } else if (!(state & INSERT)) { + // Don't mess with the paste option + return true; + } + } + input_enqueue(cstr_as_string(PASTETOGGLE_KEY)); + return true; + } + return false; +} + +static bool handle_forced_escape(TermInput *input) +{ + char *ptr = rbuffer_read_ptr(input->read_buffer); + size_t len = rbuffer_pending(input->read_buffer); + if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) { + // skip the ESC and NUL and push one <esc> to the input buffer + termkey_push_bytes(input->tk, ptr, 1); + rbuffer_consumed(input->read_buffer, 2); + tk_getkeys(input, true); + return true; + } + return false; +} + +static void read_cb(RStream *rstream, void *rstream_data, bool eof) +{ + if (eof) { + input_done(); + return; + } + + TermInput *input = rstream_data; + + do { + if (handle_bracketed_paste(input) || handle_forced_escape(input)) { + continue; + } + char *ptr = rbuffer_read_ptr(input->read_buffer); + size_t len = rbuffer_pending(input->read_buffer); + // Find the next 'esc' and push everything up to it(excluding) + size_t i; + for (i = ptr[0] == ESC ? 1 : 0; i < len; i++) { + if (ptr[i] == '\x1b') { + break; + } + } + size_t consumed = termkey_push_bytes(input->tk, ptr, i); + rbuffer_consumed(input->read_buffer, consumed); + tk_getkeys(input, false); + } while (rbuffer_pending(input->read_buffer)); +} + +static TermInput *term_input_new(void) +{ + TermInput *rv = xmalloc(sizeof(TermInput)); + // read input from stderr if stdin is not a tty + rv->in_fd = os_isatty(0) ? 0 : (os_isatty(2) ? 2 : 0); + + // Set terminal encoding based on environment(taken from libtermkey source + // code) + const char *e; + int flags = 0; + if (((e = os_getenv("LANG")) || (e = os_getenv("LC_MESSAGES")) + || (e = os_getenv("LC_ALL"))) && (e = strchr(e, '.')) && e++ && + (strcasecmp(e, "UTF-8") == 0 || strcasecmp(e, "UTF8") == 0)) { + flags |= TERMKEY_FLAG_UTF8; + } else { + flags |= TERMKEY_FLAG_RAW; + } + + rv->tk = termkey_new_abstract(os_getenv("TERM"), flags); + int curflags = termkey_get_canonflags(rv->tk); + termkey_set_canonflags(rv->tk, curflags | TERMKEY_CANON_DELBS); + // setup input handle + uv_tty_init(uv_default_loop(), &rv->input_handle, rv->in_fd, 1); + uv_tty_set_mode(&rv->input_handle, UV_TTY_MODE_RAW); + rv->input_handle.data = NULL; + rv->read_buffer = rbuffer_new(0xfff); + rv->read_stream = rstream_new(read_cb, rv->read_buffer, rv); + rstream_set_stream(rv->read_stream, (uv_stream_t *)&rv->input_handle); + rstream_start(rv->read_stream); + // initialize a timer handle for handling ESC with libtermkey + uv_timer_init(uv_default_loop(), &rv->timer_handle); + rv->timer_handle.data = rv; + // Set the pastetoggle option to a special key that will be sent when + // \e[20{0,1}~/ are received + Error err = ERROR_INIT; + vim_set_option(cstr_as_string("pastetoggle"), + STRING_OBJ(cstr_as_string(PASTETOGGLE_KEY)), &err); + return rv; +} + +static void term_input_destroy(TermInput *input) +{ + uv_tty_reset_mode(); + uv_timer_stop(&input->timer_handle); + rstream_stop(input->read_stream); + rstream_free(input->read_stream); + uv_close((uv_handle_t *)&input->input_handle, NULL); + uv_close((uv_handle_t *)&input->timer_handle, NULL); + termkey_destroy(input->tk); + event_poll(0); // Run once to remove references to input/timer handles + free(input->input_handle.data); + free(input); +} diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c new file mode 100644 index 0000000000..57119b9030 --- /dev/null +++ b/src/nvim/tui/tui.c @@ -0,0 +1,775 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> + +#include <uv.h> +#include <unibilium.h> + +#include "nvim/lib/kvec.h" + +#include "nvim/vim.h" +#include "nvim/ui.h" +#include "nvim/map.h" +#include "nvim/memory.h" +#include "nvim/api/vim.h" +#include "nvim/api/private/helpers.h" +#include "nvim/os/event.h" +#include "nvim/tui/tui.h" + +typedef struct term_input TermInput; + +#include "term_input.inl" + +typedef struct { + int top, bot, left, right; +} Rect; + +typedef struct { + char data[7]; + HlAttrs attrs; +} Cell; + +typedef struct { + PMap(cstr_t) *option_cache; + unibi_var_t params[9]; + char buf[0xffff]; + size_t bufpos; + TermInput *input; + uv_loop_t *write_loop; + unibi_term *ut; + uv_tty_t output_handle; + uv_signal_t winch_handle; + Rect scroll_region; + kvec_t(Rect) invalid_regions; + int row, col; + int bg, fg; + int out_fd; + int old_height; + bool can_use_terminal_scroll; + HlAttrs attrs, print_attrs; + Cell **screen; + struct { + size_t enable_mouse, disable_mouse; + size_t enable_bracketed_paste, disable_bracketed_paste; + size_t enter_insert_mode, exit_insert_mode; + } unibi_ext; +} TUIData; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tui/tui.c.generated.h" +#endif + +#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1}) + +#define FOREACH_CELL(ui, top, bot, left, right, go, code) \ + do { \ + TUIData *data = ui->data; \ + for (int row = top; row <= bot; ++row) { \ + Cell *cells = data->screen[row]; \ + if (go) { \ + unibi_goto(ui, row, left); \ + } \ + for (int col = left; col <= right; ++col) { \ + Cell *cell = cells + col; \ + (void)(cell); \ + code; \ + } \ + } \ + } while (0) + + +void tui_start(void) +{ + TUIData *data = xcalloc(1, sizeof(TUIData)); + UI *ui = xcalloc(1, sizeof(UI)); + ui->data = data; + data->attrs = data->print_attrs = EMPTY_ATTRS; + data->fg = data->bg = -1; + data->can_use_terminal_scroll = true; + data->bufpos = 0; + data->option_cache = pmap_new(cstr_t)(); + + // write output to stderr if stdout is not a tty + data->out_fd = os_isatty(1) ? 1 : (os_isatty(2) ? 2 : 1); + kv_init(data->invalid_regions); + // setup term input + data->input = term_input_new(); + // setup unibilium + data->ut = unibi_from_env(); + if (!data->ut) { + // For some reason could not read terminfo file, use a dummy entry that + // will be populated with common values by fix_terminfo below + data->ut = unibi_dummy(); + } + fix_terminfo(data); + // Enter alternate screen and clear + unibi_out(ui, unibi_enter_ca_mode); + unibi_out(ui, unibi_clear_screen); + // Enable bracketed paste + unibi_out(ui, (int)data->unibi_ext.enable_bracketed_paste); + + // setup output handle in a separate event loop(we wanna do synchronous + // write to the tty) + data->write_loop = xmalloc(sizeof(uv_loop_t)); + uv_loop_init(data->write_loop); + uv_tty_init(data->write_loop, &data->output_handle, data->out_fd, 0); + + // Obtain screen dimensions + update_size(ui); + + // listen for SIGWINCH + uv_signal_init(uv_default_loop(), &data->winch_handle); + uv_signal_start(&data->winch_handle, sigwinch_cb, SIGWINCH); + data->winch_handle.data = ui; + + ui->stop = tui_stop; + ui->rgb = false; + ui->data = data; + ui->resize = tui_resize; + ui->clear = tui_clear; + ui->eol_clear = tui_eol_clear; + ui->cursor_goto = tui_cursor_goto; + ui->cursor_on = tui_cursor_on; + ui->cursor_off = tui_cursor_off; + ui->mouse_on = tui_mouse_on; + ui->mouse_off = tui_mouse_off; + ui->insert_mode = tui_insert_mode; + ui->normal_mode = tui_normal_mode; + ui->set_scroll_region = tui_set_scroll_region; + ui->scroll = tui_scroll; + ui->highlight_set = tui_highlight_set; + ui->put = tui_put; + ui->bell = tui_bell; + ui->visual_bell = tui_visual_bell; + ui->update_fg = tui_update_fg; + ui->update_bg = tui_update_bg; + ui->flush = tui_flush; + ui->suspend = tui_suspend; + ui->set_title = tui_set_title; + ui->set_icon = tui_set_icon; + // Attach + ui_attach(ui); +} + +static void tui_stop(UI *ui) +{ + TUIData *data = ui->data; + // Destroy common stuff + kv_destroy(data->invalid_regions); + uv_signal_stop(&data->winch_handle); + uv_close((uv_handle_t *)&data->winch_handle, NULL); + // Destroy input stuff + term_input_destroy(data->input); + // Destroy output stuff + tui_normal_mode(ui); + tui_mouse_off(ui); + unibi_out(ui, unibi_exit_attribute_mode); + unibi_out(ui, unibi_cursor_normal); + unibi_out(ui, unibi_exit_ca_mode); + // Disable bracketed paste + unibi_out(ui, (int)data->unibi_ext.disable_bracketed_paste); + flush_buf(ui); + uv_close((uv_handle_t *)&data->output_handle, NULL); + uv_run(data->write_loop, UV_RUN_DEFAULT); + if (uv_loop_close(data->write_loop)) { + abort(); + } + free(data->write_loop); + unibi_destroy(data->ut); + char *opt_value; + map_foreach_value(data->option_cache, opt_value, { + free(opt_value); + }); + pmap_free(cstr_t)(data->option_cache); + destroy_screen(data); + free(data); + ui_detach(ui); + free(ui); +} + +static void try_resize(Event ev) +{ + UI *ui = ev.data; + update_size(ui); + ui_refresh(); +} + +static void sigwinch_cb(uv_signal_t *handle, int signum) +{ + // Queue the event because resizing can result in recursive event_poll calls + event_push((Event) { + .data = handle->data, + .handler = try_resize + }, false); +} + +static bool attrs_differ(HlAttrs a1, HlAttrs a2) +{ + return a1.foreground != a2.foreground || a1.background != a2.background + || a1.bold != a2.bold || a1.italic != a2.italic + || a1.undercurl != a2.undercurl || a1.underline != a2.underline + || a1.reverse != a2.reverse; +} + +static void update_attrs(UI *ui, HlAttrs attrs) +{ + TUIData *data = ui->data; + + if (!attrs_differ(attrs, data->print_attrs)) { + return; + } + + data->print_attrs = attrs; + unibi_out(ui, unibi_exit_attribute_mode); + + data->params[0].i = attrs.foreground != -1 ? attrs.foreground : data->fg; + if (data->params[0].i != -1) { + unibi_out(ui, unibi_set_a_foreground); + } + + data->params[0].i = attrs.background != -1 ? attrs.background : data->bg; + if (data->params[0].i != -1) { + unibi_out(ui, unibi_set_a_background); + } + + if (attrs.bold) { + unibi_out(ui, unibi_enter_bold_mode); + } + if (attrs.italic) { + unibi_out(ui, unibi_enter_italics_mode); + } + if (attrs.underline) { + unibi_out(ui, unibi_enter_underline_mode); + } + if (attrs.reverse) { + unibi_out(ui, unibi_enter_reverse_mode); + } +} + +static void print_cell(UI *ui, Cell *ptr) +{ + update_attrs(ui, ptr->attrs); + out(ui, ptr->data); +} + +static void clear_region(UI *ui, int top, int bot, int left, int right, + bool refresh) +{ + TUIData *data = ui->data; + HlAttrs clear_attrs = EMPTY_ATTRS; + clear_attrs.foreground = data->fg; + clear_attrs.background = data->bg; + update_attrs(ui, clear_attrs); + + bool cleared = false; + if (refresh && data->bg == -1 && right == ui->width -1) { + // Background is set to the default color and the right edge matches the + // screen end, try to use terminal codes for clearing the requested area. + if (left == 0) { + if (bot == ui->height - 1) { + if (top == 0) { + unibi_out(ui, unibi_clear_screen); + } else { + unibi_goto(ui, top, 0); + unibi_out(ui, unibi_clr_eos); + } + cleared = true; + } + } + + if (!cleared) { + // iterate through each line and clear with clr_eol + for (int row = top; row <= bot; ++row) { + unibi_goto(ui, row, left); + unibi_out(ui, unibi_clr_eol); + } + cleared = true; + } + } + + bool clear = refresh && !cleared; + FOREACH_CELL(ui, top, bot, left, right, clear, { + cell->data[0] = ' '; + cell->data[1] = 0; + cell->attrs = clear_attrs; + if (clear) { + print_cell(ui, cell); + } + }); + + // restore cursor + unibi_goto(ui, data->row, data->col); +} + +static void tui_resize(UI *ui, int width, int height) +{ + TUIData *data = ui->data; + destroy_screen(data); + + data->screen = xmalloc((size_t)height * sizeof(Cell *)); + for (int i = 0; i < height; i++) { + data->screen[i] = xcalloc((size_t)width, sizeof(Cell)); + } + + data->old_height = height; + data->scroll_region.top = 0; + data->scroll_region.bot = height - 1; + data->scroll_region.left = 0; + data->scroll_region.right = width - 1; + data->row = data->col = 0; +} + +static void tui_clear(UI *ui) +{ + TUIData *data = ui->data; + clear_region(ui, data->scroll_region.top, data->scroll_region.bot, + data->scroll_region.left, data->scroll_region.right, true); +} + +static void tui_eol_clear(UI *ui) +{ + TUIData *data = ui->data; + clear_region(ui, data->row, data->row, data->col, + data->scroll_region.right, true); +} + +static void tui_cursor_goto(UI *ui, int row, int col) +{ + TUIData *data = ui->data; + data->row = row; + data->col = col; + unibi_goto(ui, row, col); +} + +static void tui_cursor_on(UI *ui) +{ + unibi_out(ui, unibi_cursor_normal); +} + +static void tui_cursor_off(UI *ui) +{ + unibi_out(ui, unibi_cursor_invisible); +} + +static void tui_mouse_on(UI *ui) +{ + TUIData *data = ui->data; + unibi_out(ui, (int)data->unibi_ext.enable_mouse); +} + +static void tui_mouse_off(UI *ui) +{ + TUIData *data = ui->data; + unibi_out(ui, (int)data->unibi_ext.disable_mouse); +} + +static void tui_insert_mode(UI *ui) +{ + TUIData *data = ui->data; + unibi_out(ui, (int)data->unibi_ext.enter_insert_mode); +} + +static void tui_normal_mode(UI *ui) +{ + TUIData *data = ui->data; + unibi_out(ui, (int)data->unibi_ext.exit_insert_mode); +} + +static void tui_set_scroll_region(UI *ui, int top, int bot, int left, + int right) +{ + TUIData *data = ui->data; + data->scroll_region.top = top; + data->scroll_region.bot = bot; + data->scroll_region.left = left; + data->scroll_region.right = right; + + data->can_use_terminal_scroll = + left == 0 && right == ui->width - 1 + && ((top == 0 && bot == ui->height - 1) + || unibi_get_str(data->ut, unibi_change_scroll_region)); +} + +static void tui_scroll(UI *ui, int count) +{ + TUIData *data = ui->data; + int top = data->scroll_region.top; + int bot = data->scroll_region.bot; + int left = data->scroll_region.left; + int right = data->scroll_region.right; + + if (data->can_use_terminal_scroll) { + // Change terminal scroll region and move cursor to the top + data->params[0].i = top; + data->params[1].i = bot; + unibi_out(ui, unibi_change_scroll_region); + unibi_goto(ui, top, left); + } + + // Compute start/stop/step for the loop below, also use terminal scroll + // if possible + int start, stop, step; + if (count > 0) { + start = top; + stop = bot - count + 1; + step = 1; + if (data->can_use_terminal_scroll) { + if (count == 1) { + unibi_out(ui, unibi_delete_line); + } else { + data->params[0].i = count; + unibi_out(ui, unibi_parm_delete_line); + } + } + + } else { + start = bot; + stop = top - count - 1; + step = -1; + if (data->can_use_terminal_scroll) { + if (count == -1) { + unibi_out(ui, unibi_insert_line); + } else { + data->params[0].i = -count; + unibi_out(ui, unibi_parm_insert_line); + } + } + } + + if (data->can_use_terminal_scroll) { + // Restore terminal scroll region and cursor + data->params[0].i = 0; + data->params[1].i = ui->height - 1; + unibi_out(ui, unibi_change_scroll_region); + unibi_goto(ui, data->row, data->col); + } + + int i; + // Scroll internal screen + for (i = start; i != stop; i += step) { + Cell *target_row = data->screen[i] + left; + Cell *source_row = data->screen[i + count] + left; + memcpy(target_row, source_row, sizeof(Cell) * (size_t)(right - left + 1)); + } + + // clear emptied region, updating the terminal if its builtin scrolling + // facility was used. This is done when the background color is not the + // default, since scrolling may leave wrong background in the cleared area. + bool update_clear = data->bg != -1 && data->can_use_terminal_scroll; + if (count > 0) { + clear_region(ui, stop, stop + count - 1, left, right, update_clear); + } else { + clear_region(ui, stop + count + 1, stop, left, right, update_clear); + } + + if (!data->can_use_terminal_scroll) { + // Mark the entire scroll region as invalid for redrawing later + invalidate(ui, data->scroll_region.top, data->scroll_region.bot, + data->scroll_region.left, data->scroll_region.right); + } +} + +static void tui_highlight_set(UI *ui, HlAttrs attrs) +{ + ((TUIData *)ui->data)->attrs = attrs; +} + +static void tui_put(UI *ui, uint8_t *text, size_t size) +{ + TUIData *data = ui->data; + Cell *cell = data->screen[data->row] + data->col; + cell->data[size] = 0; + cell->attrs = data->attrs; + + if (text) { + memcpy(cell->data, text, size); + } + + print_cell(ui, cell); + data->col += 1; +} + +static void tui_bell(UI *ui) +{ + unibi_out(ui, unibi_bell); +} + +static void tui_visual_bell(UI *ui) +{ + unibi_out(ui, unibi_flash_screen); +} + +static void tui_update_fg(UI *ui, int fg) +{ + ((TUIData *)ui->data)->fg = fg; +} + +static void tui_update_bg(UI *ui, int bg) +{ + ((TUIData *)ui->data)->bg = bg; +} + +static void tui_flush(UI *ui) +{ + TUIData *data = ui->data; + + while (kv_size(data->invalid_regions)) { + Rect r = kv_pop(data->invalid_regions); + FOREACH_CELL(ui, r.top, r.bot, r.left, r.right, true, { + print_cell(ui, cell); + }); + } + + unibi_goto(ui, data->row, data->col); + flush_buf(ui); +} + +static void tui_suspend(UI *ui) +{ + tui_stop(ui); + kill(0, SIGTSTP); + tui_start(); +} + +static void tui_set_title(UI *ui, char *title) +{ + TUIData *data = ui->data; + if (!(unibi_get_str(data->ut, unibi_to_status_line) + && unibi_get_str(data->ut, unibi_from_status_line))) { + return; + } + unibi_out(ui, unibi_to_status_line); + out(ui, title); + unibi_out(ui, unibi_from_status_line); +} + +static void tui_set_icon(UI *ui, char *icon) +{ +} + +static void invalidate(UI *ui, int top, int bot, int left, int right) +{ + TUIData *data = ui->data; + Rect *intersects = NULL; + // Increase dimensions before comparing to ensure adjacent regions are + // treated as intersecting + --top; + ++bot; + --left; + ++right; + + for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { + Rect *r = &kv_A(data->invalid_regions, i); + if (!(top > r->bot || bot < r->top + || left > r->right || right < r->left)) { + intersects = r; + break; + } + } + + ++top; + --bot; + ++left; + --right; + + if (intersects) { + // If top/bot/left/right intersects with a invalid rect, we replace it + // by the union + intersects->top = MIN(top, intersects->top); + intersects->bot = MAX(bot, intersects->bot); + intersects->left = MIN(left, intersects->left); + intersects->right = MAX(right, intersects->right); + } else { + // Else just add a new entry; + kv_push(Rect, data->invalid_regions, ((Rect){top, bot, left, right})); + } +} + +static void update_size(UI *ui) +{ + TUIData *data = ui->data; + int width = 0, height = 0; + // 1 - try from a system call(ioctl/TIOCGWINSZ on unix) + if (!uv_tty_get_winsize(&data->output_handle, &width, &height)) { + goto end; + } + + // 2 - use $LINES/$COLUMNS if available + const char *val; + int advance; + if ((val = os_getenv("LINES")) + && sscanf(val, "%d%n", &height, &advance) != EOF && advance + && (val = os_getenv("COLUMNS")) + && sscanf(val, "%d%n", &width, &advance) != EOF && advance) { + goto end; + } + + // 3- read from terminfo if available + height = unibi_get_num(data->ut, unibi_lines); + width = unibi_get_num(data->ut, unibi_columns); + +end: + if (width <= 0 || height <= 0) { + // use a default of 80x24 + width = 80; + height = 24; + } + + ui->width = width; + ui->height = height; +} + +static void unibi_goto(UI *ui, int row, int col) +{ + TUIData *data = ui->data; + data->params[0].i = row; + data->params[1].i = col; + unibi_out(ui, unibi_cursor_address); +} + +static void unibi_out(UI *ui, int unibi_index) +{ + TUIData *data = ui->data; + + const char *str = NULL; + + if (unibi_index < unibi_string_begin_) { + str = unibi_get_ext_str(data->ut, (unsigned)unibi_index); + } else { + str = unibi_get_str(data->ut, (unsigned)unibi_index); + } + + if (str) { + data->bufpos += unibi_run(str, data->params, data->buf + data->bufpos, + sizeof(data->buf) - data->bufpos); + } +} + +static void out(UI *ui, const char *str) +{ + TUIData *data = ui->data; + data->bufpos += (size_t)snprintf(data->buf + data->bufpos, + sizeof(data->buf) - data->bufpos, "%s", str); +} + +static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, + const char *val) +{ + if (!unibi_get_str(ut, str)) { + unibi_set_str(ut, str, val); + } +} + +static void fix_terminfo(TUIData *data) +{ + unibi_term *ut = data->ut; + + const char *term = os_getenv("TERM"); + if (!term) { + goto end; + } + + bool inside_tmux = os_getenv("TMUX") != NULL; + +#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1)) + + if (STARTS_WITH(term, "rxvt")) { + unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[m\x1b(B"); + unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<20/>\x1b[?5l"); + unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2"); + } else if (STARTS_WITH(term, "xterm")) { + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); + } else if (STARTS_WITH(term, "screen")) { + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_"); + unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\"); + } + + if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt")) { + unibi_set_if_empty(ut, unibi_cursor_normal, "\x1b[?12l\x1b[?25h"); + unibi_set_if_empty(ut, unibi_cursor_invisible, "\x1b[?25l"); + unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<100/>\x1b[?5l"); + unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m"); + unibi_set_if_empty(ut, unibi_change_scroll_region, "\x1b[%i%p1%d;%p2%dr"); + unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[2J"); + unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); + } + + if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt") || inside_tmux) { + data->unibi_ext.enable_bracketed_paste = unibi_add_ext_str(ut, NULL, + "\x1b[?2004h"); + data->unibi_ext.disable_bracketed_paste = unibi_add_ext_str(ut, NULL, + "\x1b[?2004l"); + } + + if (os_getenv("NVIM_TUI_ENABLE_CURSOR_SHAPE") == NULL) { + goto end; + } + +#define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) + // Support changing cursor shape on some popular terminals. + const char *term_prog = os_getenv("TERM_PROGRAM"); + + if ((term_prog && !strcmp(term_prog, "iTerm.app")) + || os_getenv("ITERM_SESSION_ID") != NULL) { + // iterm + data->unibi_ext.enter_insert_mode = unibi_add_ext_str(ut, NULL, + TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07")); + data->unibi_ext.exit_insert_mode = unibi_add_ext_str(ut, NULL, + TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07")); + } else { + // xterm-like sequences for blinking bar and solid block + data->unibi_ext.enter_insert_mode = unibi_add_ext_str(ut, NULL, + TMUX_WRAP("\x1b[5 q")); + data->unibi_ext.exit_insert_mode = unibi_add_ext_str(ut, NULL, + TMUX_WRAP("\x1b[2 q")); + } + +end: + // Fill some empty slots with common terminal strings + data->unibi_ext.enable_mouse = unibi_add_ext_str(ut, NULL, + "\x1b[?1002h\x1b[?1006h"); + data->unibi_ext.disable_mouse = unibi_add_ext_str(ut, NULL, + "\x1b[?1002l\x1b[?1006l"); + unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH"); + unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m"); + unibi_set_if_empty(ut, unibi_set_a_foreground, + "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"); + unibi_set_if_empty(ut, unibi_set_a_background, + "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"); + unibi_set_if_empty(ut, unibi_enter_bold_mode, "\x1b[1m"); + unibi_set_if_empty(ut, unibi_enter_underline_mode, "\x1b[4m"); + unibi_set_if_empty(ut, unibi_enter_reverse_mode, "\x1b[7m"); + unibi_set_if_empty(ut, unibi_bell, "\x07"); + unibi_set_if_empty(data->ut, unibi_enter_ca_mode, "\x1b[?1049h"); + unibi_set_if_empty(data->ut, unibi_exit_ca_mode, "\x1b[?1049l"); + unibi_set_if_empty(ut, unibi_delete_line, "\x1b[M"); + unibi_set_if_empty(ut, unibi_parm_delete_line, "\x1b[%p1%dM"); + unibi_set_if_empty(ut, unibi_insert_line, "\x1b[L"); + unibi_set_if_empty(ut, unibi_parm_insert_line, "\x1b[%p1%dL"); + unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[J"); + unibi_set_if_empty(ut, unibi_clr_eol, "\x1b[K"); + unibi_set_if_empty(ut, unibi_clr_eos, "\x1b[J"); +} + +static void flush_buf(UI *ui) +{ + static uv_write_t req; + static uv_buf_t buf; + TUIData *data = ui->data; + buf.base = data->buf; + buf.len = data->bufpos; + uv_write(&req, (uv_stream_t *)&data->output_handle, &buf, 1, NULL); + uv_run(data->write_loop, UV_RUN_DEFAULT); + data->bufpos = 0; +} + +static void destroy_screen(TUIData *data) +{ + if (data->screen) { + for (int i = 0; i < data->old_height; i++) { + free(data->screen[i]); + } + free(data->screen); + } +} diff --git a/src/nvim/tui/tui.h b/src/nvim/tui/tui.h new file mode 100644 index 0000000000..07523bc124 --- /dev/null +++ b/src/nvim/tui/tui.h @@ -0,0 +1,8 @@ +#ifndef NVIM_TUI_TUI_H +#define NVIM_TUI_TUI_H + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tui/tui.h.generated.h" +#endif + +#endif // NVIM_TUI_TUI_H diff --git a/src/nvim/ui.c b/src/nvim/ui.c index a8ca58d633..c09cca1347 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -46,6 +46,7 @@ #include "nvim/syntax.h" #include "nvim/term.h" #include "nvim/window.h" +#include "nvim/tui/tui.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui.c.generated.h" @@ -59,8 +60,8 @@ static int row, col; static struct { int top, bot, left, right; } sr; -static int current_highlight_mask = 0; -static bool cursor_enabled = true; +static int current_attr_code = 0; +static bool cursor_enabled = true, pending_cursor_update = false; static int height, width; // This set of macros allow us to use UI_CALL to invoke any function on @@ -71,6 +72,7 @@ static int height, width; // works. #define UI_CALL(...) \ do { \ + flush_cursor_update(); \ for (size_t i = 0; i < ui_count; i++) { \ UI *ui = uis[i]; \ UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ @@ -80,38 +82,27 @@ static int height, width; #define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6 #define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__) #define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__) -#define UI_CALL_MORE(method, ...) ui->method(ui, __VA_ARGS__) -#define UI_CALL_ZERO(method) ui->method(ui) +#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__) +#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui) -void ui_write(uint8_t *s, int len) +void ui_builtin_start(void) { - if (silent_mode && !p_verbose) { - // Don't output anything in silent mode ("ex -s") unless 'verbose' set - return; - } + tui_start(); +} - if (abstract_ui) { - parse_abstract_ui_codes(s, len); - return; - } +void ui_builtin_stop(void) +{ + UI_CALL(stop); +} - if (!len) { +void ui_write(uint8_t *s, size_t len) +{ + if (silent_mode && !p_verbose) { + // Don't output anything in silent mode ("ex -s") unless 'verbose' set return; } - char_u *tofree = NULL; - - if (output_conv.vc_type != CONV_NONE) { - /* Convert characters from 'encoding' to 'termencoding'. */ - tofree = string_convert(&output_conv, s, &len); - if (tofree != NULL) - s = tofree; - } - - term_write(s, len); - - if (output_conv.vc_type != CONV_NONE) - free(tofree); + parse_abstract_ui_codes(s, len); } bool ui_rgb_attached(void) @@ -124,6 +115,11 @@ bool ui_rgb_attached(void) return false; } +bool ui_active(void) +{ + return ui_count != 0; +} + /* * If the machine has job control, use it to suspend the program, * otherwise fake it by starting a new shell. @@ -131,12 +127,8 @@ bool ui_rgb_attached(void) */ void ui_suspend(void) { - if (abstract_ui) { - UI_CALL(suspend); - UI_CALL(flush); - } else { - mch_suspend(); - } + UI_CALL(suspend); + UI_CALL(flush); } void ui_set_title(char *title) @@ -152,46 +144,16 @@ void ui_set_icon(char *icon) } /* - * Try to get the current Vim shell size. Put the result in Rows and Columns. - * Use the new sizes as defaults for 'columns' and 'lines'. - * Return OK when size could be determined, FAIL otherwise. - */ -int ui_get_shellsize(void) -{ - if (abstract_ui) { - return FAIL; - } - - int retval; - - retval = mch_get_shellsize(); - - check_shellsize(); - - /* adjust the default for 'lines' and 'columns' */ - if (retval == OK) { - set_number_default("lines", Rows); - set_number_default("columns", Columns); - } - return retval; -} - -/* * May update the shape of the cursor. */ void ui_cursor_shape(void) { - if (abstract_ui) { - ui_change_mode(); - } else { - term_cursor_shape(); - conceal_check_cursur_line(); - } + ui_change_mode(); } void ui_refresh(void) { - if (!ui_count) { + if (!ui_active()) { return; } @@ -203,7 +165,7 @@ void ui_refresh(void) height = ui->height < height ? ui->height : height; } - screen_resize(width, height, true); + screen_resize(width, height); } void ui_resize(int new_width, int new_height) @@ -241,20 +203,12 @@ void ui_cursor_off(void) void ui_mouse_on(void) { - if (abstract_ui) { - UI_CALL(mouse_on); - } else { - mch_setmouse(true); - } + UI_CALL(mouse_on); } void ui_mouse_off(void) { - if (abstract_ui) { - UI_CALL(mouse_off); - } else { - mch_setmouse(false); - } + UI_CALL(mouse_off); } // Notify that the current mode has changed. Can be used to change cursor @@ -319,77 +273,82 @@ void ui_detach(UI *ui) } } -static void highlight_start(int mask) +static void highlight_start(int attr_code) { - if (mask > HL_ALL) { - // attribute code - current_highlight_mask = mask; - } else { - // attribute mask - current_highlight_mask |= mask; - } + current_attr_code = attr_code; if (!ui_count) { return; } - set_highlight_args(current_highlight_mask); + set_highlight_args(current_attr_code); } static void highlight_stop(int mask) { - if (mask > HL_ALL) { - // attribute code - current_highlight_mask = HL_NORMAL; - } else { - // attribute mask - current_highlight_mask &= ~mask; + current_attr_code = HL_NORMAL; + + if (!ui_count) { + return; } - set_highlight_args(current_highlight_mask); + set_highlight_args(current_attr_code); } -static void set_highlight_args(int mask) +static void set_highlight_args(int attr_code) { HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1 }; - attrentry_T *aep = NULL; + HlAttrs cterm_attrs = rgb_attrs; - if (mask > HL_ALL) { - aep = syn_cterm_attr2entry(mask); - mask = aep ? aep->ae_attr : 0; + if (attr_code == HL_NORMAL) { + goto end; } - rgb_attrs.bold = mask & HL_BOLD; - rgb_attrs.underline = mask & HL_UNDERLINE; - rgb_attrs.undercurl = mask & HL_UNDERCURL; - rgb_attrs.italic = mask & HL_ITALIC; - rgb_attrs.reverse = mask & (HL_INVERSE | HL_STANDOUT); - HlAttrs cterm_attrs = rgb_attrs; + int rgb_mask = 0; + int cterm_mask = 0; + attrentry_T *aep = syn_cterm_attr2entry(attr_code); - if (aep) { - if (aep->fg_color != normal_fg) { - rgb_attrs.foreground = aep->fg_color; - } + if (!aep) { + goto end; + } - if (aep->bg_color != normal_bg) { - rgb_attrs.background = aep->bg_color; - } + rgb_mask = aep->rgb_ae_attr; + cterm_mask = aep->cterm_ae_attr; + + rgb_attrs.bold = rgb_mask & HL_BOLD; + rgb_attrs.underline = rgb_mask & HL_UNDERLINE; + rgb_attrs.undercurl = rgb_mask & HL_UNDERCURL; + rgb_attrs.italic = rgb_mask & HL_ITALIC; + rgb_attrs.reverse = rgb_mask & (HL_INVERSE | HL_STANDOUT); + cterm_attrs.bold = cterm_mask & HL_BOLD; + cterm_attrs.underline = cterm_mask & HL_UNDERLINE; + cterm_attrs.undercurl = cterm_mask & HL_UNDERCURL; + cterm_attrs.italic = cterm_mask & HL_ITALIC; + cterm_attrs.reverse = cterm_mask & (HL_INVERSE | HL_STANDOUT); + + if (aep->rgb_fg_color != normal_fg) { + rgb_attrs.foreground = aep->rgb_fg_color; + } - if (cterm_normal_fg_color != aep->ae_u.cterm.fg_color) { - cterm_attrs.foreground = aep->ae_u.cterm.fg_color - 1; - } + if (aep->rgb_bg_color != normal_bg) { + rgb_attrs.background = aep->rgb_bg_color; + } - if (cterm_normal_bg_color != aep->ae_u.cterm.bg_color) { - cterm_attrs.background = aep->ae_u.cterm.bg_color - 1; - } + if (cterm_normal_fg_color != aep->cterm_fg_color) { + cterm_attrs.foreground = aep->cterm_fg_color - 1; } + if (cterm_normal_bg_color != aep->cterm_bg_color) { + cterm_attrs.background = aep->cterm_bg_color - 1; + } + +end: UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs)); } -static void parse_abstract_ui_codes(uint8_t *ptr, int len) +static void parse_abstract_ui_codes(uint8_t *ptr, size_t len) { - if (!ui_count) { + if (!ui_active()) { return; } @@ -558,5 +517,13 @@ static void ui_cursor_goto(int new_row, int new_col) } row = new_row; col = new_col; - UI_CALL(cursor_goto, row, col); + pending_cursor_update = true; +} + +static void flush_cursor_update(void) +{ + if (pending_cursor_update) { + pending_cursor_update = false; + UI_CALL(cursor_goto, row, col); + } } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 099f2643d5..4bc3983578 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -38,6 +38,7 @@ struct ui_t { void (*suspend)(UI *ui); void (*set_title)(UI *ui, char *title); void (*set_icon)(UI *ui, char *icon); + void (*stop)(UI *ui); }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/version.c b/src/nvim/version.c index 8a5a3dc07d..02d5f66260 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -136,21 +136,6 @@ static char *(features[]) = { "+tag_binary", "+tag_old_static", "-tag_any_white", -#if defined(UNIX) - - // only Unix can have terminfo instead of termcap -# ifdef TERMINFO - "+terminfo", -# else // ifdef TERMINFO - "-terminfo", -# endif // ifdef TERMINFO -#else // unix always includes termcap support -# ifdef HAVE_TGETENT - "+tgetent", -# else // ifdef HAVE_TGETENT - "-tgetent", -# endif // ifdef HAVE_TGETENT -#endif // if defined(UNIX) "+termresponse", "+textobjects", "+title", @@ -179,6 +164,35 @@ static char *(features[]) = { // clang-format off static int included_patches[] = { + //620, + //619, + //618, + //617, + //616, + //615, + //614, + //613, + //612, + //611, + //610, + //609, + //608, + //607, + //606, + //605, + //604, + //603, + //602, + 601, + //600, + //599, + //598, + //597, + //596, + //595, + //594, + //593, + //592, //591 NA //590, //589 NA @@ -784,19 +798,6 @@ static char *(extra_patches[]) = { NULL }; -int highest_patch(void) -{ - int i; - int h = 0; - - for (i = 0; included_patches[i] != 0; ++i) { - if (included_patches[i] > h) { - h = included_patches[i]; - } - } - return h; -} - /// Checks whether patch `n` has been included. /// /// @param n The patch number. diff --git a/src/nvim/vim.h b/src/nvim/vim.h index a766d71553..5a89a3c861 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -8,7 +8,8 @@ #ifndef NVIM_VIM_H # define NVIM_VIM_H -#define min(X, Y) (X < Y ? X : Y) +#define MIN(X, Y) (X < Y ? X : Y) +#define MAX(X, Y) (X > Y ? X : Y) #include "nvim/types.h" #include "nvim/pos.h" // for linenr_T, MAXCOL, etc... @@ -289,17 +290,6 @@ enum { # endif #endif -/* We need to call mb_stricmp() even when we aren't dealing with a multi-byte - * encoding because mb_stricmp() takes care of all ascii and non-ascii - * encodings, including characters with umlauts in latin1, etc., while - * STRICMP() only handles the system locale version, which often does not - * handle non-ascii properly. */ - -# define MB_STRICMP(d, s) mb_strnicmp((char_u *)(d), (char_u *)(s), \ - (int)MAXCOL) -# define MB_STRNICMP(d, s, n) mb_strnicmp((char_u *)(d), (char_u *)(s), \ - (int)(n)) - #define STRCAT(d, s) strcat((char *)(d), (char *)(s)) #define STRNCAT(d, s, n) strncat((char *)(d), (char *)(s), (size_t)(n)) diff --git a/src/nvim/window.c b/src/nvim/window.c index 52a1853f4b..adc6e9f1c2 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1791,6 +1791,9 @@ static int close_last_window_tabpage(win_T *win, int free_buf, tabpage_T *prev_c } /* Since goto_tabpage_tp above did not trigger *Enter autocommands, do * that now. */ + char_u prev_idx[NUMBUFLEN]; + sprintf((char *)prev_idx, "%i", tabpage_index(prev_curtab)); + apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, FALSE, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); if (old_curbuf != curbuf) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index bee9c18bd8..cbdb1980f0 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -27,6 +27,11 @@ describe('vim_* functions', function() nvim('command', 'let g:v2 = [1, 2, {"v3": 3}]') eq({v1 = 'a', v2 = {1, 2, {v3 = 3}}}, nvim('eval', 'g:')) end) + + it('handles NULL-initialized strings correctly', function() + eq(1, nvim('eval',"matcharg(1) == ['', '']")) + eq({'', ''}, nvim('eval','matcharg(1)')) + end) end) describe('strwidth', function() diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index f3ac90de21..456252522d 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -4,6 +4,7 @@ local clear, nvim, buffer, curbuf, curbuf_contents, window, curwin, eq, neq, ok, feed, rawfeed, insert, eval = helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, helpers.neq, helpers.ok, helpers.feed, helpers.rawfeed, helpers.insert, helpers.eval +local wait = helpers.wait -- check if str is visible at the beginning of some line local function is_visible(str) @@ -55,6 +56,7 @@ describe('window_* functions', function() insert("epilogue") win = curwin() feed('gg') + wait() -- let nvim process the 'gg' command -- cursor position is at beginning eq({1, 0}, window('get_cursor', win)) diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua new file mode 100644 index 0000000000..847362e3de --- /dev/null +++ b/test/functional/autocmd/tabclose_spec.lua @@ -0,0 +1,36 @@ +local helpers = require('test.functional.helpers') +local clear, nvim, buffer, curbuf, curwin, eq, neq, ok = + helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, + helpers.eq, helpers.neq, helpers.ok + +describe('TabClosed', function() + describe('au TabClosed', function() + describe('with * as <afile>', function() + it('matches when closing any tab', function() + clear() + nvim('command', 'au! TabClosed * echom "tabclosed:".expand("<afile>").":".expand("<amatch>").":".tabpagenr()') + repeat + nvim('command', 'tabnew') + until nvim('eval', 'tabpagenr()') == 6 -- current tab is now 6 + eq("\ntabclosed:6:6:5", nvim('command_output', 'tabclose')) -- close last 6, current tab is now 5 + eq("\ntabclosed:5:5:4", nvim('command_output', 'close')) -- close last window on tab, closes tab + eq("\ntabclosed:2:2:3", nvim('command_output', '2tabclose')) -- close tab 2, current tab is now 3 + eq("\ntabclosed:1:1:2\ntabclosed:1:1:1", nvim('command_output', 'tabonly')) -- close tabs 1 and 2 + end) + end) + describe('with NR as <afile>', function() + it('matches when closing a tab whose index is NR', function() + tmp_path = nvim('eval', 'tempname()') + nvim('command', 'au! TabClosed 2 echom "tabclosed:match"') + repeat + nvim('command', 'tabnew') + until nvim('eval', 'tabpagenr()') == 5 -- current tab is now 5 + -- sanity check, we shouldn't match on tabs with numbers other than 2 + eq("\ntabclosed:5:5:4", nvim('command_output', 'tabclose')) + -- close tab page 2, current tab is now 3 + eq("\ntabclosed:2:2:3\ntabclosed:match", nvim('command_output', '2tabclose')) + end) + end) + end) +end) + diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua new file mode 100644 index 0000000000..970ca19726 --- /dev/null +++ b/test/functional/autocmd/tabnew_spec.lua @@ -0,0 +1,24 @@ +local helpers = require('test.functional.helpers') +local clear, nvim, buffer, curbuf, curwin, eq, neq, ok = + helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, + helpers.eq, helpers.neq, helpers.ok + +describe('TabNew', function() + describe('au TabNew', function() + clear() + describe('with * as <afile>', function() + it('matches when opening any new tab', function() + nvim('command', 'au! TabNew * echom "tabnew:".tabpagenr().":".bufnr("")') + eq("\ntabnew:2:1", nvim('command_output', 'tabnew')) + eq("\ntabnew:3:2\n\"test.x\" [New File]", nvim('command_output', 'tabnew test.x')) + end) + end) + describe('with FILE as <afile>', function() + it('matches when opening a new tab for FILE', function() + tmp_path = nvim('eval', 'tempname()') + nvim('command', 'au! TabNew '..tmp_path..' echom "tabnew:match"') + eq("\ntabnew:4:3\ntabnew:match\n\""..tmp_path.."\" [New File]", nvim('command_output', 'tabnew '..tmp_path)) + end) + end) + end) +end) diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua new file mode 100644 index 0000000000..f220c15ef7 --- /dev/null +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -0,0 +1,24 @@ +local helpers = require('test.functional.helpers') +local clear, nvim, buffer, curbuf, curwin, eq, neq, ok = + helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, + helpers.eq, helpers.neq, helpers.ok + +describe('TabNewEntered', function() + describe('au TabNewEntered', function() + describe('with * as <afile>', function() + it('matches when entering any new tab', function() + clear() + nvim('command', 'au! TabNewEntered * echom "tabnewentered:".tabpagenr().":".bufnr("")') + eq("\ntabnewentered:2:2", nvim('command_output', 'tabnew')) + eq("\n\"test.x2\" [New File]\ntabnewentered:3:3", nvim('command_output', 'tabnew test.x2')) + end) + end) + describe('with FILE as <afile>', function() + it('matches when opening a new tab for FILE', function() + tmp_path = nvim('eval', 'tempname()') + nvim('command', 'au! TabNewEntered '..tmp_path..' echom "tabnewentered:match"') + eq("\n\""..tmp_path.."\" [New File]\ntabnewentered:4:4\ntabnewentered:match", nvim('command_output', 'tabnew '..tmp_path)) + end) + end) + end) +end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index e63c79ec80..fc6bf80d7e 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,8 +1,3 @@ -if jit then - -- Disable JIT because of random errors on Travis with OS X. - jit.off(true, true) -end - require('coxpcall') local Loop = require('nvim.loop') local MsgpackStream = require('nvim.msgpack_stream') @@ -234,11 +229,15 @@ local function curbuf(method, ...) return buffer(method, buf, ...) end +local function wait() + session:request('vim_eval', '1') +end + local function curbuf_contents() -- Before inspecting the buffer, execute 'vim_eval' to wait until all -- previously sent keys are processed(vim_eval is a deferred function, and -- only processed after all input) - session:request('vim_eval', '1') + wait() return table.concat(curbuf('get_line_slice', 0, -1, true, true), '\n') end @@ -289,5 +288,6 @@ return { curbuf = curbuf, curwin = curwin, curtab = curtab, - curbuf_contents = curbuf_contents + curbuf_contents = curbuf_contents, + wait = wait } diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index f35b70f93f..19eb4104cd 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -1,23 +1,44 @@ -- vim: set foldmethod=marker foldmarker=[[,]] : -- Tests for ":highlight". +local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers') local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local execute, expect = helpers.execute, helpers.expect +local wait = helpers.wait describe(':highlight', function() setup(clear) it('is working', function() + local screen = Screen.new(35, 10) + screen:attach() -- Basic test if ":highlight" doesn't crash execute('highlight') + -- FIXME(tarruda): We need to be sure the prompt is displayed before + -- continuing, or risk a race condition where some of the following input + -- is discarded resulting in test failure + screen:expect([[ + :highlight | + SpecialKey xxx ctermfg=4 | + guifg=Blue | + EndOfBuffer xxx links to NonText| + | + NonText xxx ctermfg=12 | + gui=bold | + guifg=Blue | + Directory xxx ctermfg=4 | + -- More --^ | + ]]) + feed('q') + wait() -- wait until we're back to normal execute('hi Search') -- Test setting colors. -- Test clearing one color and all doesn't generate error or warning - execute('hi NewGroup term=bold cterm=italic ctermfg=DarkBlue ctermbg=Grey gui= guifg=#00ff00 guibg=Cyan') - execute('hi Group2 term= cterm=') - execute('hi Group3 term=underline cterm=bold') + execute('hi NewGroup cterm=italic ctermfg=DarkBlue ctermbg=Grey gui=NONE guifg=#00ff00 guibg=Cyan') + execute('hi Group2 cterm=NONE') + execute('hi Group3 cterm=bold') execute('redir! @a') execute('hi NewGroup') execute('hi Group2') @@ -29,7 +50,7 @@ describe(':highlight', function() execute('hi Group2') execute('hi clear') execute('hi Group3') - execute([[hi Crash term='asdf]]) + execute([[hi Crash cterm='asdf]]) execute('redir END') -- Filter ctermfg and ctermbg, the numbers depend on the terminal @@ -48,11 +69,15 @@ describe(':highlight', function() expect([[ - NewGroup xxx term=bold cterm=italic ctermfg=2 ctermbg=3 + NewGroup xxx cterm=italic + ctermfg=2 + ctermbg=3 + guifg=#00ff00 + guibg=Cyan Group2 xxx cleared - Group3 xxx term=underline cterm=bold + Group3 xxx cterm=bold NewGroup xxx cleared @@ -65,6 +90,7 @@ describe(':highlight', function() Group3 xxx cleared - E475: term='asdf]]) + E475: cterm='asdf]]) + screen:detach() end) end) diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 1451b268c5..c387d7484c 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -16,7 +16,7 @@ describe('mapping', function() -- Abbreviations with р (0x80) should work. execute('inoreab чкпр vim') - feed('GAчкпр <cr><esc>') + feed('GAчкпр <esc>') -- langmap should not get remapped in insert mode. execute('inoremap { FAIL_ilangmap') @@ -27,10 +27,11 @@ describe('mapping', function() execute('inoremap <expr> { "FAIL_iexplangmap"') feed('o+<esc>') + -- Assert buffer contents. expect([[ test starts here: - vim + vim + +]]) end) diff --git a/test/functional/shell/viml_system_spec.lua b/test/functional/shell/viml_system_spec.lua index d24646e712..85c055d3be 100644 --- a/test/functional/shell/viml_system_spec.lua +++ b/test/functional/shell/viml_system_spec.lua @@ -77,27 +77,7 @@ describe('system()', function() ]]) end) - it('`yes` and is directly interrupted with CTRL-C', function() - feed(':call system("yes")<cr><c-c>') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - Type :quit<Enter> to exit Vim | - ]]) - end) - - it('`yes` and is a little bit later interrupted with CTRL-C', function() + it('`yes` and is interrupted with CTRL-C', function() feed(':call system("yes")<cr>') feed('<c-c>') screen:expect([[ @@ -191,7 +171,7 @@ describe('system()', function() if xclip then describe("with a program that doesn't close stdout", function() it('will exit properly after passing input', function() - eq(nil, eval([[system('xclip -i -selection clipboard', 'clip-data')]])) + eq('', eval([[system('xclip -i -selection clipboard', 'clip-data')]])) eq('clip-data', eval([[system('xclip -o -selection clipboard')]])) end) end) @@ -247,26 +227,6 @@ describe('systemlist()', function() ]]) end) - it('`yes` and is directly interrupted with CTRL-C', function() - feed(':call systemlist("yes | xargs")<cr><c-c>') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - Type :quit<Enter> to exit Vim | - ]]) - end) - it('`yes` and is a little bit later interrupted with CTRL-C', function() feed(':call systemlist("yes | xargs")<cr>') feed('<c-c>') diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 60a49c4ed7..81af908045 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers') local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq +local expect = helpers.expect describe('mappings', function() local cid @@ -38,3 +39,10 @@ describe('mappings', function() check_mapping('<a-s-c-up>', '<c-s-a-up>') end) end) + +describe('input utf sequences that contain CSI/K_SPECIAL', function() + it('ok', function() + feed('i…<esc>') + expect('…') + end) +end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index cd8c2bc399..a20907791b 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -116,7 +116,11 @@ local debug_screen local default_screen_timeout = 2500 if os.getenv('VALGRIND') then - default_screen_timeout = 7500 + default_screen_timeout = default_screen_timeout * 3 +end + +if os.getenv('CI_TARGET') then + default_screen_timeout = default_screen_timeout * 3 end local colors = request('vim_get_color_map') diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua new file mode 100644 index 0000000000..a7a4172440 --- /dev/null +++ b/test/functional/ui/sign_spec.lua @@ -0,0 +1,45 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute + +describe('Signs', function() + local screen + + before_each(function() + clear() + screen = Screen.new() + screen:attach() + screen:set_default_attr_ignore( {{}, {bold=true, foreground=255}} ) + end) + + after_each(function() + screen:detach() + end) + + describe(':sign place', function() + it('shadows previously placed signs', function() + feed('ia<cr>b<cr>c<cr><esc>') + execute('sign define piet text=>> texthl=Search') + execute('sign define pietx text=>! texthl=Search') + execute('sign place 1 line=1 name=piet buffer=1') + execute('sign place 2 line=3 name=piet buffer=1') + execute('sign place 3 line=1 name=pietx buffer=1') + screen:expect([[ + >!a | + b | + >>c | + ^ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + ~ | + :sign place 3 line=1 name=pietx buffer=1 | + ]]) + end) + end) +end) diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua new file mode 100644 index 0000000000..ee2f8b15f8 --- /dev/null +++ b/test/functional/ui/wildmode_spec.lua @@ -0,0 +1,32 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute + +describe("'wildmode'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + describe("'wildmenu'", function() + it(':sign <tab> shows wildmenu completions', function() + execute('set wildmode') + execute('set wildmenu') + feed(':sign <tab>') + screen:expect([[ + | + ~ | + ~ | + define jump list > | + :sign define^ | + ]]) + end) + end) +end) diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index 7051ed3797..9e5a2349c8 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -170,12 +170,20 @@ describe('fs function', function() it('returns the absolute path when given an executable relative to the current dir', function() local old_dir = lfs.currentdir() + lfs.chdir(directory) local relative_executable = './' .. executable_name + -- Don't test yet; we need to chdir back first. local res = exe(relative_executable) + lfs.chdir(old_dir) - eq(absolute_executable, res) + + -- Need to canonicalize the absolute path taken from arg[0], because the + -- path may have a symlink in it. For example, /home is symlinked in + -- FreeBSD 10's VM image. + local expected = exe(absolute_executable) + eq(expected, res) end) end) diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index f17c7ba666..d4c2e088a4 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -102,7 +102,10 @@ local Gcc = { '-D "EXTERN=extern"', '-D "INIT(...)="', '-D_GNU_SOURCE', - '-DINCLUDE_GENERATED_DECLARATIONS' + '-DINCLUDE_GENERATED_DECLARATIONS', + + -- Needed for FreeBSD + '-D "_Thread_local="' } } diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 99fc337d4f..1d56ba1adb 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -12,7 +12,6 @@ option(USE_BUNDLED "Use bundled dependencies." ON) option(USE_BUNDLED_LIBUNIBILIUM "Use the bundled libunibilium." ${USE_BUNDLED}) option(USE_BUNDLED_LIBTERMKEY "Use the bundled libtermkey." ${USE_BUNDLED}) -option(USE_BUNDLED_LIBTICKIT "Use the bundled libtickit." ${USE_BUNDLED}) option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED}) option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED}) option(USE_BUNDLED_LUAJIT "Use the bundled version of luajit." ${USE_BUNDLED}) @@ -51,31 +50,21 @@ include(ExternalProject) set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.2.0.tar.gz) set(LIBUV_SHA1 38d1ba349fcfc1b221140523ba3d7cf3ea38c20b) -set(LIBUV_MD5 e7712a100635ec2ca1f145f2bb217200) -set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/ecf4b09acd29746829b6a02939db91dfdec635b4.tar.gz) -set(MSGPACK_SHA1 c160ff99f20d9d0a25bea0a57f4452f1c9bde370) -set(MSGPACK_MD5 3599eaf904b8ba0c36cea7ed80973364) +set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/b4eba4ba090e8cd6d8332410e252296a62b7e56d.tar.gz) +set(MSGPACK_SHA1 768e60e3936cd0d0e4f1d58ca86a78eeb895931b) set(LUAJIT_URL http://luajit.org/download/LuaJIT-2.0.3.tar.gz) set(LUAJIT_SHA1 2db39e7d1264918c2266b0436c313fbd12da4ceb) -set(LUAJIT_MD5 f14e9104be513913810cd59c8c658dc0) set(LUAROCKS_URL https://github.com/keplerproject/luarocks/archive/0587afbb5fe8ceb2f2eea16f486bd6183bf02f29.tar.gz) set(LUAROCKS_SHA1 61a894fd5d61987bf7e7f9c3e0c5de16ba4b68c4) -set(LUAROCKS_MD5 0f53f42909fbcd2c88be303e8f970516) set(LIBUNIBILIUM_URL https://github.com/mauke/unibilium/archive/520abbc8b26910e2580619f669b5cc2c4ef7f864.tar.gz) set(LIBUNIBILIUM_SHA1 c546e5e8861380f5c109a256f25c93419e4076bf) -set(LIBUNIBILIUM_MD5 d80d1fc45b22b1e92bebd5bf76e8a98b) -set(LIBTERMKEY_URL https://github.com/neovim/libtermkey/archive/7b3bdafdf589d08478f2493273d4d75636ecc183.tar.gz) -set(LIBTERMKEY_SHA1 28bfe54dfd9269910a132b51dee7725a2121578d) -set(LIBTERMKEY_MD5 f0bac9c2467cc80c821be937ea5c13bc) - -set(LIBTICKIT_URL https://github.com/neovim/libtickit/archive/33f4afb3891df05955429acbf5b406dfe87ec22b.tar.gz) -set(LIBTICKIT_SHA1 3aab459b9fb3cd83e85ac2e08f05e5f162c8c9d2) -set(LIBTICKIT_MD5 19ee9271c16716620d0906db74158ec6) +set(LIBTERMKEY_URL https://github.com/neovim/libtermkey/archive/8c0cb7108cc63218ea19aa898968eede19e19603.tar.gz) +set(LIBTERMKEY_SHA1 54e8b6914dab10d4467d2a563f80053a99849fcb) if(USE_BUNDLED_LIBUNIBILIUM) ExternalProject_Add(libunibilium @@ -87,7 +76,6 @@ if(USE_BUNDLED_LIBUNIBILIUM) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libunibilium -DURL=${LIBUNIBILIUM_URL} -DEXPECTED_SHA1=${LIBUNIBILIUM_SHA1} - -DEXPECTED_MD5=${LIBUNIBILIUM_MD5} -DTARGET=libunibilium -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -109,7 +97,6 @@ if(USE_BUNDLED_LIBTERMKEY) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libtermkey -DURL=${LIBTERMKEY_URL} -DEXPECTED_SHA1=${LIBTERMKEY_SHA1} - -DEXPECTED_MD5=${LIBTERMKEY_MD5} -DTARGET=libtermkey -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -124,31 +111,6 @@ if(USE_BUNDLED_LIBTERMKEY) add_dependencies(libtermkey libunibilium) endif() -if(USE_BUNDLED_LIBTICKIT) - ExternalProject_Add(libtickit - PREFIX ${DEPS_BUILD_DIR} - URL ${LIBTICKIT_URL} - DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libtickit - DOWNLOAD_COMMAND ${CMAKE_COMMAND} - -DPREFIX=${DEPS_BUILD_DIR} - -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libtickit - -DURL=${LIBTICKIT_URL} - -DEXPECTED_SHA1=${LIBTICKIT_SHA1} - -DEXPECTED_MD5=${LIBTICKIT_MD5} - -DTARGET=libtickit - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake - CONFIGURE_COMMAND "" - BUILD_IN_SOURCE 1 - BUILD_COMMAND "" - INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} - PREFIX=${DEPS_INSTALL_DIR} - PKG_CONFIG_PATH=${DEPS_LIB_DIR}/pkgconfig - CFLAGS=-fPIC - install) - list(APPEND THIRD_PARTY_DEPS libtickit) - add_dependencies(libtickit libtermkey) -endif() - if(USE_BUNDLED_LIBUV) ExternalProject_Add(libuv PREFIX ${DEPS_BUILD_DIR} @@ -159,12 +121,12 @@ if(USE_BUNDLED_LIBUV) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libuv -DURL=${LIBUV_URL} -DEXPECTED_SHA1=${LIBUV_SHA1} - -DEXPECTED_MD5=${LIBUV_MD5} -DTARGET=libuv -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND sh ${DEPS_BUILD_DIR}/src/libuv/autogen.sh && ${DEPS_BUILD_DIR}/src/libuv/configure --with-pic --disable-shared - --prefix=${DEPS_INSTALL_DIR} CC=${DEPS_C_COMPILER} + --prefix=${DEPS_INSTALL_DIR} --libdir=${DEPS_INSTALL_DIR}/lib + CC=${DEPS_C_COMPILER} INSTALL_COMMAND ${MAKE_PRG} install) list(APPEND THIRD_PARTY_DEPS libuv) endif() @@ -179,7 +141,6 @@ if(USE_BUNDLED_MSGPACK) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/msgpack -DURL=${MSGPACK_URL} -DEXPECTED_SHA1=${MSGPACK_SHA1} - -DEXPECTED_MD5=${MSGPACK_MD5} -DTARGET=msgpack -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND cmake ${DEPS_BUILD_DIR}/src/msgpack @@ -205,7 +166,6 @@ if(USE_BUNDLED_LUAJIT) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luajit -DURL=${LUAJIT_URL} -DEXPECTED_SHA1=${LUAJIT_SHA1} - -DEXPECTED_MD5=${LUAJIT_MD5} -DTARGET=luajit -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -214,6 +174,10 @@ if(USE_BUNDLED_LUAJIT) INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} CFLAGS=-fPIC + CFLAGS+=-DLUAJIT_DISABLE_JIT + CFLAGS+=-DLUA_USE_APICHECK + CFLAGS+=-DLUA_USE_ASSERT + CCDEBUG+=-g BUILDMODE=static install) list(APPEND THIRD_PARTY_DEPS luajit) @@ -234,7 +198,6 @@ if(USE_BUNDLED_LUAROCKS) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luarocks -DURL=${LUAROCKS_URL} -DEXPECTED_SHA1=${LUAROCKS_SHA1} - -DEXPECTED_MD5=${LUAROCKS_MD5} -DTARGET=luarocks -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake BUILD_IN_SOURCE 1 @@ -276,7 +239,7 @@ if(USE_BUNDLED_LUAROCKS) add_custom_command(OUTPUT ${DEPS_LIB_DIR}/luarocks/rocks/nvim-client COMMAND ${DEPS_BIN_DIR}/luarocks - ARGS build https://raw.githubusercontent.com/neovim/lua-client/af161f5f89c7877d0f650b5de6b3a6126b38f012/nvim-client-0.0.1-10.rockspec CC=${DEPS_C_COMPILER} LD=${DEPS_C_COMPILER} LIBUV_DIR=${DEPS_INSTALL_DIR} + ARGS build https://raw.githubusercontent.com/neovim/lua-client/fabecc56a7c7a3fc15fe6a1a765b58d61bc8e922/nvim-client-0.0.1-11.rockspec CC=${DEPS_C_COMPILER} LD=${DEPS_C_COMPILER} LIBUV_DIR=${DEPS_INSTALL_DIR} DEPENDS lpeg libuv) add_custom_target(nvim-client DEPENDS ${DEPS_LIB_DIR}/luarocks/rocks/nvim-client) diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/third-party/cmake/DownloadAndExtractFile.cmake index b0d28355cb..875d45795d 100644 --- a/third-party/cmake/DownloadAndExtractFile.cmake +++ b/third-party/cmake/DownloadAndExtractFile.cmake @@ -10,8 +10,8 @@ if(NOT DEFINED DOWNLOAD_DIR) message(FATAL_ERROR "DOWNLOAD_DIR must be defined.") endif() -if((NOT DEFINED EXPECTED_SHA1) OR (NOT DEFINED EXPECTED_MD5)) - message(FATAL_ERROR "EXPECTED_SHA1 or EXPECTED_MD5 must be defined.") +if(NOT DEFINED EXPECTED_SHA1) + message(FATAL_ERROR "EXPECTED_SHA1 must be defined.") endif() if(NOT DEFINED TARGET) @@ -58,16 +58,6 @@ message(STATUS "downloading... dst='${file}' timeout='${timeout_msg}'") -if((DEFINED EXPECTED_SHA1) AND (${CMAKE_VERSION} VERSION_GREATER 2.8.10)) - if(NOT (EXPECTED_SHA1 STREQUAL "0000000000000000000000000000000000000000")) - set(hash_args EXPECTED_HASH SHA1=${EXPECTED_SHA1}) - endif() -else() - if(NOT (EXPECTED_MD5 STREQUAL "00000000000000000000000000000000")) - set(hash_args EXPECTED_MD5 ${EXPECTED_MD5}) - endif() -endif() - file(DOWNLOAD ${URL} ${file} ${timeout_args} ${hash_args} @@ -85,6 +75,33 @@ if(NOT status_code EQUAL 0) ") endif() +set(NULL_SHA1 "0000000000000000000000000000000000000000") + +# Allow users to use "SKIP" or "skip" as the sha1 to skip checking the hash. +# You can still use the all zeros hash too. +if((EXPECTED_SHA1 STREQUAL "SKIP") OR (EXPECTED_SHA1 STREQUAL "skip")) + set(EXPECTED_SHA1 ${NULL_SHA1}) +endif() + +# We could avoid computing the SHA1 entirely if a NULL_SHA1 was given, +# but we want to warn users of an empty file. +file(SHA1 ${file} ACTUAL_SHA1) +if(ACTUAL_SHA1 STREQUAL "da39a3ee5e6b4b0d3255bfef95601890afd80709") + # File was empty. It's likely due to lack of SSL support. + message(FATAL_ERROR + "Failed to download ${URL}. The file is empty and likely means CMake " + "was built without SSL support. Please use a version of CMake with " + "proper SSL support. See " + "https://github.com/neovim/neovim/wiki/Building-Neovim#build-prerequisites " + "for more information.") +elseif((NOT EXPECTED_SHA1 STREQUAL NULL_SHA1) AND + (NOT EXPECTED_SHA1 STREQUAL ACTUAL_SHA1)) + # Wasn't a NULL SHA1 and we didn't match, so we fail. + message(FATAL_ERROR + "Failed to download ${URL}. Expected a SHA1 of " + "${EXPECTED_SHA1} but got ${ACTUAL_SHA1} instead.") +endif() + message(STATUS "downloading... done") # Slurped from a generated extract-TARGET.cmake file. @@ -140,4 +157,3 @@ message(STATUS "extracting... [clean up]") file(REMOVE_RECURSE "${ut_dir}") message(STATUS "extracting... done") - |