diff options
71 files changed, 1524 insertions, 998 deletions
diff --git a/.travis.yml b/.travis.yml index 84caa349ea..1437f7e25b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,8 +77,10 @@ matrix: env: CLANG_SANITIZER=TSAN - os: osx compiler: clang + osx_image: xcode7.3 # macOS 10.11 - os: osx compiler: gcc-4.9 + osx_image: xcode7.3 # macOS 10.11 fast_finish: true before_install: .ci/before_install.sh diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index dc37ff1d6d..76dd3c7d4c 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -256,7 +256,6 @@ Name triggered by ~ Options |FileType| when the 'filetype' option has been set |Syntax| when the 'syntax' option has been set -|EncodingChanged| after the 'encoding' option has been changed |TermChanged| after the value of 'term' has changed |OptionSet| after setting any option @@ -557,9 +556,6 @@ CursorMoved After the cursor was moved in Normal or Visual CursorMovedI After the cursor was moved in Insert mode. Not triggered when the popup menu is visible. Otherwise the same as CursorMoved. - *EncodingChanged* -EncodingChanged Fires off after the 'encoding' option has been - changed. Useful to set up fonts, for example. *FileAppendCmd* FileAppendCmd Before appending to a file. Should do the appending to the file. Use the '[ and '] @@ -618,9 +614,6 @@ FileChangedShell When Vim notices that the modification time of *FileChangedShellPost* FileChangedShellPost After handling a file that was changed outside of Vim. Can be used to update the statusline. - *FileEncoding* -FileEncoding Obsolete. It still works and is equivalent - to |EncodingChanged|. *FileReadCmd* FileReadCmd Before reading a file with a ":read" command. Should do the reading of the file. |Cmd-event| diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt new file mode 100644 index 0000000000..d108aca62f --- /dev/null +++ b/runtime/doc/deprecated.txt @@ -0,0 +1,43 @@ +*deprecated.txt* {Nvim} + + + NVIM REFERENCE MANUAL + + +Nvim *deprecated* + +============================================================================== + +Normal commands ~ +*]f* +*[f* Same as "gf". + + +Commands ~ +*:rv* +*:rviminfo* Deprecated alias to |:rshada| command. +*:wv* +*:wviminfo* Deprecated alias to |:wshada| command. + + +Events ~ +*EncodingChanged* Never fired; 'encoding' is always "utf-8". +*FileEncoding* Never fired; equivalent to |EncodingChanged|. + +Highlight groups ~ +*hl-VisualNOS* Obsolete. |vim-differences| {Nvim} + +Functions ~ +*buffer_exists()* Obsolete name for |bufexists()|. +*buffer_name()* Obsolete name for |bufname()|. +*buffer_number()* Obsolete name for |bufnr()|. +*file_readable()* Obsolete name for |filereadable()|. +*highlight_exists()* Obsolete name for |hlexists()|. +*highlightID()* Obsolete name for |hlID()|. +*last_buffer_nr()* Obsolete name for bufnr("$"). + +Options ~ +*'fe'* 'fenc'+'enc' before Vim 6.0; no longer used. +*'vi'* +*'viminfo'* Deprecated alias to 'shada' option. + diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index ddec137079..b738da9bec 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -213,13 +213,13 @@ If the function acts on an object then {thing} is the name of that object with a {thing} that groups functions under a common concept). Use existing common {action} names if possible: - add append to, or insert into, a collection - get get a thing (or subset of things by some query) - set set a thing - del delete a thing (or group of things) - list get all things + add Append to, or insert into, a collection + get Get a thing (or subset of things by some query) + set Set a thing + del Delete a thing (or group of things) + list Get all things -Use consistent names for {thing} in all API function. E.g. a buffer is called +Use consistent names for {thing} in all API functions. E.g. a buffer is called "buf" everywhere, not "buffer" in some places and "buf" in others. Example: `nvim_get_current_line` acts on the global editor state; the common diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 6f85436ab4..8b6c14cc52 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -267,9 +267,6 @@ CTRL-^ Edit the alternate file. Mostly the alternate file is files. See |CTRL-^| above for further details. -[count]]f *]f* *[f* -[count][f Same as "gf". Deprecated. - *gf* *E446* *E447* [count]gf Edit the file whose name is under or after the cursor. Mnemonic: "goto file". diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d97a1400ce..df5713c63d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2414,8 +2414,6 @@ bufexists({expr}) *bufexists()* for MS-Windows 8.3 names in the form "c:\DOCUME~1" Use "bufexists(0)" to test for the existence of an alternate file name. - *buffer_exists()* - Obsolete name: buffer_exists(). buflisted({expr}) *buflisted()* The result is a Number, which is non-zero if a buffer called @@ -2454,8 +2452,6 @@ bufname({expr}) *bufname()* bufname(3) name of buffer 3 bufname("%") name of current buffer bufname("file2") name of buffer where "file2" matches. -< *buffer_name()* - Obsolete name: buffer_name(). *bufnr()* bufnr({expr} [, {create}]) @@ -2471,10 +2467,6 @@ bufnr({expr} [, {create}]) of existing buffers. Note that not all buffers with a smaller number necessarily exist, because ":bwipeout" may have removed them. Use bufexists() to test for the existence of a buffer. - *buffer_number()* - Obsolete name: buffer_number(). - *last_buffer_nr()* - Obsolete name for bufnr("$"): last_buffer_nr(). bufwinnr({expr}) *bufwinnr()* The result is a Number, which is the number of the first @@ -3262,8 +3254,6 @@ filereadable({file}) *filereadable()* expression, which is used as a String. If you don't care about the file being readable you can use |glob()|. - *file_readable()* - Obsolete name: file_readable(). filewritable({file}) *filewritable()* @@ -4186,8 +4176,6 @@ hlexists({name}) *hlexists()* defined in some way. Not necessarily when highlighting has been defined for it, it may also have been used for a syntax item. - *highlight_exists()* - Obsolete name: highlight_exists(). *hlID()* hlID({name}) The result is a Number, which is the ID of the highlight group @@ -4197,8 +4185,6 @@ hlID({name}) The result is a Number, which is the ID of the highlight group group. For example, to get the background color of the "Comment" group: > :echo synIDattr(synIDtrans(hlID("Comment")), "bg") -< *highlightID()* - Obsolete name: highlightID(). hostname() *hostname()* The result is a String, which is the name of the machine on diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 60acfbf700..e8ddc56f7a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 7.4. Last change: 2016 Apr 12 +*options.txt* For Vim version 7.4. Last change: 2016 Mar 19 VIM REFERENCE MANUAL by Bram Moolenaar @@ -899,7 +899,7 @@ A jump table for the options with a short description can be found at |Q_op|. - The backup file will be created in the first directory in the list where this is possible. The directory must exist, Vim will not create it for you. - - Empty means that no backup file will be created ( 'patchmode' is + - Empty means that no backup file will be created ('patchmode' is impossible!). Writing may fail because of this. - A directory "." means to put the backup file in the same directory as the edited file. @@ -1449,9 +1449,6 @@ A jump table for the options with a short description can be found at |Q_op|. comment text. Currently only used to add markers for folding, see |fold-marker|. - *'compatible'* *'cp'* *'nocompatible'* *'nocp'* -'compatible' 'cp' Removed. |vim-differences| {Nvim} - *'complete'* *'cpt'* *E535* 'complete' 'cpt' string (default: ".,w,b,u,t") local to buffer @@ -1459,7 +1456,7 @@ A jump table for the options with a short description can be found at |Q_op|. when CTRL-P or CTRL-N are used. It is also used for whole-line completion |i_CTRL-X_CTRL-L|. It indicates the type of completion and the places to scan. It is a comma separated list of flags: - . scan the current buffer ( 'wrapscan' is ignored) + . scan the current buffer ('wrapscan' is ignored) w scan buffers from other windows b scan other loaded buffers that are in the buffer list u scan the unloaded buffers that are in the buffer list @@ -1848,9 +1845,6 @@ A jump table for the options with a short description can be found at |Q_op|. the cursor would skip over it and jump to the following occurrence. - *'cryptmethod'* *'cm'* -'cryptmethod' Removed. |vim-differences| {Nvim} - *'cscopepathcomp'* *'cspc'* 'cscopepathcomp' 'cspc' number (default 0) global @@ -2129,8 +2123,14 @@ A jump table for the options with a short description can be found at |Q_op|. hor horizontally, height of windows is not affected both width and height of windows is affected - *'ed'* *'edcompatible'* *'noed'* *'noedcompatible'* -'edcompatible' 'ed' Removed. |vim-differences| {Nvim} + *'emoji'* *'emo'* +'emoji' 'emo' boolean (default: on) + global + {not in Vi} + {only available when compiled with the |+multi_byte| + feature} + When on all Unicode emoji characters are considered to be full width. + *'encoding'* *'enc'* *E543* 'encoding' 'enc' string (default: "utf-8") @@ -2337,11 +2337,6 @@ A jump table for the options with a short description can be found at |Q_op|. This option can not be changed when 'modifiable' is off. - *'fe'* - NOTE: Before version 6.0 this option specified the encoding for the - whole of Vim, this was a mistake. Now use 'encoding' instead. The - old short name was 'fe', which is no longer used. - *'fileencodings'* *'fencs'* 'fileencodings' 'fencs' string (default: "ucs-bom,utf-8,default,latin1") global @@ -3096,7 +3091,7 @@ A jump table for the options with a short description can be found at |Q_op|. The same applies to the modeless selection. *'go-P'* 'P' Like autoselect but using the "+ register instead of the "* - register. + register. *'go-A'* 'A' Autoselect for the modeless selection. Like 'a', but only applies to the modeless selection. @@ -3164,9 +3159,6 @@ A jump table for the options with a short description can be found at |Q_op|. removing it after the GUI has started has no effect. - *'guipty'* *'noguipty'* -'guipty' Removed. |vim-differences| {Nvim} - *'guitablabel'* *'gtl'* 'guitablabel' 'gtl' string (default empty) global @@ -3411,12 +3403,6 @@ A jump table for the options with a short description can be found at |Q_op|. Can be overruled by using "\c" or "\C" in the pattern, see |/ignorecase|. - *'imactivatefunc'* *'imaf'* -'imactivatefunc' 'imaf' Removed. |vim-differences| {Nvim} - - *'imactivatekey'* *'imak'* -'imactivatekey' 'imak' Removed. |vim-differences| {Nvim} - *'imcmdline'* *'imc'* *'noimcmdline'* *'noimc'* 'imcmdline' 'imc' boolean (default off) global @@ -3473,9 +3459,6 @@ A jump table for the options with a short description can be found at |Q_op|. The value is set to 1 when it is not -1 and setting the 'keymap' option to a valid keymap name. - *'imstatusfunc'* *'imsf'* -'imstatusfunc' 'imsf' Removed. |vim-differences| {Nvim} - *'include'* *'inc'* 'include' 'inc' string (default "^\s*#\s*include") global or local to buffer |global-local| @@ -3732,9 +3715,6 @@ A jump table for the options with a short description can be found at |Q_op|. Insert two spaces after a '.', '?' and '!' with a join command. Otherwise only one space is inserted. - *'key'* -'key' Removed. |vim-differences| {Nvim} - *'keymap'* *'kmp'* *E544* 'keymap' 'kmp' string (default "") local to buffer @@ -4013,9 +3993,6 @@ A jump table for the options with a short description can be found at |Q_op|. Note that using the "-u NONE" and "--noplugin" command line arguments reset this option. |-u| |--noplugin| - *'macatsui'* *'nomacatsui'* -'macatsui' Removed. |vim-differences| {Nvim} - *'magic'* *'nomagic'* 'magic' boolean (default on) global @@ -4410,7 +4387,7 @@ A jump table for the options with a short description can be found at |Q_op|. respectively; see |CTRL-A| for more info on these commands. alpha If included, single alphabetical characters will be incremented or decremented. This is useful for a list with a - letter index a), b), etc. *octal-nrformats* + letter index a), b), etc. *octal-nrformats* octal If included, numbers that start with a zero will be considered to be octal. Example: Using CTRL-A on "007" results in "010". hex If included, numbers starting with "0x" or "0X" will be @@ -4440,7 +4417,7 @@ A jump table for the options with a short description can be found at |Q_op|. relative to the cursor. Together with 'number' there are these four combinations (cursor in line 3): - 'nonu' 'nu' 'nonu' 'nu' + 'nonu' 'nu' 'nonu' 'nu' 'nornu' 'nornu' 'rnu' 'rnu' |apple | 1 apple | 2 apple | 2 apple @@ -4859,9 +4836,6 @@ A jump table for the options with a short description can be found at |Q_op|. For the ":substitute" command the number of substitutions is used instead of the number of lines. - *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* -'restorescreen' 'rs' Removed. |vim-differences| {Nvim} - *'revins'* *'ri'* *'norevins'* *'nori'* 'revins' 'ri' boolean (default off) global @@ -5569,9 +5543,6 @@ A jump table for the options with a short description can be found at |Q_op|. shm=a Abbreviation, but no loss of information. shm=at Abbreviation, and truncate message when necessary. - *'shortname'* *'sn'* *'noshortname'* *'nosn'* -'shortname' 'sn' Removed. |vim-differences| {Nvim} - *'showbreak'* *'sbr'* *E595* 'showbreak' 'sbr' string (default "") global @@ -6185,9 +6156,6 @@ A jump table for the options with a short description can be found at |Q_op|. This option is used together with 'bufhidden' and 'buftype' to specify special kinds of buffers. See |special-buffers|. - *'swapsync'* *'sws'* -'swapsync' 'sws' Removed. |vim-differences| {Nvim} - *'switchbuf'* *'swb'* 'switchbuf' 'swb' string (default "") global @@ -6411,9 +6379,6 @@ A jump table for the options with a short description can be found at |Q_op|. Resetting this option is useful when using a ":tag" command in a mapping which should not change the tagstack. - *'term'* *E529* *E530* *E531* -'term' Removed. |vim-differences| {Nvim} - *'termbidi'* *'tbidi'* *'notermbidi'* *'notbidi'* 'termbidi' 'tbidi' boolean (default off) @@ -6427,9 +6392,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'arabicshape' is ignored, but 'rightleft' isn't changed automatically. For further details see |arabic.txt|. - *'termencoding'* *'tenc'* -'termencoding' 'tenc' Removed. |vim-differences| {Nvim} - *'termguicolors'* *'tgc'* 'termguicolors' 'tgc' boolean (default off) global @@ -6584,27 +6546,9 @@ A jump table for the options with a short description can be found at |Q_op|. to be garbled (e.g., when it contains a CR or NL character). {not available when compiled without the |+statusline| feature} - *'toolbar'* *'tb'* -'toolbar' 'tb' Removed. |vim-differences| {Nvim} - - *'toolbariconsize'* *'tbis'* -'toolbariconsize' 'tbis' Removed. |vim-differences| {Nvim} - - *'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'* -'ttybuiltin' 'tbi' Removed. |vim-differences| {Nvim} - *'ttyfast'* *'tf'* *'nottyfast'* *'notf'* 'ttyfast' 'tf' Removed. |vim-differences| {Nvim} - *'ttymouse'* *'ttym'* -'ttymouse' 'ttym' Removed. |vim-differences| {Nvim} - - *'ttyscroll'* *'tsl'* -'ttyscroll' 'tsl' Removed. |vim-differences| {Nvim} - - *'ttytype'* *'tty'* -'ttytype' 'tty' Alias for 'term'. Removed. |vim-differences| {Nvim} - *'undodir'* *'udir'* *E926* 'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo") global @@ -6759,12 +6703,6 @@ A jump table for the options with a short description can be found at |Q_op|. with Unix. The Unix version of Vim cannot source dos format scripts, but the Windows version of Vim can source unix format scripts. - *'viminfo'* *'vi'* -'viminfo' 'vi' string - global - Deprecated alias for 'shada' option. Is kept for compatibility - reasons. - *'virtualedit'* *'ve'* 'virtualedit' 've' string (default "") global @@ -6810,9 +6748,6 @@ A jump table for the options with a short description can be found at |Q_op|. Give a warning message when a shell command is used while the buffer has been changed. - *'weirdinvert'* *'wiv'* *'noweirdinvert'* *'nowiv'* -'weirdinvert' 'wiv' Removed. |vim-differences| {Nvim} - *'whichwrap'* *'ww'* 'whichwrap' 'ww' string (Vim default: "b,s", Vi default: "") global diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 63dbb00896..a8b5966950 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -12,16 +12,16 @@ Nvim delegates some features to dynamic "providers". Python integration *provider-python* Nvim supports the Vim legacy |python-vim| and |python3| interfaces via -external Python interpreters connected via |RPC|, - +external Python interpreters connected via |RPC|. Note: Only the Vim 7.3 API is supported; bindeval (Vim 7.4) is not. - PYTHON QUICKSTART ~ -If you used a package manager to install Nvim there's a good chance that -it also provides the `neovim` Python package. If it doesn't, follow these -steps to install the package with Python's package manager, `pip`. +If you used a package manager to install Nvim, you might already have the +required `neovim` Python package. Run |:CheckHealth| to see if your system is +up-to-date. + +Following are steps to install the package with Python's `pip` tool. Note: Depending on your system, `pip` might refer to Python 2 or Python 3, which is why the following instructions mention `pip2` or `pip3` @@ -74,24 +74,19 @@ Note: This requires you to install the python3-neovim module properly. > let g:python3_host_skip_check = 1 -TROUBLESHOOTING *python-trouble* - -If you have trouble with a plugin that uses the `neovim` Python client, use -the |:CheckHealth| command to diagnose your setup. - ============================================================================== Ruby integration *provider-ruby* Nvim supports the Vim legacy |ruby-vim| interface via external Ruby interpreters connected via |RPC|. +Run |:CheckHealth| to see if your system is up-to-date. RUBY QUICKSTART ~ To use Vim Ruby plugins with Nvim, just install the latest `neovim` RubyGem: > $ gem install neovim - RUBY PROVIDER CONFIGURATION ~ *g:loaded_ruby_provider* To disable Ruby support: > diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index e2a44541ae..44d68c7b38 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1507,12 +1507,6 @@ However, to properly parse such a complex file, an external filter should be used. See the description further above how to make such a filter known by Vim. - *errorformat-Perl* -In $VIMRUNTIME/tools you can find the efm_perl.pl script, which filters Perl -error messages into a format that quickfix mode will understand. See the -start of the file about how to use it. (This script is deprecated, see -|compiler-perl|.) - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/quotes.txt b/runtime/doc/quotes.txt deleted file mode 100644 index c35fb2f139..0000000000 --- a/runtime/doc/quotes.txt +++ /dev/null @@ -1,275 +0,0 @@ -*quotes.txt* For Vim version 7.4. Last change: 2010 Nov 03 - - - VIM REFERENCE MANUAL by Bram Moolenaar - - - *quotes* -Here are some nice quotes about Vim that I collected from news and mail. - - -vim (vim) noun - Ebullient vitality and energy. [Latin, accusative of vis, -strength] (Dictionary) - -Vim is so much better than vi that a great many of my old vi :map's became -immediately obsolete! (Tony Nugent, Australia) - -Coming with a very GUI mindset from Windows, I always thought of people using -Vi as some kind of outer space alien in human clothes. Once I tried I really -got addicted by its power and now I found myself typing Vim keypresses in the -oddest places! That's why I would like to see Vim embedded in every -application which deals with text editing. (Jos Fonseca) - -I was a 12-year emacs user who switched to Vim about a year ago after finally -giving up on the multiple incompatible versions, flaky contributed packages, -disorganized keystrokes, etc. And it was one of the best moves I ever made. -(Joel Burton) - -Although all of the programs were used during the preparation of the new and -revised material, most of the editing was done with Vim versions 4.5 and 5.0 -under GNU-Linux (Redhat 4.2). (Arnold Robbins, Israel, author of "Learning -the Vi editor") - -Out of all the open software i've ever seen and used, and i've seen a lot, Vim -is the best, most useful and highest quality to work with, second only to the -linux kernel itself. (Peter Jay Salzman) - -It's well worth noting that the _entirety_ of SourceForge was written using -Vim and its nifty PHP syntax highlighting. I think the entire SF.net tech -staff uses Vim and we're all excited to have you aboard! (Tim Perdue) - -Vim is one of a select bunch of tools for which I have no substitute. It is -a brilliant piece of work! (Biju Chacko) - -A previous girlfriend of mine switched to emacs. Needless to say, the -relationship went nowhere. (Geoffrey Mann) - -I rarely think about Vim, in the same way that I guess a fish rarely thinks -about water. It's the environment in which everything else happens. I'm a -fairly busy system administrator working on a lot of different platforms. Vim -is the only thing that's consistent across all my systems, and it's just about -the only thing that doesn't break from time to time. When a new system comes -in the door without Vim, I install it right away. Great to have a tool that's -the same everywhere, that's completely reliable, so I can ignore it and think -about other things. (Pete Schaeffer) - -Having recently succeeded in running Vim via telnet through a Nokia -Communicator, I can now report that it works nicely on a Palm Pilot too. -(Allan Kelly, Scotland) - -You've done a tremendous job with 'VIM', Bram! The more I use it, the more -impressed I get (I am an old 'vi' die hard who once started out with early -versions of 'emacs' in the late 1970's and was relieved by finding 'vi' in the -first UNIX I came across in 1983). In my opinion, it's about time 'VIM' -replace 'emacs' as the standard for top editors. (Bo Thide', Sweden) - -I love and use VIM heavily too. (Larry Wall) - -Vi is like a Ferrari, if you're a beginner, it handles like a bitch, but once -you get the hang of it, it's small, powerful and FAST! (Unknown) -VIM is like a new model Ferrari, and sounds like one too - "VIIIIIIMMM!" -(Stephen Riehm, Germany) - -Schon bei Nutzung eines Bruchteils der VIM-Funktionen wird der Benutzer recht -schnell die Vorzuege dieses Editors kennen- und schaetzenlernen. -Translated: Even when only using a fraction of VIM-functions, the user will -quickly get used to and appreciate the advantages of this editor. (Garry -Glendown, conclusion of an article on VIM in iX magazine 9/1998) - -I've recently acquired the O'Reilly book on VI (it also discusses VIM -in-depth), and I'm amazed at just how powerful this application is. (Jeffrey -Rankin) - -This guide was written using the Windows 9.x distribution of GVIM, which is -quite possibly the greatest thing to come along since God created the naked -girl. (Michael DiBernardo) - -Boy, I thought I knew almost everything about VIM, but every time I browse the -online documentation, I hit upon a minor but cool aspect of a VIM feature that -I didn't know before! I must say the documentation is one the finest I've -ever seen in a product -- even better than most commercial products. -(Gautam Mudunuri) - -VIM 4.5 is really a fantastic editor. It has sooooo many features and more -importantly, the defaults are so well thought out that you really don't have -to change anything!! Words cannot express my amazement and gratitude to the -creators of VIM. Keep it up. (Vikas, USA) - -I wonder how long it will be before people will refer to other Vi editors as -VIM clones? (Darren Hiebert) - -I read about [auto-positioning-in-file-based-on-the-errors-from-make] in one -of those "Perfect Programmer's Editor" threads and was delighted to discover -that VIM already supports it. (Brendan Macmillan, Australia) - -I just discovered VIM (5.0) and I'm telling everyone I know about it! -I tell them VIM stands for VI for the new (M)illenium. Thanks so much! -(Matt F. Valentine) - -I think from now on "vi" should be called "Vim Imitation", not the other way -around. (Rungun Ramanathan) - -The Law of VIM: -For each member b of the possible behaviour space B of program P, there exists -a finite time t before which at least one user u in the total user space U of -program P will request b becomes a member of the allowed behaviour space B' -(B' <= B). -In other words: Sooner or later everyone wants everything as an option. -(Negri) - -Whenever I move to a new computing platform, the first thing I do is to port -VIM. Lately, I am simply stunned by its ease of compilation using the -configure facility. (A.M. Sabuncu, Turkey) - -The options are really excellent and very powerful. (Anish Maharaj) - -The Spring user-interface designs are in, and word from the boutiques is that -80x24 text-only mode is back with a *vengeance! Vi editor clone VIM burst onto -March desk-tops with a dazzling show of pastel syntax highlights for its 5.0 -look. Strident and customizable, VIM raises eyebrows with its interpretation -of the classic Vi single-key macro collection. -http://www.ntk.net/index.cgi?back=archive98/now0327.txt&line=179#l - -I just wanted to take this opportunity to let you know that VIM 5 ROCKS! -Syntax highlighting: how did I survive without it?! Thank you for creating -mankind's best editor! (Mun Johl, USA) - -Thanks again for VIM. I use it every day on Linux. (Eric Foster-Johnson, -author of the book "UNIX Programming Tools") - -The BEST EDITOR EVER (Stuart Woolford) - -I have used most of VIM's fancy features at least once, many frequently, and I -can honestly say that I couldn't live with anything less anymore. My -productivity has easily doubled compared to what it was when I used vi. -(Sitaram Chamarty) - -I luv VIM. It is incredible. I'm naming my first-born Vimberly. (Jose -Unpingco, USA) - -Hint: "VIM" is "vi improved" - much better! (Sven Guckes, Germany) - -I use VIM every day. I spend more time in VIM than in any other program... -It's the best vi clone there is. I think it's great. (Craig Sanders, -Australia) - -I strongly advise using VIM--its infinite undo/redo saved me much grief. -(Terry Brown) - -Thanks very much for writing what in my opinion is the finest text editor on -the planet. If I were to get another cat, I would name it "Vim". -(Bob Sheehan, USA) - -I typed :set all and the screen FILLED up with options. A whole screen of -things to be set and unset. I saw some of my old friends like wrapmargin, -modelines and showmode, but the screen was FILLED with new friends! I love -them all! I love VIM! I'm so happy that I've found this editor! I feel -like how I once felt when I started using vi after a couple of years of using -ed. I never thought I'd forsake my beloved ed, but vi ... oh god, vi was -great. And now, VIM. (Peter Jay Salzman, USA) - -I am really happy with such a wonderful software package. Much better than -almost any expensive, off the shelf program. (Jeff Walker) - -Whenever I reread the VIM documentation I'm overcome with excitement at the -power of the editor. (William Edward Webber, Australia) - -Hurrah for VIM!! It is "at your fingertips" like vi, and has the extensions -that vi sorely needs: highlighting for executing commands on blocks, an easily -navigable and digestible help screen, and more. (Paul Pax) - -The reason WHY I don't have this amazingly useful macro anymore, is that I -now use VIM - and this is built in!! (Stephen Riehm, Germany) - -I am a user of VIM and I love it. I use it to do all my programming, C, -C++, HTML what ever. (Tim Allwine) - -I discovered VIM after years of struggling with the original vi, and I just -can't live without it anymore. (Emmanuel Mogenet, USA) - -Emacs has not a bit of chance to survive so long as VIM is around. Besides, -it also has the most detailed software documentation I have ever seen---much -better than most commercial software! (Leiming Qian) - -This version of VIM will just blow people apart when they discover just how -fantastic it is! (Tony Nugent, Australia) - -I took your advice & finally got VIM & I'm really impressed. Instant convert. -(Patrick Killelea, USA) - -VIM is by far my favorite piece of shareware and I have been particularly -pleased with version 3.0. This is really a solid piece of work. (Robert -Colon, USA) - -VIM is a joy to use, it is so well thought and practical that I wonder why -anybody would use visual development tools. VIM is powerful and elegant, it -looks deceptively simple but is almost as complex as a 747 (especially when I -look at my growing vimrc), keep up that wonderful job, VIM is a centerpiece -of the free software world. (Louis-David Mitterand, USA) - -I cannot believe how great it is to use VIM. I think the guys at work are -getting tired of hearing me bragging about it. Others eyes are lighting up. -(Rick Croote) - -Emacs takes way too much time to start up and run, it is too big and bulky for -effective use and the interface is more confusing than it is of any help. VIM -however is short, it is fast, it is powerful, it has a good interface and it -is all purpose. (Paal Ditlefsen Ekran) - -From the first time I got VIM3.0, I was very enthusiastic. It has almost no -problems. The swapfile handling and the backup possibilities are robust, also -the protection against editing one file twice. It is very compatible to the -real VI (and that is a MUST, because my brain is trained over years in using -it). (Gert van Antwerpen, Holland) - -Visual mode in VIM is a very powerful thing! (Tony Nugent, Australia) - -I have to say that VIM is =THE= single greatest piece of source code to ever -come across the net (Jim Battle, USA). - -In fact, if you do want to get a new vi I'd suggest VIM-3.0. This is, by -far, the best version of vi I've ever seen (Albert W. Schueller). - -I should mention that VIM is a very good editor and can compete with anything -(Ilya Beloozerov). - -To tell the truth sometimes I used elvis, vile, xvi, calvin, etc. And this is -the reason that I can state that VIM is the best! (Ferenc Deak, Hungary) - -VIM is by far the best editor that I have used in a long time, and I have -looked at just about every thing that is available for every platform that I -use. VIM is the best on all of them. (Guy L. Oliver) - -VIM is the greatest editor since the stone chisel. (Jose Unpingco, USA) - -I would like to say that with VIM I am finally making the 'emacs to vi' -transition - as an Editor it is so much better in many ways: keyboard layout, -memory usage, text alteration to name 3. (Mark Adam) - -In fact, now if I want to know what a particular setting does in vi, I fire up -VIM and check out its help! (Nikhil Patel, USA) - -As a vi user, VIM has made working with text a far more pleasant task than -before I encountered this program. (Steinar Knutsen, Norway) - -I use VIM since version 3.0. Since that time, it is the ONLY editor I use, -with Solaris, Linux and OS/2 Warp. I suggest all my friends to use VIM, they -try, and they continue using it. VIM is really the best software I have ever -downloaded from the Internet, and the best editor I know of. (Marco -Eccettuato, Italy) - - -In summary: - __ ___ _ _ _ ___ _____ ` - \ \ / (_)_ __ ___ (_)___ | | | |/ _ \_ _| ` - \ \ / /| | '_ ` _ \ | / __| | |_| | | | || | ` - \ V / | | | | | | | | \__ \ | _ | |_| || | ` - \_/ |_|_| |_| |_| |_|___/ |_| |_|\___/ |_| ` - ____ _____ _ _ _____ _____ _ _ ` - / ___|_ _| | | | ___| ___| | | ` - \___ \ | | | | | | |_ | |_ | | | ` - ___) || | | |_| | _| | _| |_|_| ` - |____/ |_| \___/|_| |_| (_|_) (Tony Nugent, Australia) ` - - - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index c6f51d47b9..dccbe2562e 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1178,9 +1178,6 @@ running) you have additional options: already set (registers, marks, |v:oldfiles|, etc.) will be overwritten. - *:rv* *:rviminfo* -:rv[iminfo][!] [file] Deprecated alias to |:rshada| command. - *:wsh* *:wshada* *E137* :wsh[ada][!] [file] Write to ShaDa file [file] (default: see above). The information in the file is first read in to make @@ -1196,9 +1193,6 @@ running) you have additional options: Note: Executing :wshada will reset all |'quote| marks. - *:wv* *:wviminfo* -:wv[iminfo][!] [file] Deprecated alias to |:wshada| command. - *:o* *:ol* *:oldfiles* :o[ldfiles] List the files that have marks stored in the ShaDa file. This list is read on startup and only changes diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index d9440b6df8..308fa90ab3 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4926,8 +4926,6 @@ TabLineSel tab pages line, active tab page label Title titles for output from ":set all", ":autocmd" etc. *hl-Visual* Visual Visual mode selection - *hl-VisualNOS* -VisualNOS Removed. |vim-differences| {Nvim} *hl-WarningMsg* WarningMsg warning messages *hl-WildMenu* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index dd4dbc1272..c505652b1b 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -220,10 +220,12 @@ planned for the current milestone. These features are in Vim, but have been intentionally removed from Nvim. -Vi-compatible mode: + *'cp'* *'nocompatible'* *'nocp'* *'compatible'* +Nvim is always "non-compatible" with Vi. ":set nocompatible" is ignored ":set compatible" is an error + *'ed'* *'edcompatible'* *'noed'* *'noedcompatible'* Ed-compatible mode: ":set noedcompatible" is ignored ":set edcompatible" is an error @@ -233,8 +235,8 @@ Ed-compatible mode: ":set nottyfast" is an error Encryption support: - 'cryptmethod' - 'key' + *'cryptmethod'* *'cm'* + *'key'* MS-DOS support: 'bioskey' @@ -245,27 +247,27 @@ Highlight groups: Other options: 'antialias' - 'cpoptions' ('g', 'w', 'H', '*', '-', 'j', and all POSIX flags were removed) - 'guioptions' (only the 't' flag was removed) - 'guipty' - 'imactivatefunc' - 'imactivatekey' - 'imstatusfunc' - 'macatsui' - 'restorescreen' + 'cpoptions' ("g", "w", "H", "*", "-", "j", and all POSIX flags were removed) + 'guioptions' "t" flag was removed + *'guipty'* (Nvim uses pipes and PTYs consistently on all platforms.) + *'imactivatefunc'* *'imaf'* + *'imactivatekey'* *'imak'* + *'imstatusfunc'* *'imsf'* + *'macatsui'* + *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* 'shelltype' - 'shortname' - 'swapsync' - 'term' - 'termencoding' (Vim 7.4.852 also removed this for Windows) + *'shortname'* *'sn'* *'noshortname'* *'nosn'* + *'swapsync'* *'sws'* + *'term'* *E529* *E530* *E531* + *'termencoding'* *'tenc'* (Vim 7.4.852 also removed this for Windows) 'textauto' 'textmode' - 'toolbar' - 'toolbariconsize' - 'ttybuiltin' - 'ttymouse' - 'ttyscroll' - 'ttytype' + *'toolbar'* *'tb'* + *'toolbariconsize'* *'tbis'* + *'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'* + *'ttymouse'* *'ttym'* + *'ttyscroll'* *'tsl'* + *'ttytype'* *'tty'* 'weirdinvert' Other commands: diff --git a/runtime/optwin.vim b/runtime/optwin.vim index bfcf4d5294..890c183a5a 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1195,6 +1195,8 @@ if has("multi_byte") endif call append("$", "ambiwidth\twidth of ambiguous width characters") call <SID>OptionG("ambw", &ambw) + call append("$", "emoji\temoji characters are full width") + call <SID>BinOptionG("emo", &emo) endif diff --git a/scripts/download-unicode-files.sh b/scripts/download-unicode-files.sh index cb15270cf8..54fc32550c 100755 --- a/scripts/download-unicode-files.sh +++ b/scripts/download-unicode-files.sh @@ -1,11 +1,11 @@ #!/bin/sh set -e - -files="UnicodeData.txt CaseFolding.txt EastAsianWidth.txt" +data_files="UnicodeData.txt CaseFolding.txt EastAsianWidth.txt" +emoji_files="emoji-data.txt" UNIDIR_DEFAULT=unicode -DOWNLOAD_URL_BASE_DEFAULT='http://unicode.org/Public/UNIDATA' +DOWNLOAD_URL_BASE_DEFAULT='http://unicode.org/Public' if test x$1 = 'x--help' ; then echo 'Usage:' @@ -21,8 +21,16 @@ fi UNIDIR=${1:-$UNIDIR_DEFAULT} DOWNLOAD_URL_BASE=${2:-$DOWNLOAD_URL_BASE_DEFAULT} -for filename in $files ; do - curl -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/$filename" +for filename in $data_files ; do + curl -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/UNIDATA/$filename" + ( + cd "$UNIDIR" + git add $filename + ) +done + +for filename in $emoji_files ; do + curl -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/emoji/3.0/$filename" ( cd "$UNIDIR" git add $filename diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 40507d0afe..00ed84212b 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -111,14 +111,20 @@ end local deprecated_aliases = require("api.dispatch_deprecated") for i,f in ipairs(shallowcopy(functions)) do local ismethod = false - if startswith(f.name, "nvim_buf_") then - ismethod = true - elseif startswith(f.name, "nvim_win_") then - ismethod = true - elseif startswith(f.name, "nvim_tabpage_") then - ismethod = true - elseif not startswith(f.name, "nvim_") then + if startswith(f.name, "nvim_") then + -- TODO(bfredl) after 0.1.6 allow method definitions + -- to specify the since and deprecated_since field + f.since = 1 + if startswith(f.name, "nvim_buf_") then + ismethod = true + elseif startswith(f.name, "nvim_win_") then + ismethod = true + elseif startswith(f.name, "nvim_tabpage_") then + ismethod = true + end + else f.noeval = true + f.since = 0 f.deprecated_since = 1 end f.method = ismethod @@ -133,13 +139,31 @@ for i,f in ipairs(shallowcopy(functions)) do end local newf = shallowcopy(f) newf.name = newname + if newname == "ui_try_resize" then + -- The return type was incorrectly set to Object in 0.1.5. + -- Keep it that way for clients that rely on this. + newf.return_type = "Object" + end newf.impl_name = f.name newf.noeval = true + newf.since = 0 newf.deprecated_since = 1 functions[#functions+1] = newf end end +-- don't expose internal attributes like "impl_name" in public metadata +exported_attributes = {'name', 'parameters', 'return_type', 'method', + 'since', 'deprecated_since'} +exported_functions = {} +for _,f in ipairs(functions) do + local f_exported = {} + for _,attr in ipairs(exported_attributes) do + f_exported[attr] = f[attr] + end + exported_functions[#exported_functions+1] = f_exported +end + -- start building the output output = io.open(outputf, 'wb') @@ -174,9 +198,9 @@ static const uint8_t msgpack_metadata[] = { ]]) -- serialize the API metadata using msgpack and embed into the resulting -- binary for easy querying by clients -packed = mpack.pack(functions) -for i = 1, #packed do - output:write(string.byte(packed, i)..', ') +packed_exported_functions = mpack.pack(exported_functions) +for i = 1, #packed_exported_functions do + output:write(string.byte(packed_exported_functions, i)..', ') if i % 10 == 0 then output:write('\n ') end @@ -365,5 +389,5 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, output:close() mpack_output = io.open(mpack_outputf, 'wb') -mpack_output:write(packed) +mpack_output:write(mpack.pack(functions)) mpack_output:close() diff --git a/scripts/genunicodetables.lua b/scripts/genunicodetables.lua index 36339e2fc6..66430ba26e 100644 --- a/scripts/genunicodetables.lua +++ b/scripts/genunicodetables.lua @@ -12,19 +12,27 @@ -- 2 then interval applies only to first, third, fifth, … character in range. -- Fourth value is number that should be added to the codepoint to yield -- folded/lower/upper codepoint. +-- 4. emoji_width and emoji_all tables: sorted lists of non-overlapping closed +-- intervals of Emoji characters. emoji_width contains all the characters +-- which don't have ambiguous or double width, and emoji_all has all Emojis. if arg[1] == '--help' then print('Usage:') - print(' genunicodetables.lua UnicodeData.txt CaseFolding.txt ' .. - 'EastAsianWidth.txt') - print(' unicode_tables.generated.h') + print(' genunicodetables.lua unicode/ unicode_tables.generated.h') os.exit(0) end -local unicodedata_fname = arg[1] -local casefolding_fname = arg[2] -local eastasianwidth_fname = arg[3] +local basedir = arg[1] +local pathsep = package.config:sub(1, 1) +local get_path = function(fname) + return basedir .. pathsep .. fname +end + +local unicodedata_fname = get_path('UnicodeData.txt') +local casefolding_fname = get_path('CaseFolding.txt') +local eastasianwidth_fname = get_path('EastAsianWidth.txt') +local emoji_fname = get_path('emoji-data.txt') -local utf_tables_fname = arg[4] +local utf_tables_fname = arg[2] local split_on_semicolons = function(s) local ret = {} @@ -79,6 +87,10 @@ local parse_width_props = function(eaw_fp) return fp_lines_to_lists(eaw_fp, 2, true) end +local parse_emoji_props = function(emoji_fp) + return fp_lines_to_lists(emoji_fp, 2, true) +end + local make_range = function(start, end_, step, add) if step and add then return (' {0x%x, 0x%x, %d, %d},\n'):format( @@ -168,6 +180,7 @@ local build_width_table = function(ut_fp, dataprops, widthprops, widths, local start = -1 local end_ = -1 local dataidx = 1 + local ret = {} for _, p in ipairs(widthprops) do if widths[p[2]:sub(1, 1)] then local rng_start, rng_end = p[1]:find('%.%.') @@ -200,6 +213,7 @@ local build_width_table = function(ut_fp, dataprops, widthprops, widths, else if start >= 0 then ut_fp:write(make_range(start, end_)) + table.insert(ret, {start, end_}) end start = n end @@ -209,6 +223,72 @@ local build_width_table = function(ut_fp, dataprops, widthprops, widths, end if start >= 0 then ut_fp:write(make_range(start, end_)) + table.insert(ret, {start, end_}) + end + ut_fp:write('};\n') + return ret +end + +local build_emoji_table = function(ut_fp, emojiprops, doublewidth, ambiwidth) + local emojiwidth = {} + local emoji = {} + for _, p in ipairs(emojiprops) do + if p[2]:match('Emoji%s+#') then + local rng_start, rng_end = p[1]:find('%.%.') + if rng_start then + n = tonumber(p[1]:sub(1, rng_start - 1), 16) + n_last = tonumber(p[1]:sub(rng_end + 1), 16) + else + n = tonumber(p[1], 16) + n_last = n + end + if #emoji > 0 and n - 1 == emoji[#emoji][2] then + emoji[#emoji][2] = n_last + else + table.insert(emoji, { n, n_last }) + end + + -- Characters below 1F000 may be considered single width traditionally, + -- making them double width causes problems. + if n >= 0x1f000 then + -- exclude characters that are in the ambiguous/doublewidth table + for _, ambi in ipairs(ambiwidth) do + if n >= ambi[1] and n <= ambi[2] then + n = ambi[2] + 1 + end + if n_last >= ambi[1] and n_last <= ambi[2] then + n_last = ambi[1] - 1 + end + end + for _, double in ipairs(doublewidth) do + if n >= double[1] and n <= double[2] then + n = double[2] + 1 + end + if n_last >= double[1] and n_last <= double[2] then + n_last = double[1] - 1 + end + end + + if n <= n_last then + if #emojiwidth > 0 and n - 1 == emojiwidth[#emojiwidth][2] then + emojiwidth[#emojiwidth][2] = n_last + else + table.insert(emojiwidth, { n, n_last }) + end + end + end + end + end + + ut_fp:write('static const struct interval emoji_all[] = {\n') + for _, p in ipairs(emoji) do + ut_fp:write(make_range(p[1], p[2])) + end + ut_fp:write('};\n') + + ut_fp:write('static const struct interval emoji_width[] = {\n') + for _, p in ipairs(emojiwidth) do + ut_fp:write(make_range(p[1], p[2])) end ut_fp:write('};\n') end @@ -233,7 +313,15 @@ local eaw_fp = io.open(eastasianwidth_fname, 'r') local widthprops = parse_width_props(eaw_fp) eaw_fp:close() -build_width_table(ut_fp, dataprops, widthprops, {W=true, F=true}, 'doublewidth') -build_width_table(ut_fp, dataprops, widthprops, {A=true}, 'ambiguous') +local doublewidth = build_width_table(ut_fp, dataprops, widthprops, + {W=true, F=true}, 'doublewidth') +local ambiwidth = build_width_table(ut_fp, dataprops, widthprops, + {A=true}, 'ambiguous') + +local emoji_fp = io.open(emoji_fname, 'r') +local emojiprops = parse_emoji_props(emoji_fp) +emoji_fp:close() + +build_emoji_table(ut_fp, emojiprops, doublewidth, ambiwidth) ut_fp:close() diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index a2750105c3..c3efb27e3a 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -132,15 +132,15 @@ preprocess_patch() { # Remove *.proto, Make*, gui_*, some if_* local na_src='proto\|Make*\|gui_*' na_src="$na_src"'\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv' - 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<'${na_src}'@norm! d/\v(^diff)|%$
' +w +q "$file" + 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%('${na_src}'\)@norm! d/\v(^diff)|%$
' +w +q "$file" # Remove todo.txt, version*.txt, tags local na_doc='todo\.txt\|version\d\.txt\|tags' - 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/doc/'${na_doc}'@norm! d/\v(^diff)|%$
' +w +q "$file" + 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/doc/\%('${na_doc}'\)@norm! d/\v(^diff)|%$
' +w +q "$file" # Remove some testdir/Make_*.mak files local na_src_testdir='Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms' - 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/'${na_src_testdir}'@norm! d/\v(^diff)|%$
' +w +q "$file" + 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\%('${na_src_testdir}'\)@norm! d/\v(^diff)|%$
' +w +q "$file" # Rename src/ paths to src/nvim/ LC_ALL=C sed -e 's/\( [ab]\/src\)/\1\/nvim/g' "$file" > "$file".tmp && mv "$file".tmp "$file" diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 63558170b3..7636f8fa82 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,3 @@ -# libuv queue.h pointer arithmetic is not accepted by asan -fun:queue_node_data +# multiqueue.h pointer arithmetic is not accepted by asan +fun:multiqueue_node_data fun:dictwatcher_node_data diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 7c2c2feebc..59582d0734 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -36,9 +36,7 @@ set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua) set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) -set(UNICODEDATA_FILE ${UNICODE_DIR}/UnicodeData.txt) -set(CASEFOLDING_FILE ${UNICODE_DIR}/CaseFolding.txt) -set(EASTASIANWIDTH_FILE ${UNICODE_DIR}/EastAsianWidth.txt) +file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) include_directories(${GENERATED_DIR}) @@ -192,15 +190,11 @@ endforeach() add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} COMMAND ${LUA_PRG} ${UNICODE_TABLES_GENERATOR} - ${UNICODEDATA_FILE} - ${CASEFOLDING_FILE} - ${EASTASIANWIDTH_FILE} + ${UNICODE_DIR} ${GENERATED_UNICODE_TABLES} DEPENDS ${UNICODE_TABLES_GENERATOR} - ${UNICODEDATA_FILE} - ${CASEFOLDING_FILE} - ${EASTASIANWIDTH_FILE} + ${UNICODE_FILES} ) add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${API_METADATA} diff --git a/src/nvim/diff.c b/src/nvim/diff.c index ef5acf4845..f7b96ba3e1 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -906,8 +906,7 @@ void ex_diffpatch(exarg_T *eap) eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new); #endif // ifdef UNIX } else { - // Build the patch command and execute it. Ignore errors. Switch to - // cooked mode to allow the user to respond to prompts. + // Build the patch command and execute it. Ignore errors. #ifdef UNIX vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"", tmp_new, tmp_orig, fullname != NULL ? fullname : eap->arg); @@ -915,8 +914,7 @@ void ex_diffpatch(exarg_T *eap) vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"", tmp_new, tmp_orig, eap->arg); #endif // ifdef UNIX - // Avoid ShellCmdPost stuff - block_autocmds(); + block_autocmds(); // Avoid ShellCmdPost stuff (void)call_shell(buf, kShellOptFilter, NULL); unblock_autocmds(); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 18d5ea533d..892748ff5c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -502,7 +502,7 @@ static int insert_check(VimState *state) Insstart_orig = Insstart; } - if (stop_insert_mode) { + if (stop_insert_mode && !pum_visible()) { // ":stopinsert" used or 'insertmode' reset s->count = 0; return 0; // exit insert mode @@ -962,7 +962,7 @@ static int insert_handle_key(InsertState *s) break; case K_EVENT: // some event - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); break; case K_FOCUSGAINED: // Neovim has been given focus diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c762ce9fff..0b1fd6670e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -292,8 +292,8 @@ typedef enum { .vv_di = { \ .di_tv = { .v_type = type }, \ .di_flags = 0, \ + .di_key = { 0 }, \ }, \ - .vv_filler = { 0 }, \ .vv_flags = flags, \ } @@ -303,8 +303,7 @@ typedef enum { // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - dictitem_T vv_di; ///< Value of the variable, with name. - char vv_filler[16]; ///< Space for longest name from below. + dictitem16_T vv_di; ///< Value and name for key (max 16 chars) char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { @@ -416,7 +415,7 @@ typedef struct { dict_T *self; int *status_ptr; uint64_t id; - Queue *events; + MultiQueue *events; } TerminalJobData; typedef struct dict_watcher { @@ -514,6 +513,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; + assert(STRLEN(p->vv_name) <= 16); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -2684,6 +2684,10 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) } else if (c == '=') { got_eq = TRUE; xp->xp_context = EXPAND_EXPRESSION; + } else if (c == '#' + && xp->xp_context == EXPAND_EXPRESSION) { + // Autoload function/variable contains '#' + break; } else if ((c == '<' || c == '#') && xp->xp_context == EXPAND_FUNCTIONS && vim_strchr(xp->xp_pattern, '(') == NULL) { @@ -4948,7 +4952,8 @@ static list_T *rettv_list_alloc(typval_T *rettv) list_T *l = list_alloc(); rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + rettv->v_lock = VAR_UNLOCKED; + l->lv_refcount++; return l; } @@ -6128,7 +6133,8 @@ static void rettv_dict_alloc(typval_T *rettv) rettv->vval.v_dict = d; rettv->v_type = VAR_DICT; - ++d->dv_refcount; + rettv->v_lock = VAR_UNLOCKED; + d->dv_refcount++; } /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. @@ -11686,7 +11692,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *rv = list_alloc(); ui_busy_start(); - Queue *waiting_jobs = queue_new_parent(loop_on_put, &main_loop); + MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); // For each item in the input list append an integer to the output list. -3 // is used to represent an invalid job id, -2 is for a interrupted job and // -1 for jobs that were skipped or timed out. @@ -11702,8 +11708,8 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) data->status_ptr = &rv->lv_last->li_tv.vval.v_number; // Process any pending events for the job because we'll temporarily // replace the parent queue - queue_process_events(data->events); - queue_replace_parent(data->events, waiting_jobs); + multiqueue_process_events(data->events); + multiqueue_replace_parent(data->events, waiting_jobs); } } @@ -11763,11 +11769,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } // restore the parent queue for the job - queue_process_events(data->events); - queue_replace_parent(data->events, main_loop.events); + multiqueue_process_events(data->events); + multiqueue_replace_parent(data->events, main_loop.events); } - queue_free(waiting_jobs); + multiqueue_free(waiting_jobs); ui_busy_stop(); rv->lv_refcount++; rettv->v_type = VAR_LIST; @@ -16394,7 +16400,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->callback = func; time_watcher_init(&main_loop, &timer->tw, timer); - timer->tw.events = queue_new_child(main_loop.events); + timer->tw.events = multiqueue_new_child(main_loop.events); // if main loop is blocked, don't queue up multiple events timer->tw.blockable = true; time_watcher_start(&timer->tw, timer_due_cb, timeout, @@ -16471,7 +16477,7 @@ static void timer_stop(timer_T *timer) static void timer_close_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; - queue_free(timer->tw.events); + multiqueue_free(timer->tw.events); user_func_unref(timer->callback); pmap_del(uint64_t)(timers, timer->timer_id); timer_decref(timer); @@ -21719,7 +21725,7 @@ static inline TerminalJobData *common_job_init(char **argv, data->on_stderr = on_stderr; data->on_exit = on_exit; data->self = self; - data->events = queue_new_child(main_loop.events); + data->events = multiqueue_new_child(main_loop.events); data->rpc = rpc; if (pty) { data->proc.pty = pty_process_init(&main_loop, data); @@ -21828,7 +21834,7 @@ static inline void free_term_job_data_event(void **argv) if (data->self) { dict_unref(data->self); } - queue_free(data->events); + multiqueue_free(data->events); pmap_del(uint64_t)(jobs, data->id); xfree(data); } @@ -21837,7 +21843,7 @@ static inline void free_term_job_data(TerminalJobData *data) { // data->queue may still be used after this function returns(process_wait), so // only free in the next event loop iteration - queue_put(main_loop.fast_events, free_term_job_data_event, 1, data); + multiqueue_put(main_loop.fast_events, free_term_job_data_event, 1, data); } // vimscript job callbacks must be executed on Nvim main loop @@ -21956,7 +21962,7 @@ static inline void term_delayed_free(void **argv) { TerminalJobData *j = argv[0]; if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { - queue_put(j->events, term_delayed_free, 1, j); + multiqueue_put(j->events, term_delayed_free, 1, j); return; } @@ -21971,7 +21977,7 @@ static void term_close(void *d) data->exited = true; process_stop((Process *)&data->proc); } - queue_put(data->events, term_delayed_free, 1, data); + multiqueue_put(data->events, term_delayed_free, 1, data); } static void term_job_data_decref(TerminalJobData *data) diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 884c987f10..91d544074f 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -114,6 +114,16 @@ struct dictitem_S { typedef struct dictitem_S dictitem_T; +/// A dictitem with a 16 character key (plus NUL) +struct dictitem16_S { + typval_T di_tv; ///< type and value of the variable + char_u di_flags; ///< flags (only used for variable) + char_u di_key[17]; ///< key +}; + +typedef struct dictitem16_S dictitem16_T; + + #define DI_FLAGS_RO 1 // "di_flags" value: read-only variable #define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox #define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 6f3e6b9253..d562ac1ed3 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -18,9 +18,9 @@ void loop_init(Loop *loop, void *data) loop->uv.data = loop; loop->children = kl_init(WatcherPtr); loop->children_stop_requests = 0; - loop->events = queue_new_parent(loop_on_put, loop); - loop->fast_events = queue_new_child(loop->events); - loop->thread_events = queue_new_parent(NULL, NULL); + loop->events = multiqueue_new_parent(loop_on_put, loop); + loop->fast_events = multiqueue_new_child(loop->events); + loop->thread_events = multiqueue_new_parent(NULL, NULL); uv_mutex_init(&loop->mutex); uv_async_init(&loop->uv, &loop->async, async_cb); uv_signal_init(&loop->uv, &loop->children_watcher); @@ -53,19 +53,19 @@ void loop_poll_events(Loop *loop, int ms) } loop->recursive--; // Can re-enter uv_run now - queue_process_events(loop->fast_events); + multiqueue_process_events(loop->fast_events); } // Schedule an event from another thread void loop_schedule(Loop *loop, Event event) { uv_mutex_lock(&loop->mutex); - queue_put_event(loop->thread_events, event); + multiqueue_put_event(loop->thread_events, event); uv_async_send(&loop->async); uv_mutex_unlock(&loop->mutex); } -void loop_on_put(Queue *queue, void *data) +void loop_on_put(MultiQueue *queue, void *data) { Loop *loop = data; // Sometimes libuv will run pending callbacks(timer for example) before @@ -76,7 +76,7 @@ void loop_on_put(Queue *queue, void *data) uv_stop(&loop->uv); } -void loop_close(Loop *loop) +void loop_close(Loop *loop, bool wait) { uv_mutex_destroy(&loop->mutex); uv_close((uv_handle_t *)&loop->children_watcher, NULL); @@ -84,11 +84,11 @@ void loop_close(Loop *loop) uv_close((uv_handle_t *)&loop->poll_timer, NULL); uv_close((uv_handle_t *)&loop->async, NULL); do { - uv_run(&loop->uv, UV_RUN_DEFAULT); - } while (uv_loop_close(&loop->uv)); - queue_free(loop->fast_events); - queue_free(loop->thread_events); - queue_free(loop->events); + uv_run(&loop->uv, wait ? UV_RUN_DEFAULT : UV_RUN_NOWAIT); + } while (uv_loop_close(&loop->uv) && wait); + multiqueue_free(loop->fast_events); + multiqueue_free(loop->thread_events); + multiqueue_free(loop->events); kl_destroy(WatcherPtr, loop->children); } @@ -96,9 +96,9 @@ static void async_cb(uv_async_t *handle) { Loop *l = handle->loop->data; uv_mutex_lock(&l->mutex); - while (!queue_empty(l->thread_events)) { - Event ev = queue_get(l->thread_events); - queue_put_event(l->fast_events, ev); + while (!multiqueue_empty(l->thread_events)) { + Event ev = multiqueue_get(l->thread_events); + multiqueue_put_event(l->fast_events, ev); } uv_mutex_unlock(&l->mutex); } diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h index 407aa4245f..e7d7bdd483 100644 --- a/src/nvim/event/loop.h +++ b/src/nvim/event/loop.h @@ -7,7 +7,7 @@ #include "nvim/lib/klist.h" #include "nvim/os/time.h" -#include "nvim/event/queue.h" +#include "nvim/event/multiqueue.h" typedef void * WatcherPtr; @@ -16,7 +16,7 @@ KLIST_INIT(WatcherPtr, WatcherPtr, _noop) typedef struct loop { uv_loop_t uv; - Queue *events, *fast_events, *thread_events; + MultiQueue *events, *fast_events, *thread_events; klist_t(WatcherPtr) *children; uv_signal_t children_watcher; uv_timer_t children_kill_timer, poll_timer; @@ -26,10 +26,10 @@ typedef struct loop { int recursive; } Loop; -#define CREATE_EVENT(queue, handler, argc, ...) \ +#define CREATE_EVENT(multiqueue, handler, argc, ...) \ do { \ - if (queue) { \ - queue_put((queue), (handler), argc, __VA_ARGS__); \ + if (multiqueue) { \ + multiqueue_put((multiqueue), (handler), argc, __VA_ARGS__); \ } else { \ void *argv[argc] = { __VA_ARGS__ }; \ (handler)(argv); \ @@ -37,12 +37,12 @@ typedef struct loop { } while (0) // Poll for events until a condition or timeout -#define LOOP_PROCESS_EVENTS_UNTIL(loop, queue, timeout, condition) \ +#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \ do { \ int remaining = timeout; \ uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ while (!(condition)) { \ - LOOP_PROCESS_EVENTS(loop, queue, remaining); \ + LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \ if (remaining == 0) { \ break; \ } else if (remaining > 0) { \ @@ -56,10 +56,10 @@ typedef struct loop { } \ } while (0) -#define LOOP_PROCESS_EVENTS(loop, queue, timeout) \ +#define LOOP_PROCESS_EVENTS(loop, multiqueue, timeout) \ do { \ - if (queue && !queue_empty(queue)) { \ - queue_process_events(queue); \ + if (multiqueue && !multiqueue_empty(multiqueue)) { \ + multiqueue_process_events(multiqueue); \ } else { \ loop_poll_events(loop, timeout); \ } \ diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c new file mode 100644 index 0000000000..7efdfc4cad --- /dev/null +++ b/src/nvim/event/multiqueue.c @@ -0,0 +1,209 @@ +// Multi-level queue for selective async event processing. Multiqueue supports +// a parent-child relationship with the following properties: +// +// - pushing a node to a child queue will push a corresponding link node to the +// parent queue +// - removing a link node from a parent queue will remove the next node +// in the linked child queue +// - removing a node from a child queue will remove the corresponding link node +// in the parent queue +// +// These properties allow Nvim to organize and process events from different +// sources with a certain degree of control. How the multiqueue is used: +// +// +----------------+ +// | Main loop | +// +----------------+ +// ^ +// | +// +----------------+ +// +-------------->| Event loop |<------------+ +// | +--+-------------+ | +// | ^ ^ | +// | | | | +// +-----------+ +-----------+ +---------+ +---------+ +// | Channel 1 | | Channel 2 | | Job 1 | | Job 2 | +// +-----------+ +-----------+ +---------+ +---------+ +// +// +// The lower boxes represent event emitters, each with its own private queue +// having the event loop queue as the parent. +// +// When idle, the main loop spins the event loop which queues events from many +// sources (channels, jobs, user...). Each event emitter pushes events to its +// private queue which is propagated to the event loop queue. When the main loop +// consumes an event, the corresponding event is removed from the emitter's +// queue. +// +// The main reason for this queue hierarchy is to allow focusing on a single +// event emitter while blocking the main loop. For example, if the `jobwait` +// VimL function is called on job1, the main loop will temporarily stop polling +// the event loop queue and poll job1 queue instead. Same with channels, when +// calling `rpcrequest` we want to temporarily stop processing events from +// other sources and focus on a specific channel. + +#include <assert.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> + + +#include <uv.h> + +#include "nvim/event/multiqueue.h" +#include "nvim/memory.h" +#include "nvim/os/time.h" + +typedef struct multiqueue_item MultiQueueItem; +struct multiqueue_item { + union { + MultiQueue *queue; + struct { + Event event; + MultiQueueItem *parent; + } item; + } data; + bool link; // true: current item is just a link to a node in a child queue + QUEUE node; +}; + +struct multiqueue { + MultiQueue *parent; + QUEUE headtail; + put_callback put_cb; + void *data; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/multiqueue.c.generated.h" +#endif + +static Event NILEVENT = { .handler = NULL, .argv = {NULL} }; + +MultiQueue *multiqueue_new_parent(put_callback put_cb, void *data) +{ + return multiqueue_new(NULL, put_cb, data); +} + +MultiQueue *multiqueue_new_child(MultiQueue *parent) + FUNC_ATTR_NONNULL_ALL +{ + assert(!parent->parent); + return multiqueue_new(parent, NULL, NULL); +} + +static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb, + void *data) +{ + MultiQueue *rv = xmalloc(sizeof(MultiQueue)); + QUEUE_INIT(&rv->headtail); + rv->parent = parent; + rv->put_cb = put_cb; + rv->data = data; + return rv; +} + +void multiqueue_free(MultiQueue *this) +{ + assert(this); + while (!QUEUE_EMPTY(&this->headtail)) { + QUEUE *q = QUEUE_HEAD(&this->headtail); + MultiQueueItem *item = multiqueue_node_data(q); + if (this->parent) { + QUEUE_REMOVE(&item->data.item.parent->node); + xfree(item->data.item.parent); + } + QUEUE_REMOVE(q); + xfree(item); + } + + xfree(this); +} + +Event multiqueue_get(MultiQueue *this) +{ + return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this); +} + +void multiqueue_put_event(MultiQueue *this, Event event) +{ + assert(this); + multiqueue_push(this, event); + if (this->parent && this->parent->put_cb) { + this->parent->put_cb(this->parent, this->parent->data); + } +} + +void multiqueue_process_events(MultiQueue *this) +{ + assert(this); + while (!multiqueue_empty(this)) { + Event event = multiqueue_get(this); + if (event.handler) { + event.handler(event.argv); + } + } +} + +bool multiqueue_empty(MultiQueue *this) +{ + assert(this); + return QUEUE_EMPTY(&this->headtail); +} + +void multiqueue_replace_parent(MultiQueue *this, MultiQueue *new_parent) +{ + assert(multiqueue_empty(this)); + this->parent = new_parent; +} + +static Event multiqueue_remove(MultiQueue *this) +{ + assert(!multiqueue_empty(this)); + QUEUE *h = QUEUE_HEAD(&this->headtail); + QUEUE_REMOVE(h); + MultiQueueItem *item = multiqueue_node_data(h); + Event rv; + + if (item->link) { + assert(!this->parent); + // remove the next node in the linked queue + MultiQueue *linked = item->data.queue; + assert(!multiqueue_empty(linked)); + MultiQueueItem *child = + multiqueue_node_data(QUEUE_HEAD(&linked->headtail)); + QUEUE_REMOVE(&child->node); + rv = child->data.item.event; + xfree(child); + } else { + if (this->parent) { + // remove the corresponding link node in the parent queue + QUEUE_REMOVE(&item->data.item.parent->node); + xfree(item->data.item.parent); + } + rv = item->data.item.event; + } + + xfree(item); + return rv; +} + +static void multiqueue_push(MultiQueue *this, Event event) +{ + MultiQueueItem *item = xmalloc(sizeof(MultiQueueItem)); + item->link = false; + item->data.item.event = event; + QUEUE_INSERT_TAIL(&this->headtail, &item->node); + if (this->parent) { + // push link node to the parent queue + item->data.item.parent = xmalloc(sizeof(MultiQueueItem)); + item->data.item.parent->link = true; + item->data.item.parent->data.queue = this; + QUEUE_INSERT_TAIL(&this->parent->headtail, &item->data.item.parent->node); + } +} + +static MultiQueueItem *multiqueue_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, MultiQueueItem, node); +} diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h new file mode 100644 index 0000000000..def6b95a10 --- /dev/null +++ b/src/nvim/event/multiqueue.h @@ -0,0 +1,19 @@ +#ifndef NVIM_EVENT_MULTIQUEUE_H +#define NVIM_EVENT_MULTIQUEUE_H + +#include <uv.h> + +#include "nvim/event/defs.h" +#include "nvim/lib/queue.h" + +typedef struct multiqueue MultiQueue; +typedef void (*put_callback)(MultiQueue *multiq, void *data); + +#define multiqueue_put(q, h, ...) \ + multiqueue_put_event(q, event_create(1, h, __VA_ARGS__)); + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/multiqueue.h.generated.h" +#endif +#endif // NVIM_EVENT_MULTIQUEUE_H diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 51f20b7eac..39dd5fd55a 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -126,7 +126,7 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL // Wait until all children exit and all close events are processed. LOOP_PROCESS_EVENTS_UNTIL( loop, loop->events, -1, - kl_empty(loop->children) && queue_empty(loop->events)); + kl_empty(loop->children) && multiqueue_empty(loop->events)); pty_process_teardown(loop); } @@ -163,7 +163,8 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL /// indistinguishable from the process returning -1 by itself. Which /// is possible on some OS. Returns -2 if an user has interruped the /// wait. -int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) +int process_wait(Process *proc, int ms, MultiQueue *events) + FUNC_ATTR_NONNULL_ARG(1) { // The default status is -1, which represents a timeout int status = -1; @@ -208,7 +209,7 @@ int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) decref(proc); if (events) { // the decref call created an exit event, process it now - queue_process_events(events); + multiqueue_process_events(events); } } else { proc->refcount--; @@ -362,7 +363,7 @@ static void flush_stream(Process *proc, Stream *stream) // Poll for data and process the generated events. loop_poll_events(proc->loop, 0); if (proc->events) { - queue_process_events(proc->events); + multiqueue_process_events(proc->events); } // Stream can be closed if it is empty. @@ -402,7 +403,7 @@ static void on_process_exit(Process *proc) // OS. We are still in the libuv loop, so we cannot call code that polls for // more data directly. Instead delay the reading after the libuv loop by // queueing process_close_handles() as an event. - Queue *queue = proc->events ? proc->events : loop->events; + MultiQueue *queue = proc->events ? proc->events : loop->events; CREATE_EVENT(queue, process_close_handles, 1, proc); } diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index a4c6e7eeb2..5cbf7f9ce7 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -27,7 +27,7 @@ struct process { process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; bool closed, term_sent, detach; - Queue *events; + MultiQueue *events; }; static inline Process process_init(Loop *loop, ProcessType type, void *data) diff --git a/src/nvim/event/queue.c b/src/nvim/event/queue.c deleted file mode 100644 index c5ef22d426..0000000000 --- a/src/nvim/event/queue.c +++ /dev/null @@ -1,208 +0,0 @@ -// Queue for selective async event processing. Instances of this queue support a -// parent/child relationship with the following properties: -// -// - pushing a node to a child queue will push a corresponding link node to the -// parent queue -// - removing a link node from a parent queue will remove the next node -// in the linked child queue -// - removing a node from a child queue will remove the corresponding link node -// in the parent queue -// -// These properties allow neovim to organize and process events from different -// sources with a certain degree of control. Here's how the queue is used: -// -// +----------------+ -// | Main loop | -// +----------------+ -// ^ -// | -// +----------------+ -// +-------------->| Event loop |<------------+ -// | +--+-------------+ | -// | ^ ^ | -// | | | | -// +-----------+ +-----------+ +---------+ +---------+ -// | Channel 1 | | Channel 2 | | Job 1 | | Job 2 | -// +-----------+ +-----------+ +---------+ +---------+ -// -// -// In the above diagram, the lower boxes represents event emitters, each with -// it's own private queue that have the event loop queue as the parent. -// -// When idle, the main loop spins the event loop which queues events from many -// sources(channels, jobs, user...). Each event emitter pushes events to its own -// private queue which is propagated to the event loop queue. When the main loop -// consumes an event, the corresponding event is removed from the emitter's -// queue. -// -// The main reason for this queue hierarchy is to allow focusing on a single -// event emitter while blocking the main loop. For example, if the `jobwait` -// vimscript function is called on job1, the main loop will temporarily stop -// polling the event loop queue and poll job1 queue instead. Same with channels, -// when calling `rpcrequest`, we want to temporarily stop processing events from -// other sources and focus on a specific channel. - -#include <assert.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> - - -#include <uv.h> - -#include "nvim/event/queue.h" -#include "nvim/memory.h" -#include "nvim/os/time.h" - -typedef struct queue_item QueueItem; -struct queue_item { - union { - Queue *queue; - struct { - Event event; - QueueItem *parent; - } item; - } data; - bool link; // this is just a link to a node in a child queue - QUEUE node; -}; - -struct queue { - Queue *parent; - QUEUE headtail; - put_callback put_cb; - void *data; -}; - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/queue.c.generated.h" -#endif - -static Event NILEVENT = {.handler = NULL, .argv = {NULL}}; - -Queue *queue_new_parent(put_callback put_cb, void *data) -{ - return queue_new(NULL, put_cb, data); -} - -Queue *queue_new_child(Queue *parent) - FUNC_ATTR_NONNULL_ALL -{ - assert(!parent->parent); - return queue_new(parent, NULL, NULL); -} - -static Queue *queue_new(Queue *parent, put_callback put_cb, void *data) -{ - Queue *rv = xmalloc(sizeof(Queue)); - QUEUE_INIT(&rv->headtail); - rv->parent = parent; - rv->put_cb = put_cb; - rv->data = data; - return rv; -} - -void queue_free(Queue *queue) -{ - assert(queue); - while (!QUEUE_EMPTY(&queue->headtail)) { - QUEUE *q = QUEUE_HEAD(&queue->headtail); - QueueItem *item = queue_node_data(q); - if (queue->parent) { - QUEUE_REMOVE(&item->data.item.parent->node); - xfree(item->data.item.parent); - } - QUEUE_REMOVE(q); - xfree(item); - } - - xfree(queue); -} - -Event queue_get(Queue *queue) -{ - return queue_empty(queue) ? NILEVENT : queue_remove(queue); -} - -void queue_put_event(Queue *queue, Event event) -{ - assert(queue); - queue_push(queue, event); - if (queue->parent && queue->parent->put_cb) { - queue->parent->put_cb(queue->parent, queue->parent->data); - } -} - -void queue_process_events(Queue *queue) -{ - assert(queue); - while (!queue_empty(queue)) { - Event event = queue_get(queue); - if (event.handler) { - event.handler(event.argv); - } - } -} - -bool queue_empty(Queue *queue) -{ - assert(queue); - return QUEUE_EMPTY(&queue->headtail); -} - -void queue_replace_parent(Queue *queue, Queue *new_parent) -{ - assert(queue_empty(queue)); - queue->parent = new_parent; -} - -static Event queue_remove(Queue *queue) -{ - assert(!queue_empty(queue)); - QUEUE *h = QUEUE_HEAD(&queue->headtail); - QUEUE_REMOVE(h); - QueueItem *item = queue_node_data(h); - Event rv; - - if (item->link) { - assert(!queue->parent); - // remove the next node in the linked queue - Queue *linked = item->data.queue; - assert(!queue_empty(linked)); - QueueItem *child = - queue_node_data(QUEUE_HEAD(&linked->headtail)); - QUEUE_REMOVE(&child->node); - rv = child->data.item.event; - xfree(child); - } else { - if (queue->parent) { - // remove the corresponding link node in the parent queue - QUEUE_REMOVE(&item->data.item.parent->node); - xfree(item->data.item.parent); - } - rv = item->data.item.event; - } - - xfree(item); - return rv; -} - -static void queue_push(Queue *queue, Event event) -{ - QueueItem *item = xmalloc(sizeof(QueueItem)); - item->link = false; - item->data.item.event = event; - QUEUE_INSERT_TAIL(&queue->headtail, &item->node); - if (queue->parent) { - // push link node to the parent queue - item->data.item.parent = xmalloc(sizeof(QueueItem)); - item->data.item.parent->link = true; - item->data.item.parent->data.queue = queue; - QUEUE_INSERT_TAIL(&queue->parent->headtail, &item->data.item.parent->node); - } -} - -static QueueItem *queue_node_data(QUEUE *q) -{ - return QUEUE_DATA(q, QueueItem, node); -} diff --git a/src/nvim/event/queue.h b/src/nvim/event/queue.h deleted file mode 100644 index 85fc59f8b2..0000000000 --- a/src/nvim/event/queue.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef NVIM_EVENT_QUEUE_H -#define NVIM_EVENT_QUEUE_H - -#include <uv.h> - -#include "nvim/event/defs.h" -#include "nvim/lib/queue.h" - -typedef struct queue Queue; -typedef void (*put_callback)(Queue *queue, void *data); - -#define queue_put(q, h, ...) \ - queue_put_event(q, event_create(1, h, __VA_ARGS__)); - - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/queue.h.generated.h" -#endif -#endif // NVIM_EVENT_QUEUE_H diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h index e32608acc0..7fe352edef 100644 --- a/src/nvim/event/signal.h +++ b/src/nvim/event/signal.h @@ -14,7 +14,7 @@ struct signal_watcher { void *data; signal_cb cb; signal_close_cb close_cb; - Queue *events; + MultiQueue *events; }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h index ad59fdbe3a..eb0823c76d 100644 --- a/src/nvim/event/socket.h +++ b/src/nvim/event/socket.h @@ -30,7 +30,7 @@ struct socket_watcher { void *data; socket_cb cb; socket_close_cb close_cb; - Queue *events; + MultiQueue *events; }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index a176fac1c0..d27497e4a4 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -53,7 +53,7 @@ struct stream { size_t pending_reqs; size_t num_bytes; bool closed; - Queue *events; + MultiQueue *events; }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c index f68a66345f..77260546db 100644 --- a/src/nvim/event/time.c +++ b/src/nvim/event/time.c @@ -51,7 +51,7 @@ static void time_watcher_cb(uv_timer_t *handle) FUNC_ATTR_NONNULL_ALL { TimeWatcher *watcher = handle->data; - if (watcher->blockable && !queue_empty(watcher->events)) { + if (watcher->blockable && !multiqueue_empty(watcher->events)) { // the timer blocked and there already is an unprocessed event waiting return; } diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h index 14df176ea3..a6de89ad6e 100644 --- a/src/nvim/event/time.h +++ b/src/nvim/event/time.h @@ -12,7 +12,7 @@ struct time_watcher { uv_timer_t uv; void *data; time_cb cb, close_cb; - Queue *events; + MultiQueue *events; bool blockable; }; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index c36c3a7b0e..0226499e78 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1154,15 +1154,11 @@ static void do_filter( } read_linecount = curbuf->b_ml.ml_line_count; - /* - * When call_shell() fails wait_return() is called to give the user a - * chance to read the error messages. Otherwise errors are ignored, so you - * can see the error messages from the command that appear on stdout; use - * 'u' to fix the text - * Switch to cooked mode when not redirecting stdin, avoids that something - * like ":r !cat" hangs. - * Pass on the kShellDoOut flag when the output is being redirected. - */ + // When call_shell() fails wait_return() is called to give the user a chance + // to read the error messages. Otherwise errors are ignored, so you can see + // the error messages from the command that appear on stdout; use 'u' to fix + // the text. + // Pass on the kShellDoOut flag when the output is being redirected. if (call_shell( cmd_buf, kShellOptFilter | shell_flags, diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 1a97dc3d6f..4254697241 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -357,7 +357,7 @@ static int command_line_execute(VimState *state, int key) s->c = key; if (s->c == K_EVENT) { - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); redrawcmdline(); return 1; } @@ -2548,10 +2548,9 @@ static void cmdline_del(int from) ccline.cmdpos = from; } -/* - * this function is called when the screen size changes and with incremental - * search - */ +// This function is called when the screen size changes and with incremental +// search and in other situations where the command line may have been +// overwritten. void redrawcmdline(void) { if (cmd_silent) @@ -3669,27 +3668,54 @@ expand_cmdline ( return EXPAND_OK; } -/* - * Cleanup matches for help tags: remove "@en" if "en" is the only language. - */ - +// Cleanup matches for help tags: +// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first +// tag matches it. Otherwise remove "@en" if "en" is the only language. static void cleanup_help_tags(int num_file, char_u **file) { - int i, j; - int len; + char_u buf[4]; + char_u *p = buf; + + if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) { + *p++ = '@'; + *p++ = p_hlg[0]; + *p++ = p_hlg[1]; + } + *p = NUL; - for (i = 0; i < num_file; ++i) { - len = (int)STRLEN(file[i]) - 3; - if (len > 0 && STRCMP(file[i] + len, "@en") == 0) { - /* Sorting on priority means the same item in another language may - * be anywhere. Search all items for a match up to the "@en". */ - for (j = 0; j < num_file; ++j) + for (int i = 0; i < num_file; i++) { + int len = (int)STRLEN(file[i]) - 3; + if (len <= 0) { + continue; + } + if (STRCMP(file[i] + len, "@en") == 0) { + // Sorting on priority means the same item in another language may + // be anywhere. Search all items for a match up to the "@en". + int j; + for (j = 0; j < num_file; j++) { if (j != i && (int)STRLEN(file[j]) == len + 3 - && STRNCMP(file[i], file[j], len + 1) == 0) + && STRNCMP(file[i], file[j], len + 1) == 0) { break; - if (j == num_file) + } + } + if (j == num_file) { + // item only exists with @en, remove it + file[i][len] = NUL; + } + } + } + + if (*buf != NUL) { + for (int i = 0; i < num_file; i++) { + int len = (int)STRLEN(file[i]) - 3; + if (len <= 0) { + continue; + } + if (STRCMP(file[i] + len, buf) == 0) { + // remove the default language file[i][len] = NUL; + } } } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 57d12d396e..87fb928b30 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -927,9 +927,9 @@ 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 */ -/* volatile because it is used in signal handler catch_sigint(). */ -EXTERN volatile int got_int INIT(= FALSE); /* set to TRUE when interrupt - signal occurred */ +// volatile because it is used in a signal handler. +EXTERN volatile int got_int INIT(= false); // set to true when interrupt + // signal occurred 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: diff --git a/src/nvim/log.c b/src/nvim/log.c index c31af6b287..bbb4dfb944 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -146,10 +146,10 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char* fmt, va_list args) { static const char *log_levels[] = { - [DEBUG_LOG_LEVEL] = "debug", - [INFO_LOG_LEVEL] = "info", - [WARNING_LOG_LEVEL] = "warning", - [ERROR_LOG_LEVEL] = "error" + [DEBUG_LOG_LEVEL] = "DEBUG", + [INFO_LOG_LEVEL] = "INFO ", + [WARNING_LOG_LEVEL] = "WARN ", + [ERROR_LOG_LEVEL] = "ERROR" }; assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL); @@ -166,8 +166,8 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, // print the log message prefixed by the current timestamp and pid int64_t pid = os_get_pid(); - if (fprintf(log_file, "%s [%s @ %s:%d] %" PRId64 " - ", date_time, - log_levels[log_level], func_name, line_num, pid) < 0) { + if (fprintf(log_file, "%s %s %" PRId64 "/%s:%d: ", date_time, + log_levels[log_level], pid, func_name, line_num) < 0) { return false; } if (vfprintf(log_file, fmt, args) < 0) { diff --git a/src/nvim/main.c b/src/nvim/main.c index a153931d8a..005f4dcc77 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -156,7 +156,7 @@ void event_teardown(void) return; } - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); input_stop(); channel_teardown(); process_teardown(&main_loop); @@ -165,7 +165,7 @@ void event_teardown(void) signal_teardown(); terminal_teardown(); - loop_close(&main_loop); + loop_close(&main_loop, true); } /// Performs early initialization. diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index f577fd847e..c08b9e8fcf 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -949,6 +949,9 @@ int utf_char2cells(int c) if (intable(doublewidth, ARRAY_SIZE(doublewidth), c)) return 2; #endif + if (p_emoji && intable(emoji_width, ARRAY_SIZE(emoji_width), c)) { + return 2; + } } /* Characters below 0x100 are influenced by 'isprint' option */ else if (c >= 0x80 && !vim_isprintc(c)) @@ -1712,16 +1715,20 @@ int utf_class(int c) return (int)classes[mid].class; } + // emoji + if (intable(emoji_all, ARRAY_SIZE(emoji_all), c)) { + return 3; + } + /* most other characters are "word" characters */ return 2; } -/* - * Code for Unicode case-dependent operations. Based on notes in - * http://www.unicode.org/Public/UNIDATA/CaseFolding.txt - * This code uses simple case folding, not full case folding. - * Last updated for Unicode 5.2. - */ +int utf_ambiguous_width(int c) +{ + return c >= 0x80 && (intable(ambiguous, ARRAY_SIZE(ambiguous), c) + || intable(emoji_all, ARRAY_SIZE(emoji_all), c)); +} /* * Generic conversion function for case operations. diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index cd12f258b6..fef1d08db7 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -65,7 +65,7 @@ typedef struct { uint64_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; kvec_t(WBuffer *) delayed_notifications; - Queue *events; + MultiQueue *events; } Channel; typedef struct { @@ -440,7 +440,7 @@ static void handle_request(Channel *channel, msgpack_object *request) if (handler.async) { on_request_event((void **)&event_data); } else { - queue_put(channel->events, on_request_event, 1, event_data); + multiqueue_put(channel->events, on_request_event, 1, event_data); } } @@ -620,7 +620,7 @@ static void close_channel(Channel *channel) case kChannelTypeStdio: stream_close(&channel->data.std.in, NULL, NULL); stream_close(&channel->data.std.out, NULL, NULL); - queue_put(main_loop.fast_events, exit_event, 1, channel); + multiqueue_put(main_loop.fast_events, exit_event, 1, channel); return; default: abort(); @@ -654,7 +654,7 @@ static void free_channel(Channel *channel) kv_destroy(channel->call_stack); kv_destroy(channel->delayed_notifications); if (channel->type != kChannelTypeProc) { - queue_free(channel->events); + multiqueue_free(channel->events); } xfree(channel); } @@ -664,10 +664,11 @@ static void close_cb(Stream *stream, void *data) decref(data); } -static Channel *register_channel(ChannelType type, uint64_t id, Queue *events) +static Channel *register_channel(ChannelType type, uint64_t id, + MultiQueue *events) { Channel *rv = xmalloc(sizeof(Channel)); - rv->events = events ? events : queue_new_child(main_loop.events); + rv->events = events ? events : multiqueue_new_child(main_loop.events); rv->type = type; rv->refcount = 1; rv->closed = false; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d0a9d3aca7..6dcbf50750 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -459,7 +459,7 @@ void normal_enter(bool cmdwin, bool noexmode) normal_state_init(&state); state.cmdwin = cmdwin; state.noexmode = noexmode; - state.toplevel = !cmdwin && !noexmode; + state.toplevel = (!cmdwin || cmdwin_result == 0) && !noexmode; state_enter(&state.state); } @@ -1360,7 +1360,7 @@ static int normal_check(VimState *state) // Dict internally somewhere. // "may_garbage_collect" is reset in vgetc() which is invoked through // do_exmode() and normal_cmd(). - may_garbage_collect = s->toplevel; + may_garbage_collect = !s->cmdwin && !s->noexmode; // Update w_curswant if w_set_curswant has been set. // Postponed until here to avoid computing w_virtcol too often. @@ -7880,15 +7880,15 @@ static void nv_event(cmdarg_T *cap) { // Garbage collection should have been executed before blocking for events in // the `os_inchar` in `state_enter`, but we also disable it here in case the - // `os_inchar` branch was not executed(!queue_empty(loop.events), which could - // have `may_garbage_collect` set to true in `normal_check`). + // `os_inchar` branch was not executed (!multiqueue_empty(loop.events), which + // could have `may_garbage_collect` set to true in `normal_check`). // // That is because here we may run code that calls `os_inchar` // later(`f_confirm` or `get_keystroke` for example), but in these cases it is // not safe to perform garbage collection because there could be unreferenced // lists or dicts being used. may_garbage_collect = false; - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); cap->retval |= CA_COMMAND_BUSY; // don't call edit() now } diff --git a/src/nvim/option.c b/src/nvim/option.c index d8908cca90..e2a5d38bee 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2518,15 +2518,15 @@ did_set_string_option ( else if (varp == &p_sbo) { if (check_opt_strings(p_sbo, p_scbopt_values, TRUE) != OK) errmsg = e_invarg; - } - /* 'ambiwidth' */ - else if (varp == &p_ambw) { - if (check_opt_strings(p_ambw, p_ambw_values, FALSE) != OK) + } else if (varp == &p_ambw || (bool *)varp == &p_emoji) { + // 'ambiwidth' + if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) { errmsg = e_invarg; - else if (set_chars_option(&p_lcs) != NULL) + } else if (set_chars_option(&p_lcs) != NULL) { errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'"); - else if (set_chars_option(&p_fcs) != NULL) + } else if (set_chars_option(&p_fcs) != NULL) { errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'"); + } } /* 'background' */ else if (varp == &p_bg) { @@ -3658,14 +3658,16 @@ set_bool_option ( /* when 'insertmode' is set from an autocommand need to do work here */ else if ((int *)varp == &p_im) { if (p_im) { - if ((State & INSERT) == 0) - need_start_insertmode = TRUE; - stop_insert_mode = FALSE; - } else { - need_start_insertmode = FALSE; - stop_insert_mode = TRUE; - if (restart_edit != 0 && mode_displayed) - clear_cmdline = TRUE; /* remove "(insert)" */ + if ((State & INSERT) == 0) { + need_start_insertmode = true; + } + stop_insert_mode = false; + } else if (old_value) { // only reset if it was set previously + need_start_insertmode = false; + stop_insert_mode = true; + if (restart_edit != 0 && mode_displayed) { + clear_cmdline = true; // remove "(insert)" + } restart_edit = 0; } } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e36cceaaf4..e085b973ea 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -401,24 +401,25 @@ static char *(p_dy_values[]) = {"lastline", "uhex", NULL}; #endif #define DY_LASTLINE 0x001 #define DY_UHEX 0x002 -EXTERN int p_ed; /* 'edcompatible' */ -EXTERN char_u *p_ead; /* 'eadirection' */ -EXTERN bool p_ea; /* 'equalalways' */ -EXTERN char_u *p_ep; /* 'equalprg' */ -EXTERN int p_eb; /* 'errorbells' */ -EXTERN char_u *p_ef; /* 'errorfile' */ -EXTERN char_u *p_efm; /* 'errorformat' */ -EXTERN char_u *p_gefm; /* 'grepformat' */ -EXTERN char_u *p_gp; /* 'grepprg' */ -EXTERN char_u *p_ei; /* 'eventignore' */ -EXTERN int p_ek; /* 'esckeys' */ -EXTERN int p_exrc; /* 'exrc' */ -EXTERN char_u *p_fencs; /* 'fileencodings' */ -EXTERN char_u *p_ffs; /* 'fileformats' */ -EXTERN bool p_fic; ///< 'fileignorecase' -EXTERN char_u *p_fcl; /* 'foldclose' */ -EXTERN long p_fdls; /* 'foldlevelstart' */ -EXTERN char_u *p_fdo; /* 'foldopen' */ +EXTERN int p_ed; // 'edcompatible' +EXTERN bool p_emoji; // 'emoji' +EXTERN char_u *p_ead; // 'eadirection' +EXTERN bool p_ea; // 'equalalways' +EXTERN char_u *p_ep; // 'equalprg' +EXTERN int p_eb; // 'errorbells' +EXTERN char_u *p_ef; // 'errorfile' +EXTERN char_u *p_efm; // 'errorformat' +EXTERN char_u *p_gefm; // 'grepformat' +EXTERN char_u *p_gp; // 'grepprg' +EXTERN char_u *p_ei; // 'eventignore' +EXTERN int p_ek; // 'esckeys' +EXTERN int p_exrc; // 'exrc' +EXTERN char_u *p_fencs; // 'fileencodings' +EXTERN char_u *p_ffs; // 'fileformats' +EXTERN bool p_fic; // 'fileignorecase' +EXTERN char_u *p_fcl; // 'foldclose' +EXTERN long p_fdls; // 'foldlevelstart' +EXTERN char_u *p_fdo; // 'foldopen' EXTERN unsigned fdo_flags; # ifdef IN_OPTION_C static char *(p_fdo_values[]) = {"all", "block", "hor", "mark", "percent", diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 060ec8c1e1..583c63614a 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -644,6 +644,14 @@ return { defaults={if_true={vi=false}} }, { + full_name='emoji', abbreviation='emo', + type='bool', scope={'global'}, + vi_def=true, + redraw={'everything'}, + varname='p_emoji', + defaults={if_true={vi=true}} + }, + { full_name='encoding', abbreviation='enc', type='string', scope={'global'}, deny_in_modelines=true, diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 6873e32f34..1307ab5e5a 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -87,8 +87,8 @@ static void create_cursorhold_event(void) // have been called(inbuf_poll would return kInputAvail) // TODO(tarruda): Cursorhold should be implemented as a timer set during the // `state_check` callback for the states where it can be triggered. - assert(!events_enabled || queue_empty(main_loop.events)); - queue_put(main_loop.events, cursorhold_event, 0); + assert(!events_enabled || multiqueue_empty(main_loop.events)); + multiqueue_put(main_loop.events, cursorhold_event, 0); } // Low level input function @@ -422,5 +422,5 @@ static void read_error_exit(void) static bool pending_events(void) { - return events_enabled && !queue_empty(main_loop.events); + return events_enabled && !multiqueue_empty(main_loop.events); } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 661b12accc..e9a3dcbff8 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -162,7 +162,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) /// @param input The input to the shell (NULL for no input), passed to the /// stdin of the resulting process. /// @param len The length of the input buffer (not used if `input` == NULL) -/// @param[out] output A pointer to to a location where the output will be +/// @param[out] output Pointer to a location where the output will be /// allocated and stored. Will point to NULL if the shell /// command did not output anything. If NULL is passed, /// the shell output will be ignored. @@ -207,7 +207,7 @@ static int do_os_system(char **argv, Stream in, out, err; LibuvProcess uvproc = libuv_process_init(&main_loop, &buf); Process *proc = &uvproc.process; - Queue *events = queue_new_child(main_loop.events); + MultiQueue *events = multiqueue_new_child(main_loop.events); proc->events = events; proc->argv = argv; proc->in = input != NULL ? &in : NULL; @@ -221,13 +221,13 @@ static int do_os_system(char **argv, msg_outtrans((char_u *)prog); msg_putchar('\n'); } - queue_free(events); + multiqueue_free(events); return -1; } // We want to deal with stream events as fast a possible while queueing // process events, so reset everything to NULL. It prevents closing the - // streams while there's still data in the OS buffer(due to the process + // streams while there's still data in the OS buffer (due to the process // exiting before all data is read). if (input != NULL) { proc->in->events = NULL; @@ -277,8 +277,8 @@ static int do_os_system(char **argv, } } - assert(queue_empty(events)); - queue_free(events); + assert(multiqueue_empty(events)); + multiqueue_free(events); return status; } diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h index 454972c69d..a8dfcac580 100644 --- a/src/nvim/rbuffer.h +++ b/src/nvim/rbuffer.h @@ -1,4 +1,4 @@ -// Ring buffer implementation. This is basically an array that wraps read/write +// Specialized ring buffer. This is basically an array that wraps read/write // pointers around the memory region. It should be more efficient than the old // RBuffer which required memmove() calls to relocate read/write positions. // diff --git a/src/nvim/state.c b/src/nvim/state.c index f792ec00a4..44c6441e40 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -35,8 +35,8 @@ getkey: // processing. Characters can come from mappings, scripts and other // sources, so this scenario is very common. key = safe_vgetc(); - } else if (!queue_empty(main_loop.events)) { - // Event was made available after the last queue_process_events call + } else if (!multiqueue_empty(main_loop.events)) { + // Event was made available after the last multiqueue_process_events call key = K_EVENT; } else { input_enable_events(); @@ -48,7 +48,7 @@ getkey: // directly. (void)os_inchar(NULL, 0, -1, 0); input_disable_events(); - key = !queue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); + key = !multiqueue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); } if (key == K_EVENT) { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index ff98dc9f22..8401343d7a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -166,7 +166,7 @@ void terminal_init(void) invalidated_terminals = pmap_new(ptr_t)(); time_watcher_init(&main_loop, &refresh_timer, NULL); // refresh_timer_cb will redraw the screen which can call vimscript - refresh_timer.events = queue_new_child(main_loop.events); + refresh_timer.events = multiqueue_new_child(main_loop.events); // initialize a rgb->color index map for cterm attributes(VTermScreenCell // only has RGB information and we need color indexes for terminal UIs) @@ -201,7 +201,7 @@ void terminal_init(void) void terminal_teardown(void) { time_watcher_stop(&refresh_timer); - queue_free(refresh_timer.events); + multiqueue_free(refresh_timer.events); time_watcher_close(&refresh_timer, NULL); pmap_free(ptr_t)(invalidated_terminals); map_free(int, int)(color_indexes); @@ -445,7 +445,7 @@ static int terminal_execute(VimState *state, int key) case K_EVENT: // We cannot let an event free the terminal yet. It is still needed. s->term->refcount++; - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); s->term->refcount--; if (s->term->buf_handle == 0) { s->close = true; diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 74bbf418fa..e2d1e67a22 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -24,6 +24,10 @@ " " If cleanup after each Test_ function is needed, define a TearDown function. " It will be called after each Test_ function. +" +" When debugging a test it can be useful to add messages to v:errors: +" call add(v:errors, "this happened") + " Check that the screen size is at least 24 x 80 characters. if &lines < 24 || &columns < 80 @@ -47,94 +51,108 @@ lang mess C " Always use forward slashes. set shellslash +function RunTheTest(test) + echo 'Executing ' . a:test + if exists("*SetUp") + call SetUp() + endif + + call add(s:messages, 'Executing ' . a:test) + let s:done += 1 + try + exe 'call ' . a:test + catch + call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) + endtry + + if exists("*TearDown") + call TearDown() + endif +endfunc + " Source the test script. First grab the file name, in case the script -" navigates away. -let testname = expand('%') -let done = 0 -let fail = 0 -let errors = [] -let messages = [] +" navigates away. g:testname can be used by the tests. +let g:testname = expand('%') +let s:done = 0 +let s:fail = 0 +let s:errors = [] +let s:messages = [] if expand('%') =~ 'test_viml.vim' - " this test has intentional errors, don't use try/catch. + " this test has intentional s:errors, don't use try/catch. source % else try source % catch - let fail += 1 - call add(errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) + let s:fail += 1 + call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) endtry endif +" Names of flaky tests. +let s:flaky = [] + " Locate Test_ functions and execute them. set nomore redir @q silent function /^Test_ redir END -let tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) +let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) " If there is an extra argument filter the function names against it. if argc() > 1 - let tests = filter(tests, 'v:val =~ argv(1)') + let s:tests = filter(s:tests, 'v:val =~ argv(1)') endif " Execute the tests in alphabetical order. -for test in sort(tests) - echo 'Executing ' . test - if exists("*SetUp") - call SetUp() - endif +for s:test in sort(s:tests) + call RunTheTest(s:test) - call add(messages, 'Executing ' . test) - let done += 1 - try - exe 'call ' . test - catch - let fail += 1 - call add(v:errors, 'Caught exception in ' . test . ': ' . v:exception . ' @ ' . v:throwpoint) - endtry + if len(v:errors) > 0 && index(s:flaky, s:test) >= 0 + call add(s:messages, 'Flaky test failed, running it again') + let v:errors = [] + call RunTheTest(s:test) + endif if len(v:errors) > 0 - let fail += 1 - call add(errors, 'Found errors in ' . test . ':') - call extend(errors, v:errors) + let s:fail += 1 + call add(s:errors, 'Found errors in ' . s:test . ':') + call extend(s:errors, v:errors) let v:errors = [] endif - if exists("*TearDown") - call TearDown() - endif endfor -if fail == 0 +if s:fail == 0 " Success, create the .res file so that make knows it's done. - exe 'split ' . fnamemodify(testname, ':r') . '.res' + exe 'split ' . fnamemodify(g:testname, ':r') . '.res' write endif -if len(errors) > 0 +if len(s:errors) > 0 " Append errors to test.log split test.log call append(line('$'), '') - call append(line('$'), 'From ' . testname . ':') - call append(line('$'), errors) + call append(line('$'), 'From ' . g:testname . ':') + call append(line('$'), s:errors) write endif -let message = 'Executed ' . done . (done > 1 ? ' tests': ' test') +let message = 'Executed ' . s:done . (s:done > 1 ? ' tests': ' test') echo message -call add(messages, message) -if fail > 0 - let message = fail . ' FAILED' +call add(s:messages, message) +if s:fail > 0 + let message = s:fail . ' FAILED' echo message - call add(messages, message) + call add(s:messages, message) + call extend(s:messages, s:errors) endif " Append messages to "messages" split messages call append(line('$'), '') -call append(line('$'), 'From ' . testname . ':') -call append(line('$'), messages) +call append(line('$'), 'From ' . g:testname . ':') +call append(line('$'), s:messages) write qall! diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index 42f8391aba..cc1c81c7f6 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -48,3 +48,118 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*{address}\*') helpclose endfunc + +let s:langs = ['en', 'ab', 'ja'] + +func s:doc_config_setup() + let s:helpfile_save = &helpfile + let &helpfile="Xdir1/doc-en/doc/testdoc.txt" + let s:rtp_save = &rtp + let &rtp="Xdir1/doc-en" + if has('multi_lang') + let s:helplang_save=&helplang + endif + + call delete('Xdir1', 'rf') + + for lang in s:langs + if lang ==# 'en' + let tagfname = 'tags' + let docfname = 'testdoc.txt' + else + let tagfname = 'tags-' . lang + let docfname = 'testdoc.' . lang . 'x' + endif + let docdir = "Xdir1/doc-" . lang . "/doc" + call mkdir(docdir, "p") + call writefile(["\t*test-char*", "\t*test-col*"], docdir . '/' . docfname) + call writefile(["test-char\t" . docfname . "\t/*test-char*", + \ "test-col\t" . docfname . "\t/*test-col*"], + \ docdir . '/' . tagfname) + endfor +endfunc + +func s:doc_config_teardown() + call delete('Xdir1', 'rf') + + let &helpfile = s:helpfile_save + let &rtp = s:rtp_save + if has('multi_lang') + let &helplang = s:helplang_save + endif +endfunc + +func s:get_cmd_compl_list(cmd) + let list = [] + let str = '' + for cnt in range(1, 999) + call feedkeys(a:cmd . repeat("\<Tab>", cnt) . "'\<C-B>let str='\<CR>", 'tx') + if str ==# a:cmd[1:] + break + endif + call add(list, str) + endfor + return list +endfunc + +func Test_help_complete() + try + let list = [] + call s:doc_config_setup() + + " 'helplang=' and help file lang is 'en' + if has('multi_lang') + set helplang= + endif + let list = s:get_cmd_compl_list(":h test") + call assert_equal(['h test-col', 'h test-char'], list) + + if has('multi_lang') + " 'helplang=ab' and help file lang is 'en' + set helplang=ab + let list = s:get_cmd_compl_list(":h test") + call assert_equal(['h test-col', 'h test-char'], list) + + " 'helplang=' and help file lang is 'en' and 'ab' + set rtp+=Xdir1/doc-ab + set helplang= + let list = s:get_cmd_compl_list(":h test") + call assert_equal(sort(['h test-col@en', 'h test-col@ab', + \ 'h test-char@en', 'h test-char@ab']), sort(list)) + + " 'helplang=ab' and help file lang is 'en' and 'ab' + set helplang=ab + let list = s:get_cmd_compl_list(":h test") + call assert_equal(sort(['h test-col', 'h test-col@en', + \ 'h test-char', 'h test-char@en']), sort(list)) + + " 'helplang=' and help file lang is 'en', 'ab' and 'ja' + set rtp+=Xdir1/doc-ja + set helplang= + let list = s:get_cmd_compl_list(":h test") + call assert_equal(sort(['h test-col@en', 'h test-col@ab', + \ 'h test-col@ja', 'h test-char@en', + \ 'h test-char@ab', 'h test-char@ja']), sort(list)) + + " 'helplang=ab' and help file lang is 'en', 'ab' and 'ja' + set helplang=ab + let list = s:get_cmd_compl_list(":h test") + call assert_equal(sort(['h test-col', 'h test-col@en', + \ 'h test-col@ja', 'h test-char', + \ 'h test-char@en', 'h test-char@ja']), sort(list)) + + " 'helplang=ab,ja' and help file lang is 'en', 'ab' and 'ja' + set helplang=ab,ja + let list = s:get_cmd_compl_list(":h test") + call assert_equal(sort(['h test-col', 'h test-col@ja', + \ 'h test-col@en', 'h test-char', + \ 'h test-char@ja', 'h test-char@en']), sort(list)) + endif + catch + call assert_exception('X') + finally + call s:doc_config_teardown() + endtry +endfunc + +" vim: et sw=2: diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 78fc81e3d2..63be8bf609 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -28,3 +28,38 @@ func Test_noinsert_complete() set completeopt-=noinsert iunmap <F5> endfunc + +let g:months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] +let g:setting = '' + +func ListMonths() + if g:setting != '' + exe ":set" g:setting + endif + call complete(col('.'), g:months) + return '' +endfunc + +func! Test_popup_completion_insertmode() + inoremap <F5> <C-R>=ListMonths()<CR> + new + call feedkeys("a\<F5>\<down>\<enter>\<esc>", 'tx') + call assert_equal('February', getline(1)) + %d + let g:setting = 'noinsertmode' + call feedkeys("a\<F5>\<down>\<enter>\<esc>", 'tx') + call assert_equal('February', getline(1)) + call assert_false(pumvisible()) + %d + let g:setting = '' + call feedkeys("a\<F5>". repeat("\<c-n>",12)."\<enter>\<esc>", 'tx') + call assert_equal('', getline(1)) + %d + call feedkeys("a\<F5>\<c-p>\<enter>\<esc>", 'tx') + call assert_equal('', getline(1)) + %d + call feedkeys("a\<F5>\<c-p>\<c-p>\<enter>\<esc>", 'tx') + call assert_equal('December', getline(1)) + bwipe! + iunmap <F5> +endfunc diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 68ea00ee63..740716f0ef 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -340,7 +340,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, // ls *.md | xargs nvim input->in_fd = 2; stream_close(&input->read_stream, NULL, NULL); - queue_put(input->loop->fast_events, restart_reading, 1, input); + multiqueue_put(input->loop->fast_events, restart_reading, 1, input); } else { loop_schedule(&main_loop, event_create(1, input_done_event, 0)); } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index bfc03dfb81..04b5868d2c 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1,3 +1,5 @@ +// Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread. + #include <assert.h> #include <stdbool.h> #include <stdio.h> @@ -17,13 +19,13 @@ #include "nvim/api/private/helpers.h" #include "nvim/event/loop.h" #include "nvim/event/signal.h" -#include "nvim/tui/tui.h" -#include "nvim/tui/input.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/strings.h" #include "nvim/ugrid.h" -#include "nvim/ui_bridge.h" +#include "nvim/tui/input.h" +#include "nvim/tui/tui.h" +#include "nvim/tui/ui_bridge.h" // Space reserved in the output buffer to restore the cursor to normal when // flushing. No existing terminal will require 32 bytes to do that. @@ -65,7 +67,7 @@ typedef struct { struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; - int enter_insert_mode, enter_replace_mode, exit_insert_mode; + int set_cursor_shape_bar, set_cursor_shape_ul, set_cursor_shape_block; int set_rgb_foreground, set_rgb_background; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; @@ -122,9 +124,9 @@ static void terminfo_start(UI *ui) data->unibi_ext.disable_mouse = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; - data->unibi_ext.enter_insert_mode = -1; - data->unibi_ext.enter_replace_mode = -1; - data->unibi_ext.exit_insert_mode = -1; + data->unibi_ext.set_cursor_shape_bar = -1; + data->unibi_ext.set_cursor_shape_ul = -1; + data->unibi_ext.set_cursor_shape_block = -1; data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1; data->out_fd = 1; @@ -137,6 +139,8 @@ static void terminfo_start(UI *ui) data->ut = unibi_dummy(); } fix_terminfo(data); + // Initialize the cursor shape. + unibi_out(ui, data->unibi_ext.set_cursor_shape_block); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear @@ -237,7 +241,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) signal_watcher_stop(&data->cont_handle); signal_watcher_close(&data->cont_handle, NULL); signal_watcher_close(&data->winch_handle, NULL); - loop_close(&tui_loop); + loop_close(&tui_loop, false); kv_destroy(data->invalid_regions); xfree(data); xfree(ui); @@ -250,11 +254,6 @@ static void tui_scheduler(Event event, void *d) loop_schedule(data->loop, event); } -static void refresh_event(void **argv) -{ - ui_refresh(); -} - static void sigcont_cb(SignalWatcher *watcher, int signum, void *data) { ((TUIData *)data)->cont_received = true; @@ -265,8 +264,7 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) got_winch = true; UI *ui = data; update_size(ui); - // run refresh_event in nvim main loop - loop_schedule(&main_loop, event_create(1, refresh_event, 0)); + ui_schedule_refresh(); } static bool attrs_differ(HlAttrs a1, HlAttrs a2) @@ -461,16 +459,16 @@ static void tui_mode_change(UI *ui, int mode) if (mode == INSERT) { if (data->showing_mode != INSERT) { - unibi_out(ui, data->unibi_ext.enter_insert_mode); + unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); } } else if (mode == REPLACE) { if (data->showing_mode != REPLACE) { - unibi_out(ui, data->unibi_ext.enter_replace_mode); + unibi_out(ui, data->unibi_ext.set_cursor_shape_ul); } } else { assert(mode == NORMAL); if (data->showing_mode != NORMAL) { - unibi_out(ui, data->unibi_ext.exit_insert_mode); + unibi_out(ui, data->unibi_ext.set_cursor_shape_block); } } data->showing_mode = mode; @@ -632,8 +630,8 @@ static void tui_suspend(UI *ui) // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT // before continuing. This is done in another callback to avoid // loop_poll_events recursion - queue_put_event(data->loop->fast_events, - event_create(1, suspend_event, 1, ui)); + multiqueue_put_event(data->loop->fast_events, + event_create(1, suspend_event, 1, ui)); } static void tui_set_title(UI *ui, char *title) @@ -864,23 +862,23 @@ static void fix_terminfo(TUIData *data) || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { // Konsole uses a proprietary escape code to set the cursor shape // and does not support DECSCUSR. - data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07")); - data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=2;BlinkingCursorEnabled=1\x07")); - data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07")); } else if (!vte_version || atoi(vte_version) >= 3900) { // Assume that the terminal supports DECSCUSR unless it is an // old VTE based terminal. This should not get wrapped for tmux, // which will handle it via its Ss/Se terminfo extension - usually // according to its terminal-overrides. - data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, - "\x1b[5 q"); - data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, - "\x1b[3 q"); - data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, - "\x1b[2 q"); + data->unibi_ext.set_cursor_shape_bar = + (int)unibi_add_ext_str(ut, NULL, "\x1b[5 q"); + data->unibi_ext.set_cursor_shape_ul = + (int)unibi_add_ext_str(ut, NULL, "\x1b[3 q"); + data->unibi_ext.set_cursor_shape_block = + (int)unibi_add_ext_str(ut, NULL, "\x1b[2 q"); } end: diff --git a/src/nvim/ui_bridge.c b/src/nvim/tui/ui_bridge.c index 34b95baf6c..48f4b1bda6 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/tui/ui_bridge.c @@ -1,5 +1,5 @@ -// FIXME(tarruda): This module is very repetitive. It might be a good idea to -// automatically generate it with a lua script during build +// UI wrapper for the built-in TUI. Sends UI requests to the TUI thread. + #include <assert.h> #include <stdbool.h> #include <stdio.h> @@ -9,11 +9,11 @@ #include "nvim/vim.h" #include "nvim/ui.h" #include "nvim/memory.h" -#include "nvim/ui_bridge.h" #include "nvim/ugrid.h" +#include "nvim/tui/ui_bridge.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "ui_bridge.c.generated.h" +# include "tui/ui_bridge.c.generated.h" #endif #define UI(b) (((UIBridgeData *)b)->ui) diff --git a/src/nvim/ui_bridge.h b/src/nvim/tui/ui_bridge.h index 561ddb6b24..003ed3c2c1 100644 --- a/src/nvim/ui_bridge.h +++ b/src/nvim/tui/ui_bridge.h @@ -1,6 +1,6 @@ // Bridge used for communication between a builtin UI thread and nvim core -#ifndef NVIM_UI_BRIDGE_H -#define NVIM_UI_BRIDGE_H +#ifndef NVIM_TUI_UI_BRIDGE_H +#define NVIM_TUI_UI_BRIDGE_H #include <uv.h> @@ -11,7 +11,7 @@ typedef struct ui_bridge_data UIBridgeData; typedef void(*ui_main_fn)(UIBridgeData *bridge, UI *ui); struct ui_bridge_data { UI bridge; // actual UI passed to ui_attach - UI *ui; // UI pointer that will have it's callback called in + UI *ui; // UI pointer that will have its callback called in // another thread event_scheduler scheduler; uv_thread_t ui_thread; @@ -39,6 +39,6 @@ struct ui_bridge_data { #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "ui_bridge.h.generated.h" +# include "tui/ui_bridge.h.generated.h" #endif -#endif // NVIM_UI_BRIDGE_H +#endif // NVIM_TUI_UI_BRIDGE_H diff --git a/src/nvim/ui.c b/src/nvim/ui.c index dd278893c2..648d633e07 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -53,12 +53,10 @@ static bool pending_cursor_update = false; static int busy = 0; static int height, width; -// This set of macros allow us to use UI_CALL to invoke any function on -// registered UI instances. The functions can have 0-5 arguments(configurable -// by SELECT_NTH) +// UI_CALL invokes a function on all registered UI instances. The functions can +// have 0-5 arguments (configurable by SELECT_NTH). // -// See http://stackoverflow.com/a/11172679 for a better explanation of how it -// works. +// See http://stackoverflow.com/a/11172679 for how it works. #ifdef _MSC_VER # define UI_CALL(funname, ...) \ do { \ @@ -169,8 +167,8 @@ void ui_refresh(void) for (size_t i = 0; i < ui_count; i++) { UI *ui = uis[i]; - width = ui->width < width ? ui->width : width; - height = ui->height < height ? ui->height : height; + width = MIN(ui->width, width); + height = MIN(ui->height, height); pum_external &= ui->pum_external; } @@ -179,6 +177,16 @@ void ui_refresh(void) pum_set_external(pum_external); } +static void ui_refresh_handler(void **argv) +{ + ui_refresh(); +} + +void ui_schedule_refresh(void) +{ + loop_schedule(&main_loop, event_create(1, ui_refresh_handler, 0)); +} + void ui_resize(int new_width, int new_height) { width = new_width; @@ -252,7 +260,7 @@ void ui_detach_impl(UI *ui) } if (--ui_count) { - ui_refresh(); + ui_schedule_refresh(); } } @@ -389,7 +397,10 @@ static void send_output(uint8_t **ptr) size_t clen = (size_t)mb_ptr2len(p); UI_CALL(put, p, (size_t)clen); col++; - if (mb_ptr2cells(p) > 1) { + if (utf_ambiguous_width(*p)) { + pending_cursor_update = true; + flush_cursor_update(); + } else if (mb_ptr2cells(p) > 1) { // double cell character, blank the next cell UI_CALL(put, NULL, 0); col++; diff --git a/src/nvim/version.c b/src/nvim/version.c index 2efa81af1d..9f8fcd1232 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -480,7 +480,7 @@ static int included_patches[] = { // 1964, // 1963 NA // 1962, - // 1961, + 1961, 1960, // 1959 NA // 1958 NA @@ -620,10 +620,10 @@ static int included_patches[] = { // 1824 NA // 1823, // 1822 NA - // 1821, - // 1820, + 1821, + 1820, // 1819 NA - // 1818, + 1818, // 1817 NA // 1816, // 1815, @@ -762,9 +762,9 @@ static int included_patches[] = { // 1684 NA // 1683 NA 1682, - // 1681, + 1681, // 1680 NA - // 1679, + 1679, // 1678 NA // 1677 NA 1676, @@ -772,8 +772,8 @@ static int included_patches[] = { // 1674 NA 1673, // 1672 NA - // 1671, - // 1670, + 1671, + 1670, // 1669 NA // 1668 NA // 1667 NA @@ -795,13 +795,13 @@ static int included_patches[] = { // 1651 NA // 1650, 1649, - // 1648, + 1648, // 1647, // 1646 NA // 1645, // 1644, 1643, - // 1642, + 1642, 1641, // 1640, // 1639, @@ -813,8 +813,8 @@ static int included_patches[] = { // 1633 NA // 1632 NA // 1631 NA - // 1630, - // 1629, + 1630, + 1629, // 1628 NA // 1627 NA // 1626 NA @@ -823,7 +823,7 @@ static int included_patches[] = { // 1623 NA // 1622 NA // 1621 NA - // 1620, + 1620, // 1619, // 1618 NA // 1617 NA @@ -839,7 +839,7 @@ static int included_patches[] = { // 1607, // 1606, // 1605, - // 1604, + 1604, 1603, // 1602 NA // 1601 NA @@ -966,7 +966,7 @@ static int included_patches[] = { 1480, 1479, 1478, - // 1477, + 1477, // 1476 NA // 1475 NA // 1474 NA diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index d5b7442b57..325f41e506 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,6 +1,8 @@ require('coxpcall') local lfs = require('lfs') local ChildProcessStream = require('nvim.child_process_stream') +local SocketStream = require('nvim.socket_stream') +local TcpStream = require('nvim.tcp_stream') local Session = require('nvim.session') local global_helpers = require('test.helpers') @@ -223,6 +225,14 @@ local function spawn(argv, merge, env) return Session.new(child_stream) end +-- Creates a new Session connected by domain socket (named pipe) or TCP. +local function connect(file_or_address) + local addr, port = string.match(file_or_address, "(.*):(%d+)") + local stream = (addr and port) and TcpStream.open(addr, port) or + SocketStream.open(file_or_address) + return Session.new(stream) +end + local function clear(...) local args = {unpack(nvim_argv)} local new_args @@ -291,8 +301,7 @@ local function write_file(name, text, dont_dedent) file:close() end --- Tries to get platform name, from $SYSTEM_NAME, uname, --- fallback is 'Windows' +-- Tries to get platform name from $SYSTEM_NAME, uname; fallback is "Windows". local uname = (function() local platform = nil return (function() @@ -508,6 +517,7 @@ return function(after_each) return { prepend_argv = prepend_argv, clear = clear, + connect = connect, spawn = spawn, dedent = dedent, source = source, diff --git a/test/functional/normal/count_spec.lua b/test/functional/normal/count_spec.lua new file mode 100644 index 0000000000..700e1f3e81 --- /dev/null +++ b/test/functional/normal/count_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local clear = helpers.clear +local execute = helpers.execute + +describe('v:count/v:count1', function() + before_each(function() + clear() + + execute('map <silent> _x :<C-u>let g:count = "v:count=". v:count .", v:count1=". v:count1<CR>') + end) + + describe('in cmdwin', function() + it('equal 0/1 when no count is given', function() + feed('q:_x') + eq('v:count=0, v:count1=1', eval('g:count')) + end) + + it('equal 2/2 when count of 2 is given', function() + feed('q:2_x') + eq('v:count=2, v:count1=2', eval('g:count')) + end) + end) + + describe('in normal mode', function() + it('equal 0/1 when no count is given', function() + feed('_x') + eq('v:count=0, v:count1=1', eval('g:count')) + end) + + it('equal 2/2 when count of 2 is given', function() + feed('2_x') + eq('v:count=2, v:count1=2', eval('g:count')) + end) + end) +end) diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua new file mode 100644 index 0000000000..58d6c75940 --- /dev/null +++ b/test/functional/terminal/api_spec.lua @@ -0,0 +1,60 @@ +local helpers = require('test.functional.helpers')(after_each) +local child_session = require('test.functional.terminal.helpers') +local ok = helpers.ok + +if helpers.pending_win32(pending) then return end + +describe('api', function() + local screen + local socket_name = "Xtest_functional_api.sock" + + before_each(function() + helpers.clear() + os.remove(socket_name) + screen = child_session.screen_setup(0, '["'..helpers.nvim_prog + ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile"]') + end) + after_each(function() + os.remove(socket_name) + end) + + it("qa! RPC request during insert-mode", function() + -- Start the socket from the child nvim. + child_session.feed_data(":echo serverstart('"..socket_name.."')\n") + + -- Wait for socket creation by abusing expect(). + screen:expect([[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + ]]..socket_name..[[ | + {3:-- TERMINAL --} | + ]]) + + local socket_session1 = helpers.connect(socket_name) + local socket_session2 = helpers.connect(socket_name) + + child_session.feed_data("i[tui] insert-mode") + + ok(socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true})) + ok(socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true})) + + socket_session1:notify("nvim_input", "\n[socket 1] this is more than 25 columns") + socket_session2:notify("nvim_input", "\n[socket 2] input") + + screen:expect([[ + [tui] insert-mode | + [socket 1] this is more t{4: }| + han 25 columns {4: }| + [socket 2] input{1: } {4: }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + + socket_session1:request("nvim_command", "qa!") + end) +end) + diff --git a/test/unit/fixtures/multiqueue.c b/test/unit/fixtures/multiqueue.c new file mode 100644 index 0000000000..da63e55919 --- /dev/null +++ b/test/unit/fixtures/multiqueue.c @@ -0,0 +1,16 @@ +#include <string.h> +#include <stdlib.h> +#include "nvim/event/multiqueue.h" +#include "multiqueue.h" + + +void ut_multiqueue_put(MultiQueue *this, const char *str) +{ + multiqueue_put(this, NULL, 1, str); +} + +const char *ut_multiqueue_get(MultiQueue *this) +{ + Event event = multiqueue_get(this); + return event.argv[0]; +} diff --git a/test/unit/fixtures/multiqueue.h b/test/unit/fixtures/multiqueue.h new file mode 100644 index 0000000000..78a3a89063 --- /dev/null +++ b/test/unit/fixtures/multiqueue.h @@ -0,0 +1,4 @@ +#include "nvim/event/multiqueue.h" + +void ut_multiqueue_put(MultiQueue *queue, const char *str); +const char *ut_multiqueue_get(MultiQueue *queue); diff --git a/test/unit/fixtures/queue.c b/test/unit/fixtures/queue.c deleted file mode 100644 index bbb6274b21..0000000000 --- a/test/unit/fixtures/queue.c +++ /dev/null @@ -1,16 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include "nvim/event/queue.h" -#include "queue.h" - - -void ut_queue_put(Queue *queue, const char *str) -{ - queue_put(queue, NULL, 1, str); -} - -const char *ut_queue_get(Queue *queue) -{ - Event event = queue_get(queue); - return event.argv[0]; -} diff --git a/test/unit/fixtures/queue.h b/test/unit/fixtures/queue.h deleted file mode 100644 index ae949c9f29..0000000000 --- a/test/unit/fixtures/queue.h +++ /dev/null @@ -1,4 +0,0 @@ -#include "nvim/event/queue.h" - -void ut_queue_put(Queue *queue, const char *str); -const char *ut_queue_get(Queue *queue); diff --git a/test/unit/queue_spec.lua b/test/unit/queue_spec.lua index 9326c1cad6..d802367835 100644 --- a/test/unit/queue_spec.lua +++ b/test/unit/queue_spec.lua @@ -3,28 +3,28 @@ local helpers = require("test.unit.helpers") local ffi = helpers.ffi local eq = helpers.eq -local queue = helpers.cimport("./test/unit/fixtures/queue.h") +local multiqueue = helpers.cimport("./test/unit/fixtures/multiqueue.h") -describe('queue', function() +describe("multiqueue (multi-level event-queue)", function() local parent, child1, child2, child3 local function put(q, str) - queue.ut_queue_put(q, str) + multiqueue.ut_multiqueue_put(q, str) end local function get(q) - return ffi.string(queue.ut_queue_get(q)) + return ffi.string(multiqueue.ut_multiqueue_get(q)) end local function free(q) - queue.queue_free(q) + multiqueue.multiqueue_free(q) end before_each(function() - parent = queue.queue_new_parent(ffi.NULL, ffi.NULL) - child1 = queue.queue_new_child(parent) - child2 = queue.queue_new_child(parent) - child3 = queue.queue_new_child(parent) + parent = multiqueue.multiqueue_new_parent(ffi.NULL, ffi.NULL) + child1 = multiqueue.multiqueue_new_child(parent) + child2 = multiqueue.multiqueue_new_child(parent) + child3 = multiqueue.multiqueue_new_child(parent) put(child1, 'c1i1') put(child1, 'c1i2') put(child2, 'c2i1') diff --git a/unicode/emoji-data.txt b/unicode/emoji-data.txt new file mode 100644 index 0000000000..731b62c4c1 --- /dev/null +++ b/unicode/emoji-data.txt @@ -0,0 +1,403 @@ +# emoji-data.txt +# Date: 2016-06-02, 09:26:10 GMT +# © 2016 Unicode®, Inc. +# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Emoji Data for UTR #51 +# Version: 3.0 +# +# For documentation and usage, see http://www.unicode.org/reports/tr51 +# +# Warning: the format has changed from Version 1.0 +# Format: +# codepoint(s) ; property(=Yes) # version [count] name(s) + +# ================================================ + +# All omitted code points have Emoji=No +# @missing: 0000..10FFFF ; Emoji ; No + +0023 ; Emoji # 1.1 [1] (#) NUMBER SIGN +002A ; Emoji # 1.1 [1] (*) ASTERISK +0030..0039 ; Emoji # 1.1 [10] (0..9) DIGIT ZERO..DIGIT NINE +00A9 ; Emoji # 1.1 [1] (©) COPYRIGHT SIGN +00AE ; Emoji # 1.1 [1] (®) REGISTERED SIGN +203C ; Emoji # 1.1 [1] (‼) DOUBLE EXCLAMATION MARK +2049 ; Emoji # 3.0 [1] (⁉) EXCLAMATION QUESTION MARK +2122 ; Emoji # 1.1 [1] (™) TRADE MARK SIGN +2139 ; Emoji # 3.0 [1] (ℹ) INFORMATION SOURCE +2194..2199 ; Emoji # 1.1 [6] (↔..↙) LEFT RIGHT ARROW..SOUTH WEST ARROW +21A9..21AA ; Emoji # 1.1 [2] (↩..↪) LEFTWARDS ARROW WITH HOOK..RIGHTWARDS ARROW WITH HOOK +231A..231B ; Emoji # 1.1 [2] (⌚..⌛) WATCH..HOURGLASS +2328 ; Emoji # 1.1 [1] (⌨) KEYBOARD +23CF ; Emoji # 4.0 [1] (⏏) EJECT SYMBOL +23E9..23F3 ; Emoji # 6.0 [11] (⏩..⏳) BLACK RIGHT-POINTING DOUBLE TRIANGLE..HOURGLASS WITH FLOWING SAND +23F8..23FA ; Emoji # 7.0 [3] (⏸..⏺) DOUBLE VERTICAL BAR..BLACK CIRCLE FOR RECORD +24C2 ; Emoji # 1.1 [1] (Ⓜ) CIRCLED LATIN CAPITAL LETTER M +25AA..25AB ; Emoji # 1.1 [2] (▪..▫) BLACK SMALL SQUARE..WHITE SMALL SQUARE +25B6 ; Emoji # 1.1 [1] (▶) BLACK RIGHT-POINTING TRIANGLE +25C0 ; Emoji # 1.1 [1] (◀) BLACK LEFT-POINTING TRIANGLE +25FB..25FE ; Emoji # 3.2 [4] (◻..◾) WHITE MEDIUM SQUARE..BLACK MEDIUM SMALL SQUARE +2600..2604 ; Emoji # 1.1 [5] (☀..☄) BLACK SUN WITH RAYS..COMET +260E ; Emoji # 1.1 [1] (☎) BLACK TELEPHONE +2611 ; Emoji # 1.1 [1] (☑) BALLOT BOX WITH CHECK +2614..2615 ; Emoji # 4.0 [2] (☔..☕) UMBRELLA WITH RAIN DROPS..HOT BEVERAGE +2618 ; Emoji # 4.1 [1] (☘) SHAMROCK +261D ; Emoji # 1.1 [1] (☝) WHITE UP POINTING INDEX +2620 ; Emoji # 1.1 [1] (☠) SKULL AND CROSSBONES +2622..2623 ; Emoji # 1.1 [2] (☢..☣) RADIOACTIVE SIGN..BIOHAZARD SIGN +2626 ; Emoji # 1.1 [1] (☦) ORTHODOX CROSS +262A ; Emoji # 1.1 [1] (☪) STAR AND CRESCENT +262E..262F ; Emoji # 1.1 [2] (☮..☯) PEACE SYMBOL..YIN YANG +2638..263A ; Emoji # 1.1 [3] (☸..☺) WHEEL OF DHARMA..WHITE SMILING FACE +2648..2653 ; Emoji # 1.1 [12] (♈..♓) ARIES..PISCES +2660 ; Emoji # 1.1 [1] (♠) BLACK SPADE SUIT +2663 ; Emoji # 1.1 [1] (♣) BLACK CLUB SUIT +2665..2666 ; Emoji # 1.1 [2] (♥..♦) BLACK HEART SUIT..BLACK DIAMOND SUIT +2668 ; Emoji # 1.1 [1] (♨) HOT SPRINGS +267B ; Emoji # 3.2 [1] (♻) BLACK UNIVERSAL RECYCLING SYMBOL +267F ; Emoji # 4.1 [1] (♿) WHEELCHAIR SYMBOL +2692..2694 ; Emoji # 4.1 [3] (⚒..⚔) HAMMER AND PICK..CROSSED SWORDS +2696..2697 ; Emoji # 4.1 [2] (⚖..⚗) SCALES..ALEMBIC +2699 ; Emoji # 4.1 [1] (⚙) GEAR +269B..269C ; Emoji # 4.1 [2] (⚛..⚜) ATOM SYMBOL..FLEUR-DE-LIS +26A0..26A1 ; Emoji # 4.0 [2] (⚠..⚡) WARNING SIGN..HIGH VOLTAGE SIGN +26AA..26AB ; Emoji # 4.1 [2] (⚪..⚫) MEDIUM WHITE CIRCLE..MEDIUM BLACK CIRCLE +26B0..26B1 ; Emoji # 4.1 [2] (⚰..⚱) COFFIN..FUNERAL URN +26BD..26BE ; Emoji # 5.2 [2] (⚽..⚾) SOCCER BALL..BASEBALL +26C4..26C5 ; Emoji # 5.2 [2] (⛄..⛅) SNOWMAN WITHOUT SNOW..SUN BEHIND CLOUD +26C8 ; Emoji # 5.2 [1] (⛈) THUNDER CLOUD AND RAIN +26CE ; Emoji # 6.0 [1] (⛎) OPHIUCHUS +26CF ; Emoji # 5.2 [1] (⛏) PICK +26D1 ; Emoji # 5.2 [1] (⛑) HELMET WITH WHITE CROSS +26D3..26D4 ; Emoji # 5.2 [2] (⛓..⛔) CHAINS..NO ENTRY +26E9..26EA ; Emoji # 5.2 [2] (⛩..⛪) SHINTO SHRINE..CHURCH +26F0..26F5 ; Emoji # 5.2 [6] (⛰..⛵) MOUNTAIN..SAILBOAT +26F7..26FA ; Emoji # 5.2 [4] (⛷..⛺) SKIER..TENT +26FD ; Emoji # 5.2 [1] (⛽) FUEL PUMP +2702 ; Emoji # 1.1 [1] (✂) BLACK SCISSORS +2705 ; Emoji # 6.0 [1] (✅) WHITE HEAVY CHECK MARK +2708..2709 ; Emoji # 1.1 [2] (✈..✉) AIRPLANE..ENVELOPE +270A..270B ; Emoji # 6.0 [2] (✊..✋) RAISED FIST..RAISED HAND +270C..270D ; Emoji # 1.1 [2] (✌..✍) VICTORY HAND..WRITING HAND +270F ; Emoji # 1.1 [1] (✏) PENCIL +2712 ; Emoji # 1.1 [1] (✒) BLACK NIB +2714 ; Emoji # 1.1 [1] (✔) HEAVY CHECK MARK +2716 ; Emoji # 1.1 [1] (✖) HEAVY MULTIPLICATION X +271D ; Emoji # 1.1 [1] (✝) LATIN CROSS +2721 ; Emoji # 1.1 [1] (✡) STAR OF DAVID +2728 ; Emoji # 6.0 [1] (✨) SPARKLES +2733..2734 ; Emoji # 1.1 [2] (✳..✴) EIGHT SPOKED ASTERISK..EIGHT POINTED BLACK STAR +2744 ; Emoji # 1.1 [1] (❄) SNOWFLAKE +2747 ; Emoji # 1.1 [1] (❇) SPARKLE +274C ; Emoji # 6.0 [1] (❌) CROSS MARK +274E ; Emoji # 6.0 [1] (❎) NEGATIVE SQUARED CROSS MARK +2753..2755 ; Emoji # 6.0 [3] (❓..❕) BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT +2757 ; Emoji # 5.2 [1] (❗) HEAVY EXCLAMATION MARK SYMBOL +2763..2764 ; Emoji # 1.1 [2] (❣..❤) HEAVY HEART EXCLAMATION MARK ORNAMENT..HEAVY BLACK HEART +2795..2797 ; Emoji # 6.0 [3] (➕..➗) HEAVY PLUS SIGN..HEAVY DIVISION SIGN +27A1 ; Emoji # 1.1 [1] (➡) BLACK RIGHTWARDS ARROW +27B0 ; Emoji # 6.0 [1] (➰) CURLY LOOP +27BF ; Emoji # 6.0 [1] (➿) DOUBLE CURLY LOOP +2934..2935 ; Emoji # 3.2 [2] (⤴..⤵) ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS..ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS +2B05..2B07 ; Emoji # 4.0 [3] (⬅..⬇) LEFTWARDS BLACK ARROW..DOWNWARDS BLACK ARROW +2B1B..2B1C ; Emoji # 5.1 [2] (⬛..⬜) BLACK LARGE SQUARE..WHITE LARGE SQUARE +2B50 ; Emoji # 5.1 [1] (⭐) WHITE MEDIUM STAR +2B55 ; Emoji # 5.2 [1] (⭕) HEAVY LARGE CIRCLE +3030 ; Emoji # 1.1 [1] (〰) WAVY DASH +303D ; Emoji # 3.2 [1] (〽) PART ALTERNATION MARK +3297 ; Emoji # 1.1 [1] (㊗) CIRCLED IDEOGRAPH CONGRATULATION +3299 ; Emoji # 1.1 [1] (㊙) CIRCLED IDEOGRAPH SECRET +1F004 ; Emoji # 5.1 [1] (🀄) MAHJONG TILE RED DRAGON +1F0CF ; Emoji # 6.0 [1] (🃏) PLAYING CARD BLACK JOKER +1F170..1F171 ; Emoji # 6.0 [2] (🅰..🅱) NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER B +1F17E ; Emoji # 6.0 [1] (🅾) NEGATIVE SQUARED LATIN CAPITAL LETTER O +1F17F ; Emoji # 5.2 [1] (🅿) NEGATIVE SQUARED LATIN CAPITAL LETTER P +1F18E ; Emoji # 6.0 [1] (🆎) NEGATIVE SQUARED AB +1F191..1F19A ; Emoji # 6.0 [10] (🆑..🆚) SQUARED CL..SQUARED VS +1F1E6..1F1FF ; Emoji # 6.0 [26] (🇦..🇿) REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z +1F201..1F202 ; Emoji # 6.0 [2] (🈁..🈂) SQUARED KATAKANA KOKO..SQUARED KATAKANA SA +1F21A ; Emoji # 5.2 [1] (🈚) SQUARED CJK UNIFIED IDEOGRAPH-7121 +1F22F ; Emoji # 5.2 [1] (🈯) SQUARED CJK UNIFIED IDEOGRAPH-6307 +1F232..1F23A ; Emoji # 6.0 [9] (🈲..🈺) SQUARED CJK UNIFIED IDEOGRAPH-7981..SQUARED CJK UNIFIED IDEOGRAPH-55B6 +1F250..1F251 ; Emoji # 6.0 [2] (🉐..🉑) CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT +1F300..1F320 ; Emoji # 6.0 [33] (🌀..🌠) CYCLONE..SHOOTING STAR +1F321 ; Emoji # 7.0 [1] (🌡) THERMOMETER +1F324..1F32C ; Emoji # 7.0 [9] (🌤..🌬) WHITE SUN WITH SMALL CLOUD..WIND BLOWING FACE +1F32D..1F32F ; Emoji # 8.0 [3] (🌭..🌯) HOT DOG..BURRITO +1F330..1F335 ; Emoji # 6.0 [6] (🌰..🌵) CHESTNUT..CACTUS +1F336 ; Emoji # 7.0 [1] (🌶) HOT PEPPER +1F337..1F37C ; Emoji # 6.0 [70] (🌷..🍼) TULIP..BABY BOTTLE +1F37D ; Emoji # 7.0 [1] (🍽) FORK AND KNIFE WITH PLATE +1F37E..1F37F ; Emoji # 8.0 [2] (🍾..🍿) BOTTLE WITH POPPING CORK..POPCORN +1F380..1F393 ; Emoji # 6.0 [20] (🎀..🎓) RIBBON..GRADUATION CAP +1F396..1F397 ; Emoji # 7.0 [2] (🎖..🎗) MILITARY MEDAL..REMINDER RIBBON +1F399..1F39B ; Emoji # 7.0 [3] (🎙..🎛) STUDIO MICROPHONE..CONTROL KNOBS +1F39E..1F39F ; Emoji # 7.0 [2] (🎞..🎟) FILM FRAMES..ADMISSION TICKETS +1F3A0..1F3C4 ; Emoji # 6.0 [37] (🎠..🏄) CAROUSEL HORSE..SURFER +1F3C5 ; Emoji # 7.0 [1] (🏅) SPORTS MEDAL +1F3C6..1F3CA ; Emoji # 6.0 [5] (🏆..🏊) TROPHY..SWIMMER +1F3CB..1F3CE ; Emoji # 7.0 [4] (🏋..🏎) WEIGHT LIFTER..RACING CAR +1F3CF..1F3D3 ; Emoji # 8.0 [5] (🏏..🏓) CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL +1F3D4..1F3DF ; Emoji # 7.0 [12] (🏔..🏟) SNOW CAPPED MOUNTAIN..STADIUM +1F3E0..1F3F0 ; Emoji # 6.0 [17] (🏠..🏰) HOUSE BUILDING..EUROPEAN CASTLE +1F3F3..1F3F5 ; Emoji # 7.0 [3] (🏳..🏵) WAVING WHITE FLAG..ROSETTE +1F3F7 ; Emoji # 7.0 [1] (🏷) LABEL +1F3F8..1F3FF ; Emoji # 8.0 [8] (🏸..🏿) BADMINTON RACQUET AND SHUTTLECOCK..EMOJI MODIFIER FITZPATRICK TYPE-6 +1F400..1F43E ; Emoji # 6.0 [63] (🐀..🐾) RAT..PAW PRINTS +1F43F ; Emoji # 7.0 [1] (🐿) CHIPMUNK +1F440 ; Emoji # 6.0 [1] (👀) EYES +1F441 ; Emoji # 7.0 [1] (👁) EYE +1F442..1F4F7 ; Emoji # 6.0[182] (👂..📷) EAR..CAMERA +1F4F8 ; Emoji # 7.0 [1] (📸) CAMERA WITH FLASH +1F4F9..1F4FC ; Emoji # 6.0 [4] (📹..📼) VIDEO CAMERA..VIDEOCASSETTE +1F4FD ; Emoji # 7.0 [1] (📽) FILM PROJECTOR +1F4FF ; Emoji # 8.0 [1] (📿) PRAYER BEADS +1F500..1F53D ; Emoji # 6.0 [62] (🔀..🔽) TWISTED RIGHTWARDS ARROWS..DOWN-POINTING SMALL RED TRIANGLE +1F549..1F54A ; Emoji # 7.0 [2] (🕉..🕊) OM SYMBOL..DOVE OF PEACE +1F54B..1F54E ; Emoji # 8.0 [4] (🕋..🕎) KAABA..MENORAH WITH NINE BRANCHES +1F550..1F567 ; Emoji # 6.0 [24] (🕐..🕧) CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY +1F56F..1F570 ; Emoji # 7.0 [2] (🕯..🕰) CANDLE..MANTELPIECE CLOCK +1F573..1F579 ; Emoji # 7.0 [7] (🕳..🕹) HOLE..JOYSTICK +1F57A ; Emoji # 9.0 [1] (🕺) MAN DANCING +1F587 ; Emoji # 7.0 [1] (🖇) LINKED PAPERCLIPS +1F58A..1F58D ; Emoji # 7.0 [4] (🖊..🖍) LOWER LEFT BALLPOINT PEN..LOWER LEFT CRAYON +1F590 ; Emoji # 7.0 [1] (🖐) RAISED HAND WITH FINGERS SPLAYED +1F595..1F596 ; Emoji # 7.0 [2] (🖕..🖖) REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS +1F5A4 ; Emoji # 9.0 [1] (🖤) BLACK HEART +1F5A5 ; Emoji # 7.0 [1] (🖥) DESKTOP COMPUTER +1F5A8 ; Emoji # 7.0 [1] (🖨) PRINTER +1F5B1..1F5B2 ; Emoji # 7.0 [2] (🖱..🖲) THREE BUTTON MOUSE..TRACKBALL +1F5BC ; Emoji # 7.0 [1] (🖼) FRAME WITH PICTURE +1F5C2..1F5C4 ; Emoji # 7.0 [3] (🗂..🗄) CARD INDEX DIVIDERS..FILE CABINET +1F5D1..1F5D3 ; Emoji # 7.0 [3] (🗑..🗓) WASTEBASKET..SPIRAL CALENDAR PAD +1F5DC..1F5DE ; Emoji # 7.0 [3] (🗜..🗞) COMPRESSION..ROLLED-UP NEWSPAPER +1F5E1 ; Emoji # 7.0 [1] (🗡) DAGGER KNIFE +1F5E3 ; Emoji # 7.0 [1] (🗣) SPEAKING HEAD IN SILHOUETTE +1F5E8 ; Emoji # 7.0 [1] (🗨) LEFT SPEECH BUBBLE +1F5EF ; Emoji # 7.0 [1] (🗯) RIGHT ANGER BUBBLE +1F5F3 ; Emoji # 7.0 [1] (🗳) BALLOT BOX WITH BALLOT +1F5FA ; Emoji # 7.0 [1] (🗺) WORLD MAP +1F5FB..1F5FF ; Emoji # 6.0 [5] (🗻..🗿) MOUNT FUJI..MOYAI +1F600 ; Emoji # 6.1 [1] (😀) GRINNING FACE +1F601..1F610 ; Emoji # 6.0 [16] (😁..😐) GRINNING FACE WITH SMILING EYES..NEUTRAL FACE +1F611 ; Emoji # 6.1 [1] (😑) EXPRESSIONLESS FACE +1F612..1F614 ; Emoji # 6.0 [3] (😒..😔) UNAMUSED FACE..PENSIVE FACE +1F615 ; Emoji # 6.1 [1] (😕) CONFUSED FACE +1F616 ; Emoji # 6.0 [1] (😖) CONFOUNDED FACE +1F617 ; Emoji # 6.1 [1] (😗) KISSING FACE +1F618 ; Emoji # 6.0 [1] (😘) FACE THROWING A KISS +1F619 ; Emoji # 6.1 [1] (😙) KISSING FACE WITH SMILING EYES +1F61A ; Emoji # 6.0 [1] (😚) KISSING FACE WITH CLOSED EYES +1F61B ; Emoji # 6.1 [1] (😛) FACE WITH STUCK-OUT TONGUE +1F61C..1F61E ; Emoji # 6.0 [3] (😜..😞) FACE WITH STUCK-OUT TONGUE AND WINKING EYE..DISAPPOINTED FACE +1F61F ; Emoji # 6.1 [1] (😟) WORRIED FACE +1F620..1F625 ; Emoji # 6.0 [6] (😠..😥) ANGRY FACE..DISAPPOINTED BUT RELIEVED FACE +1F626..1F627 ; Emoji # 6.1 [2] (😦..😧) FROWNING FACE WITH OPEN MOUTH..ANGUISHED FACE +1F628..1F62B ; Emoji # 6.0 [4] (😨..😫) FEARFUL FACE..TIRED FACE +1F62C ; Emoji # 6.1 [1] (😬) GRIMACING FACE +1F62D ; Emoji # 6.0 [1] (😭) LOUDLY CRYING FACE +1F62E..1F62F ; Emoji # 6.1 [2] (😮..😯) FACE WITH OPEN MOUTH..HUSHED FACE +1F630..1F633 ; Emoji # 6.0 [4] (😰..😳) FACE WITH OPEN MOUTH AND COLD SWEAT..FLUSHED FACE +1F634 ; Emoji # 6.1 [1] (😴) SLEEPING FACE +1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) DIZZY FACE..WEARY CAT FACE +1F641..1F642 ; Emoji # 7.0 [2] (🙁..🙂) SLIGHTLY FROWNING FACE..SLIGHTLY SMILING FACE +1F643..1F644 ; Emoji # 8.0 [2] (🙃..🙄) UPSIDE-DOWN FACE..FACE WITH ROLLING EYES +1F645..1F64F ; Emoji # 6.0 [11] (🙅..🙏) FACE WITH NO GOOD GESTURE..PERSON WITH FOLDED HANDS +1F680..1F6C5 ; Emoji # 6.0 [70] (🚀..🛅) ROCKET..LEFT LUGGAGE +1F6CB..1F6CF ; Emoji # 7.0 [5] (🛋..🛏) COUCH AND LAMP..BED +1F6D0 ; Emoji # 8.0 [1] (🛐) PLACE OF WORSHIP +1F6D1..1F6D2 ; Emoji # 9.0 [2] (🛑..🛒) OCTAGONAL SIGN..SHOPPING TROLLEY +1F6E0..1F6E5 ; Emoji # 7.0 [6] (🛠..🛥) HAMMER AND WRENCH..MOTOR BOAT +1F6E9 ; Emoji # 7.0 [1] (🛩) SMALL AIRPLANE +1F6EB..1F6EC ; Emoji # 7.0 [2] (🛫..🛬) AIRPLANE DEPARTURE..AIRPLANE ARRIVING +1F6F0 ; Emoji # 7.0 [1] (🛰) SATELLITE +1F6F3 ; Emoji # 7.0 [1] (🛳) PASSENGER SHIP +1F6F4..1F6F6 ; Emoji # 9.0 [3] (🛴..🛶) SCOOTER..CANOE +1F910..1F918 ; Emoji # 8.0 [9] (🤐..🤘) ZIPPER-MOUTH FACE..SIGN OF THE HORNS +1F919..1F91E ; Emoji # 9.0 [6] (🤙..🤞) CALL ME HAND..HAND WITH INDEX AND MIDDLE FINGERS CROSSED +1F920..1F927 ; Emoji # 9.0 [8] (🤠..🤧) FACE WITH COWBOY HAT..SNEEZING FACE +1F930 ; Emoji # 9.0 [1] (🤰) PREGNANT WOMAN +1F933..1F93A ; Emoji # 9.0 [8] (🤳..🤺) SELFIE..FENCER +1F93C..1F93E ; Emoji # 9.0 [3] (🤼..🤾) WRESTLERS..HANDBALL +1F940..1F945 ; Emoji # 9.0 [6] (🥀..🥅) WILTED FLOWER..GOAL NET +1F947..1F94B ; Emoji # 9.0 [5] (🥇..🥋) FIRST PLACE MEDAL..MARTIAL ARTS UNIFORM +1F950..1F95E ; Emoji # 9.0 [15] (🥐..🥞) CROISSANT..PANCAKES +1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) CRAB..UNICORN FACE +1F985..1F991 ; Emoji # 9.0 [13] (🦅..🦑) EAGLE..SQUID +1F9C0 ; Emoji # 8.0 [1] (🧀) CHEESE WEDGE + +# Total elements: 1123 + +# ================================================ + +# All omitted code points have Emoji_Presentation=No +# @missing: 0000..10FFFF ; Emoji_Presentation ; No + +231A..231B ; Emoji_Presentation # 1.1 [2] (⌚..⌛) WATCH..HOURGLASS +23E9..23EC ; Emoji_Presentation # 6.0 [4] (⏩..⏬) BLACK RIGHT-POINTING DOUBLE TRIANGLE..BLACK DOWN-POINTING DOUBLE TRIANGLE +23F0 ; Emoji_Presentation # 6.0 [1] (⏰) ALARM CLOCK +23F3 ; Emoji_Presentation # 6.0 [1] (⏳) HOURGLASS WITH FLOWING SAND +25FD..25FE ; Emoji_Presentation # 3.2 [2] (◽..◾) WHITE MEDIUM SMALL SQUARE..BLACK MEDIUM SMALL SQUARE +2614..2615 ; Emoji_Presentation # 4.0 [2] (☔..☕) UMBRELLA WITH RAIN DROPS..HOT BEVERAGE +2648..2653 ; Emoji_Presentation # 1.1 [12] (♈..♓) ARIES..PISCES +267F ; Emoji_Presentation # 4.1 [1] (♿) WHEELCHAIR SYMBOL +2693 ; Emoji_Presentation # 4.1 [1] (⚓) ANCHOR +26A1 ; Emoji_Presentation # 4.0 [1] (⚡) HIGH VOLTAGE SIGN +26AA..26AB ; Emoji_Presentation # 4.1 [2] (⚪..⚫) MEDIUM WHITE CIRCLE..MEDIUM BLACK CIRCLE +26BD..26BE ; Emoji_Presentation # 5.2 [2] (⚽..⚾) SOCCER BALL..BASEBALL +26C4..26C5 ; Emoji_Presentation # 5.2 [2] (⛄..⛅) SNOWMAN WITHOUT SNOW..SUN BEHIND CLOUD +26CE ; Emoji_Presentation # 6.0 [1] (⛎) OPHIUCHUS +26D4 ; Emoji_Presentation # 5.2 [1] (⛔) NO ENTRY +26EA ; Emoji_Presentation # 5.2 [1] (⛪) CHURCH +26F2..26F3 ; Emoji_Presentation # 5.2 [2] (⛲..⛳) FOUNTAIN..FLAG IN HOLE +26F5 ; Emoji_Presentation # 5.2 [1] (⛵) SAILBOAT +26FA ; Emoji_Presentation # 5.2 [1] (⛺) TENT +26FD ; Emoji_Presentation # 5.2 [1] (⛽) FUEL PUMP +2705 ; Emoji_Presentation # 6.0 [1] (✅) WHITE HEAVY CHECK MARK +270A..270B ; Emoji_Presentation # 6.0 [2] (✊..✋) RAISED FIST..RAISED HAND +2728 ; Emoji_Presentation # 6.0 [1] (✨) SPARKLES +274C ; Emoji_Presentation # 6.0 [1] (❌) CROSS MARK +274E ; Emoji_Presentation # 6.0 [1] (❎) NEGATIVE SQUARED CROSS MARK +2753..2755 ; Emoji_Presentation # 6.0 [3] (❓..❕) BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT +2757 ; Emoji_Presentation # 5.2 [1] (❗) HEAVY EXCLAMATION MARK SYMBOL +2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) HEAVY PLUS SIGN..HEAVY DIVISION SIGN +27B0 ; Emoji_Presentation # 6.0 [1] (➰) CURLY LOOP +27BF ; Emoji_Presentation # 6.0 [1] (➿) DOUBLE CURLY LOOP +2B1B..2B1C ; Emoji_Presentation # 5.1 [2] (⬛..⬜) BLACK LARGE SQUARE..WHITE LARGE SQUARE +2B50 ; Emoji_Presentation # 5.1 [1] (⭐) WHITE MEDIUM STAR +2B55 ; Emoji_Presentation # 5.2 [1] (⭕) HEAVY LARGE CIRCLE +1F004 ; Emoji_Presentation # 5.1 [1] (🀄) MAHJONG TILE RED DRAGON +1F0CF ; Emoji_Presentation # 6.0 [1] (🃏) PLAYING CARD BLACK JOKER +1F18E ; Emoji_Presentation # 6.0 [1] (🆎) NEGATIVE SQUARED AB +1F191..1F19A ; Emoji_Presentation # 6.0 [10] (🆑..🆚) SQUARED CL..SQUARED VS +1F1E6..1F1FF ; Emoji_Presentation # 6.0 [26] (🇦..🇿) REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z +1F201 ; Emoji_Presentation # 6.0 [1] (🈁) SQUARED KATAKANA KOKO +1F21A ; Emoji_Presentation # 5.2 [1] (🈚) SQUARED CJK UNIFIED IDEOGRAPH-7121 +1F22F ; Emoji_Presentation # 5.2 [1] (🈯) SQUARED CJK UNIFIED IDEOGRAPH-6307 +1F232..1F236 ; Emoji_Presentation # 6.0 [5] (🈲..🈶) SQUARED CJK UNIFIED IDEOGRAPH-7981..SQUARED CJK UNIFIED IDEOGRAPH-6709 +1F238..1F23A ; Emoji_Presentation # 6.0 [3] (🈸..🈺) SQUARED CJK UNIFIED IDEOGRAPH-7533..SQUARED CJK UNIFIED IDEOGRAPH-55B6 +1F250..1F251 ; Emoji_Presentation # 6.0 [2] (🉐..🉑) CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT +1F300..1F320 ; Emoji_Presentation # 6.0 [33] (🌀..🌠) CYCLONE..SHOOTING STAR +1F32D..1F32F ; Emoji_Presentation # 8.0 [3] (🌭..🌯) HOT DOG..BURRITO +1F330..1F335 ; Emoji_Presentation # 6.0 [6] (🌰..🌵) CHESTNUT..CACTUS +1F337..1F37C ; Emoji_Presentation # 6.0 [70] (🌷..🍼) TULIP..BABY BOTTLE +1F37E..1F37F ; Emoji_Presentation # 8.0 [2] (🍾..🍿) BOTTLE WITH POPPING CORK..POPCORN +1F380..1F393 ; Emoji_Presentation # 6.0 [20] (🎀..🎓) RIBBON..GRADUATION CAP +1F3A0..1F3C4 ; Emoji_Presentation # 6.0 [37] (🎠..🏄) CAROUSEL HORSE..SURFER +1F3C5 ; Emoji_Presentation # 7.0 [1] (🏅) SPORTS MEDAL +1F3C6..1F3CA ; Emoji_Presentation # 6.0 [5] (🏆..🏊) TROPHY..SWIMMER +1F3CF..1F3D3 ; Emoji_Presentation # 8.0 [5] (🏏..🏓) CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL +1F3E0..1F3F0 ; Emoji_Presentation # 6.0 [17] (🏠..🏰) HOUSE BUILDING..EUROPEAN CASTLE +1F3F4 ; Emoji_Presentation # 7.0 [1] (🏴) WAVING BLACK FLAG +1F3F8..1F3FF ; Emoji_Presentation # 8.0 [8] (🏸..🏿) BADMINTON RACQUET AND SHUTTLECOCK..EMOJI MODIFIER FITZPATRICK TYPE-6 +1F400..1F43E ; Emoji_Presentation # 6.0 [63] (🐀..🐾) RAT..PAW PRINTS +1F440 ; Emoji_Presentation # 6.0 [1] (👀) EYES +1F442..1F4F7 ; Emoji_Presentation # 6.0[182] (👂..📷) EAR..CAMERA +1F4F8 ; Emoji_Presentation # 7.0 [1] (📸) CAMERA WITH FLASH +1F4F9..1F4FC ; Emoji_Presentation # 6.0 [4] (📹..📼) VIDEO CAMERA..VIDEOCASSETTE +1F4FF ; Emoji_Presentation # 8.0 [1] (📿) PRAYER BEADS +1F500..1F53D ; Emoji_Presentation # 6.0 [62] (🔀..🔽) TWISTED RIGHTWARDS ARROWS..DOWN-POINTING SMALL RED TRIANGLE +1F54B..1F54E ; Emoji_Presentation # 8.0 [4] (🕋..🕎) KAABA..MENORAH WITH NINE BRANCHES +1F550..1F567 ; Emoji_Presentation # 6.0 [24] (🕐..🕧) CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY +1F57A ; Emoji_Presentation # 9.0 [1] (🕺) MAN DANCING +1F595..1F596 ; Emoji_Presentation # 7.0 [2] (🖕..🖖) REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS +1F5A4 ; Emoji_Presentation # 9.0 [1] (🖤) BLACK HEART +1F5FB..1F5FF ; Emoji_Presentation # 6.0 [5] (🗻..🗿) MOUNT FUJI..MOYAI +1F600 ; Emoji_Presentation # 6.1 [1] (😀) GRINNING FACE +1F601..1F610 ; Emoji_Presentation # 6.0 [16] (😁..😐) GRINNING FACE WITH SMILING EYES..NEUTRAL FACE +1F611 ; Emoji_Presentation # 6.1 [1] (😑) EXPRESSIONLESS FACE +1F612..1F614 ; Emoji_Presentation # 6.0 [3] (😒..😔) UNAMUSED FACE..PENSIVE FACE +1F615 ; Emoji_Presentation # 6.1 [1] (😕) CONFUSED FACE +1F616 ; Emoji_Presentation # 6.0 [1] (😖) CONFOUNDED FACE +1F617 ; Emoji_Presentation # 6.1 [1] (😗) KISSING FACE +1F618 ; Emoji_Presentation # 6.0 [1] (😘) FACE THROWING A KISS +1F619 ; Emoji_Presentation # 6.1 [1] (😙) KISSING FACE WITH SMILING EYES +1F61A ; Emoji_Presentation # 6.0 [1] (😚) KISSING FACE WITH CLOSED EYES +1F61B ; Emoji_Presentation # 6.1 [1] (😛) FACE WITH STUCK-OUT TONGUE +1F61C..1F61E ; Emoji_Presentation # 6.0 [3] (😜..😞) FACE WITH STUCK-OUT TONGUE AND WINKING EYE..DISAPPOINTED FACE +1F61F ; Emoji_Presentation # 6.1 [1] (😟) WORRIED FACE +1F620..1F625 ; Emoji_Presentation # 6.0 [6] (😠..😥) ANGRY FACE..DISAPPOINTED BUT RELIEVED FACE +1F626..1F627 ; Emoji_Presentation # 6.1 [2] (😦..😧) FROWNING FACE WITH OPEN MOUTH..ANGUISHED FACE +1F628..1F62B ; Emoji_Presentation # 6.0 [4] (😨..😫) FEARFUL FACE..TIRED FACE +1F62C ; Emoji_Presentation # 6.1 [1] (😬) GRIMACING FACE +1F62D ; Emoji_Presentation # 6.0 [1] (😭) LOUDLY CRYING FACE +1F62E..1F62F ; Emoji_Presentation # 6.1 [2] (😮..😯) FACE WITH OPEN MOUTH..HUSHED FACE +1F630..1F633 ; Emoji_Presentation # 6.0 [4] (😰..😳) FACE WITH OPEN MOUTH AND COLD SWEAT..FLUSHED FACE +1F634 ; Emoji_Presentation # 6.1 [1] (😴) SLEEPING FACE +1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) DIZZY FACE..WEARY CAT FACE +1F641..1F642 ; Emoji_Presentation # 7.0 [2] (🙁..🙂) SLIGHTLY FROWNING FACE..SLIGHTLY SMILING FACE +1F643..1F644 ; Emoji_Presentation # 8.0 [2] (🙃..🙄) UPSIDE-DOWN FACE..FACE WITH ROLLING EYES +1F645..1F64F ; Emoji_Presentation # 6.0 [11] (🙅..🙏) FACE WITH NO GOOD GESTURE..PERSON WITH FOLDED HANDS +1F680..1F6C5 ; Emoji_Presentation # 6.0 [70] (🚀..🛅) ROCKET..LEFT LUGGAGE +1F6CC ; Emoji_Presentation # 7.0 [1] (🛌) SLEEPING ACCOMMODATION +1F6D0 ; Emoji_Presentation # 8.0 [1] (🛐) PLACE OF WORSHIP +1F6D1..1F6D2 ; Emoji_Presentation # 9.0 [2] (🛑..🛒) OCTAGONAL SIGN..SHOPPING TROLLEY +1F6EB..1F6EC ; Emoji_Presentation # 7.0 [2] (🛫..🛬) AIRPLANE DEPARTURE..AIRPLANE ARRIVING +1F6F4..1F6F6 ; Emoji_Presentation # 9.0 [3] (🛴..🛶) SCOOTER..CANOE +1F910..1F918 ; Emoji_Presentation # 8.0 [9] (🤐..🤘) ZIPPER-MOUTH FACE..SIGN OF THE HORNS +1F919..1F91E ; Emoji_Presentation # 9.0 [6] (🤙..🤞) CALL ME HAND..HAND WITH INDEX AND MIDDLE FINGERS CROSSED +1F920..1F927 ; Emoji_Presentation # 9.0 [8] (🤠..🤧) FACE WITH COWBOY HAT..SNEEZING FACE +1F930 ; Emoji_Presentation # 9.0 [1] (🤰) PREGNANT WOMAN +1F933..1F93A ; Emoji_Presentation # 9.0 [8] (🤳..🤺) SELFIE..FENCER +1F93C..1F93E ; Emoji_Presentation # 9.0 [3] (🤼..🤾) WRESTLERS..HANDBALL +1F940..1F945 ; Emoji_Presentation # 9.0 [6] (🥀..🥅) WILTED FLOWER..GOAL NET +1F947..1F94B ; Emoji_Presentation # 9.0 [5] (🥇..🥋) FIRST PLACE MEDAL..MARTIAL ARTS UNIFORM +1F950..1F95E ; Emoji_Presentation # 9.0 [15] (🥐..🥞) CROISSANT..PANCAKES +1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) CRAB..UNICORN FACE +1F985..1F991 ; Emoji_Presentation # 9.0 [13] (🦅..🦑) EAGLE..SQUID +1F9C0 ; Emoji_Presentation # 8.0 [1] (🧀) CHEESE WEDGE + +# Total elements: 910 + +# ================================================ + +# All omitted code points have Emoji_Modifier=No +# @missing: 0000..10FFFF ; Emoji_Modifier ; No + +1F3FB..1F3FF ; Emoji_Modifier # 8.0 [5] (🏻..🏿) EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 + +# Total elements: 5 + +# ================================================ + +# All omitted code points have Emoji_Modifier_Base=No +# @missing: 0000..10FFFF ; Emoji_Modifier_Base ; No + +261D ; Emoji_Modifier_Base # 1.1 [1] (☝) WHITE UP POINTING INDEX +26F9 ; Emoji_Modifier_Base # 5.2 [1] (⛹) PERSON WITH BALL +270A..270B ; Emoji_Modifier_Base # 6.0 [2] (✊..✋) RAISED FIST..RAISED HAND +270C..270D ; Emoji_Modifier_Base # 1.1 [2] (✌..✍) VICTORY HAND..WRITING HAND +1F385 ; Emoji_Modifier_Base # 6.0 [1] (🎅) FATHER CHRISTMAS +1F3C3..1F3C4 ; Emoji_Modifier_Base # 6.0 [2] (🏃..🏄) RUNNER..SURFER +1F3CA ; Emoji_Modifier_Base # 6.0 [1] (🏊) SWIMMER +1F3CB ; Emoji_Modifier_Base # 7.0 [1] (🏋) WEIGHT LIFTER +1F442..1F443 ; Emoji_Modifier_Base # 6.0 [2] (👂..👃) EAR..NOSE +1F446..1F450 ; Emoji_Modifier_Base # 6.0 [11] (👆..👐) WHITE UP POINTING BACKHAND INDEX..OPEN HANDS SIGN +1F466..1F469 ; Emoji_Modifier_Base # 6.0 [4] (👦..👩) BOY..WOMAN +1F46E ; Emoji_Modifier_Base # 6.0 [1] (👮) POLICE OFFICER +1F470..1F478 ; Emoji_Modifier_Base # 6.0 [9] (👰..👸) BRIDE WITH VEIL..PRINCESS +1F47C ; Emoji_Modifier_Base # 6.0 [1] (👼) BABY ANGEL +1F481..1F483 ; Emoji_Modifier_Base # 6.0 [3] (💁..💃) INFORMATION DESK PERSON..DANCER +1F485..1F487 ; Emoji_Modifier_Base # 6.0 [3] (💅..💇) NAIL POLISH..HAIRCUT +1F4AA ; Emoji_Modifier_Base # 6.0 [1] (💪) FLEXED BICEPS +1F575 ; Emoji_Modifier_Base # 7.0 [1] (🕵) SLEUTH OR SPY +1F57A ; Emoji_Modifier_Base # 9.0 [1] (🕺) MAN DANCING +1F590 ; Emoji_Modifier_Base # 7.0 [1] (🖐) RAISED HAND WITH FINGERS SPLAYED +1F595..1F596 ; Emoji_Modifier_Base # 7.0 [2] (🖕..🖖) REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS +1F645..1F647 ; Emoji_Modifier_Base # 6.0 [3] (🙅..🙇) FACE WITH NO GOOD GESTURE..PERSON BOWING DEEPLY +1F64B..1F64F ; Emoji_Modifier_Base # 6.0 [5] (🙋..🙏) HAPPY PERSON RAISING ONE HAND..PERSON WITH FOLDED HANDS +1F6A3 ; Emoji_Modifier_Base # 6.0 [1] (🚣) ROWBOAT +1F6B4..1F6B6 ; Emoji_Modifier_Base # 6.0 [3] (🚴..🚶) BICYCLIST..PEDESTRIAN +1F6C0 ; Emoji_Modifier_Base # 6.0 [1] (🛀) BATH +1F918 ; Emoji_Modifier_Base # 8.0 [1] (🤘) SIGN OF THE HORNS +1F919..1F91E ; Emoji_Modifier_Base # 9.0 [6] (🤙..🤞) CALL ME HAND..HAND WITH INDEX AND MIDDLE FINGERS CROSSED +1F926 ; Emoji_Modifier_Base # 9.0 [1] (🤦) FACE PALM +1F930 ; Emoji_Modifier_Base # 9.0 [1] (🤰) PREGNANT WOMAN +1F933..1F939 ; Emoji_Modifier_Base # 9.0 [7] (🤳..🤹) SELFIE..JUGGLING +1F93C..1F93E ; Emoji_Modifier_Base # 9.0 [3] (🤼..🤾) WRESTLERS..HANDBALL + +# Total elements: 83 + +#EOF |