diff options
Diffstat (limited to 'src')
124 files changed, 12655 insertions, 5775 deletions
diff --git a/src/.asan-blacklist b/src/.asan-blacklist new file mode 100644 index 0000000000..63558170b3 --- /dev/null +++ b/src/.asan-blacklist @@ -0,0 +1,3 @@ +# libuv queue.h pointer arithmetic is not accepted by asan +fun:queue_node_data +fun:dictwatcher_node_data diff --git a/src/.valgrind.supp b/src/.valgrind.supp new file mode 100644 index 0000000000..8b630fcaaf --- /dev/null +++ b/src/.valgrind.supp @@ -0,0 +1,16 @@ +{ + nss_parse_service_list + Memcheck:Leak + fun:malloc + fun:nss_parse_service_list + fun:__nss_database_lookup +} +{ + uv_spawn_with_optimizations + Memcheck:Leak + fun:malloc + fun:uv_spawn + fun:pipe_process_spawn + fun:process_spawn + fun:job_start +} diff --git a/src/Doxyfile b/src/Doxyfile new file mode 100644 index 0000000000..de31c8355f --- /dev/null +++ b/src/Doxyfile @@ -0,0 +1,1890 @@ +# Doxyfile 1.8.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed +# in front of the TAG it is preceding . +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "Neovim" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = contrib/doxygen/logo-devdoc.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = build/doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, +# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, +# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields or simple typedef fields will be shown +# inline in the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO (the default), structs, classes, and unions are shown on a separate +# page (for HTML and Man pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can +# be an expensive process and often the same symbol appear multiple times in +# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too +# small doxygen will become slower. If the cache is too large, memory is wasted. +# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid +# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 +# symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src/ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.h *.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be ignored. +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = contrib/doxygen/header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = contrib/doxygen/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = contrib/doxygen/customdoxygen.css + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = contrib/doxygen/extra.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript +# pieces of code that will be used on startup of the MathJax code. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search +# engine library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4 will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images +# or other source files which should be copied to the LaTeX output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files +# that can be used to generate PDF. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. If left blank docbook will be used as the default path. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed +# in the related pages index. If set to NO, only the current project's +# pages will be listed. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate an inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/src/clint.py b/src/clint.py new file mode 100755 index 0000000000..efc5f18378 --- /dev/null +++ b/src/clint.py @@ -0,0 +1,3545 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Does neovim-lint on c files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with neovim style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import codecs +import copy +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata +import json +import collections # for defaultdict + + +_USAGE = """ +Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] [--root=subdir] + [--linelength=digits] [--record-errors=file] + [--suppress-errors=file] + <file> [file] ... + + The style guidelines this tries to follow are those in + http://neovim.io/development-wiki/style-guide/style-guide.xml + + Note: This is Google's cpplint.py modified for use with the Neovim project, + which follows the Google C++ coding convention except with the following + modifications: + + * Function names are lower_case. + * Struct and enum names that are not typedef-ed are struct lower_case and + enum lower_case. + * The opening brace for functions appear on the next line. + * All control structures must always use braces. + + Neovim is a C project. As a result, for .c and .h files, the following rules + are suppressed: + + * [whitespace/braces] { should almost always be at the end of the previous + line + * [build/include] Include the directory when naming .h files + * [runtime/int] Use int16/int64/etc, rather than the C type. + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the + extensions with the --extensions flag. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category. + + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuing that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ + + linelength=digits + This is the allowed line length for the project. The default value is + 80 characters. + + Examples: + --linelength=120 + + extensions=extension,extension,... + The allowed file extensions that cpplint will check + + Examples: + --extensions=hpp,cpp + + record-errors=file + Record errors to the given location. This file may later be used for error + suppression using suppress-errors flag. + + suppress-errors=file + Errors listed in the given file will not be reported. +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +_ERROR_CATEGORIES = [ + 'build/deprecated', + 'build/endif_comment', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/printf_format', + 'build/storage_class', + 'readability/alt_tokens', + 'readability/bool', + 'readability/braces', + 'readability/fn_size', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/nolint', + 'readability/nul', + 'readability/todo', + 'readability/utf8', + 'readability/increment', + 'runtime/arrays', + 'runtime/int', + 'runtime/invalid_increment', + 'runtime/memset', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/threadsafe_fn', + 'syntax/parenthesis', + 'whitespace/alignment', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_conditional_body', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/indent', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo', + 'whitespace/line_continuation', + 'whitespace/cast', +] + +# The default state of the category filter. This is overrided by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' +} + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments and multi-line strings +# but those have always been troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + + +_regexp_compile_cache = {} + +# Finds occurrences of NOLINT or NOLINT(...). +_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +# {(str, int)}: a set of error categories and line numbers which are expected to +# be suppressed +_error_suppressions_2 = set() + +# The allowed line length of files. +# This is set by --linelength flag. +_line_length = 80 + +# The allowed extensions for file names +# This is set by --extensions flag. +_valid_extensions = set(['c', 'h']) + + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). + matched = _RE_SUPPRESSION.search(raw_line) + if matched: + category = matched.group(1) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(linenum) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault( + category, set()).add(linenum) + else: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ParseKnownErrorSuppressions(filename, raw_lines, linenum): + """Updates the global list of error-suppressions from suppress-file. + + Args: + filename: str, the name of the input file. + raw_lines: list, all file lines + linenum: int, the number of the current line. + """ + key = tuple(raw_lines[linenum - 1 if linenum else 0:linenum + 2]) + if key in _cpplint_state.suppressed_errors[filename]: + for category in _cpplint_state.suppressed_errors[filename][key]: + _error_suppressions_2.add((category, linenum)) + + +def ResetNolintSuppressions(): + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() + + +def ResetKnownErrorSuppressions(): + "Resets the set of suppress-errors=file suppressions to empty." + _error_suppressions_2.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + + +def IsErrorInSuppressedErrorsList(category, linenum): + """Returns true if the specified error is suppressed by suppress-errors=file + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to presense in + suppressions file. + """ + return (category, linenum) in _error_suppressions_2 + + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(dict): + + """Tracks line numbers for includes, and the order in which includes appear. + + As a dict, an _IncludeState object serves as a mapping between include + filename and line number on which that file was included. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _C_SECTION = 2 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _C_SECTION: 'C system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + dict.__init__(self) + self.ResetSection() + + def ResetSection(self): + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + def SetLastHeader(self, header_path): + self._last_header = header_path + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-', '_').lower() + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + self.record_errors_file = None + self.suppressed_errors = collections.defaultdict( + lambda: collections.defaultdict(set)) + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters. + E.g. "+whitespace/indent". + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with + '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/bad" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with ' + '+ or - (%s does not)' % filt) + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.items(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + + def SuppressErrorsFrom(self, fname): + """Open file and read a list of suppressed errors from it""" + if fname is None: + return + try: + with open(fname) as fp: + for line in fp: + fname, lines, category = json.loads(line) + lines = tuple(lines) + self.suppressed_errors[fname][lines].add(category) + except IOError: + pass + + def RecordErrorsTo(self, fname): + """Open file with suppressed errors for writing""" + if fname is None: + return + self.record_errors_file = open(fname, 'w') + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _SuppressErrorsFrom(fname): + """Sets the file containing suppressed errors.""" + _cpplint_state.SuppressErrorsFrom(fname) + + +def _RecordErrorsTo(fname): + """Sets the file containing suppressed errors to write to.""" + _cpplint_state.RecordErrorsTo(fname) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + + +class _FunctionState(object): + + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int( + math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class FileInfo: + + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RelativePath(self): + """FullName with <prefix>/src/nvim/ chopped off.""" + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + not os.path.exists(os.path.join(root_dir, ".git"))): + root_dir = os.path.dirname(root_dir) + + if os.path.exists(os.path.join(root_dir, ".git")): + root_dir = os.path.join(root_dir, "src", "nvim") + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RelativePath() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and isn't suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + if IsErrorInSuppressedErrorsList(category, linenum): + return False + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Matches strings. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"([^"]*)"') +# Matches characters. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'(.)'") +# Matches multi-line C++ comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r"""(\s*/\*.*\*/\s*$| + /\*.*\*/\s+| + \s+/\*.*\*/(?=\W)| + /\*.*\*/)""", re.VERBOSE) + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '// dummy' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', + 5, 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + + """Holds 5 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments, + 2) lines member contains lines without comments, and + 3) raw_lines member contains all the lines with multiline comments replaced. + 4) init_lines member contains all the lines without processing. + 5) elided_with_space_strings is like elided, but with string literals + looking like `" "`. + All these three members are of <type 'list'>, and of the same length. + """ + + def __init__(self, lines, init_lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + self.init_lines = init_lines + self.lines_without_raw_strings = lines + self.elided_with_space_strings = [] + for linenum in range(len(self.lines_without_raw_strings)): + self.lines.append(CleanseComments( + self.lines_without_raw_strings[linenum])) + elided = self._CollapseStrings( + self.lines_without_raw_strings[linenum]) + self.elided.append(CleanseComments(elided)) + elided = CleanseComments(self._CollapseStrings( + self.lines_without_raw_strings[linenum], True)) + self.elided_with_space_strings.append(elided) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided, keep_spaces=False): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + keep_spaces: If true, collapse to + + Returns: + The line with collapsed strings. + """ + if not _RE_PATTERN_INCLUDE.match(elided): + # Remove escaped characters first to make quote/single quote + # collapsing basic. Things that look like escaped characters + # shouldn't occur outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub( + '' if not keep_spaces else lambda m: ' ' * len(m.group(0)), + elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub( + "''" if not keep_spaces + else lambda m: "'" + (' ' * len(m.group(1))) + "'", + elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub( + '""' if not keep_spaces + else lambda m: '"' + (' ' * len(m.group(1))) + '"', + elided) + return elided + + +BRACES = { + '(': ')', + '{': '}', + '[': ']', + # '<': '>', C++-specific pair removed +} + + +CLOSING_BRACES = dict(((v, k) for k, v in BRACES.items())) + + +def GetExprBracesPosition(clean_lines, linenum, pos): + """List positions of all kinds of braces + + If input points to ( or { or [ then function proceeds until finding the + position which closes it. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + pos: A position on the line. + + Yields: + A tuple (linenum, pos, brace, depth) that points to each brace. + Additionally each new line (linenum, pos, 's', depth) is yielded, for each + line end (linenum, pos, 'e', depth) is yielded and at the very end it + yields (linenum, pos, None, None). + """ + depth = 0 + yielded_line_start = True + startpos = pos + while linenum < clean_lines.NumLines() - 1: + line = clean_lines.elided_with_space_strings[linenum] + if not line.startswith('#') or yielded_line_start: + # Ignore #ifdefs, but not if it is macros that are checked + for i, brace in enumerate(line[startpos:]): + pos = i + startpos + if brace != ' ' and not yielded_line_start: + yield (linenum, pos, 's', depth) + yielded_line_start = True + if brace in BRACES: + depth += 1 + yield (linenum, pos, brace, depth) + elif brace in CLOSING_BRACES: + yield (linenum, pos, brace, depth) + depth -= 1 + if depth == 0: + yield (linenum, pos, None, None) + return + yield (linenum, len(line) - 1, 'e', depth) + yielded_line_start = False + startpos = 0 + linenum += 1 + + +def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): + """Find the position just after the matching endchar. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + depth: nesting level at startpos. + startchar: expression opening character. + endchar: expression closing character. + + Returns: + On finding matching endchar: (index just after matching endchar, 0) + Otherwise: (-1, new depth at end of this line) + """ + for i in range(startpos, len(line)): + if line[i] == startchar: + depth += 1 + elif line[i] == endchar: + depth -= 1 + if depth == 0: + return (i + 1, 0) + return (-1, depth) + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[', finds the + linenum/pos that correspond to the closing of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + startchar = line[pos] + if startchar not in BRACES: + return (line, clean_lines.NumLines(), -1) + endchar = BRACES[startchar] + + # Check first line + (end_pos, num_open) = FindEndOfExpressionInLine( + line, pos, 0, startchar, endchar) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while linenum < clean_lines.NumLines() - 1: + linenum += 1 + line = clean_lines.elided[linenum] + (end_pos, num_open) = FindEndOfExpressionInLine( + line, 0, num_open, startchar, endchar) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find endchar before end of file, give up + return (line, clean_lines.NumLines(), -1) + + +def FindStartOfExpressionInLine(line, endpos, depth, startchar, endchar): + """Find position at the matching startchar. + + This is almost the reverse of FindEndOfExpressionInLine, but note + that the input position and returned position differs by 1. + + Args: + line: a CleansedLines line. + endpos: start searching at this position. + depth: nesting level at endpos. + startchar: expression opening character. + endchar: expression closing character. + + Returns: + On finding matching startchar: (index at matching startchar, 0) + Otherwise: (-1, new depth at beginning of this line) + """ + for i in range(endpos, -1, -1): + if line[i] == endchar: + depth += 1 + elif line[i] == startchar: + depth -= 1 + if depth == 0: + return (i, 0) + return (-1, depth) + + +def ReverseCloseExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the opening of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *at* the opening brace, or + (line, 0, -1) if we never find the matching opening brace. Note + we ignore strings and comments when matching; and the line we + return is the 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + endchar = line[pos] + if endchar not in ')}]>': + return (line, 0, -1) + if endchar == ')': + startchar = '(' + if endchar == ']': + startchar = '[' + if endchar == '}': + startchar = '{' + if endchar == '>': + startchar = '<' + + # Check last line + (start_pos, num_open) = FindStartOfExpressionInLine( + line, pos, 0, startchar, endchar) + if start_pos > -1: + return (line, linenum, start_pos) + + # Continue scanning backward + while linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (start_pos, num_open) = FindStartOfExpressionInLine( + line, len(line) - 1, num_open, startchar, endchar) + if start_pos > -1: + return (line, linenum, start_pos) + + # Did not find startchar before beginning of file, give up + return (line, 0, -1) + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + + fileinfo = FileInfo(filename) + file_path_from_root = fileinfo.RelativePath() + return 'NVIM_' + re.sub(r'[-./\s]', '_', file_path_from_root).upper() + + +def CheckForHeaderGuard(filename, lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = None + ifndef_linenum = 0 + define = None + endif = None + endif_linenum = 0 + for linenum, line in enumerate(lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + if not define: + error(filename, 0, 'build/header_guard', 5, + 'No #define header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + if define != ifndef: + error(filename, 0, 'build/header_guard', 5, + '#ifndef and #define don\'t match, suggested CPP variable is: %s' + % cppvar) + return + + if endif != ('#endif // %s' % cppvar): + error_level = 0 + if endif != ('#endif // %s' % (cppvar + '_')): + error_level = 5 + + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, + error) + error(filename, endif_linenum, 'build/header_guard', error_level, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckForBadCharacters(filename, lines, error): + """Logs an error for each line containing bad characters. + + Two kinds of bad characters: + + 1. Unicode replacement characters: These indicate that either the file + contained invalid UTF-8 (likely) or Unicode replacement characters (which + it shouldn't). Note that it's possible for this to throw off line + numbering if the invalid UTF-8 occurred adjacent to a newline. + + 2. NUL bytes. These are problematic for some tools. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8' + ' (or Unicode replacement character).') + if '\0' in line: + error(filename, linenum, 'readability/nul', + 5, 'Line contains NUL byte.') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. ' + 'Use C++11 raw strings or concatenation instead.') + + +def CheckForOldStyleComments(filename, line, linenum, error): + """Logs an error if we see /*-style comment + + Args: + filename: The name of the current file. + line: The text of the line to check. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + if line.find('/*') >= 0 and line[-1] != '\\': + error(filename, linenum, 'readability/old_style_comment', 5, + '/*-style comment found, it should be replaced with //-style. ' + '/*-style comments are only allowed inside macros. ' + 'Note that you should not use /*-style comments to document ' + 'macros itself, use doxygen-style comments for this.') + + +threading_list = ( + ('asctime(', 'os_asctime_r('), + ('ctime(', 'os_ctime_r('), + ('getgrgid(', 'os_getgrgid_r('), + ('getgrnam(', 'os_getgrnam_r('), + ('getlogin(', 'os_getlogin_r('), + ('getpwnam(', 'os_getpwnam_r('), + ('getpwuid(', 'os_getpwuid_r('), + ('gmtime(', 'os_gmtime_r('), + ('localtime(', 'os_localtime_r('), + ('strtok(', 'os_strtok_r('), + ('ttyname(', 'os_ttyname_r('), + ('asctime_r(', 'os_asctime_r('), + ('ctime_r(', 'os_ctime_r('), + ('getgrgid_r(', 'os_getgrgid_r('), + ('getgrnam_r(', 'os_getgrnam_r('), + ('getlogin_r(', 'os_getlogin_r('), + ('getpwnam_r(', 'os_getpwnam_r('), + ('getpwuid_r(', 'os_getpwuid_r('), + ('gmtime_r(', 'os_gmtime_r('), + ('localtime_r(', 'os_localtime_r('), + ('strtok_r(', 'os_strtok_r('), + ('ttyname_r(', 'os_ttyname_r('), +) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_function, multithread_safe_function in threading_list: + ix = line.find(single_thread_function) + # Comparisons made explicit for clarity -- pylint: + # disable=g-explicit-bool-comparison + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Use ' + multithread_safe_function + + '...) instead of ' + single_thread_function + + '...). If it is missing, consider implementing it;' + + ' see os_localtime_r for an example.') + + +memory_functions = ( + ('malloc(', 'xmalloc('), + ('calloc(', 'xcalloc('), + ('realloc(', 'xrealloc('), + ('strdup(', 'xstrdup('), + ('free(', 'xfree('), +) +memory_ignore_pattern = re.compile(r'src/nvim/memory.c$') + + +def CheckMemoryFunctions(filename, clean_lines, linenum, error): + """Checks for calls to invalid functions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + if memory_ignore_pattern.search(filename): + return + line = clean_lines.elided[linenum] + for function, suggested_function in memory_functions: + ix = line.find(function) + # Comparisons made explicit for clarity -- pylint: + # disable=g-explicit-bool-comparison + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/memory_fn', 2, + 'Use ' + suggested_function + + '...) instead of ' + function + '...).') + + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +class _BlockInfo(object): + + """Stores information about a generic block of code.""" + + def __init__(self, seen_open_brace): + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + + +class _PreprocessorInfo(object): + + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class _NestingState(object): + + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 1 type of + # object is possible: + # - _BlockInfo: some type of block. + self.stack = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else + # case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy( + self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Update pp_stack first + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was + # _END_ASM, we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen an opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + else: + self.stack.append(_BlockInfo(True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack.pop() + line = matched.group(2) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%" PRId64 instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and <? operators, and their >?= and <?= cousins. + + Additionally, check for constructor/destructor style violations and + reference members, as it is very convenient to do so while checking for + gcc-2 compliance. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + """ + + # Remove comments from the line, but leave in strings for now. + line = clean_lines.lines[linenum] + + if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): + error(filename, linenum, 'runtime/printf_format', 3, + '"%q" in format strings is deprecated. Use "%" PRId64 instead.') + + if Search(r'printf\s*\(.*".*%\d+\$', line): + error(filename, linenum, 'runtime/printf_format', 2, + '%N$ formats are unconventional. Try rewriting to avoid them.') + + # Remove escaped backslashes before looking for undefined escapes. + line = line.replace('\\\\', '') + + if Search(r'("|\').*\\(%|\[|\(|{)', line): + error(filename, linenum, 'build/printf_format', 3, + '%, [, (, and { are undefined character escapes. Unescape them.') + + # For the rest, work with both comments and strings removed. + line = clean_lines.elided[linenum] + + if Search(r'\b(const|volatile|void|char|short|int|long' + r'|float|double|signed|unsigned' + r'|u?int8_t|u?int16_t|u?int32_t|u?int64_t' + r'|u?int_least8_t|u?int_least16_t|u?int_least32_t' + r'|u?int_least64_t' + r'|u?int_fast8_t|u?int_fast16_t|u?int_fast32_t' + r'|u?int_fast64_t' + r'|u?intptr_t|u?intmax_t)' + r'\s+(register|static|extern|typedef)\b', + line): + error(filename, linenum, 'build/storage_class', 5, + 'Storage class (static, extern, typedef, etc) should be first.') + + if Match(r'\s*#\s*endif\s*[^/\s]+', line): + error(filename, linenum, 'build/endif_comment', 5, + 'Uncommented text after #endif is non-standard. Use a comment.') + + if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and <? (max and min) operators are' + ' non-standard and deprecated.') + + +def CheckSpacingForFunctionCall(filename, line, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + line: The text of the line to check. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + # look inside the parens for function calls + fncall = match.group(1) + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|sizeof)\b', fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + # a ( used for a fn call + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'#\s*define|typedef', fncall) and + not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall)): + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in range(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + # Declarations and trivial functions + if Search(r'(;|})', start_line): + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was + # found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?(:?)(\s|$)?') + + +def CheckComment(comment, filename, linenum, error): + """Checks for common mistakes in TODO comments. + + Args: + comment: The text of the comment from the line in question. + filename: The name of the current file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + colon = match.group(3) + if not colon: + error(filename, linenum, 'readability/todo', 2, + 'Missing colon in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(4) + # Comparisons made explicit for correctness -- pylint: + # disable=g-explicit-bool-comparison + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username): should be followed by a space') + + +def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): + """Find the corresponding > to close a template. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_suffix: Remainder of the current line after the initial <. + + Returns: + True if a matching bracket exists. + """ + line = init_suffix + nesting_stack = ['<'] + while True: + # Find the next operator that can tell us whether < is used as an + # opening bracket or as a less-than operator. We only want to + # warn on the latter case. + # + # We could also check all other operators and terminate the search + # early, e.g. if we got something like this "a<b+c", the "<" is + # most likely a less-than operator, but then we will get false + # positives for default arguments and other template expressions. + match = Search(r'^[^<>(),;\[\]]*([<>(),;\[\]])(.*)$', line) + if match: + # Found an operator, update nesting stack + operator = match.group(1) + line = match.group(2) + + if nesting_stack[-1] == '<': + # Expecting closing angle bracket + if operator in ('<', '(', '['): + nesting_stack.append(operator) + elif operator == '>': + nesting_stack.pop() + if not nesting_stack: + # Found matching angle bracket + return True + elif operator == ',': + # Got a comma after a bracket, this is most likely a + # template argument. We have not seen a closing angle + # bracket yet, but it's probably a few lines later if we + # look for it, so just return early here. + return True + else: + # Got some other operator. + return False + + else: + # Expecting closing parenthesis or closing bracket + if operator in ('<', '(', '['): + nesting_stack.append(operator) + elif operator in (')', ']'): + # We don't bother checking for matching () or []. If we got + # something like (] or [), it would have been a syntax + # error. + nesting_stack.pop() + + else: + # Scan the next line + linenum += 1 + if linenum >= len(clean_lines.elided): + break + line = clean_lines.elided[linenum] + + # Exhausted all remaining lines and still no matching angle bracket. + # Most likely the input was incomplete, otherwise we should have + # seen a semicolon and returned early. + return True + + +def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): + """Find the corresponding < that started a template. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_prefix: Part of the current line before the initial >. + + Returns: + True if a matching bracket exists. + """ + line = init_prefix + nesting_stack = ['>'] + while True: + # Find the previous operator + match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) + if match: + # Found an operator, update nesting stack + operator = match.group(2) + line = match.group(1) + + if nesting_stack[-1] == '>': + # Expecting opening angle bracket + if operator in ('>', ')', ']'): + nesting_stack.append(operator) + elif operator == '<': + nesting_stack.pop() + if not nesting_stack: + # Found matching angle bracket + return True + elif operator == ',': + # Got a comma before a bracket, this is most likely a + # template argument. The opening angle bracket is probably + # there if we look for it, so just return early here. + return True + else: + # Got some other operator. + return False + + else: + # Expecting opening parenthesis or opening bracket + if operator in ('>', ')', ']'): + nesting_stack.append(operator) + elif operator in ('(', '['): + nesting_stack.pop() + + else: + # Scan the previous line + linenum -= 1 + if linenum < 0: + break + line = clean_lines.elided[linenum] + + # Exhausted all earlier lines and still no matching angle bracket. + return False + + +def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): + """Checks for the correctness of alignment inside expressions + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + startpos: Position where to start searching for expression start. + """ + level_starts = {} + line = clean_lines.elided_with_space_strings[linenum] + prev_line_start = Search(r'\S', line).start() + depth_line_starts = {} + pos = min([ + idx + for idx in ( + line.find(k, startpos) + for k in BRACES + if k != '{' + ) + if idx >= 0 + ] + [len(line) + 1]) + if pos == len(line) + 1: + return + ignore_error_levels = set() + firstlinenum = linenum + for linenum, pos, brace, depth in GetExprBracesPosition( + clean_lines, linenum, pos + ): + line = clean_lines.elided_with_space_strings[linenum] + if depth is None: + if pos < len(line) - 1: + CheckExpressionAlignment(filename, clean_lines, linenum, error, + pos + 1) + return + elif depth <= 0: + error(filename, linenum, 'syntax/parenthesis', 4, + 'Unbalanced parenthesis') + return + if brace == 's': + assert firstlinenum != linenum + if level_starts[depth][1]: + if line[pos] == BRACES[depth_line_starts[depth][1]]: + if pos != depth_line_starts[depth][0]: + if depth not in ignore_error_levels: + error(filename, linenum, 'whitespace/indent', 2, + 'End of the inner expression should have ' + 'the same indent as start') + else: + if (pos != depth_line_starts[depth][0] + 4 + and not (depth_line_starts[depth][1] == '{' + and pos == depth_line_starts[depth][0] + 2)): + if depth not in ignore_error_levels: + error(filename, linenum, 'whitespace/indent', 2, + 'Inner expression indentation should be 4') + else: + if (pos != level_starts[depth][0] + 1 + + (level_starts[depth][2] == '{')): + if depth not in ignore_error_levels: + error(filename, linenum, 'whitespace/alignment', 2, + 'Inner expression should be aligned ' + 'as opening brace + 1 (+ 2 in case of {)') + prev_line_start = pos + elif brace == 'e': + pass + else: + opening = brace in BRACES + if opening: + # Only treat {} as part of the expression if it is preceded by + # "=" (brace initializer) or "(type)" (construct like (struct + # foo) { ... }). + if brace == '{' and not (Search( + r'(?:= *|\((?:struct )?\w+(\s*\[\w*\])?\)) *$', + line[:pos]) + ): + ignore_error_levels.add(depth) + line_ended_with_opening = ( + pos == len(line) - 2 * (line.endswith(' \\')) - 1) + level_starts[depth] = (pos, line_ended_with_opening, brace) + if line_ended_with_opening: + depth_line_starts[depth] = (prev_line_start, brace) + else: + del level_starts[depth] + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row, + spaces after {, spaces before }. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside + # C++11 raw strings, + raw = clean_lines.lines_without_raw_strings + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + if IsBlankLine(line): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line + # after,both start with alnums and are indented the same + # amount. This ignores whitespace at the start of a + # namespace block because those are not usually indented. + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are + # indented 4 spaces (because they did not fit in a 80 column line + # when placed on the same line as the function name). We also check + # for the case where the previous line is indented 6 spaces, which + # may happen when the initializers of a constructor do not fit into + # a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, + # which should be indented 4 spaces to cause 6 space indentation + # afterwards. + search_position = linenum - 2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We + # use a simple heuristic here: If the line is indented 4 spaces; + # and we have a closing paren, without the opening paren, + # followed by an opening brace or colon (for initializer lists) + # we assume that it is the last line of a function header. If + # we have a colon indented 4 spaces, it is an initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Redundant blank line at the start of a code block ' + 'should be deleted.') + # Ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Redundant blank line at the end of a code block ' + 'should be deleted.') + + # Next, we complain if there's a comment too near the text + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: + # disable=g-explicit-bool-comparison + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + if (not Match(r'^\s*{ //', line) and + ((commentpos >= 1 and + line[commentpos - 1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos - 2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + # There should always be a space between the // and the comment + commentend = commentpos + 2 + if commentend < len(line) and not line[commentend] == ' ': + # but some lines are exceptions -- e.g. if they're big + # comment delimiters like: + # //---------------------------------------------------------- + # or are an empty C++ style Doxygen comment, like: + # /// + # or C++ style Doxygen comments placed after the variable: + # ///< Header comment + # //!< Header comment + # or they begin with multiple slashes followed by a space: + # //////// Header comment + match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or + Search(r'^/$', line[commentend:]) or + Search(r'^!< ', line[commentend:]) or + Search(r'^/< ', line[commentend:]) or + Search(r'^/+ ', line[commentend:])) + if not match: + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + CheckComment(line[commentpos:], filename, linenum, error) + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Don't try to do spacing checks for operator methods + line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + match = Search(r'(?:[^ (*/![])+(?<!\+\+|--)\*', line) + if match: + error(filename, linenum, 'whitespace/operators', 2, + 'Missing space before asterisk in %s' % match.group(0)) + + # You should always have whitespace around binary operators. + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + + # Boolean operators should be placed on the next line. + if Search(r'(?:&&|\|\|)$', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Boolean operator should be placed on the same line as the start ' + 'of its right operand') + + # We allow no-spaces around << when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + # Also ignore using ns::operator<<; + match = Search(r'(operator|\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) + if (match and + not (match.group(1).isdigit() and match.group(2).isdigit()) and + not (match.group(1) == 'operator' and match.group(2) == ';')): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <<') + elif not Match(r'#.*include', line): + # Avoid false positives on -> + reduced_line = line.replace('->', '') + + # Look for < that is not surrounded by spaces. This is only + # triggered if both sides are missing spaces, even though + # technically should should flag if at least one side is missing a + # space. This is done to avoid some false positives with shifts. + match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) + if (match and not FindNextMatchingAngleBracket(clean_lines, linenum, + match.group(1))): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <') + + # Look for > that is not surrounded by spaces. Similar to the + # above, we only trigger if both sides are missing spaces to avoid + # false positives with shifts. + match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) + if (match and + not FindPreviousMatchingAngleBracket(clean_lines, linenum, + match.group(1))): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type<type<type>> alpha + match = Search(r'>>[a-zA-Z_]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >>') + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + # A pet peeve of mine: no spaces after an if, while, switch, or for + match = Search(r' (if\(|for\(|while\(|switch\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Missing space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if len(match.group(2)) not in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + # You should always have a space after a comma (either as fn arg or + # operator). + # + # This does not apply when the non-space character following the + # comma is another comma, since the only time when that happens is + # for empty macro arguments. + # + # We run this check in two passes: first pass on elided lines to + # verify that lines contain missing whitespaces, second pass on raw + # lines to confirm that those missing whitespaces are not due to + # elided comments. + if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + # Next we will look for issues with function calls. + CheckSpacingForFunctionCall(filename, line, linenum, error) + + # Check whether everything inside expressions is aligned correctly + if any((line.find(k) >= 0 for k in BRACES if k != '{')): + CheckExpressionAlignment(filename, clean_lines, linenum, error) + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces. And since you should never have braces at the beginning of a line, + # this is an easy test. + match = Match(r'^(.*[^ ({]){', line) + if match: + # Try a bit harder to check for brace initialization. This + # happens in one of the following forms: + # Constructor() : initializer_list_{} { ... } + # Constructor{}.MemberFunction() + # Type variable{}; + # FunctionCall(type{}, ...); + # LastArgument(..., type{}); + # LOG(INFO) << type{} << " ..."; + # map_of_type[{...}] = ...; + # + # We check for the character following the closing brace, and + # silence the warning if it's one of those listed above, i.e. + # "{.;,)<]". + # + # To account for nested initializer list, we allow any number of + # closing braces up to "{;,)<". We can't simply silence the + # warning on first sight of closing brace, because that would + # cause false negatives for things that are not initializer lists. + # Silence this: But not this: + # Outer{ if (...) { + # Inner{...} if (...){ // Missing space before { + # }; } + # + # There is a false negative with this approach if people inserted + # spurious semicolons, e.g. "if (cond){};", but we will catch the + # spurious semicolon with a separate check. + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + trailing_text = '' + if endpos > -1: + trailing_text = endline[endpos:] + for offset in range(endlinenum + 1, + min(endlinenum + 3, clean_lines.NumLines() - 1)): + trailing_text += clean_lines.elided[offset] + if not Match(r'^[\s}]*[{.;,)<\]]', trailing_text): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'new char * []'. + if Search(r'\w\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # You shouldn't have a space before a semicolon at the end of the line. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use {} instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty' + ' statement, use {} instead.') + elif Search(r'\s+;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') + + if Search(r'\{(?!\})\S', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space after {') + if Search(r'\S(?<!\{)\}', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before }') + + if Search(r'\S {2,}\\$', line): + error(filename, linenum, 'whitespace/line_continuation', 5, + 'Too many spaces before \\, line continuation character must be ' + 'preceded by exactly one space. For “blank lines†' + 'it is preferred to use the same amount of spaces as preceding ' + 'indent') + + if Match(r'^ +#', line): + error(filename, linenum, 'whitespace/indent', 5, + 'Must not indent preprocessor directives, use 1-space indent ' + 'after the hash') + + cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line) + match = Search(r'(?<!\bkvec_t)' + r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' + r' +' + r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) + if match and line[0] == ' ': + error(filename, linenum, 'whitespace/cast', 2, + 'Should leave no spaces after a cast: {!r}'.format( + match.group(0))) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if not (filename.endswith('.c') or filename.endswith('.h')): + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, which + # is commonly used to control the lifetime of stack-allocated + # variables. Braces are also used for brace initializers inside + # function calls. We don't detect this perfectly: we just don't + # complain if the last non-whitespace character on the previous + # non-blank line is ',', ';', ':', '(', '{', or '}', or if the + # previous line starts a preprocessor block. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if (not Search(r'[,;:}{(]\s*$', prevline) and + not Match(r'\s*#', prevline)): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end' + ' of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + # If there is no preceding closing brace, there should be one. + if Match(r'\s*else\s*', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + else: + error(filename, linenum, 'readability/braces', 5, + 'An else should always have braces before it') + + # If should always have a brace + for blockstart in ('if', 'while', 'for'): + if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line): + pos = line.find(blockstart) + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression( + clean_lines, linenum, pos) + if endline[endpos:].find('{') == -1: + error(filename, linenum, 'readability/braces', 5, + '{0} should always use braces'.format(blockstart)) + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression( + clean_lines, linenum, pos) + # must be brace after if + if endline[endpos:].find('{') == -1: + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side,' + ' it should have it on both') + else: # common case: else not followed by a multi-line if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side,' + ' it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Block bodies should not be followed by a semicolon. Due to C++11 + # brace initialization, there are more places where semicolons are + # required than not, so we use a whitelist approach to check these + # rather than a blacklist. These are the places where "};" should + # be replaced by just "}": + # 1. Some flavor of block following closing parenthesis: + # for (;;) {}; + # while (...) {}; + # switch (...) {}; + # Function(...) {}; + # if (...) {}; + # if (...) else if (...) {}; + # + # 2. else block: + # if (...) else {}; + # + # 3. const member function: + # Function(...) const {}; + # + # 4. Block following some statement: + # x = 42; + # {}; + # + # 5. Block at the beginning of a function: + # Function(...) { + # {}; + # } + # + # Note that naively checking for the preceding "{" will also match + # braces inside multi-dimensional arrays, but this is fine since + # that expression will not contain semicolons. + # + # 6. Block following another block: + # while (true) {} + # {}; + # + # 7. End of namespaces: + # namespace {}; + # + # These semicolons seems far more common than other kinds of + # redundant semicolons, possibly due to people converting classes + # to namespaces. For now we do not warn for this case. + # + # Try matching case 1 first. + match = Match(r'^(.*\)\s*)\{', line) + if match: + # Matched closing parenthesis (case 1). Check the token before the + # matching opening parenthesis, and don't warn if it looks like a + # macro. This avoids these false positives: + # - macro that defines a base class + # - multi-line macro that defines a base class + # - macro that defines the whole class-head + # + # But we still issue warnings for macros that we know are safe to + # warn, specifically: + # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P + # - TYPED_TEST + # - INTERFACE_DEF + # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: + # + # We implement a whitelist of safe macros instead of a blacklist of + # unsafe macros, even though the latter appears less frequently in + # google code and would have been easier to implement. This is because + # the downside for getting the whitelist wrong means some extra + # semicolons, while the downside for getting the blacklist wrong + # would result in compile errors. + # + # In addition to macros, we also don't want to warn on compound + # literals. + closing_brace_pos = match.group(1).rfind(')') + opening_parenthesis = ReverseCloseExpression( + clean_lines, linenum, closing_brace_pos) + if opening_parenthesis[2] > -1: + line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] + macro = Search(r'\b([A-Z_]+)\s*$', line_prefix) + if ((macro and + macro.group(1) not in ( + 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', + 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', + 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or + Search(r'\s+=\s*$', line_prefix) or + Search(r'^\s*return\s*$', line_prefix)): + match = None + + else: + # Try matching cases 2-3. + match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) + if not match: + # Try matching cases 4-6. These are always matched on separate + # lines. + # + # Note that we can't simply concatenate the previous line to the + # current line and do a single match, otherwise we may output + # duplicate warnings for the blank line case: + # if (cond) { + # // blank line + # } + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if prevline and Search(r'[;{}]\s*$', prevline): + match = Match(r'^(\s*)\{', line) + + # Check matching closing brace + if match: + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if endpos > -1 and Match(r'^\s*;', endline[endpos:]): + # Current {} pair is eligible for semicolon check, and we have found + # the redundant semicolon, output warning here. + # + # Note: because we are scanning forward for opening braces, and + # outputting warnings for the matching closing brace, if there are + # nested blocks with trailing semicolons, we will get the error + # messages in reversed order. + error(filename, endlinenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def CheckEmptyBlockBody(filename, clean_lines, linenum, error): + """Look for empty loop/conditional body with only a single semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + # + # We also check "if" blocks here, since an empty conditional block + # is likely an error. + line = clean_lines.elided[linenum] + matched = Match(r'\s*(for|while|if)\s*\(', line) + if matched: + # Find the end of the conditional expression + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a + # semicolon. No warning for all other cases, including whitespace or + # newline, since we have a separate check for semicolons preceded by + # whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + if matched.group(1) == 'if': + error(filename, end_linenum, + 'whitespace/empty_conditional_body', 5, + 'Empty conditional bodies should use {}') + else: + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, str): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside + # C++11 raw strings, + raw_lines = clean_lines.lines_without_raw_strings + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: + # RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + # There are certain situations we allow one space, notably for section + # labels + elif ((initial_spaces == 1 or initial_spaces == 3) and + not Match(r'\s*\w+\s*:\s*$', cleansed_line)): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way + # to split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + extended_length = int((_line_length * 1.25)) + if line_width > extended_length: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than %i characters' % + extended_length) + elif line_width > _line_length: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= %i characters long' % _line_length) + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 0, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckEmptyBlockBody(filename, clean_lines, linenum, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckAltTokens(filename, clean_lines, linenum, error) + + +_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + """ + if is_system: + return _C_SYS_HEADER + return _OTHER_HEADER + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename : The name of the current file. + clean_lines : A CleansedLines instance containing the file. + linenum : The number of the line to check. + include_state : An _IncludeState instance in which the headers are + inserted. + error : The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + # XXX: neovim doesn't currently use this style + # if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): + # error(filename, linenum, 'build/include', 4, + # 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + if include in include_state: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) + else: + include_state[include] = linenum + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: c system, c++ system, other.' + % error_message) + canonical_include = include_state.CanonicalizeAlphabeticalOrder( + include) + include_state.SetLastHeader(canonical_include) + + +def _GetTextInside(text, start_pattern): + r"""Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the + text following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the + end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation couldn't be found. + """ + # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.values()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, + include_state, nesting_state, error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename : The name of the current file. + clean_lines : A CleansedLines instance containing the file. + linenum : The number of the line to check. + file_extension : The extension (without the dot) of the filename. + include_state : An _IncludeState instance in which the headers are + inserted. + nesting_state : A _NestingState instance which maintains information + about the current stack of nested blocks being parsed. + error : The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Reset include state across preprocessor directives. This is meant + # to silence warnings for conditional includes. + if Match(r'^\s*#\s*(?:ifdef|elif|else|endif)\b', line): + include_state.ResetSection() + + # TODO(unknown): figure out if they're using default arguments in fn proto. + + # Check if people are using the verboten C basic types. + match = Search(r'\b(short|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16_t/int64_t/etc, rather than the C type %s' + % match.group(1)) + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\b', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(sugawarayu): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): + continue + if Search(r'arraysize\(\w+\)', tok): + continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: + continue + if Match(r'\d+', tok): + continue + if Match(r'0[xX][0-9a-fA-F]+', tok): + continue + if Match(r'k[A-Z0-9]\w*', tok): + continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): + continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): + continue + # A catch all for tricky sizeof cases, including + # 'sizeof expression', 'sizeof(*type)', 'sizeof(const type)', + # 'sizeof(struct StructName)' requires skipping the next token + # because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + "Do not use variable-length arrays. Use an appropriately" + " named ('k' followed by CamelCase) compile-time constant for" + " the size.") + + # Detect TRUE and FALSE. + match = Search(r'\b(TRUE|FALSE)\b', line) + if match: + token = match.group(1) + error(filename, linenum, 'readability/bool', 4, + 'Use %s instead of %s.' % (token.lower(), token)) + + # Detect preincrement/predecrement + match = Match(r'^\s*(?:\+\+|--)', line) + if match: + error(filename, linenum, 'readability/increment', 5, + 'Do not use preincrement in statements, ' + 'use postincrement instead') + # Detect preincrement/predecrement in for(;; preincrement) + match = Search(r';\s*(\+\+|--)', line) + if match: + end_pos, end_depth = FindEndOfExpressionInLine(line, match.start(1), 1, + '(', ')') + expr = line[match.start(1):end_pos] + if end_depth == 0 and ';' not in expr and ' = ' not in expr: + error(filename, linenum, 'readability/increment', 4, + 'Do not use preincrement in statements, including ' + 'for(;; action)') + + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename : Filename of the file that is being processed. + file_extension : The extension (dot not included) of the file. + clean_lines : An array of strings, each representing a line of + the file, with comments stripped. + line : Number of line being processed. + include_state : An _IncludeState instance in which the headers are + inserted. + function_state : A _FunctionState instance which counts function + lines, etc. + nesting_state : A _NestingState instance which maintains + information about the current stack of nested + blocks being parsed. + error : A callable to which errors are reported, which + takes 4 arguments: filename, line number, error + level, and message + extra_check_functions : An array of additional check functions that will + be run on each source line. Each function takes 4 + arguments : filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + init_lines = clean_lines.init_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: + return + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckForOldStyleComments(filename, init_lines[line], line, error) + CheckStyle( + filename, clean_lines, line, file_extension, nesting_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + nesting_state, error) + CheckForNonStandardConstructs(filename, clean_lines, line, + nesting_state, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckMemoryFunctions(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + nesting_state = _NestingState() + + ResetNolintSuppressions() + ResetKnownErrorSuppressions() + + for line in range(1, len(lines)): + ParseKnownErrorSuppressions(filename, lines, line) + + init_lines = lines[:] + + if _cpplint_state.record_errors_file: + def RecordedError(filename, linenum, category, confidence, message): + if not IsErrorSuppressedByNolint(category, linenum): + key = init_lines[linenum - 1 if linenum else 0:linenum + 2] + err = [filename, key, category] + json.dump(err, _cpplint_state.record_errors_file) + _cpplint_state.record_errors_file.write('\n') + Error(filename, linenum, category, confidence, message) + + error = RecordedError + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines, init_lines) + for line in range(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForBadCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does neovim-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + + try: + # Support the Unix convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. If it is not expected to be present (i.e. os.linesep != + # '\r\n' as in Windows), a warning is issued below if this file + # is processed. + + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open( + filename, 'r', 'utf8', 'replace').read().split('\n') + + carriage_return_found = False + # Remove trailing '\r'. + for linenum in range(len(lines)): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + carriage_return_found = True + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if filename != '-' and file_extension not in _valid_extensions: + sys.stderr.write('Ignoring %s; not a valid file name ' + '(%s)\n' % (filename, ', '.join(_valid_extensions))) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + if carriage_return_found and os.linesep != '\r\n': + # Use 0 for linenum since outputting only one error for potentially + # several lines. + Error(filename, 0, 'whitespace/newline', 1, + 'One or more unexpected \\r (^M) found;' + 'better to use only a \\n') + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + if message: + sys.stderr.write(_USAGE) + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.stdout.write(_USAGE) + sys.exit(0) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stdout.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', + 'output=', + 'verbose=', + 'counting=', + 'filter=', + 'root=', + 'linelength=', + 'extensions=', + 'record-errors=', + 'suppress-errors=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + record_errors_file = None + suppress_errors_file = None + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if val not in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs,' + ' vs7 and eclipse.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage( + 'Valid counting options are total, toplevel, and detailed') + counting_style = val + elif opt == '--linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + PrintUsage('Line length must be digits.') + elif opt == '--extensions': + global _valid_extensions + try: + _valid_extensions = set(val.split(',')) + except ValueError: + PrintUsage('Extensions must be comma separated list.') + elif opt == '--record-errors': + record_errors_file = val + elif opt == '--suppress-errors': + suppress_errors_file = val + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + _SuppressErrorsFrom(suppress_errors_file) + _RecordErrorsTo(record_errors_file) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() + +# vim: ts=4 sts=4 sw=4 + +# Ignore "too complex" warnings when using pymode. +# pylama:ignore=C901 diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index ab6f69f66c..d80add2835 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -1,6 +1,3 @@ -include(CheckLibraryExists) -include(CheckCCompilerFlag) - option(USE_GCOV "Enable gcov support" OFF) if(NOT CLANG_TSAN) @@ -78,6 +75,9 @@ foreach(sfile ${NEOVIM_SOURCES}) if(${f} MATCHES "^(regexp_nfa.c)$") list(APPEND to_remove ${sfile}) endif() + if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$") + list(APPEND to_remove ${sfile}) + endif() endforeach() list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) @@ -95,7 +95,6 @@ set(CONV_SOURCES mbyte.c memline.c message.c - ops.c regexp.c screen.c search.c @@ -269,7 +268,7 @@ if(CLANG_ASAN_UBSAN) set(SANITIZE_RECOVER -fno-sanitize-recover) # Clang 3.5- endif() set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "${SANITIZE_RECOVER} -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/src/.asan-blacklist") set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") elseif(CLANG_MSAN) message(STATUS "Enabling Clang memory sanitizer for nvim.") diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index fbfa87d5ae..5fb95a163f 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -12,8 +12,8 @@ #define REMOTE_TYPE(type) typedef uint64_t type #ifdef INCLUDE_GENERATED_DECLARATIONS - #define ArrayOf(...) Array - #define DictionaryOf(...) Dictionary +# define ArrayOf(...) Array +# define DictionaryOf(...) Dictionary #endif // Basic types @@ -41,6 +41,12 @@ typedef bool Boolean; typedef int64_t Integer; typedef double Float; +/// Maximum value of an Integer +#define API_INTEGER_MAX INT64_MAX + +/// Minimum value of an Integer +#define API_INTEGER_MIN INT64_MIN + typedef struct { char *data; size_t size; diff --git a/src/nvim/api/private/handle.c b/src/nvim/api/private/handle.c index abbda95073..69df7294ad 100644 --- a/src/nvim/api/private/handle.c +++ b/src/nvim/api/private/handle.c @@ -7,24 +7,24 @@ #define HANDLE_INIT(name) name##_handles = pmap_new(uint64_t)() -#define HANDLE_IMPL(type, name) \ - static PMap(uint64_t) *name##_handles = NULL; \ - \ - type *handle_get_##name(uint64_t handle) \ - { \ - return pmap_get(uint64_t)(name##_handles, handle); \ - } \ - \ - void handle_register_##name(type *name) \ - { \ - assert(!name->handle); \ - name->handle = next_handle++; \ - pmap_put(uint64_t)(name##_handles, name->handle, name); \ - } \ - \ - void handle_unregister_##name(type *name) \ - { \ - pmap_del(uint64_t)(name##_handles, name->handle); \ +#define HANDLE_IMPL(type, name) \ + static PMap(uint64_t) *name##_handles = NULL; \ + \ + type *handle_get_##name(uint64_t handle) \ + { \ + return pmap_get(uint64_t)(name##_handles, handle); \ + } \ + \ + void handle_register_##name(type *name) \ + { \ + assert(!name->handle); \ + name->handle = next_handle++; \ + pmap_put(uint64_t)(name##_handles, name->handle, name); \ + } \ + \ + void handle_unregister_##name(type *name) \ + { \ + pmap_del(uint64_t)(name##_handles, name->handle); \ } static uint64_t next_handle = 1; diff --git a/src/nvim/api/private/handle.h b/src/nvim/api/private/handle.h index 1a196f6797..804e266dc3 100644 --- a/src/nvim/api/private/handle.h +++ b/src/nvim/api/private/handle.h @@ -4,9 +4,9 @@ #include "nvim/vim.h" #include "nvim/buffer_defs.h" -#define HANDLE_DECLS(type, name) \ - type *handle_get_##name(uint64_t handle); \ - void handle_register_##name(type *name); \ +#define HANDLE_DECLS(type, name) \ + type *handle_get_##name(uint64_t handle); \ + void handle_register_##name(type *name); \ void handle_unregister_##name(type *name); HANDLE_DECLS(buf_T, buffer) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index db3e499427..c88bf2127a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -17,6 +17,13 @@ #include "nvim/map.h" #include "nvim/option.h" #include "nvim/option_defs.h" +#include "nvim/eval/typval_encode.h" +#include "nvim/lib/kvec.h" + +/// Helper structure for vim_to_object +typedef struct { + kvec_t(Object) stack; ///< Object stack. +} EncodedData; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.c.generated.h" @@ -310,6 +317,179 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) } } +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL() \ + kv_push(edata->stack, NIL) + +#define TYPVAL_ENCODE_CONV_BOOL(num) \ + kv_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) + +#define TYPVAL_ENCODE_CONV_NUMBER(num) \ + kv_push(edata->stack, INTEGER_OBJ((Integer)(num))) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER + +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \ + kv_push(edata->stack, FLOATING_OBJ((Float)(flt))) + +#define TYPVAL_ENCODE_CONV_STRING(str, len) \ + do { \ + const size_t len_ = (size_t)(len); \ + const char *const str_ = (const char *)(str); \ + assert(len_ == 0 || str_ != NULL); \ + kv_push(edata->stack, STRING_OBJ(((String) { \ + .data = xmemdupz((len_?str_:""), len_), \ + .size = len_ \ + }))); \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING + +#define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \ + TYPVAL_ENCODE_CONV_NIL() + +#define TYPVAL_ENCODE_CONV_FUNC(fun) \ + TYPVAL_ENCODE_CONV_NIL() + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ + kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \ + kv_push(edata->stack, \ + DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) + +static inline void typval_encode_list_start(EncodedData *const edata, + const size_t len) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + const Object obj = OBJECT_INIT; + kv_push(edata->stack, ARRAY_OBJ(((Array) { + .capacity = len, + .size = 0, + .items = xmalloc(len * sizeof(*obj.data.array.items)), + }))); +} + +#define TYPVAL_ENCODE_CONV_LIST_START(len) \ + typval_encode_list_start(edata, (size_t)(len)) + +static inline void typval_encode_between_list_items(EncodedData *const edata) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + Object item = kv_pop(edata->stack); + Object *const list = &kv_last(edata->stack); + assert(list->type == kObjectTypeArray); + assert(list->data.array.size < list->data.array.capacity); + list->data.array.items[list->data.array.size++] = item; +} + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \ + typval_encode_between_list_items(edata) + +static inline void typval_encode_list_end(EncodedData *const edata) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + typval_encode_between_list_items(edata); +#ifndef NDEBUG + const Object *const list = &kv_last(edata->stack); + assert(list->data.array.size == list->data.array.capacity); +#endif +} + +#define TYPVAL_ENCODE_CONV_LIST_END() \ + typval_encode_list_end(edata) + +static inline void typval_encode_dict_start(EncodedData *const edata, + const size_t len) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + const Object obj = OBJECT_INIT; + kv_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { + .capacity = len, + .size = 0, + .items = xmalloc(len * sizeof(*obj.data.dictionary.items)), + }))); +} + +#define TYPVAL_ENCODE_CONV_DICT_START(len) \ + typval_encode_dict_start(edata, (size_t)(len)) + +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair) + +static inline void typval_encode_after_key(EncodedData *const edata) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + Object key = kv_pop(edata->stack); + Object *const dict = &kv_last(edata->stack); + assert(dict->type == kObjectTypeDictionary); + assert(dict->data.dictionary.size < dict->data.dictionary.capacity); + if (key.type == kObjectTypeString) { + dict->data.dictionary.items[dict->data.dictionary.size].key + = key.data.string; + } else { + api_free_object(key); + dict->data.dictionary.items[dict->data.dictionary.size].key + = STATIC_CSTR_TO_STRING("__INVALID_KEY__"); + } +} + +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \ + typval_encode_after_key(edata) + +static inline void typval_encode_between_dict_items(EncodedData *const edata) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + Object val = kv_pop(edata->stack); + Object *const dict = &kv_last(edata->stack); + assert(dict->type == kObjectTypeDictionary); + assert(dict->data.dictionary.size < dict->data.dictionary.capacity); + dict->data.dictionary.items[dict->data.dictionary.size++].value = val; +} + +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \ + typval_encode_between_dict_items(edata) + +static inline void typval_encode_dict_end(EncodedData *const edata) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + typval_encode_between_dict_items(edata); +#ifndef NDEBUG + const Object *const dict = &kv_last(edata->stack); + assert(dict->data.dictionary.size == dict->data.dictionary.capacity); +#endif +} + +#define TYPVAL_ENCODE_CONV_DICT_END() \ + typval_encode_dict_end(edata) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ + TYPVAL_ENCODE_CONV_NIL() + +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata) + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS + /// Convert a vim object to an `Object` instance, recursively expanding /// Arrays/Dictionaries. /// @@ -317,13 +497,12 @@ void set_option_to(void *to, int type, String name, Object value, Error *err) /// @return The converted value Object vim_to_object(typval_T *obj) { - Object rv; - // We use a lookup table to break out of cyclic references - PMap(ptr_t) *lookup = pmap_new(ptr_t)(); - rv = vim_to_object_rec(obj, lookup); - // Free the table - pmap_free(ptr_t)(lookup); - return rv; + EncodedData edata = { .stack = KV_INITIAL_VALUE }; + encode_vim_to_object(&edata, obj, "vim_to_object argument"); + Object ret = kv_A(edata.stack, 0); + assert(kv_size(edata.stack) == 1); + kv_destroy(edata.stack); + return ret; } buf_T *find_buffer_by_handle(Buffer buffer, Error *err) @@ -633,131 +812,6 @@ Object copy_object(Object obj) } } -/// Recursion helper for the `vim_to_object`. This uses a pointer table -/// to avoid infinite recursion due to cyclic references -/// -/// @param obj The source object -/// @param lookup Lookup table containing pointers to all processed objects -/// @return The converted value -static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup) -{ - Object rv = OBJECT_INIT; - - if (obj->v_type == VAR_LIST || obj->v_type == VAR_DICT) { - // Container object, add it to the lookup table - if (pmap_has(ptr_t)(lookup, obj)) { - // It's already present, meaning we alredy processed it so just return - // nil instead. - return rv; - } - pmap_put(ptr_t)(lookup, obj, NULL); - } - - switch (obj->v_type) { - case VAR_SPECIAL: - switch (obj->vval.v_special) { - case kSpecialVarTrue: - case kSpecialVarFalse: { - rv.type = kObjectTypeBoolean; - rv.data.boolean = (obj->vval.v_special == kSpecialVarTrue); - break; - } - case kSpecialVarNull: { - rv.type = kObjectTypeNil; - break; - } - } - break; - - case VAR_STRING: - rv.type = kObjectTypeString; - rv.data.string = cstr_to_string((char *) obj->vval.v_string); - break; - - case VAR_NUMBER: - rv.type = kObjectTypeInteger; - rv.data.integer = obj->vval.v_number; - break; - - case VAR_FLOAT: - rv.type = kObjectTypeFloat; - rv.data.floating = obj->vval.v_float; - break; - - case VAR_LIST: - { - list_T *list = obj->vval.v_list; - listitem_T *item; - - if (list != NULL) { - rv.type = kObjectTypeArray; - assert(list->lv_len >= 0); - rv.data.array.size = (size_t)list->lv_len; - rv.data.array.items = xmalloc(rv.data.array.size * sizeof(Object)); - - uint32_t i = 0; - for (item = list->lv_first; item != NULL; item = item->li_next) { - rv.data.array.items[i] = vim_to_object_rec(&item->li_tv, lookup); - i++; - } - } - } - break; - - case VAR_DICT: - { - dict_T *dict = obj->vval.v_dict; - hashtab_T *ht; - uint64_t todo; - hashitem_T *hi; - dictitem_T *di; - - if (dict != NULL) { - ht = &obj->vval.v_dict->dv_hashtab; - todo = ht->ht_used; - rv.type = kObjectTypeDictionary; - - // Count items - rv.data.dictionary.size = 0; - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - rv.data.dictionary.size++; - } - } - - rv.data.dictionary.items = - xmalloc(rv.data.dictionary.size * sizeof(KeyValuePair)); - todo = ht->ht_used; - uint32_t i = 0; - - // Convert all - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - di = dict_lookup(hi); - // Convert key - rv.data.dictionary.items[i].key = - cstr_to_string((char *) hi->hi_key); - // Convert value - rv.data.dictionary.items[i].value = - vim_to_object_rec(&di->di_tv, lookup); - todo--; - i++; - } - } - } - } - break; - - case VAR_UNKNOWN: - case VAR_FUNC: - break; - } - - return rv; -} - - static void set_option_value_for(char *key, int numval, char *stringval, diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a0f14ac7a4..a946e35149 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -8,69 +8,70 @@ #include "nvim/memory.h" #include "nvim/lib/kvec.h" -#define api_set_error(err, errtype, ...) \ - do { \ - snprintf((err)->msg, \ - sizeof((err)->msg), \ - __VA_ARGS__); \ - (err)->set = true; \ - (err)->type = kErrorType##errtype; \ +#define api_set_error(err, errtype, ...) \ + do { \ + snprintf((err)->msg, \ + sizeof((err)->msg), \ + __VA_ARGS__); \ + (err)->set = true; \ + (err)->type = kErrorType##errtype; \ } while (0) #define OBJECT_OBJ(o) o -#define BOOLEAN_OBJ(b) ((Object) { \ - .type = kObjectTypeBoolean, \ - .data.boolean = b \ - }) - -#define INTEGER_OBJ(i) ((Object) { \ - .type = kObjectTypeInteger, \ - .data.integer = i \ - }) - -#define STRING_OBJ(s) ((Object) { \ - .type = kObjectTypeString, \ - .data.string = s \ - }) - -#define BUFFER_OBJ(s) ((Object) { \ - .type = kObjectTypeBuffer, \ - .data.buffer = s \ - }) - -#define WINDOW_OBJ(s) ((Object) { \ - .type = kObjectTypeWindow, \ - .data.window = s \ - }) - -#define TABPAGE_OBJ(s) ((Object) { \ - .type = kObjectTypeTabpage, \ - .data.tabpage = s \ - }) - -#define ARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeArray, \ - .data.array = a \ - }) - -#define DICTIONARY_OBJ(d) ((Object) { \ - .type = kObjectTypeDictionary, \ - .data.dictionary = d \ - }) +#define BOOLEAN_OBJ(b) ((Object) { \ + .type = kObjectTypeBoolean, \ + .data.boolean = b }) + +#define INTEGER_OBJ(i) ((Object) { \ + .type = kObjectTypeInteger, \ + .data.integer = i }) + +#define FLOATING_OBJ(f) ((Object) { \ + .type = kObjectTypeFloat, \ + .data.floating = f }) + +#define STRING_OBJ(s) ((Object) { \ + .type = kObjectTypeString, \ + .data.string = s }) + +#define BUFFER_OBJ(s) ((Object) { \ + .type = kObjectTypeBuffer, \ + .data.buffer = s }) + +#define WINDOW_OBJ(s) ((Object) { \ + .type = kObjectTypeWindow, \ + .data.window = s }) + +#define TABPAGE_OBJ(s) ((Object) { \ + .type = kObjectTypeTabpage, \ + .data.tabpage = s }) + +#define ARRAY_OBJ(a) ((Object) { \ + .type = kObjectTypeArray, \ + .data.array = a }) + +#define DICTIONARY_OBJ(d) ((Object) { \ + .type = kObjectTypeDictionary, \ + .data.dictionary = d }) #define NIL ((Object) {.type = kObjectTypeNil}) -#define PUT(dict, k, v) \ - kv_push(KeyValuePair, \ - dict, \ - ((KeyValuePair) {.key = cstr_to_string(k), .value = v})) +#define PUT(dict, k, v) \ + kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) -#define ADD(array, item) \ - kv_push(Object, array, item) +#define ADD(array, item) \ + kv_push(array, item) #define STATIC_CSTR_AS_STRING(s) ((String) {.data = s, .size = sizeof(s) - 1}) +/// Create a new String instance, putting data in allocated memory +/// +/// @param[in] s String to work with. Must be a string literal. +#define STATIC_CSTR_TO_STRING(s) ((String){ \ + .data = xmemdupz(s, sizeof(s) - 1), \ + .size = sizeof(s) - 1 }) + // Helpers used by the generated msgpack-rpc api wrappers #define api_init_boolean #define api_init_integer diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 46d72b847d..ac7cc65ee4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -57,6 +57,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) bool remap = true; bool insert = false; bool typed = false; + bool execute = false; if (keys.size == 0) { return; @@ -68,6 +69,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) case 'm': remap = true; break; case 't': typed = true; break; case 'i': insert = true; break; + case 'x': execute = true; break; } } @@ -86,8 +88,12 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) xfree(keys_esc); } - if (vgetc_busy) + if (vgetc_busy) { typebuf_was_filled = true; + } + if (execute) { + exec_normal(true); + } } /// Passes input keys to Neovim. Unlike `vim_feedkeys`, this will use a @@ -641,14 +647,14 @@ static void write_msg(String message, bool to_err) static size_t out_pos = 0, err_pos = 0; static char out_line_buf[LINE_BUFFER_SIZE], err_line_buf[LINE_BUFFER_SIZE]; -#define PUSH_CHAR(i, pos, line_buf, msg) \ - if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \ - line_buf[pos] = NUL; \ - msg((uint8_t *)line_buf); \ - pos = 0; \ - continue; \ - } \ - \ +#define PUSH_CHAR(i, pos, line_buf, msg) \ + if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \ + line_buf[pos] = NUL; \ + msg((uint8_t *)line_buf); \ + pos = 0; \ + continue; \ + } \ + \ line_buf[pos++] = message.data[i]; ++no_wait_return; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 72716daf0e..438a85dd5d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1249,6 +1249,10 @@ void enter_buffer(buf_T *buf) /* mark cursor position as being invalid */ curwin->w_valid = 0; + if (buf->terminal) { + terminal_resize(buf->terminal, curwin->w_width, curwin->w_height); + } + /* Make sure the buffer is loaded. */ if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */ /* If there is no filetype, allow for detecting one. Esp. useful for @@ -1534,6 +1538,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_cms); clear_string_option(&buf->b_p_nf); clear_string_option(&buf->b_p_syn); + clear_string_option(&buf->b_s.b_syn_isk); clear_string_option(&buf->b_s.b_p_spc); clear_string_option(&buf->b_s.b_p_spf); vim_regfree(buf->b_s.b_cap_prog); @@ -2848,7 +2853,7 @@ typedef enum { /// is "curwin". /// /// Items are drawn interspersed with the text that surrounds it -/// Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation +/// Specials: %-<wid>(xxx%) => group, %= => separation marker, %< => truncation /// Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional /// /// If maxwidth is not zero, the string will be filled at any middle marker @@ -2892,7 +2897,7 @@ int build_stl_str_hl( Normal, Empty, Group, - Middle, + Separate, Highlight, TabPage, ClickFunc, @@ -2993,14 +2998,14 @@ int build_stl_str_hl( continue; } - // STL_MIDDLEMARK: Separation place between left and right aligned items. - if (*fmt_p == STL_MIDDLEMARK) { + // STL_SEPARATE: Separation place between left and right aligned items. + if (*fmt_p == STL_SEPARATE) { fmt_p++; // Ignored when we are inside of a grouping if (groupdepth > 0) { continue; } - item[curitem].type = Middle; + item[curitem].type = Separate; item[curitem++].start = out_p; continue; } @@ -3839,27 +3844,53 @@ int build_stl_str_hl( width = maxwidth; // If there is room left in our statusline, and room left in our buffer, - // add characters at the middle marker (if there is one) to + // add characters at the separate marker (if there is one) to // fill up the available space. } else if (width < maxwidth - && STRLEN(out) + maxwidth - width + 1 < outlen) { - for (int item_idx = 0; item_idx < itemcnt; item_idx++) { - if (item[item_idx].type == Middle) { - // Move the statusline to make room for the middle characters - char_u *middle_end = item[item_idx].start + (maxwidth - width); - STRMOVE(middle_end, item[item_idx].start); - - // Fill the middle section with our fill character - for (char_u *s = item[item_idx].start; s < middle_end; s++) - *s = fillchar; + && STRLEN(out) + maxwidth - width + 1 < outlen) { + // Find how many separators there are, which we will use when + // figuring out how many groups there are. + int num_separators = 0; + for (int i = 0; i < itemcnt; i++) { + if (item[i].type == Separate) { + num_separators++; + } + } + + // If we have separated groups, then we deal with it now + if (num_separators) { + // Create an array of the start location for each + // separator mark. + int separator_locations[STL_MAX_ITEM]; + int index = 0; + for (int i = 0; i < itemcnt; i++) { + if (item[i].type == Separate) { + separator_locations[index] = i; + index++; + } + } - // Adjust the offset of any items after the middle - for (item_idx++; item_idx < itemcnt; item_idx++) - item[item_idx].start += maxwidth - width; + int standard_spaces = (maxwidth - width) / num_separators; + int final_spaces = (maxwidth - width) - + standard_spaces * (num_separators - 1); - width = maxwidth; - break; + for (int i = 0; i < num_separators; i++) { + int dislocation = (i == (num_separators - 1)) ? + final_spaces : standard_spaces; + char_u *sep_loc = item[separator_locations[i]].start + dislocation; + STRMOVE(sep_loc, item[separator_locations[i]].start); + for (char_u *s = item[separator_locations[i]].start; s < sep_loc; s++) { + *s = fillchar; + } + + for (int item_idx = separator_locations[i] + 1; + item_idx < itemcnt; + item_idx++) { + item[item_idx].start += dislocation; + } } + + width = maxwidth; } } @@ -4950,7 +4981,7 @@ int bufhl_add_hl(buf_T *buf, bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum, true); - bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo); + bufhl_hl_item_T *hlentry = kv_pushp(*lineinfo); hlentry->src_id = src_id; hlentry->hl_id = hl_id; hlentry->start = col_start; diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index d51a2f7dae..36cbec7e60 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -76,14 +76,14 @@ static inline void restore_win_for_buf(win_T *save_curwin, } } -#define WITH_BUFFER(b, code) \ - do { \ - buf_T *save_curbuf = NULL; \ - win_T *save_curwin = NULL; \ - tabpage_T *save_curtab = NULL; \ - switch_to_win_for_buf(b, &save_curwin, &save_curtab, &save_curbuf); \ - code; \ - restore_win_for_buf(save_curwin, save_curtab, save_curbuf); \ +#define WITH_BUFFER(b, code) \ + do { \ + buf_T *save_curbuf = NULL; \ + win_T *save_curwin = NULL; \ + tabpage_T *save_curtab = NULL; \ + switch_to_win_for_buf(b, &save_curwin, &save_curtab, &save_curbuf); \ + code; \ + restore_win_for_buf(save_curwin, save_curtab, save_curbuf); \ } while (0) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 0324f6b88a..b515c4e1e4 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -438,15 +438,17 @@ typedef struct { linenr_T b_sst_check_lnum; uint16_t b_sst_lasttick; /* last display tick */ - /* for spell checking */ - garray_T b_langp; /* list of pointers to slang_T, see spell.c */ - bool b_spell_ismw[256]; /* flags: is midword char */ - char_u *b_spell_ismw_mb; /* multi-byte midword chars */ - char_u *b_p_spc; /* 'spellcapcheck' */ - regprog_T *b_cap_prog; /* program for 'spellcapcheck' */ - char_u *b_p_spf; /* 'spellfile' */ - char_u *b_p_spl; /* 'spelllang' */ - int b_cjk; /* all CJK letters as OK */ + // for spell checking + garray_T b_langp; // list of pointers to slang_T, see spell.c + bool b_spell_ismw[256]; // flags: is midword char + char_u *b_spell_ismw_mb; // multi-byte midword chars + char_u *b_p_spc; // 'spellcapcheck' + regprog_T *b_cap_prog; // program for 'spellcapcheck' + char_u *b_p_spf; // 'spellfile' + char_u *b_p_spl; // 'spelllang' + int b_cjk; // all CJK letters as OK + char_u b_syn_chartab[32]; // syntax iskeyword option + char_u *b_syn_isk; // iskeyword option } synblock_T; diff --git a/src/nvim/charset.c b/src/nvim/charset.c index d0dc7b66fc..22ca0fb0cc 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -43,20 +43,29 @@ static bool chartab_initialized = false; #define GET_CHARTAB(buf, c) \ ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) -/// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword +// Table used below, see init_chartab() for an explanation +static char_u g_chartab[256]; + +// Flags for g_chartab[]. +#define CT_CELL_MASK 0x07 ///< mask: nr of display cells (1, 2 or 4) +#define CT_PRINT_CHAR 0x10 ///< flag: set for printable chars +#define CT_ID_CHAR 0x20 ///< flag: set for ID chars +#define CT_FNAME_CHAR 0x40 ///< flag: set for file name chars + +/// Fill g_chartab[]. Also fills curbuf->b_chartab[] with flags for keyword /// characters for current buffer. /// /// Depends on the option settings 'iskeyword', 'isident', 'isfname', /// 'isprint' and 'encoding'. /// -/// The index in chartab[] depends on 'encoding': +/// The index in g_chartab[] depends on 'encoding': /// - For non-multi-byte index with the byte (same as the character). /// - For DBCS index with the first byte. /// - For UTF-8 index with the character (when first byte is up to 0x80 it is /// the same as the character, if the first byte is 0x80 and above it depends /// on further bytes). /// -/// The contents of chartab[]: +/// The contents of g_chartab[]: /// - The lower two bits, masked by CT_CELL_MASK, give the number of display /// cells the character occupies (1 or 2). Not valid for UTF-8 above 0x80. /// - CT_PRINT_CHAR bit is set when the character is printable (no need to @@ -94,32 +103,32 @@ int buf_init_chartab(buf_T *buf, int global) c = 0; while (c < ' ') { - chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; + g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; } while (c <= '~') { - chartab[c++] = 1 + CT_PRINT_CHAR; + g_chartab[c++] = 1 + CT_PRINT_CHAR; } if (p_altkeymap) { while (c < YE) { - chartab[c++] = 1 + CT_PRINT_CHAR; + g_chartab[c++] = 1 + CT_PRINT_CHAR; } } while (c < 256) { if (enc_utf8 && (c >= 0xa0)) { // UTF-8: bytes 0xa0 - 0xff are printable (latin1) - chartab[c++] = CT_PRINT_CHAR + 1; + g_chartab[c++] = CT_PRINT_CHAR + 1; } else if ((enc_dbcs == DBCS_JPNU) && (c == 0x8e)) { // euc-jp characters starting with 0x8e are single width - chartab[c++] = CT_PRINT_CHAR + 1; + g_chartab[c++] = CT_PRINT_CHAR + 1; } else if ((enc_dbcs != 0) && (MB_BYTE2LEN(c) == 2)) { // other double-byte chars can be printable AND double-width - chartab[c++] = CT_PRINT_CHAR + 2; + g_chartab[c++] = CT_PRINT_CHAR + 2; } else { // the rest is unprintable by default - chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; + g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; } } @@ -128,7 +137,7 @@ int buf_init_chartab(buf_T *buf, int global) if (((enc_dbcs != 0) && (MB_BYTE2LEN(c) > 1)) || ((enc_dbcs == DBCS_JPNU) && (c == 0x8e)) || (enc_utf8 && (c >= 0xa0))) { - chartab[c] |= CT_FNAME_CHAR; + g_chartab[c] |= CT_FNAME_CHAR; } } } @@ -231,9 +240,9 @@ int buf_init_chartab(buf_T *buf, int global) if (i == 0) { // (re)set ID flag if (tilde) { - chartab[c] &= (uint8_t)~CT_ID_CHAR; + g_chartab[c] &= (uint8_t)~CT_ID_CHAR; } else { - chartab[c] |= CT_ID_CHAR; + g_chartab[c] |= CT_ID_CHAR; } } else if (i == 1) { // (re)set printable @@ -244,20 +253,20 @@ int buf_init_chartab(buf_T *buf, int global) || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) && !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) { if (tilde) { - chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) - + ((dy_flags & DY_UHEX) ? 4 : 2)); - chartab[c] &= (uint8_t)~CT_PRINT_CHAR; + g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + + ((dy_flags & DY_UHEX) ? 4 : 2)); + g_chartab[c] &= (uint8_t)~CT_PRINT_CHAR; } else { - chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + 1); - chartab[c] |= CT_PRINT_CHAR; + g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + 1); + g_chartab[c] |= CT_PRINT_CHAR; } } } else if (i == 2) { // (re)set fname flag if (tilde) { - chartab[c] &= (uint8_t)~CT_FNAME_CHAR; + g_chartab[c] &= (uint8_t)~CT_FNAME_CHAR; } else { - chartab[c] |= CT_FNAME_CHAR; + g_chartab[c] |= CT_FNAME_CHAR; } } else { // i == 3 // (re)set keyword flag @@ -492,9 +501,9 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) return buf; } -// Catch 22: chartab[] can't be initialized before the options are +// Catch 22: g_chartab[] can't be initialized before the options are // initialized, and initializing options may cause transchar() to be called! -// When chartab_initialized == false don't use chartab[]. +// When chartab_initialized == false don't use g_chartab[]. // Does NOT work for multi-byte characters, c must be <= 255. // Also doesn't work for the first byte of a multi-byte, "c" must be a // character! @@ -633,7 +642,7 @@ int byte2cells(int b) if (enc_utf8 && (b >= 0x80)) { return 0; } - return chartab[b] & CT_CELL_MASK; + return g_chartab[b] & CT_CELL_MASK; } /// Return number of display cells occupied by character "c". @@ -665,7 +674,7 @@ int char2cells(int c) return 2; } } - return chartab[c & 0xff] & CT_CELL_MASK; + return g_chartab[c & 0xff] & CT_CELL_MASK; } /// Return number of display cells occupied by character at "*p". @@ -682,7 +691,7 @@ int ptr2cells(char_u *p) } // For DBCS we can tell the cell count from the first byte. - return chartab[*p] & CT_CELL_MASK; + return g_chartab[*p] & CT_CELL_MASK; } /// Return the number of character cells string "s" will take on the screen, @@ -806,7 +815,7 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) bool vim_isIDc(int c) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return c > 0 && c < 0x100 && (chartab[c] & CT_ID_CHAR); + return c > 0 && c < 0x100 && (g_chartab[c] & CT_ID_CHAR); } /// Check that "c" is a keyword character: @@ -878,7 +887,7 @@ bool vim_iswordp_buf(char_u *p, buf_T *buf) bool vim_isfilec(int c) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return c >= 0x100 || (c > 0 && (chartab[c] & CT_FNAME_CHAR)); + return c >= 0x100 || (c > 0 && (g_chartab[c] & CT_FNAME_CHAR)); } /// Check that "c" is a valid file-name character or a wildcard character @@ -906,7 +915,7 @@ bool vim_isprintc(int c) if (enc_utf8 && (c >= 0x100)) { return utf_printable(c); } - return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR)); + return c >= 0x100 || (c > 0 && (g_chartab[c] & CT_PRINT_CHAR)); } /// Strict version of vim_isprintc(c), don't return true if "c" is the head @@ -925,7 +934,7 @@ bool vim_isprintc_strict(int c) if (enc_utf8 && (c >= 0x100)) { return utf_printable(c); } - return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR)); + return c >= 0x100 || (c > 0 && (g_chartab[c] & CT_PRINT_CHAR)); } /// like chartabsize(), but also check for line breaks on the screen @@ -1247,7 +1256,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, if (enc_utf8 && (c >= 0x80)) { incr = utf_ptr2cells(ptr); } else { - incr = CHARSIZE(c); + incr = g_chartab[c] & CT_CELL_MASK; } // If a double-cell char doesn't fit at the end of a line @@ -1261,7 +1270,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, head = 1; } } else { - incr = CHARSIZE(c); + incr = g_chartab[c] & CT_CELL_MASK; } } @@ -1582,7 +1591,7 @@ static char_u latin1lower[257] = /// /// @param c character to check bool vim_islower(int c) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (c <= '@') { return false; @@ -1613,7 +1622,7 @@ bool vim_islower(int c) /// /// @param c character to check bool vim_isupper(int c) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (c <= '@') { return false; diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 995ad123ae..78d6f2a76c 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -1,14 +1,6 @@ #ifndef NVIM_CHARSET_H #define NVIM_CHARSET_H -/* - * Flags for chartab[]. - */ -#define CT_CELL_MASK 0x07 /* mask: nr of display cells (1, 2 or 4) */ -#define CT_PRINT_CHAR 0x10 /* flag: set for printable chars */ -#define CT_ID_CHAR 0x20 /* flag: set for ID chars */ -#define CT_FNAME_CHAR 0x40 /* flag: set for file name chars */ - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 9525024c1b..aad145b3e5 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1757,12 +1757,12 @@ char_u* keymap_init(void) vim_snprintf(buf, buflen, "keymap/%s_%s.vim", curbuf->b_p_keymap, p_enc); - if (source_runtime((char_u *)buf, FALSE) == FAIL) { + if (source_runtime((char_u *)buf, 0) == FAIL) { // try finding "keymap/'keymap'.vim" in 'runtimepath' vim_snprintf(buf, buflen, "keymap/%s.vim", curbuf->b_p_keymap); - if (source_runtime((char_u *)buf, FALSE) == FAIL) { + if (source_runtime((char_u *)buf, 0) == FAIL) { xfree(buf); return (char_u *)N_("E544: Keymap file not found"); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e131da8fe0..03ef41f849 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -961,7 +961,7 @@ static int insert_handle_key(InsertState *s) break; case K_EVENT: // some event - queue_process_events(loop.events); + queue_process_events(main_loop.events); break; case K_FOCUSGAINED: // Neovim has been given focus @@ -6952,8 +6952,8 @@ static void ins_reg(void) AppendCharToRedobuff(literally); AppendCharToRedobuff(regname); - do_put(regname, NULL, BACKWARD, 1L, - (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND); + do_put(regname, NULL, BACKWARD, 1, + (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND); } else if (insert_reg(regname, literally) == FAIL) { vim_beep(BO_REG); need_redraw = true; // remove the '"' @@ -7701,7 +7701,7 @@ static void ins_mouse(int c) undisplay_dollar(); tpos = curwin->w_cursor; - if (do_mouse(NULL, c, BACKWARD, 1L, 0)) { + if (do_mouse(NULL, c, BACKWARD, 1, 0)) { win_T *new_curwin = curwin; if (curwin != old_curwin && win_valid(old_curwin)) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 420a712e3e..47d44b148a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -67,6 +67,7 @@ #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/ui.h" +#include "nvim/main.h" #include "nvim/mouse.h" #include "nvim/terminal.h" #include "nvim/undo.h" @@ -76,7 +77,7 @@ #include "nvim/eval/decode.h" #include "nvim/os/os.h" #include "nvim/event/libuv_process.h" -#include "nvim/event/pty_process.h" +#include "nvim/os/pty_process.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/event/time.h" @@ -90,6 +91,7 @@ #include "nvim/os/input.h" #include "nvim/event/loop.h" #include "nvim/lib/queue.h" +#include "nvim/eval/typval_encode.h" #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -433,6 +435,7 @@ typedef struct { TimeWatcher tw; int timer_id; int repeat_count; + long timeout; bool stopped; ufunc_T *callback; } timer_T; @@ -507,6 +510,7 @@ void eval_init(void) /* add to compat scope dict */ hash_add(&compat_hashtab, p->vv_di.di_key); } + vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; dict_T *const msgpack_types_dict = dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { @@ -1889,7 +1893,7 @@ ex_let_one ( } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE); + write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false); arg_end = arg + 1; } xfree(ptofree); @@ -2977,11 +2981,16 @@ int do_unlet(char_u *name, int forceit) } else if (current_funccal != NULL && ht == ¤t_funccal->l_vars.dv_hashtab) { d = ¤t_funccal->l_vars; + } else if (ht == &compat_hashtab) { + d = &vimvardict; } else { di = find_var_in_ht(ht, *name, (char_u *)"", false); d = di->di_tv.vval.v_dict; } - + if (d == NULL) { + EMSG2(_(e_intern2), "do_unlet()"); + return FAIL; + } hi = hash_find(ht, varname); if (!HASHITEM_EMPTY(hi)) { di = HI2DI(hi); @@ -2990,6 +2999,11 @@ int do_unlet(char_u *name, int forceit) || tv_check_lock(d->dv_lock, name, false)) { return FAIL; } + + if (d == NULL || tv_check_lock(d->dv_lock, name, false)) { + return FAIL; + } + typval_T oldtv; bool watched = is_watched(dict); @@ -6672,6 +6686,7 @@ static struct fst { { "acos", 1, 1, f_acos }, // WJMc { "add", 2, 2, f_add }, { "and", 2, 2, f_and }, + { "api_info", 0, 0, f_api_info }, { "append", 2, 2, f_append }, { "argc", 0, 0, f_argc }, { "argidx", 0, 0, f_argidx }, @@ -6700,6 +6715,7 @@ static struct fst { { "byteidx", 2, 2, f_byteidx }, { "byteidxcomp", 2, 2, f_byteidxcomp }, { "call", 2, 3, f_call }, + { "capture", 1, 1, f_capture }, { "ceil", 1, 1, f_ceil }, { "changenr", 0, 0, f_changenr }, { "char2nr", 1, 2, f_char2nr }, @@ -7454,6 +7470,15 @@ static void f_and(typval_T *argvars, typval_T *rettv) & get_tv_number_chk(&argvars[1], NULL); } + +/// "api_info()" function +static void f_api_info(typval_T *argvars, typval_T *rettv) +{ + Dictionary metadata = api_metadata(); + (void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL); + api_free_dictionary(metadata); +} + /* * "append(lnum, string/list)" function */ @@ -7529,25 +7554,9 @@ static void f_argidx(typval_T *argvars, typval_T *rettv) static void f_arglistid(typval_T *argvars, typval_T *rettv) { rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_UNKNOWN) { - tabpage_T *tp = NULL; - if (argvars[1].v_type != VAR_UNKNOWN) { - long n = get_tv_number(&argvars[1]); - if (n >= 0) { - tp = find_tabpage(n); - } - } else { - tp = curtab; - } - - if (tp != NULL) { - win_T *wp = find_win_by_nr(&argvars[0], tp); - if (wp != NULL) { - rettv->vval.v_number = wp->w_alist->id; - } - } - } else { - rettv->vval.v_number = curwin->w_alist->id; + win_T *wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp != NULL) { + rettv->vval.v_number = wp->w_alist->id; } } @@ -7659,7 +7668,8 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv) ga_concat(&ga, (char_u *)"v:exception is not set"); assert_error(&ga); ga_clear(&ga); - } else if (strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) { + } else if (error != NULL + && strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) { prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], &vimvars[VV_EXCEPTION].vv_tv); @@ -8077,6 +8087,38 @@ static void f_call(typval_T *argvars, typval_T *rettv) (void)func_call(func, &argvars[1], selfdict, rettv); } +// "capture(command)" function +static void f_capture(typval_T *argvars, typval_T *rettv) +{ + int save_msg_silent = msg_silent; + garray_T *save_capture_ga = capture_ga; + + if (check_secure()) { + return; + } + + garray_T capture_local; + capture_ga = &capture_local; + ga_init(capture_ga, (int)sizeof(char), 80); + + msg_silent++; + if (argvars[0].v_type != VAR_LIST) { + do_cmdline_cmd((char *)get_tv_string(&argvars[0])); + } else if (argvars[0].vval.v_list != NULL) { + for (listitem_T *li = argvars[0].vval.v_list->lv_first; + li != NULL; li = li->li_next) { + do_cmdline_cmd((char *)get_tv_string(&li->li_tv)); + } + } + msg_silent = save_msg_silent; + + ga_append(capture_ga, NUL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = capture_ga->ga_data; + + capture_ga = save_capture_ga; +} + /* * "ceil({float})" function */ @@ -9887,7 +9929,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv) static void f_getcwd(typval_T *argvars, typval_T *rettv) { // Possible scope of working directory to return. - CdScope scope = MIN_CD_SCOPE; + CdScope scope = kCdScopeInvalid; // Numbers of the scope objects (window, tab) we want the working directory // of. A `-1` means to skip this scope, a `0` means the current object. @@ -9916,26 +9958,27 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv) return; } scope_number[i] = argvars[i].vval.v_number; - // The scope is the current iteration step. - scope = i; // It is an error for the scope number to be less than `-1`. if (scope_number[i] < -1) { EMSG(_(e_invarg)); return; } + // Use the narrowest scope the user requested + if (scope_number[i] >= 0 && scope == kCdScopeInvalid) { + // The scope is the current iteration step. + scope = i; + } else if (scope_number[i] < 0) { + scope = i + 1; + } } - // Normalize scope, the number of the new scope will be 0. - if (scope_number[scope] < 0) { - // Arguments to `getcwd` always end at second-highest scope, so scope will - // always be <= `MAX_CD_SCOPE`. - scope++; + // If the user didn't specify anything, default to window scope + if (scope == kCdScopeInvalid) { + scope = MIN_CD_SCOPE; } // Find the tabpage by number - if (scope_number[kCdScopeTab] == -1) { - tp = NULL; - } else if (scope_number[kCdScopeTab] > 0) { + if (scope_number[kCdScopeTab] > 0) { tp = find_tabpage(scope_number[kCdScopeTab]); if (!tp) { EMSG(_("E5000: Cannot find tab number.")); @@ -9944,16 +9987,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv) } // Find the window in `tp` by number, `NULL` if none. - if (scope_number[kCdScopeWindow] == -1) { - win = NULL; - } else if (scope_number[kCdScopeWindow] >= 0) { - if (!tp) { + if (scope_number[kCdScopeWindow] >= 0) { + if (scope_number[kCdScopeTab] < 0) { EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); return; } if (scope_number[kCdScopeWindow] > 0) { - win = find_win_by_nr(&argvars[0], curtab); + win = find_win_by_nr(&argvars[0], tp); if (!win) { EMSG(_("E5002: Cannot find window number.")); return; @@ -9988,6 +10029,9 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv) } } break; + case kCdScopeInvalid: + // We should never get here + assert(false); } if (from) { @@ -10459,9 +10503,33 @@ find_win_by_nr ( return NULL; } -/* - * "getwinvar()" function - */ +/// Find window specified by "wvp" in tabpage "tvp". +static win_T *find_tabwin(typval_T *wvp, typval_T *tvp) +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + + if (wvp->v_type != VAR_UNKNOWN) { + if (tvp->v_type != VAR_UNKNOWN) { + long n = get_tv_number(tvp); + if (n >= 0) { + tp = find_tabpage(n); + } + } else { + tp = curtab; + } + + if (tp != NULL) { + wp = find_win_by_nr(wvp, tp); + } + } else { + wp = curwin; + } + + return wp; +} + +/// "getwinvar()" function static void f_getwinvar(typval_T *argvars, typval_T *rettv) { getwinvar(argvars, rettv, 0); @@ -10714,6 +10782,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) "mouse", "multi_byte", "multi_lang", + "packages", "path_extra", "persistent_undo", "postscript", @@ -10835,7 +10904,7 @@ static void f_has_key(typval_T *argvars, typval_T *rettv) static void f_haslocaldir(typval_T *argvars, typval_T *rettv) { // Possible scope of working directory to return. - CdScope scope = MIN_CD_SCOPE; + CdScope scope = kCdScopeInvalid; // Numbers of the scope objects (window, tab) we want the working directory // of. A `-1` means to skip this scope, a `0` means the current object. @@ -10860,25 +10929,26 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv) return; } scope_number[i] = argvars[i].vval.v_number; - // The scope is the current iteration step. - scope = i; if (scope_number[i] < -1) { EMSG(_(e_invarg)); return; } + // Use the narrowest scope the user requested + if (scope_number[i] >= 0 && scope == kCdScopeInvalid) { + // The scope is the current iteration step. + scope = i; + } else if (scope_number[i] < 0) { + scope = i + 1; + } } - // Normalize scope, the number of the new scope will be 0. - if (scope_number[scope] < 0) { - // Arguments to `haslocaldir` always end at second-highest scope, so scope - // will always be <= `MAX_CD_SCOPE`. - scope++; + // If the user didn't specify anything, default to window scope + if (scope == kCdScopeInvalid) { + scope = MIN_CD_SCOPE; } // Find the tabpage by number - if (scope_number[kCdScopeTab] == -1) { - tp = NULL; - } else if (scope_number[kCdScopeTab] > 0) { + if (scope_number[kCdScopeTab] > 0) { tp = find_tabpage(scope_number[kCdScopeTab]); if (!tp) { EMSG(_("5000: Cannot find tab number.")); @@ -10887,16 +10957,14 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv) } // Find the window in `tp` by number, `NULL` if none. - if (scope_number[kCdScopeWindow] == -1) { - win = NULL; - } else if (scope_number[kCdScopeWindow] >= 0) { - if (!tp) { + if (scope_number[kCdScopeWindow] >= 0) { + if (scope_number[kCdScopeTab] < 0) { EMSG(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); return; } if (scope_number[kCdScopeWindow] > 0) { - win = find_win_by_nr(&argvars[0], curtab); + win = find_win_by_nr(&argvars[0], tp); if (!win) { EMSG(_("E5002: Cannot find window number.")); return; @@ -10917,6 +10985,9 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv) // The global scope never has a local directory rettv->vval.v_number = 0; break; + case kCdScopeInvalid: + // We should never get here + assert(false); } } @@ -11726,8 +11797,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv) dict_T *job_opts = NULL; ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL; + char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; + + char *new_cwd = (char *)get_dict_string(job_opts, (char_u *)"cwd", false); + if (new_cwd && strlen(new_cwd) > 0) { + cwd = new_cwd; + // The new cwd must be a directory. + if (!os_isdir((char_u *)cwd)) { + EMSG2(_(e_invarg2), "expected valid directory"); + shell_free_argv(argv); + return; + } + } + if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); return; @@ -11737,7 +11821,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv) bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0; bool detach = job_opts && get_dict_number(job_opts, (uint8_t *)"detach") != 0; TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - job_opts, pty, detach); + job_opts, pty, detach, cwd); Process *proc = (Process *)&data->proc; if (pty) { @@ -11812,7 +11896,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv) list_T *rv = list_alloc(); ui_busy_start(); - Queue *waiting_jobs = queue_new_parent(loop_on_put, &loop); + Queue *waiting_jobs = queue_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. @@ -11890,7 +11974,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv) } // restore the parent queue for the job queue_process_events(data->events); - queue_replace_parent(data->events, loop.events); + queue_replace_parent(data->events, main_loop.events); } queue_free(waiting_jobs); @@ -14891,7 +14975,8 @@ static void f_setreg(typval_T *argvars, typval_T *rettv) } *curval++ = NULL; - write_reg_contents_lst(regname, lstval, -1, append, yank_type, block_len); + write_reg_contents_lst(regname, lstval, STRLEN(lstval), + append, yank_type, block_len); free_lstval: while (curallocval > allocval) @@ -14902,7 +14987,8 @@ free_lstval: if (strval == NULL) { return; } - write_reg_contents_ex(regname, strval, -1, append, yank_type, block_len); + write_reg_contents_ex(regname, strval, STRLEN(strval), + append, yank_type, block_len); } rettv->vval.v_number = 0; } @@ -15078,13 +15164,18 @@ typedef struct { int idx; } sortItem_T; -static int item_compare_ic; -static bool item_compare_numeric; -static bool item_compare_numbers; -static bool item_compare_float; -static char_u *item_compare_func; -static dict_T *item_compare_selfdict; -static int item_compare_func_err; +/// struct storing information about current sort +typedef struct { + int item_compare_ic; + bool item_compare_numeric; + bool item_compare_numbers; + bool item_compare_float; + char_u *item_compare_func; + dict_T *item_compare_selfdict; + int item_compare_func_err; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; + #define ITEM_COMPARE_FAIL 999 /* @@ -15104,14 +15195,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) typval_T *tv1 = &si1->item->li_tv; typval_T *tv2 = &si2->item->li_tv; - if (item_compare_numbers) { + if (sortinfo->item_compare_numbers) { long v1 = get_tv_number(tv1); long v2 = get_tv_number(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } - if (item_compare_float) { + if (sortinfo->item_compare_float) { float_T v1 = get_tv_float(tv1); float_T v2 = get_tv_float(tv2); @@ -15122,7 +15213,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) // do that for string variables. Use a single quote when comparing with // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { - if (tv2->v_type != VAR_STRING || item_compare_numeric) { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { p1 = (char_u *)"'"; } else { p1 = tv1->vval.v_string; @@ -15131,7 +15222,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { - if (tv1->v_type != VAR_STRING || item_compare_numeric) { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { p2 = (char_u *)"'"; } else { p2 = tv2->vval.v_string; @@ -15139,12 +15230,14 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } else { tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); } - if (p1 == NULL) + if (p1 == NULL) { p1 = (char_u *)""; - if (p2 == NULL) + } + if (p2 == NULL) { p2 = (char_u *)""; - if (!item_compare_numeric) { - if (item_compare_ic) { + } + if (!sortinfo->item_compare_numeric) { + if (sortinfo->item_compare_ic) { res = STRICMP(p1, p2); } else { res = STRCMP(p1, p2); @@ -15185,9 +15278,10 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) typval_T argv[3]; int dummy; - /* shortcut after failure in previous call; compare all items equal */ - if (item_compare_func_err) + // shortcut after failure in previous call; compare all items equal + if (sortinfo->item_compare_func_err) { return 0; + } si1 = (sortItem_T *)s1; si2 = (sortItem_T *)s2; @@ -15197,19 +15291,22 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si2->item->li_tv, &argv[1]); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - res = call_func(item_compare_func, (int)STRLEN(item_compare_func), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, - item_compare_selfdict); + rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this + res = call_func(sortinfo->item_compare_func, + (int)STRLEN(sortinfo->item_compare_func), + &rettv, 2, argv, 0L, 0L, &dummy, true, + sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); - if (res == FAIL) + if (res == FAIL) { res = ITEM_COMPARE_FAIL; - else - res = get_tv_number_chk(&rettv, &item_compare_func_err); - if (item_compare_func_err) - res = ITEM_COMPARE_FAIL; /* return value has wrong type */ + } else { + res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); + } + if (sortinfo->item_compare_func_err) { + res = ITEM_COMPARE_FAIL; // return value has wrong type + } clear_tv(&rettv); // When the result would be zero, compare the pointers themselves. Makes @@ -15242,6 +15339,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) long len; long i; + // Pointer to current info struct used in compare function. Save and restore + // the current one for nested calls. + sortinfo_T info; + sortinfo_T *old_sortinfo = sortinfo; + sortinfo = &info; + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { @@ -15252,61 +15355,70 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) ? N_("sort() argument") : N_("uniq() argument")), true)) { - return; + goto theend; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; ++l->lv_refcount; len = list_len(l); - if (len <= 1) - return; /* short list sorts pretty quickly */ + if (len <= 1) { + goto theend; // short list sorts pretty quickly + } - item_compare_ic = FALSE; - item_compare_numeric = false; - item_compare_numbers = false; - item_compare_float = false; - item_compare_func = NULL; - item_compare_selfdict = NULL; + info.item_compare_ic = false; + info.item_compare_numeric = false; + info.item_compare_numbers = false; + info.item_compare_float = false; + info.item_compare_func = NULL; + info.item_compare_selfdict = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) { - item_compare_func = argvars[1].vval.v_string; + info.item_compare_func = argvars[1].vval.v_string; } else { int error = FALSE; i = get_tv_number_chk(&argvars[1], &error); - if (error) - return; /* type error; errmsg already given */ - if (i == 1) - item_compare_ic = TRUE; - else - item_compare_func = get_tv_string(&argvars[1]); - if (item_compare_func != NULL) { - if (STRCMP(item_compare_func, "n") == 0) { - item_compare_func = NULL; - item_compare_numeric = true; - } else if (STRCMP(item_compare_func, "N") == 0) { - item_compare_func = NULL; - item_compare_numbers = true; - } else if (STRCMP(item_compare_func, "f") == 0) { - item_compare_func = NULL; - item_compare_float = true; - } else if (STRCMP(item_compare_func, "i") == 0) { - item_compare_func = NULL; - item_compare_ic = TRUE; + if (error) { + goto theend; // type error; errmsg already given + } + if (i == 1) { + info.item_compare_ic = true; + } else if (argvars[1].v_type != VAR_NUMBER) { + info.item_compare_func = get_tv_string(&argvars[1]); + } else if (i != 0) { + EMSG(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) { + if (*info.item_compare_func == NUL) { + // empty string means default sort + info.item_compare_func = NULL; + } else if (STRCMP(info.item_compare_func, "n") == 0) { + info.item_compare_func = NULL; + info.item_compare_numeric = true; + } else if (STRCMP(info.item_compare_func, "N") == 0) { + info.item_compare_func = NULL; + info.item_compare_numbers = true; + } else if (STRCMP(info.item_compare_func, "f") == 0) { + info.item_compare_func = NULL; + info.item_compare_float = true; + } else if (STRCMP(info.item_compare_func, "i") == 0) { + info.item_compare_func = NULL; + info.item_compare_ic = true; } } } if (argvars[2].v_type != VAR_UNKNOWN) { - /* optional third argument: {dict} */ + // optional third argument: {dict} if (argvars[2].v_type != VAR_DICT) { EMSG(_(e_dictreq)); - return; + goto theend; } - item_compare_selfdict = argvars[2].vval.v_dict; + info.item_compare_selfdict = argvars[2].vval.v_dict; } } @@ -15322,19 +15434,20 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) i++; } - item_compare_func_err = FALSE; + info.item_compare_func_err = false; // Test the compare function. - if (item_compare_func != NULL + if (info.item_compare_func != NULL && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) == ITEM_COMPARE_FAIL) { EMSG(_("E702: Sort compare function failed")); } else { // Sort the array with item pointers. qsort(ptrs, (size_t)len, sizeof (sortItem_T), - item_compare_func == NULL ? item_compare_not_keeping_zero : - item_compare2_not_keeping_zero); + (info.item_compare_func == NULL ? + item_compare_not_keeping_zero : + item_compare2_not_keeping_zero)); - if (!item_compare_func_err) { + if (!info.item_compare_func_err) { // Clear the list and append the items in the sorted order. l->lv_first = NULL; l->lv_last = NULL; @@ -15350,21 +15463,24 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) int (*item_compare_func_ptr)(const void *, const void *); // f_uniq(): ptrs will be a stack of items to remove. - item_compare_func_err = FALSE; - item_compare_func_ptr = item_compare_func ? item_compare2_keeping_zero : - item_compare_keeping_zero; + info.item_compare_func_err = false; + if (info.item_compare_func != NULL) { + item_compare_func_ptr = item_compare2_keeping_zero; + } else { + item_compare_func_ptr = item_compare_keeping_zero; + } for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { if (item_compare_func_ptr(&li, &li->li_next) == 0) { ptrs[i++].item = li; } - if (item_compare_func_err) { + if (info.item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); break; } } - if (!item_compare_func_err) { + if (!info.item_compare_func_err) { while (--i >= 0) { assert(ptrs[i].item->li_next); li = ptrs[i].item->li_next; @@ -15383,6 +15499,9 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) xfree(ptrs); } + +theend: + sortinfo = old_sortinfo; } /// "sort"({list})" function @@ -16419,8 +16538,21 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) ufunc_T *on_stdout = NULL, *on_stderr = NULL, *on_exit = NULL; dict_T *job_opts = NULL; + char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; + + char *new_cwd = (char *)get_dict_string(job_opts, (char_u *)"cwd", false); + if (new_cwd && strlen(new_cwd) > 0) { + cwd = new_cwd; + // The new cwd must be a directory. + if (!os_isdir((char_u *)cwd)) { + EMSG2(_(e_invarg2), "expected valid directory"); + shell_free_argv(argv); + return; + } + } + if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); return; @@ -16428,7 +16560,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) } TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - job_opts, true, false); + job_opts, true, false, cwd); data->proc.pty.width = curwin->w_width; data->proc.pty.height = curwin->w_height; data->proc.pty.term_name = xstrdup("xterm-256color"); @@ -16443,11 +16575,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) topts.resize_cb = term_resize; topts.close_cb = term_close; - char *cwd = "."; - if (argvars[1].v_type == VAR_STRING - && os_isdir(argvars[1].vval.v_string)) { - cwd = (char *)argvars[1].vval.v_string; - } int pid = data->proc.pty.process.pid; char buf[1024]; @@ -16514,6 +16641,9 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv) } if (dict_find(dict, (char_u *)"repeat", -1) != NULL) { repeat = get_dict_number(dict, (char_u *)"repeat"); + if (repeat == 0) { + repeat = 1; + } } } @@ -16531,11 +16661,12 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv) timer = xmalloc(sizeof *timer); timer->stopped = false; timer->repeat_count = repeat; + timer->timeout = timeout; timer->timer_id = last_timer_id++; timer->callback = func; - time_watcher_init(&loop, &timer->tw, timer); - timer->tw.events = queue_new_child(loop.events); + time_watcher_init(&main_loop, &timer->tw, timer); + timer->tw.events = queue_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, @@ -16568,7 +16699,7 @@ static void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; if (timer->stopped) { - return; + return; } // if repeat was negative repeat forever if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { @@ -16585,6 +16716,14 @@ static void timer_due_cb(TimeWatcher *tw, void *data) call_user_func(timer->callback, ARRAY_SIZE(argv), argv, &rettv, curwin->w_cursor.lnum, curwin->w_cursor.lnum, NULL); clear_tv(&rettv); + + if (!timer->stopped && timer->timeout == 0) { + // special case: timeout=0 means the callback will be + // invoked again on the next event loop tick. + // we don't use uv_idle_t to not spin the event loop + // when the main loop is blocked. + time_watcher_start(&timer->tw, timer_due_cb, 0, 0); + } } static void timer_stop(timer_T *timer) @@ -16608,6 +16747,14 @@ static void timer_free_cb(TimeWatcher *tw, void *data) xfree(timer); } +void timer_teardown(void) +{ + timer_T *timer; + map_foreach_value(timers, timer, { + timer_stop(timer); + }) +} + /* * "tolower(string)" function */ @@ -17707,7 +17854,8 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) { - xfree(vimvars[idx].vv_str); + clear_tv(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_STRING; if (val == NULL) { vimvars[idx].vv_str = NULL; } else if (len == -1) { @@ -17723,7 +17871,8 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val, /// @param[in,out] val Value to set to. Reference count will be incremented. void set_vim_var_list(const VimVarIndex idx, list_T *const val) { - list_unref(vimvars[idx].vv_list); + clear_tv(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { val->lv_refcount++; @@ -17737,7 +17886,8 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) /// Also keys of the dictionary will be made read-only. void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { - dict_unref(vimvars[idx].vv_dict); + clear_tv(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_type = VAR_DICT; vimvars[idx].vv_dict = val; if (val != NULL) { @@ -18014,45 +18164,147 @@ void free_tv(typval_T *varp) } } -/* - * Free the memory for a variable value and set the value to NULL or 0. - */ +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL() \ + do { \ + tv->vval.v_special = kSpecialVarFalse; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_BOOL(ignored) \ + TYPVAL_ENCODE_CONV_NIL() + +#define TYPVAL_ENCODE_CONV_NUMBER(ignored) \ + do { \ + (void)ignored; \ + tv->vval.v_number = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(ignored) \ + assert(false) + +#define TYPVAL_ENCODE_CONV_FLOAT(ignored) \ + do { \ + tv->vval.v_float = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STRING(str, ignored) \ + do { \ + xfree(str); \ + tv->vval.v_string = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING(ignored1, ignored2) + +#define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3) + +#define TYPVAL_ENCODE_CONV_FUNC(fun) \ + do { \ + func_unref(fun); \ + if (fun != empty_string) { \ + xfree(fun); \ + } \ + tv->vval.v_string = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ + do { \ + list_unref(tv->vval.v_list); \ + tv->vval.v_list = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \ + do { \ + dict_unref(tv->vval.v_dict); \ + tv->vval.v_dict = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_START(ignored) \ + do { \ + if (tv->vval.v_list->lv_refcount > 1) { \ + tv->vval.v_list->lv_refcount--; \ + tv->vval.v_list = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + return OK; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() + +#define TYPVAL_ENCODE_CONV_LIST_END() \ + do { \ + typval_T *const cur_tv = cur_mpsv->tv; \ + assert(cur_tv->v_type == VAR_LIST); \ + list_unref(cur_tv->vval.v_list); \ + cur_tv->vval.v_list = NULL; \ + cur_tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_DICT_START(ignored) \ + do { \ + if (tv->vval.v_dict->dv_refcount > 1) { \ + tv->vval.v_dict->dv_refcount--; \ + tv->vval.v_dict = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + return OK; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(ignored1, ignored2) + +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() + +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() + +#define TYPVAL_ENCODE_CONV_DICT_END() \ + do { \ + typval_T *const cur_tv = cur_mpsv->tv; \ + assert(cur_tv->v_type == VAR_DICT); \ + dict_unref(cur_tv->vval.v_dict); \ + cur_tv->vval.v_dict = NULL; \ + cur_tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2) + +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored) + +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_FUNC +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_RECURSE + +/// Free memory for a variable value and set the value to NULL or 0 +/// +/// @param[in,out] varp Value to free. void clear_tv(typval_T *varp) { - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - if (varp->vval.v_string != empty_string) { - xfree(varp->vval.v_string); - } - varp->vval.v_string = NULL; - break; - case VAR_STRING: - xfree(varp->vval.v_string); - varp->vval.v_string = NULL; - break; - case VAR_LIST: - list_unref(varp->vval.v_list); - varp->vval.v_list = NULL; - break; - case VAR_DICT: - dict_unref(varp->vval.v_dict); - varp->vval.v_dict = NULL; - break; - case VAR_NUMBER: - varp->vval.v_number = 0; - break; - case VAR_FLOAT: - varp->vval.v_float = 0.0; - break; - case VAR_SPECIAL: - varp->vval.v_special = kSpecialVarFalse; - break; - case VAR_UNKNOWN: - break; - } - varp->v_lock = 0; + if (varp != NULL && varp->v_type != VAR_UNKNOWN) { + encode_vim_to_nothing(varp, varp, "clear_tv argument"); } } @@ -19917,11 +20169,15 @@ theend: */ static int eval_fname_script(char_u *p) { - if (p[0] == '<' && (STRNICMP(p + 1, "SID>", 4) == 0 - || STRNICMP(p + 1, "SNR>", 4) == 0)) + // Use mb_stricmp() because in Turkish comparing the "I" may not work with + // the standard library function. + if (p[0] == '<' && (mb_strnicmp(p + 1, (char_u *)"SID>", 4) == 0 + || mb_strnicmp(p + 1, (char_u *)"SNR>", 4) == 0)) { return 5; - if (p[0] == 's' && p[1] == ':') + } + if (p[0] == 's' && p[1] == ':') { return 2; + } return 0; } @@ -20246,9 +20502,10 @@ script_autoload ( tofree = NULL; } - /* Try loading the package from $VIMRUNTIME/autoload/<name>.vim */ - if (source_runtime(scriptname, FALSE) == OK) - ret = TRUE; + // Try loading the package from $VIMRUNTIME/autoload/<name>.vim + if (source_runtime(scriptname, 0) == OK) { + ret = true; + } } xfree(tofree); @@ -21704,7 +21961,8 @@ static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_exit, dict_T *self, bool pty, - bool detach) + bool detach, + char *cwd) { TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); data->stopped = false; @@ -21712,11 +21970,11 @@ 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(loop.events); + data->events = queue_new_child(main_loop.events); if (pty) { - data->proc.pty = pty_process_init(&loop, data); + data->proc.pty = pty_process_init(&main_loop, data); } else { - data->proc.uv = libuv_process_init(&loop, data); + data->proc.uv = libuv_process_init(&main_loop, data); } Process *proc = (Process *)&data->proc; proc->argv = argv; @@ -21728,6 +21986,7 @@ static inline TerminalJobData *common_job_init(char **argv, proc->cb = on_process_exit; proc->events = data->events; proc->detach = detach; + proc->cwd = cwd; return data; } @@ -21814,7 +22073,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(loop.fast_events, free_term_job_data_event, 1, data); + queue_put(main_loop.fast_events, free_term_job_data_event, 1, data); } // vimscript job callbacks must be executed on Nvim main loop @@ -22076,17 +22335,19 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) bool eval_has_provider(char *name) { - -#define check_provider(name) \ - if (has_##name == -1) { \ - has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \ - if (!has_##name) { \ - script_autoload((uint8_t *)"provider#" #name "#Call", false); \ - has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \ - } \ +#define check_provider(name) \ + if (has_##name == -1) { \ + has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \ + if (!has_##name) { \ + script_autoload((uint8_t *)"provider#" #name "#Call", false); \ + has_##name = !!find_func((uint8_t *)"provider#" #name "#Call"); \ + } \ } - static int has_clipboard = -1, has_python = -1, has_python3 = -1; + static int has_clipboard = -1; + static int has_python = -1; + static int has_python3 = -1; + static int has_ruby = -1; if (!strcmp(name, "clipboard")) { check_provider(clipboard); @@ -22097,14 +22358,16 @@ bool eval_has_provider(char *name) } else if (!strcmp(name, "python")) { check_provider(python); return has_python; + } else if (!strcmp(name, "ruby")) { + check_provider(ruby); + return has_ruby; } return false; } -// Compute the `DictWatcher` address from a QUEUE node. This only exists because -// ASAN doesn't handle `QUEUE_DATA` pointer arithmetic, and we blacklist this -// function on .asan-blacklist. +// Compute the `DictWatcher` address from a QUEUE node. This only exists for +// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic). static DictWatcher *dictwatcher_node_data(QUEUE *q) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 0774ef515f..43e9f76c0f 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -101,7 +101,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, FUNC_ATTR_NONNULL_ALL { if (kv_size(*container_stack) == 0) { - kv_push(ValuesStackItem, *stack, obj); + kv_push(*stack, obj); return OK; } ContainerStackItem last_container = kv_last(*container_stack); @@ -190,7 +190,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, *next_map_special = true; return OK; } - kv_push(ValuesStackItem, *stack, obj); + kv_push(*stack, obj); } return OK; } @@ -628,10 +628,8 @@ int json_decode_string(const char *const buf, const size_t buf_len, convert_setup(&conv, (char_u *) "utf-8", p_enc); conv.vc_fail = true; int ret = OK; - ValuesStack stack; - kv_init(stack); - ContainerStack container_stack; - kv_init(container_stack); + ValuesStack stack = KV_INITIAL_VALUE; + ContainerStack container_stack = KV_INITIAL_VALUE; rettv->v_type = VAR_UNKNOWN; bool didcomma = false; bool didcolon = false; @@ -815,13 +813,13 @@ json_decode_string_cycle_start: .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, }; - kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { + kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack), .s = p, .container = tv, .special_val = NULL, })); - kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); + kv_push(stack, OBJ(tv, false, didcomma, didcolon)); break; } case '{': { @@ -845,13 +843,13 @@ json_decode_string_cycle_start: .vval = { .v_dict = dict }, }; } - kv_push(ContainerStackItem, container_stack, ((ContainerStackItem) { + kv_push(container_stack, ((ContainerStackItem) { .stack_index = kv_size(stack), .s = p, .container = tv, .special_val = val_list, })); - kv_push(ValuesStackItem, stack, OBJ(tv, false, didcomma, didcolon)); + kv_push(stack, OBJ(tv, false, didcomma, didcolon)); break; } default: { diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index c651a50be9..670437ceda 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -6,6 +6,7 @@ #include <msgpack.h> #include <inttypes.h> +#include <stddef.h> #include <assert.h> #include <math.h> @@ -22,6 +23,7 @@ #include "nvim/ascii.h" #include "nvim/vim.h" // For _() #include "nvim/lib/kvec.h" +#include "nvim/eval/typval_encode.h" #define ga_concat(a, b) ga_concat(a, (char_u *)b) #define utf_ptr2char(b) utf_ptr2char((char_u *)b) @@ -32,29 +34,6 @@ #define convert_setup(vcp, from, to) \ (convert_setup(vcp, (char_u *)from, (char_u *)to)) -/// Structure representing current VimL to messagepack conversion state -typedef struct { - enum { - kMPConvDict, ///< Convert dict_T *dictionary. - kMPConvList, ///< Convert list_T *list. - kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. - } type; - union { - struct { - dict_T *dict; ///< Currently converted dictionary. - hashitem_T *hi; ///< Currently converted dictionary item. - size_t todo; ///< Amount of items left to process. - } d; ///< State of dictionary conversion. - struct { - list_T *list; ///< Currently converted list. - listitem_T *li; ///< Currently converted list item. - } l; ///< State of list or generic mapping conversion. - } data; ///< Data to convert. -} MPConvStackVal; - -/// Stack used to convert VimL values to messagepack. -typedef kvec_t(MPConvStackVal) MPConvStack; - const char *const encode_special_var_names[] = { [kSpecialVarNull] = "null", [kSpecialVarTrue] = "true", @@ -275,368 +254,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, : OK); } -/// Code for checking whether container references itself -/// -/// @param[in,out] val Container to check. -/// @param copyID_attr Name of the container attribute that holds copyID. -/// After checking whether value of this attribute is -/// copyID (variable) it is set to copyID. -#define CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ - do { \ - if ((val)->copyID_attr == copyID) { \ - CONV_RECURSE((val), conv_type); \ - } \ - (val)->copyID_attr = copyID; \ - } while (0) - -#define TV_STRLEN(tv) \ - (tv->vval.v_string == NULL ? 0 : STRLEN(tv->vval.v_string)) - -/// Define functions which convert VimL value to something else -/// -/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const -/// tv)` which returns OK or FAIL and helper functions. -/// -/// @param firstargtype Type of the first argument. It will be used to return -/// the results. -/// @param firstargname Name of the first argument. -/// @param name Name of the target converter. -#define DEFINE_VIML_CONV_FUNCTIONS(scope, name, firstargtype, firstargname) \ -static int name##_convert_one_value(firstargtype firstargname, \ - MPConvStack *const mpstack, \ - typval_T *const tv, \ - const int copyID, \ - const char *const objname) \ - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - switch (tv->v_type) { \ - case VAR_STRING: { \ - CONV_STRING(tv->vval.v_string, TV_STRLEN(tv)); \ - break; \ - } \ - case VAR_NUMBER: { \ - CONV_NUMBER(tv->vval.v_number); \ - break; \ - } \ - case VAR_FLOAT: { \ - CONV_FLOAT(tv->vval.v_float); \ - break; \ - } \ - case VAR_FUNC: { \ - CONV_FUNC(tv->vval.v_string); \ - break; \ - } \ - case VAR_LIST: { \ - if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ - CONV_EMPTY_LIST(); \ - break; \ - } \ - CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, kMPConvList); \ - CONV_LIST_START(tv->vval.v_list); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvList, \ - .data = { \ - .l = { \ - .list = tv->vval.v_list, \ - .li = tv->vval.v_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case VAR_SPECIAL: { \ - switch (tv->vval.v_special) { \ - case kSpecialVarNull: { \ - CONV_NIL(); \ - break; \ - } \ - case kSpecialVarTrue: \ - case kSpecialVarFalse: { \ - CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \ - break; \ - } \ - } \ - break; \ - } \ - case VAR_DICT: { \ - if (tv->vval.v_dict == NULL \ - || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ - CONV_EMPTY_DICT(); \ - break; \ - } \ - const dictitem_T *type_di; \ - const dictitem_T *val_di; \ - if (CONV_ALLOW_SPECIAL \ - && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ - && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ - (char_u *) "_TYPE", -1)) != NULL \ - && type_di->di_tv.v_type == VAR_LIST \ - && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ - (char_u *) "_VAL", -1)) != NULL) { \ - size_t i; \ - for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \ - if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \ - break; \ - } \ - } \ - if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - switch ((MessagePackType) i) { \ - case kMPNil: { \ - CONV_NIL(); \ - break; \ - } \ - case kMPBoolean: { \ - if (val_di->di_tv.v_type != VAR_NUMBER) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_BOOL(val_di->di_tv.vval.v_number); \ - break; \ - } \ - case kMPInteger: { \ - const list_T *val_list; \ - varnumber_T sign; \ - varnumber_T highest_bits; \ - varnumber_T high_bits; \ - varnumber_T low_bits; \ - /* List of 4 integers; first is signed (should be 1 or -1, but */ \ - /* this is not checked), second is unsigned and have at most */ \ - /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ - /* bits is not checked), other unsigned and have at most 31 */ \ - /* non-zero bits (number of bits is not checked).*/ \ - if (val_di->di_tv.v_type != VAR_LIST \ - || (val_list = val_di->di_tv.vval.v_list) == NULL \ - || val_list->lv_len != 4 \ - || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ - || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ - || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ - || (highest_bits = \ - val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ - || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ - || (high_bits = \ - val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ - || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ - || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ - | (uint64_t) (((uint64_t) high_bits) << 31) \ - | (uint64_t) low_bits); \ - if (sign > 0) { \ - CONV_UNSIGNED_NUMBER(number); \ - } else { \ - CONV_NUMBER(-number); \ - } \ - break; \ - } \ - case kMPFloat: { \ - if (val_di->di_tv.v_type != VAR_FLOAT) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_FLOAT(val_di->di_tv.vval.v_float); \ - break; \ - } \ - case kMPString: \ - case kMPBinary: { \ - const bool is_string = ((MessagePackType) i == kMPString); \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - size_t len; \ - char *buf; \ - if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \ - &buf)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - if (is_string) { \ - CONV_STR_STRING(buf, len); \ - } else { \ - CONV_STRING(buf, len); \ - } \ - xfree(buf); \ - break; \ - } \ - case kMPArray: { \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, \ - kMPConvList); \ - CONV_LIST_START(val_di->di_tv.vval.v_list); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvList, \ - .data = { \ - .l = { \ - .list = val_di->di_tv.vval.v_list, \ - .li = val_di->di_tv.vval.v_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case kMPMap: { \ - if (val_di->di_tv.v_type != VAR_LIST) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - list_T *const val_list = val_di->di_tv.vval.v_list; \ - if (val_list == NULL || val_list->lv_len == 0) { \ - CONV_EMPTY_DICT(); \ - break; \ - } \ - for (const listitem_T *li = val_list->lv_first; li != NULL; \ - li = li->li_next) { \ - if (li->li_tv.v_type != VAR_LIST \ - || li->li_tv.vval.v_list->lv_len != 2) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - } \ - CHECK_SELF_REFERENCE(val_list, lv_copyID, kMPConvPairs); \ - CONV_DICT_START(val_list->lv_len); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvPairs, \ - .data = { \ - .l = { \ - .list = val_list, \ - .li = val_list->lv_first, \ - }, \ - }, \ - })); \ - break; \ - } \ - case kMPExt: { \ - const list_T *val_list; \ - varnumber_T type; \ - if (val_di->di_tv.v_type != VAR_LIST \ - || (val_list = val_di->di_tv.vval.v_list) == NULL \ - || val_list->lv_len != 2 \ - || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ - || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ - || type < INT8_MIN \ - || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - size_t len; \ - char *buf; \ - if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ - &len, &buf)) { \ - goto name##_convert_one_value_regular_dict; \ - } \ - CONV_EXT_STRING(buf, len, type); \ - xfree(buf); \ - break; \ - } \ - } \ - break; \ - } \ -name##_convert_one_value_regular_dict: \ - CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, kMPConvDict); \ - CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \ - kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { \ - .type = kMPConvDict, \ - .data = { \ - .d = { \ - .dict = tv->vval.v_dict, \ - .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ - .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ - }, \ - }, \ - })); \ - break; \ - } \ - case VAR_UNKNOWN: { \ - EMSG2(_(e_intern2), #name "_convert_one_value()"); \ - return FAIL; \ - } \ - } \ - return OK; \ -} \ -\ -scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \ - const char *const objname) \ - FUNC_ATTR_WARN_UNUSED_RESULT \ -{ \ - const int copyID = get_copyID(); \ - MPConvStack mpstack; \ - kv_init(mpstack); \ - if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ - == FAIL) { \ - goto encode_vim_to_##name##_error_ret; \ - } \ - while (kv_size(mpstack)) { \ - MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); \ - typval_T *cur_tv = NULL; \ - switch (cur_mpsv->type) { \ - case kMPConvDict: { \ - if (!cur_mpsv->data.d.todo) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ - CONV_DICT_END(); \ - continue; \ - } else if (cur_mpsv->data.d.todo \ - != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ - CONV_DICT_BETWEEN_ITEMS(); \ - } \ - while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ - cur_mpsv->data.d.hi++; \ - } \ - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ - cur_mpsv->data.d.todo--; \ - cur_mpsv->data.d.hi++; \ - CONV_STR_STRING(&di->di_key[0], STRLEN(&di->di_key[0])); \ - CONV_DICT_AFTER_KEY(); \ - cur_tv = &di->di_tv; \ - break; \ - } \ - case kMPConvList: { \ - if (cur_mpsv->data.l.li == NULL) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ - CONV_LIST_END(cur_mpsv->data.l.list); \ - continue; \ - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ - CONV_LIST_BETWEEN_ITEMS(); \ - } \ - cur_tv = &cur_mpsv->data.l.li->li_tv; \ - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ - break; \ - } \ - case kMPConvPairs: { \ - if (cur_mpsv->data.l.li == NULL) { \ - (void) kv_pop(mpstack); \ - cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ - CONV_DICT_END(); \ - continue; \ - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ - CONV_DICT_BETWEEN_ITEMS(); \ - } \ - const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ - CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair); \ - if (name##_convert_one_value(firstargname, &mpstack, \ - &kv_pair->lv_first->li_tv, copyID, \ - objname) == FAIL) { \ - goto encode_vim_to_##name##_error_ret; \ - } \ - CONV_DICT_AFTER_KEY(); \ - cur_tv = &kv_pair->lv_last->li_tv; \ - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ - break; \ - } \ - } \ - assert(cur_tv != NULL); \ - if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ - objname) == FAIL) { \ - goto encode_vim_to_##name##_error_ret; \ - } \ - } \ - kv_destroy(mpstack); \ - return OK; \ -encode_vim_to_##name##_error_ret: \ - kv_destroy(mpstack); \ - return FAIL; \ -} - -#define CONV_STRING(buf, len) \ +#define TYPVAL_ENCODE_CONV_STRING(buf, len) \ do { \ const char *const buf_ = (const char *) buf; \ if (buf == NULL) { \ @@ -655,19 +273,19 @@ encode_vim_to_##name##_error_ret: \ } \ } while (0) -#define CONV_STR_STRING(buf, len) \ - CONV_STRING(buf, len) +#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \ + TYPVAL_ENCODE_CONV_STRING(buf, len) -#define CONV_EXT_STRING(buf, len, type) +#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) -#define CONV_NUMBER(num) \ +#define TYPVAL_ENCODE_CONV_NUMBER(num) \ do { \ char numbuf[NUMBUFLEN]; \ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \ ga_concat(gap, numbuf); \ } while (0) -#define CONV_FLOAT(flt) \ +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \ do { \ const float_T flt_ = (flt); \ switch (fpclassify(flt_)) { \ @@ -690,51 +308,51 @@ encode_vim_to_##name##_error_ret: \ } \ } while (0) -#define CONV_FUNC(fun) \ +#define TYPVAL_ENCODE_CONV_FUNC(fun) \ do { \ ga_concat(gap, "function("); \ - CONV_STRING(fun, STRLEN(fun)); \ + TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \ ga_append(gap, ')'); \ } while (0) -#define CONV_EMPTY_LIST() \ +#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ ga_concat(gap, "[]") -#define CONV_LIST_START(lst) \ +#define TYPVAL_ENCODE_CONV_LIST_START(len) \ ga_append(gap, '[') -#define CONV_EMPTY_DICT() \ +#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \ ga_concat(gap, "{}") -#define CONV_NIL() \ +#define TYPVAL_ENCODE_CONV_NIL() \ ga_concat(gap, "v:null") -#define CONV_BOOL(num) \ +#define TYPVAL_ENCODE_CONV_BOOL(num) \ ga_concat(gap, ((num)? "v:true": "v:false")) -#define CONV_UNSIGNED_NUMBER(num) +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) -#define CONV_DICT_START(len) \ +#define TYPVAL_ENCODE_CONV_DICT_START(len) \ ga_append(gap, '{') -#define CONV_DICT_END() \ +#define TYPVAL_ENCODE_CONV_DICT_END() \ ga_append(gap, '}') -#define CONV_DICT_AFTER_KEY() \ +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \ ga_concat(gap, ": ") -#define CONV_DICT_BETWEEN_ITEMS() \ +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \ ga_concat(gap, ", ") -#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) -#define CONV_LIST_END(lst) \ +#define TYPVAL_ENCODE_CONV_LIST_END() \ ga_append(gap, ']') -#define CONV_LIST_BETWEEN_ITEMS() \ - CONV_DICT_BETWEEN_ITEMS() +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \ + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() -#define CONV_RECURSE(val, conv_type) \ +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ do { \ if (!did_echo_string_emsg) { \ /* Only give this message once for a recursive call to avoid */ \ @@ -761,15 +379,14 @@ encode_vim_to_##name##_error_ret: \ } \ vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \ ga_concat(gap, &ebuf[0]); \ - return OK; \ } while (0) -#define CONV_ALLOW_SPECIAL false +#define TYPVAL_ENCODE_ALLOW_SPECIALS false -DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap) -#undef CONV_RECURSE -#define CONV_RECURSE(val, conv_type) \ +#undef TYPVAL_ENCODE_CONV_RECURSE +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ do { \ char ebuf[NUMBUFLEN + 7]; \ size_t backref = 0; \ @@ -796,10 +413,10 @@ DEFINE_VIML_CONV_FUNCTIONS(static, string, garray_T *const, gap) return OK; \ } while (0) -DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap) +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap) -#undef CONV_RECURSE -#define CONV_RECURSE(val, conv_type) \ +#undef TYPVAL_ENCODE_CONV_RECURSE +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ do { \ if (!did_echo_string_emsg) { \ /* Only give this message once for a recursive call to avoid */ \ @@ -808,30 +425,29 @@ DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap) EMSG(_("E724: unable to correctly dump variable " \ "with self-referencing container")); \ } \ - return OK; \ } while (0) -#undef CONV_ALLOW_SPECIAL -#define CONV_ALLOW_SPECIAL true +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#define TYPVAL_ENCODE_ALLOW_SPECIALS true -#undef CONV_NIL -#define CONV_NIL() \ +#undef TYPVAL_ENCODE_CONV_NIL +#define TYPVAL_ENCODE_CONV_NIL() \ ga_concat(gap, "null") -#undef CONV_BOOL -#define CONV_BOOL(num) \ +#undef TYPVAL_ENCODE_CONV_BOOL +#define TYPVAL_ENCODE_CONV_BOOL(num) \ ga_concat(gap, ((num)? "true": "false")) -#undef CONV_UNSIGNED_NUMBER -#define CONV_UNSIGNED_NUMBER(num) \ +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \ do { \ char numbuf[NUMBUFLEN]; \ vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \ ga_concat(gap, numbuf); \ } while (0) -#undef CONV_FLOAT -#define CONV_FLOAT(flt) \ +#undef TYPVAL_ENCODE_CONV_FLOAT +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \ do { \ const float_T flt_ = (flt); \ switch (fpclassify(flt_)) { \ @@ -1019,24 +635,24 @@ static inline int convert_to_json_string(garray_T *const gap, return OK; } -#undef CONV_STRING -#define CONV_STRING(buf, len) \ +#undef TYPVAL_ENCODE_CONV_STRING +#define TYPVAL_ENCODE_CONV_STRING(buf, len) \ do { \ if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \ return FAIL; \ } \ } while (0) -#undef CONV_EXT_STRING -#define CONV_EXT_STRING(buf, len, type) \ +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \ do { \ xfree(buf); \ EMSG(_("E474: Unable to convert EXT string to JSON")); \ return FAIL; \ } while (0) -#undef CONV_FUNC -#define CONV_FUNC(fun) \ +#undef TYPVAL_ENCODE_CONV_FUNC +#define TYPVAL_ENCODE_CONV_FUNC(fun) \ return conv_error(_("E474: Error while dumping %s, %s: " \ "attempt to dump function reference"), \ mpstack, objname) @@ -1044,9 +660,8 @@ static inline int convert_to_json_string(garray_T *const gap, /// Check whether given key can be used in json_encode() /// /// @param[in] tv Key to check. -static inline bool check_json_key(const typval_T *const tv) +bool encode_check_json_key(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE - FUNC_ATTR_ALWAYS_INLINE { if (tv->v_type == VAR_STRING) { return true; @@ -1080,38 +695,38 @@ static inline bool check_json_key(const typval_T *const tv) return true; } -#undef CONV_SPECIAL_DICT_KEY_CHECK -#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) \ +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) \ do { \ - if (!check_json_key(&kv_pair->lv_first->li_tv)) { \ + if (!encode_check_json_key(&key)) { \ EMSG(_("E474: Invalid key in special dictionary")); \ - goto encode_vim_to_##name##_error_ret; \ + goto label; \ } \ } while (0) -DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_NIL -#undef CONV_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_SPECIAL_DICT_KEY_CHECK -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap) + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS /// Return a string with the string representation of a variable. /// Puts quotes around strings, so that they can be parsed back by eval(). @@ -1181,7 +796,7 @@ char *encode_tv2json(typval_T *tv, size_t *len) return (char *) ga.ga_data; } -#define CONV_STRING(buf, len) \ +#define TYPVAL_ENCODE_CONV_STRING(buf, len) \ do { \ if (buf == NULL) { \ msgpack_pack_bin(packer, 0); \ @@ -1192,7 +807,7 @@ char *encode_tv2json(typval_T *tv, size_t *len) } \ } while (0) -#define CONV_STR_STRING(buf, len) \ +#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \ do { \ if (buf == NULL) { \ msgpack_pack_str(packer, 0); \ @@ -1203,7 +818,7 @@ char *encode_tv2json(typval_T *tv, size_t *len) } \ } while (0) -#define CONV_EXT_STRING(buf, len, type) \ +#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \ do { \ if (buf == NULL) { \ msgpack_pack_ext(packer, 0, (int8_t) type); \ @@ -1214,30 +829,30 @@ char *encode_tv2json(typval_T *tv, size_t *len) } \ } while (0) -#define CONV_NUMBER(num) \ +#define TYPVAL_ENCODE_CONV_NUMBER(num) \ msgpack_pack_int64(packer, (int64_t) (num)) -#define CONV_FLOAT(flt) \ +#define TYPVAL_ENCODE_CONV_FLOAT(flt) \ msgpack_pack_double(packer, (double) (flt)) -#define CONV_FUNC(fun) \ +#define TYPVAL_ENCODE_CONV_FUNC(fun) \ return conv_error(_("E951: Error while dumping %s, %s: " \ "attempt to dump function reference"), \ mpstack, objname) -#define CONV_EMPTY_LIST() \ +#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ msgpack_pack_array(packer, 0) -#define CONV_LIST_START(lst) \ - msgpack_pack_array(packer, (size_t) (lst)->lv_len) +#define TYPVAL_ENCODE_CONV_LIST_START(len) \ + msgpack_pack_array(packer, (size_t) (len)) -#define CONV_EMPTY_DICT() \ +#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \ msgpack_pack_map(packer, 0) -#define CONV_NIL() \ +#define TYPVAL_ENCODE_CONV_NIL() \ msgpack_pack_nil(packer) -#define CONV_BOOL(num) \ +#define TYPVAL_ENCODE_CONV_BOOL(num) \ do { \ if ((num)) { \ msgpack_pack_true(packer); \ @@ -1246,51 +861,51 @@ char *encode_tv2json(typval_T *tv, size_t *len) } \ } while (0) -#define CONV_UNSIGNED_NUMBER(num) \ +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \ msgpack_pack_uint64(packer, (num)) -#define CONV_DICT_START(len) \ +#define TYPVAL_ENCODE_CONV_DICT_START(len) \ msgpack_pack_map(packer, (size_t) (len)) -#define CONV_DICT_END() +#define TYPVAL_ENCODE_CONV_DICT_END() -#define CONV_DICT_AFTER_KEY() +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() -#define CONV_DICT_BETWEEN_ITEMS() +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() -#define CONV_SPECIAL_DICT_KEY_CHECK(name, kv_pair) +#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) -#define CONV_LIST_END(lst) +#define TYPVAL_ENCODE_CONV_LIST_END() -#define CONV_LIST_BETWEEN_ITEMS() +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() -#define CONV_RECURSE(val, conv_type) \ +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ return conv_error(_("E952: Unable to dump %s: " \ "container references itself in %s"), \ mpstack, objname) -#define CONV_ALLOW_SPECIAL true - -DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) - -#undef CONV_STRING -#undef CONV_STR_STRING -#undef CONV_EXT_STRING -#undef CONV_NUMBER -#undef CONV_FLOAT -#undef CONV_FUNC -#undef CONV_EMPTY_LIST -#undef CONV_LIST_START -#undef CONV_EMPTY_DICT -#undef CONV_NIL -#undef CONV_BOOL -#undef CONV_UNSIGNED_NUMBER -#undef CONV_DICT_START -#undef CONV_DICT_END -#undef CONV_DICT_AFTER_KEY -#undef CONV_DICT_BETWEEN_ITEMS -#undef CONV_SPECIAL_DICT_KEY_CHECK -#undef CONV_LIST_END -#undef CONV_LIST_BETWEEN_ITEMS -#undef CONV_RECURSE -#undef CONV_ALLOW_SPECIAL +#define TYPVAL_ENCODE_ALLOW_SPECIALS true + +TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer) + +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_FUNC +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h new file mode 100644 index 0000000000..98fa7b26c6 --- /dev/null +++ b/src/nvim/eval/typval_encode.h @@ -0,0 +1,570 @@ +/// @file eval/typval_convert.h +/// +/// Contains set of macros used to convert (possibly recursive) typval_T into +/// something else. For these macros to work the following macros must be +/// defined: + +/// @def TYPVAL_ENCODE_CONV_NIL +/// @brief Macros used to convert NIL value +/// +/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS +/// is false) and `v:null`. Accepts no arguments, but still must be +/// a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_BOOL +/// @brief Macros used to convert boolean value +/// +/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS +/// is false) and `v:true`/`v:false`. +/// +/// @param num Boolean value to convert. Value is an expression which +/// evaluates to some integer. + +/// @def TYPVAL_ENCODE_CONV_NUMBER +/// @brief Macros used to convert integer +/// +/// @param num Integer to convert, must accept both varnumber_T and int64_t. + +/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +/// @brief Macros used to convert unsigned integer +/// +/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be +/// defined. +/// +/// @param num Integer to convert, must accept uint64_t. + +/// @def TYPVAL_ENCODE_CONV_FLOAT +/// @brief Macros used to convert floating-point number +/// +/// @param flt Number to convert, must accept float_T. + +/// @def TYPVAL_ENCODE_CONV_STRING +/// @brief Macros used to convert plain string +/// +/// Is used to convert VAR_STRING objects as well as BIN strings represented as +/// special dictionary. +/// +/// @param buf String to convert. Is a char[] buffer, not NUL-terminated. +/// @param len String length. + +/// @def TYPVAL_ENCODE_CONV_STR_STRING +/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings +/// +/// Is used to convert dictionary keys and STR strings represented as special +/// dictionaries. + +/// @def TYPVAL_ENCODE_CONV_EXT_STRING +/// @brief Macros used to convert EXT string +/// +/// Is used to convert EXT strings represented as special dictionaries. Never +/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be +/// defined. +/// +/// @param buf String to convert. Is a char[] buffer, not NUL-terminated. +/// @param len String length. +/// @param type EXT type. + +/// @def TYPVAL_ENCODE_CONV_FUNC +/// @brief Macros used to convert a function reference +/// +/// @param fun Function name. + +/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST +/// @brief Macros used to convert an empty list +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT +/// @brief Macros used to convert an empty dictionary +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_LIST_START +/// @brief Macros used before starting to convert non-empty list +/// +/// @param len List length. Is an expression which evaluates to an integer. + +/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +/// @brief Macros used after finishing converting non-last list item +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_LIST_END +/// @brief Macros used after converting non-empty list +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_DICT_START +/// @brief Macros used before starting to convert non-empty dictionary +/// +/// @param len Dictionary length. Is an expression which evaluates to an +/// integer. + +/// @def TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK +/// @brief Macros used to check special dictionary key +/// +/// @param label Label for goto in case check was not successfull. +/// @param key typval_T key to check. + +/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +/// @brief Macros used after finishing converting dictionary key +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +/// @brief Macros used after finishing converting non-last dictionary value +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_DICT_END +/// @brief Macros used after converting non-empty dictionary +/// +/// Accepts no arguments, but still must be a function-like macros. + +/// @def TYPVAL_ENCODE_CONV_RECURSE +/// @brief Macros used when self-containing container is detected +/// +/// @param val Container for which this situation was detected. +/// @param conv_type Type of the stack entry, @see MPConvStackValType. + +/// @def TYPVAL_ENCODE_ALLOW_SPECIALS +/// @brief Macros that specifies whether special dictionaries are special +/// +/// Must be something that evaluates to boolean, most likely `true` or `false`. +/// If it is false then special dictionaries are not treated specially. +#ifndef NVIM_EVAL_TYPVAL_ENCODE_H +#define NVIM_EVAL_TYPVAL_ENCODE_H + +#include <stddef.h> +#include <inttypes.h> +#include <assert.h> + +#include "nvim/lib/kvec.h" +#include "nvim/eval_defs.h" +#include "nvim/eval/encode.h" +#include "nvim/func_attr.h" + +/// Type of the stack entry +typedef enum { + kMPConvDict, ///< Convert dict_T *dictionary. + kMPConvList, ///< Convert list_T *list. + kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. +} MPConvStackValType; + +/// Structure representing current VimL to messagepack conversion state +typedef struct { + MPConvStackValType type; ///< Type of the stack entry. + typval_T *tv; ///< Currently converted typval_T. + union { + struct { + dict_T *dict; ///< Currently converted dictionary. + hashitem_T *hi; ///< Currently converted dictionary item. + size_t todo; ///< Amount of items left to process. + } d; ///< State of dictionary conversion. + struct { + list_T *list; ///< Currently converted list. + listitem_T *li; ///< Currently converted list item. + } l; ///< State of list or generic mapping conversion. + } data; ///< Data to convert. +} MPConvStackVal; + +/// Stack used to convert VimL values to messagepack. +typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack; + +// Defines for MPConvStack +#define _mp_size kv_size +#define _mp_init kvi_init +#define _mp_destroy kvi_destroy +#define _mp_push kvi_push +#define _mp_pop kv_pop +#define _mp_last kv_last + +/// Code for checking whether container references itself +/// +/// @param[in,out] val Container to check. +/// @param copyID_attr Name of the container attribute that holds copyID. +/// After checking whether value of this attribute is +/// copyID (variable) it is set to copyID. +/// @param conv_type Type of the conversion, @see MPConvStackValType. +#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \ + do { \ + if ((val)->copyID_attr == copyID) { \ + TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \ + return OK; \ + } \ + (val)->copyID_attr = copyID; \ + } while (0) + +/// Length of the string stored in typval_T +/// +/// @param[in] tv String for which to compute length for. Must be typval_T +/// with VAR_STRING. +/// +/// @return Length of the string stored in typval_T, including 0 for NULL +/// string. +static inline size_t tv_strlen(const typval_T *const tv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ALL +{ + assert(tv->v_type == VAR_STRING); + return (tv->vval.v_string == NULL + ? 0 + : strlen((char *) tv->vval.v_string)); +} + +/// Define functions which convert VimL value to something else +/// +/// Creates function `vim_to_{name}(firstargtype firstargname, typval_T *const +/// tv)` which returns OK or FAIL and helper functions. +/// +/// @param scope Scope of the main function: either nothing or `static`. +/// @param name Name of the target converter. +/// @param firstargtype Type of the first argument. It will be used to return +/// the results. +/// @param firstargname Name of the first argument. +#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \ + firstargname) \ +static int name##_convert_one_value(firstargtype firstargname, \ + MPConvStack *const mpstack, \ + typval_T *const tv, \ + const int copyID, \ + const char *const objname) \ + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + switch (tv->v_type) { \ + case VAR_STRING: { \ + TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \ + break; \ + } \ + case VAR_NUMBER: { \ + TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \ + break; \ + } \ + case VAR_FLOAT: { \ + TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \ + break; \ + } \ + case VAR_FUNC: { \ + TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \ + break; \ + } \ + case VAR_LIST: { \ + if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \ + TYPVAL_ENCODE_CONV_EMPTY_LIST(); \ + break; \ + } \ + _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \ + kMPConvList); \ + TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \ + _mp_push(*mpstack, ((MPConvStackVal) { \ + .type = kMPConvList, \ + .tv = tv, \ + .data = { \ + .l = { \ + .list = tv->vval.v_list, \ + .li = tv->vval.v_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case VAR_SPECIAL: { \ + switch (tv->vval.v_special) { \ + case kSpecialVarNull: { \ + TYPVAL_ENCODE_CONV_NIL(); \ + break; \ + } \ + case kSpecialVarTrue: \ + case kSpecialVarFalse: { \ + TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \ + break; \ + } \ + } \ + break; \ + } \ + case VAR_DICT: { \ + if (tv->vval.v_dict == NULL \ + || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \ + TYPVAL_ENCODE_CONV_EMPTY_DICT(); \ + break; \ + } \ + const dictitem_T *type_di; \ + const dictitem_T *val_di; \ + if (TYPVAL_ENCODE_ALLOW_SPECIALS \ + && tv->vval.v_dict->dv_hashtab.ht_used == 2 \ + && (type_di = dict_find((dict_T *) tv->vval.v_dict, \ + (char_u *) "_TYPE", -1)) != NULL \ + && type_di->di_tv.v_type == VAR_LIST \ + && (val_di = dict_find((dict_T *) tv->vval.v_dict, \ + (char_u *) "_VAL", -1)) != NULL) { \ + size_t i; \ + for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \ + if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \ + break; \ + } \ + } \ + if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + switch ((MessagePackType) i) { \ + case kMPNil: { \ + TYPVAL_ENCODE_CONV_NIL(); \ + break; \ + } \ + case kMPBoolean: { \ + if (val_di->di_tv.v_type != VAR_NUMBER) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number); \ + break; \ + } \ + case kMPInteger: { \ + const list_T *val_list; \ + varnumber_T sign; \ + varnumber_T highest_bits; \ + varnumber_T high_bits; \ + varnumber_T low_bits; \ + /* List of 4 integers; first is signed (should be 1 or -1, but */ \ + /* this is not checked), second is unsigned and have at most */ \ + /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \ + /* bits is not checked), other unsigned and have at most 31 */ \ + /* non-zero bits (number of bits is not checked).*/ \ + if (val_di->di_tv.v_type != VAR_LIST \ + || (val_list = val_di->di_tv.vval.v_list) == NULL \ + || val_list->lv_len != 4 \ + || val_list->lv_first->li_tv.v_type != VAR_NUMBER \ + || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \ + || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \ + || (highest_bits = \ + val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \ + || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \ + || (high_bits = \ + val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \ + || val_list->lv_last->li_tv.v_type != VAR_NUMBER \ + || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \ + | (uint64_t) (((uint64_t) high_bits) << 31) \ + | (uint64_t) low_bits); \ + if (sign > 0) { \ + TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number); \ + } else { \ + TYPVAL_ENCODE_CONV_NUMBER(-number); \ + } \ + break; \ + } \ + case kMPFloat: { \ + if (val_di->di_tv.v_type != VAR_FLOAT) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float); \ + break; \ + } \ + case kMPString: \ + case kMPBinary: { \ + const bool is_string = ((MessagePackType) i == kMPString); \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + size_t len; \ + char *buf; \ + if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \ + &buf)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + if (is_string) { \ + TYPVAL_ENCODE_CONV_STR_STRING(buf, len); \ + } else { \ + TYPVAL_ENCODE_CONV_STRING(buf, len); \ + } \ + xfree(buf); \ + break; \ + } \ + case kMPArray: { \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \ + lv_copyID, kMPConvList); \ + TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \ + _mp_push(*mpstack, ((MPConvStackVal) { \ + .tv = tv, \ + .type = kMPConvList, \ + .data = { \ + .l = { \ + .list = val_di->di_tv.vval.v_list, \ + .li = val_di->di_tv.vval.v_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case kMPMap: { \ + if (val_di->di_tv.v_type != VAR_LIST) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + list_T *const val_list = val_di->di_tv.vval.v_list; \ + if (val_list == NULL || val_list->lv_len == 0) { \ + TYPVAL_ENCODE_CONV_EMPTY_DICT(); \ + break; \ + } \ + for (const listitem_T *li = val_list->lv_first; li != NULL; \ + li = li->li_next) { \ + if (li->li_tv.v_type != VAR_LIST \ + || li->li_tv.vval.v_list->lv_len != 2) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + } \ + _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \ + kMPConvPairs); \ + TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \ + _mp_push(*mpstack, ((MPConvStackVal) { \ + .tv = tv, \ + .type = kMPConvPairs, \ + .data = { \ + .l = { \ + .list = val_list, \ + .li = val_list->lv_first, \ + }, \ + }, \ + })); \ + break; \ + } \ + case kMPExt: { \ + const list_T *val_list; \ + varnumber_T type; \ + if (val_di->di_tv.v_type != VAR_LIST \ + || (val_list = val_di->di_tv.vval.v_list) == NULL \ + || val_list->lv_len != 2 \ + || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \ + || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \ + || type < INT8_MIN \ + || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + size_t len; \ + char *buf; \ + if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \ + &len, &buf)) { \ + goto name##_convert_one_value_regular_dict; \ + } \ + TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \ + xfree(buf); \ + break; \ + } \ + } \ + break; \ + } \ +name##_convert_one_value_regular_dict: \ + _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \ + kMPConvDict); \ + TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \ + _mp_push(*mpstack, ((MPConvStackVal) { \ + .tv = tv, \ + .type = kMPConvDict, \ + .data = { \ + .d = { \ + .dict = tv->vval.v_dict, \ + .hi = tv->vval.v_dict->dv_hashtab.ht_array, \ + .todo = tv->vval.v_dict->dv_hashtab.ht_used, \ + }, \ + }, \ + })); \ + break; \ + } \ + case VAR_UNKNOWN: { \ + EMSG2(_(e_intern2), #name "_convert_one_value()"); \ + return FAIL; \ + } \ + } \ + return OK; \ +} \ +\ +scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \ + const char *const objname) \ + FUNC_ATTR_WARN_UNUSED_RESULT \ +{ \ + const int copyID = get_copyID(); \ + MPConvStack mpstack; \ + _mp_init(mpstack); \ + if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \ + == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + while (_mp_size(mpstack)) { \ + MPConvStackVal *cur_mpsv = &_mp_last(mpstack); \ + typval_T *cur_tv = NULL; \ + switch (cur_mpsv->type) { \ + case kMPConvDict: { \ + if (!cur_mpsv->data.d.todo) { \ + (void) _mp_pop(mpstack); \ + cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \ + TYPVAL_ENCODE_CONV_DICT_END(); \ + continue; \ + } else if (cur_mpsv->data.d.todo \ + != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \ + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \ + } \ + while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \ + cur_mpsv->data.d.hi++; \ + } \ + dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \ + cur_mpsv->data.d.todo--; \ + cur_mpsv->data.d.hi++; \ + TYPVAL_ENCODE_CONV_STR_STRING(&di->di_key[0], \ + strlen((char *) &di->di_key[0])); \ + TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \ + cur_tv = &di->di_tv; \ + break; \ + } \ + case kMPConvList: { \ + if (cur_mpsv->data.l.li == NULL) { \ + (void) _mp_pop(mpstack); \ + cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ + TYPVAL_ENCODE_CONV_LIST_END(); \ + continue; \ + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ + TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \ + } \ + cur_tv = &cur_mpsv->data.l.li->li_tv; \ + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ + break; \ + } \ + case kMPConvPairs: { \ + if (cur_mpsv->data.l.li == NULL) { \ + (void) _mp_pop(mpstack); \ + cur_mpsv->data.l.list->lv_copyID = copyID - 1; \ + TYPVAL_ENCODE_CONV_DICT_END(); \ + continue; \ + } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \ + TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \ + } \ + const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \ + TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK( \ + encode_vim_to_##name##_error_ret, kv_pair->lv_first->li_tv); \ + if (name##_convert_one_value(firstargname, &mpstack, \ + &kv_pair->lv_first->li_tv, copyID, \ + objname) == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \ + cur_tv = &kv_pair->lv_last->li_tv; \ + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ + break; \ + } \ + } \ + assert(cur_tv != NULL); \ + if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ + objname) == FAIL) { \ + goto encode_vim_to_##name##_error_ret; \ + } \ + } \ + _mp_destroy(mpstack); \ + return OK; \ +encode_vim_to_##name##_error_ret: \ + _mp_destroy(mpstack); \ + return FAIL; \ +} + +#endif // NVIM_EVAL_TYPVAL_ENCODE_H diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 8ffc0c98ce..d5c9b2c1ec 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -155,6 +155,10 @@ typedef struct list_stack_S { /// Convert a hashitem key pointer to a dictitem pointer #define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) +/// Convert a hashitem value pointer to a dictitem pointer +#define HIVAL2DI(p) \ + ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) + /// Convert a hashitem pointer to a dictitem pointer #define HI2DI(hi) HIKEY2DI((hi)->hi_key) diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h index b802866a3d..e5335d9f25 100644 --- a/src/nvim/event/defs.h +++ b/src/nvim/event/defs.h @@ -14,19 +14,19 @@ typedef struct message { } Event; typedef void(*event_scheduler)(Event event, void *data); -#define VA_EVENT_INIT(event, p, h, a) \ - do { \ - assert(a <= EVENT_HANDLER_MAX_ARGC); \ - (event)->priority = p; \ - (event)->handler = h; \ - if (a) { \ - va_list args; \ - va_start(args, a); \ - for (int i = 0; i < a; i++) { \ - (event)->argv[i] = va_arg(args, void *); \ - } \ - va_end(args); \ - } \ +#define VA_EVENT_INIT(event, p, h, a) \ + do { \ + assert(a <= EVENT_HANDLER_MAX_ARGC); \ + (event)->priority = p; \ + (event)->handler = h; \ + if (a) { \ + va_list args; \ + va_start(args, a); \ + for (int i = 0; i < a; i++) { \ + (event)->argv[i] = va_arg(args, void *); \ + } \ + va_end(args); \ + } \ } while (0) static inline Event event_create(int priority, argv_callback cb, int argc, ...) diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index 9ef3468284..a68badcc8f 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -25,7 +25,7 @@ bool libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvopts.flags |= UV_PROCESS_DETACHED; } uvproc->uvopts.exit_cb = exit_cb; - uvproc->uvopts.cwd = NULL; + uvproc->uvopts.cwd = proc->cwd; uvproc->uvopts.env = NULL; uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio_count = 3; diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h index 0c1fcb5ed9..407aa4245f 100644 --- a/src/nvim/event/loop.h +++ b/src/nvim/event/loop.h @@ -26,43 +26,43 @@ typedef struct loop { int recursive; } Loop; -#define CREATE_EVENT(queue, handler, argc, ...) \ - do { \ - if (queue) { \ - queue_put((queue), (handler), argc, __VA_ARGS__); \ - } else { \ - void *argv[argc] = {__VA_ARGS__}; \ - (handler)(argv); \ - } \ +#define CREATE_EVENT(queue, handler, argc, ...) \ + do { \ + if (queue) { \ + queue_put((queue), (handler), argc, __VA_ARGS__); \ + } else { \ + void *argv[argc] = { __VA_ARGS__ }; \ + (handler)(argv); \ + } \ } while (0) // Poll for events until a condition or timeout -#define LOOP_PROCESS_EVENTS_UNTIL(loop, queue, timeout, condition) \ - do { \ - int remaining = timeout; \ - uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ - while (!(condition)) { \ - LOOP_PROCESS_EVENTS(loop, queue, remaining); \ - if (remaining == 0) { \ - break; \ - } else if (remaining > 0) { \ - uint64_t now = os_hrtime(); \ - remaining -= (int) ((now - before) / 1000000); \ - before = now; \ - if (remaining <= 0) { \ - break; \ - } \ - } \ - } \ +#define LOOP_PROCESS_EVENTS_UNTIL(loop, queue, timeout, condition) \ + do { \ + int remaining = timeout; \ + uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ + while (!(condition)) { \ + LOOP_PROCESS_EVENTS(loop, queue, remaining); \ + if (remaining == 0) { \ + break; \ + } else if (remaining > 0) { \ + uint64_t now = os_hrtime(); \ + remaining -= (int) ((now - before) / 1000000); \ + before = now; \ + if (remaining <= 0) { \ + break; \ + } \ + } \ + } \ } while (0) -#define LOOP_PROCESS_EVENTS(loop, queue, timeout) \ - do { \ - if (queue && !queue_empty(queue)) { \ - queue_process_events(queue); \ - } else { \ - loop_poll_events(loop, timeout); \ - } \ +#define LOOP_PROCESS_EVENTS(loop, queue, timeout) \ + do { \ + if (queue && !queue_empty(queue)) { \ + queue_process_events(queue); \ + } else { \ + loop_poll_events(loop, timeout); \ + } \ } while (0) diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 3c8c08e235..317e40e43a 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -9,7 +9,7 @@ #include "nvim/event/wstream.h" #include "nvim/event/process.h" #include "nvim/event/libuv_process.h" -#include "nvim/event/pty_process.h" +#include "nvim/os/pty_process.h" #include "nvim/globals.h" #include "nvim/log.h" @@ -22,11 +22,11 @@ #define TERM_TIMEOUT 1000000000 #define KILL_TIMEOUT (TERM_TIMEOUT * 2) -#define CLOSE_PROC_STREAM(proc, stream) \ - do { \ - if (proc->stream && !proc->stream->closed) { \ - stream_close(proc->stream, NULL); \ - } \ +#define CLOSE_PROC_STREAM(proc, stream) \ + do { \ + if (proc->stream && !proc->stream->closed) { \ + stream_close(proc->stream, NULL); \ + } \ } while (0) static bool process_is_tearing_down = false; @@ -332,9 +332,61 @@ static void process_close(Process *proc) } } +/// Flush output stream. +/// +/// @param proc Process, for which an output stream should be flushed. +/// @param stream Stream to flush. +static void flush_stream(Process *proc, Stream *stream) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (!stream || stream->closed) { + return; + } + + // Maximal remaining data size of terminated process is system + // buffer size. + // Also helps with a child process that keeps the output streams open. If it + // keeps sending data, we only accept as much data as the system buffer size. + // Otherwise this would block cleanup/teardown. + int system_buffer_size = 0; + int err = uv_recv_buffer_size((uv_handle_t *)&stream->uv.pipe, + &system_buffer_size); + if (err) { + system_buffer_size = (int)rbuffer_capacity(stream->buffer); + } + + size_t max_bytes = stream->num_bytes + (size_t)system_buffer_size; + + // Read remaining data. + while (!stream->closed && stream->num_bytes < max_bytes) { + // Remember number of bytes before polling + size_t num_bytes = stream->num_bytes; + + // Poll for data and process the generated events. + loop_poll_events(proc->loop, 0); + if (proc->events) { + queue_process_events(proc->events); + } + + // Stream can be closed if it is empty. + if (num_bytes == stream->num_bytes) { + if (stream->read_cb) { + // Stream callback could miss EOF handling if a child keeps the stream + // open. + stream->read_cb(stream, stream->buffer, 0, stream->data, true); + } + break; + } + } +} + static void process_close_handles(void **argv) { Process *proc = argv[0]; + + flush_stream(proc, proc->out); + flush_stream(proc, proc->err); + process_close_streams(proc); process_close(proc); } @@ -349,11 +401,12 @@ static void on_process_exit(Process *proc) uv_timer_stop(&loop->children_kill_timer); } - // Process handles are closed in the next event loop tick. This is done to - // give libuv more time to read data from the OS after the process exits(If - // process_close_streams is called with data still in the OS buffer, we lose - // it) - CREATE_EVENT(proc->events, process_close_handles, 1, proc); + // Process has terminated, but there could still be data to be read from the + // OS. We are still in the libuv loop, so we cannot call code that polls for + // more data directly. Instead delay the reading after the libuv loop by + // queueing process_close_handles() as an event. + Queue *queue = proc->events ? proc->events : loop->events; + CREATE_EVENT(queue, process_close_handles, 1, proc); } static void on_process_stream_close(Stream *stream, void *data) diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index e23c8ea60f..a4c6e7eeb2 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -21,6 +21,7 @@ struct process { int pid, status, refcount; // set to the hrtime of when process_stop was called for the process. uint64_t stopped_time; + char *cwd; char **argv; Stream *in, *out, *err; process_exit_cb cb; @@ -40,6 +41,7 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data) .status = 0, .refcount = 0, .stopped_time = 0, + .cwd = NULL, .argv = NULL, .in = NULL, .out = NULL, diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 9f3fbc25ff..a520143064 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -100,6 +100,10 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) { Stream *stream = uvstream->data; + if (cnt > 0) { + stream->num_bytes += (size_t)cnt; + } + if (cnt <= 0) { if (cnt != UV_ENOBUFS // cnt == 0 means libuv asked for a buffer and decided it wasn't needed: @@ -185,10 +189,6 @@ static void read_event(void **argv) static void invoke_read_cb(Stream *stream, size_t count, bool eof) { - if (stream->closed) { - return; - } - // Don't let the stream be closed before the event is processed. stream->pending_reqs++; diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index 93cc592683..cdaf40849b 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -103,7 +103,7 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) // Libuv converts ENOENT to EACCES for Windows compatibility, but if // the parent directory does not exist, ENOENT would be more accurate. *path_tail((char_u *)watcher->addr) = NUL; - if (!os_file_exists((char_u *)watcher->addr)) { + if (!os_path_exists((char_u *)watcher->addr)) { result = -ENOENT; } } diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index 71582ab357..33404158cf 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -71,6 +71,7 @@ void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream, stream->closed = false; stream->buffer = NULL; stream->events = NULL; + stream->num_bytes = 0; } void stream_close(Stream *stream, stream_close_cb on_stream_close) diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index c6baac0db7..ad4e24775b 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -49,6 +49,7 @@ struct stream { size_t curmem; size_t maxmem; size_t pending_reqs; + size_t num_bytes; void *data, *internal_data; bool closed; Queue *events; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 86f1a16216..da64533708 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1745,14 +1745,14 @@ check_overwrite ( * write to other file or b_flags set or not writing the whole file: * overwriting only allowed with '!' */ - if ( (other - || (buf->b_flags & BF_NOTEDITED) - || ((buf->b_flags & BF_NEW) - && vim_strchr(p_cpo, CPO_OVERNEW) == NULL) - || (buf->b_flags & BF_READERR)) - && !p_wa - && !bt_nofile(buf) - && os_file_exists(ffname)) { + if ((other + || (buf->b_flags & BF_NOTEDITED) + || ((buf->b_flags & BF_NEW) + && vim_strchr(p_cpo, CPO_OVERNEW) == NULL) + || (buf->b_flags & BF_READERR)) + && !p_wa + && !bt_nofile(buf) + && os_path_exists(ffname)) { if (!eap->forceit && !eap->append) { #ifdef UNIX // It is possible to open a directory on Unix. @@ -1795,7 +1795,7 @@ check_overwrite ( } swapname = makeswapname(fname, ffname, curbuf, dir); xfree(dir); - if (os_file_exists(swapname)) { + if (os_path_exists(swapname)) { if (p_confirm || cmdmod.confirm) { char_u buff[DIALOG_MSG_SIZE]; @@ -1909,7 +1909,7 @@ static int check_readonly(int *forceit, buf_T *buf) /* Handle a file being readonly when the 'readonly' option is set or when * the file exists and permissions are read-only. */ if (!*forceit && (buf->b_p_ro - || (os_file_exists(buf->b_ffname) + || (os_path_exists(buf->b_ffname) && !os_file_is_writable((char *)buf->b_ffname)))) { if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL) { char_u buff[DIALOG_MSG_SIZE]; @@ -4796,122 +4796,15 @@ void ex_viusage(exarg_T *eap) } -/* - * ":helptags" - */ -void ex_helptags(exarg_T *eap) -{ - garray_T ga; - int len; - char_u lang[2]; - expand_T xpc; - char_u *dirname; - char_u ext[5]; - char_u fname[8]; - int filecount; - char_u **files; - int add_help_tags = FALSE; - - /* Check for ":helptags ++t {dir}". */ - if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) { - add_help_tags = TRUE; - eap->arg = skipwhite(eap->arg + 3); - } - - ExpandInit(&xpc); - xpc.xp_context = EXPAND_DIRECTORIES; - dirname = ExpandOne(&xpc, eap->arg, NULL, - WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); - if (dirname == NULL || !os_isdir(dirname)) { - EMSG2(_("E150: Not a directory: %s"), eap->arg); - xfree(dirname); - return; - } - - /* Get a list of all files in the help directory and in subdirectories. */ - STRCPY(NameBuff, dirname); - add_pathsep((char *)NameBuff); - STRCAT(NameBuff, "**"); - - // Note: We cannot just do `&NameBuff` because it is a statically sized array - // so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; - if (gen_expand_wildcards(1, buff_list, &filecount, &files, - EW_FILE|EW_SILENT) == FAIL - || filecount == 0) { - EMSG2("E151: No match: %s", NameBuff); - xfree(dirname); - return; - } - - /* Go over all files in the directory to find out what languages are - * present. */ - ga_init(&ga, 1, 10); - for (int i = 0; i < filecount; ++i) { - len = (int)STRLEN(files[i]); - if (len <= 4) { - continue; - } - if (STRICMP(files[i] + len - 4, ".txt") == 0) { - /* ".txt" -> language "en" */ - lang[0] = 'e'; - lang[1] = 'n'; - } else if (files[i][len - 4] == '.' - && ASCII_ISALPHA(files[i][len - 3]) - && ASCII_ISALPHA(files[i][len - 2]) - && TOLOWER_ASC(files[i][len - 1]) == 'x') { - /* ".abx" -> language "ab" */ - lang[0] = TOLOWER_ASC(files[i][len - 3]); - lang[1] = TOLOWER_ASC(files[i][len - 2]); - } else - continue; - - int j; - /* Did we find this language already? */ - for (j = 0; j < ga.ga_len; j += 2) - if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) - break; - if (j == ga.ga_len) { - /* New language, add it. */ - ga_grow(&ga, 2); - ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0]; - ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1]; - } - } - - /* - * Loop over the found languages to generate a tags file for each one. - */ - for (int j = 0; j < ga.ga_len; j += 2) { - STRCPY(fname, "tags-xx"); - fname[5] = ((char_u *)ga.ga_data)[j]; - fname[6] = ((char_u *)ga.ga_data)[j + 1]; - if (fname[5] == 'e' && fname[6] == 'n') { - /* English is an exception: use ".txt" and "tags". */ - fname[4] = NUL; - STRCPY(ext, ".txt"); - } else { - /* Language "ab" uses ".abx" and "tags-ab". */ - STRCPY(ext, ".xxx"); - ext[1] = fname[5]; - ext[2] = fname[6]; - } - helptags_one(dirname, ext, fname, add_help_tags); - } - - ga_clear(&ga); - FreeWild(filecount, files); - - xfree(dirname); -} - -static void -helptags_one ( - char_u *dir, /* doc directory */ - char_u *ext, /* suffix, ".txt", ".itx", ".frx", etc. */ - char_u *tagfname, /* "tags" for English, "tags-fr" for French. */ - int add_help_tags /* add "help-tags" tag */ -) +/// Generate tags in one help directory +/// +/// @param dir Path to the doc directory +/// @param ext Suffix of the help files (".txt", ".itx", ".frx", etc.) +/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for +/// French) +/// @param add_help_tags Whether to add the "help-tags" tag +static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, + bool add_help_tags) { FILE *fd_tags; FILE *fd; @@ -5111,6 +5004,132 @@ helptags_one ( fclose(fd_tags); /* there is no check for an error... */ } +/// Generate tags in one help directory, taking care of translations. +static void do_helptags(char_u *dirname, bool add_help_tags) +{ + int len; + garray_T ga; + char_u lang[2]; + char_u ext[5]; + char_u fname[8]; + int filecount; + char_u **files; + + // Get a list of all files in the help directory and in subdirectories. + STRCPY(NameBuff, dirname); + add_pathsep((char *)NameBuff); + STRCAT(NameBuff, "**"); + + // Note: We cannot just do `&NameBuff` because it is a statically sized array + // so `NameBuff == &NameBuff` according to C semantics. + char_u *buff_list[1] = {NameBuff}; + if (gen_expand_wildcards(1, buff_list, &filecount, &files, + EW_FILE|EW_SILENT) == FAIL + || filecount == 0) { + EMSG2("E151: No match: %s", NameBuff); + xfree(dirname); + return; + } + + /* Go over all files in the directory to find out what languages are + * present. */ + int j; + ga_init(&ga, 1, 10); + for (int i = 0; i < filecount; i++) { + len = (int)STRLEN(files[i]); + if (len <= 4) { + continue; + } + if (STRICMP(files[i] + len - 4, ".txt") == 0) { + /* ".txt" -> language "en" */ + lang[0] = 'e'; + lang[1] = 'n'; + } else if (files[i][len - 4] == '.' + && ASCII_ISALPHA(files[i][len - 3]) + && ASCII_ISALPHA(files[i][len - 2]) + && TOLOWER_ASC(files[i][len - 1]) == 'x') { + /* ".abx" -> language "ab" */ + lang[0] = TOLOWER_ASC(files[i][len - 3]); + lang[1] = TOLOWER_ASC(files[i][len - 2]); + } else + continue; + + // Did we find this language already? + for (j = 0; j < ga.ga_len; j += 2) { + if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) { + break; + } + } + if (j == ga.ga_len) { + // New language, add it. + ga_grow(&ga, 2); + ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0]; + ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1]; + } + } + + /* + * Loop over the found languages to generate a tags file for each one. + */ + for (j = 0; j < ga.ga_len; j += 2) { + STRCPY(fname, "tags-xx"); + fname[5] = ((char_u *)ga.ga_data)[j]; + fname[6] = ((char_u *)ga.ga_data)[j + 1]; + if (fname[5] == 'e' && fname[6] == 'n') { + /* English is an exception: use ".txt" and "tags". */ + fname[4] = NUL; + STRCPY(ext, ".txt"); + } else { + /* Language "ab" uses ".abx" and "tags-ab". */ + STRCPY(ext, ".xxx"); + ext[1] = fname[5]; + ext[2] = fname[6]; + } + helptags_one(dirname, ext, fname, add_help_tags); + } + + ga_clear(&ga); + FreeWild(filecount, files); +} + + static void +helptags_cb(char_u *fname, void *cookie) +{ + do_helptags(fname, *(bool *)cookie); +} + +/* + * ":helptags" + */ +void ex_helptags(exarg_T *eap) +{ + expand_T xpc; + char_u *dirname; + bool add_help_tags = false; + + /* Check for ":helptags ++t {dir}". */ + if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) { + add_help_tags = true; + eap->arg = skipwhite(eap->arg + 3); + } + + if (STRCMP(eap->arg, "ALL") == 0) { + do_in_path(p_rtp, (char_u *)"doc", DIP_ALL + DIP_DIR, + helptags_cb, &add_help_tags); + } else { + ExpandInit(&xpc); + xpc.xp_context = EXPAND_DIRECTORIES; + dirname = ExpandOne(&xpc, eap->arg, NULL, + WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); + if (dirname == NULL || !os_isdir(dirname)) { + EMSG2(_("E150: Not a directory: %s"), eap->arg); + } else { + do_helptags(dirname, add_help_tags); + } + xfree(dirname); + } +} + struct sign { sign_T *sn_next; /* next sign in list */ diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 04fd88cc8d..3f5d9b3244 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -88,7 +88,7 @@ return { }, { command='argadd', - flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILES, TRLBAR), + flags=bit.bor(BANG, RANGE, NOTADR, ZEROR, FILES, TRLBAR), addr_type=ADDR_ARGUMENTS, func='ex_argadd', }, @@ -1849,6 +1849,18 @@ return { func='ex_print', }, { + command='packadd', + flags=bit.bor(BANG, FILE1, NEEDARG, TRLBAR, SBOXOK, CMDWIN), + addr_type=ADDR_LINES, + func='ex_packadd', + }, + { + command='packloadall', + flags=bit.bor(BANG, TRLBAR, SBOXOK, CMDWIN), + addr_type=ADDR_LINES, + func='ex_packloadall', + }, + { command='pclose', flags=bit.bor(BANG, TRLBAR), addr_type=ADDR_LINES, @@ -2158,19 +2170,19 @@ return { command='ruby', flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_script_ni', + func='ex_ruby', }, { command='rubydo', flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_ni', + func='ex_rubydo', }, { command='rubyfile', flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), addr_type=ADDR_LINES, - func='ex_ni', + func='ex_rubyfile', }, { command='rviminfo', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 5fe6209a0a..6d24ba91f2 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1,6 +1,6 @@ -/* - * ex_cmds2.c: some more functions for command line commands - */ +/// @file ex_cmds2.c +/// +/// Some more functions for command line commands #include <assert.h> #include <inttypes.h> @@ -50,15 +50,15 @@ #include "nvim/api/private/defs.h" -/* Growarray to store info about already sourced scripts. - * Also store the dev/ino, so that we don't have to stat() each - * script when going through the list. */ +/// Growarray to store info about already sourced scripts. +/// Also store the dev/ino, so that we don't have to stat() each +/// script when going through the list. typedef struct scriptitem_S { char_u *sn_name; bool file_id_valid; FileID file_id; bool sn_prof_on; ///< true when script is/was profiled - int sn_pr_force; ///< forceit: profile functions in this script + bool sn_pr_force; ///< forceit: profile functions in this script proftime_T sn_pr_child; ///< time set when going into first child int sn_pr_nest; ///< nesting for sn_pr_child // profiling the script as a whole @@ -76,29 +76,27 @@ typedef struct scriptitem_S { int sn_prl_execed; ///< line being timed was executed } scriptitem_T; -static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; +static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL }; #define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1]) -/* Struct used in sn_prl_ga for every line of a script. */ +// Struct used in sn_prl_ga for every line of a script. typedef struct sn_prl_S { int snp_count; ///< nr of times line was executed proftime_T sn_prl_total; ///< time spent in a line + children proftime_T sn_prl_self; ///< time spent in a line itself } sn_prl_T; -/* - * Structure used to store info for each sourced file. - * It is shared between do_source() and getsourceline(). - * This is required, because it needs to be handed to do_cmdline() and - * sourcing can be done recursively. - */ +/// Structure used to store info for each sourced file. +/// It is shared between do_source() and getsourceline(). +/// This is required, because it needs to be handed to do_cmdline() and +/// sourcing can be done recursively. struct source_cookie { FILE *fp; ///< opened file for sourcing char_u *nextline; ///< if not NULL: line that was read ahead int finished; ///< ":finish" used #if defined(USE_CRNL) int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS - int error; ///< TRUE if LF found after CR-LF + bool error; ///< true if LF found after CR-LF #endif linenr_T breakpoint; ///< next line with breakpoint or zero char_u *fname; ///< name of sourced file @@ -113,13 +111,11 @@ struct source_cookie { # include "ex_cmds2.c.generated.h" #endif -static int debug_greedy = FALSE; /* batch mode debugging: don't save - and restore typeahead. */ +/// batch mode debugging: don't save and restore typeahead. +static bool debug_greedy = false; -/* - * do_debug(): Debug mode. - * Repeatedly get Ex commands, until told to continue normal execution. - */ +/// Debug mode. Repeatedly get Ex commands, until told to continue normal +/// execution. void do_debug(char_u *cmd) { int save_msg_scroll = msg_scroll; @@ -130,7 +126,7 @@ void do_debug(char_u *cmd) int save_emsg_silent = emsg_silent; int save_redir_off = redir_off; tasave_T typeaheadbuf; - int typeahead_saved = FALSE; + bool typeahead_saved = false; int save_ignore_script = 0; int save_ex_normal_busy; int n; @@ -150,43 +146,44 @@ void do_debug(char_u *cmd) #define CMD_DOWN 10 - ++RedrawingDisabled; /* don't redisplay the window */ - ++no_wait_return; /* don't wait for return */ - did_emsg = FALSE; /* don't use error from debugged stuff */ - cmd_silent = FALSE; /* display commands */ - msg_silent = FALSE; /* display messages */ - emsg_silent = FALSE; /* display error messages */ - redir_off = TRUE; /* don't redirect debug commands */ + RedrawingDisabled++; // don't redisplay the window + no_wait_return++; // don't wait for return + did_emsg = false; // don't use error from debugged stuff + cmd_silent = false; // display commands + msg_silent = false; // display messages + emsg_silent = false; // display error messages + redir_off = true; // don't redirect debug commands State = NORMAL; - if (!debug_did_msg) + if (!debug_did_msg) { MSG(_("Entering Debug mode. Type \"cont\" to continue.")); - if (sourcing_name != NULL) + } + if (sourcing_name != NULL) { msg(sourcing_name); - if (sourcing_lnum != 0) + } + if (sourcing_lnum != 0) { smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd); - else + } else { smsg(_("cmd: %s"), cmd); + } - /* - * Repeat getting a command and executing it. - */ + // Repeat getting a command and executing it. for (;; ) { - msg_scroll = TRUE; - need_wait_return = FALSE; - /* Save the current typeahead buffer and replace it with an empty one. - * This makes sure we get input from the user here and don't interfere - * with the commands being executed. Reset "ex_normal_busy" to avoid - * the side effects of using ":normal". Save the stuff buffer and make - * it empty. Set ignore_script to avoid reading from script input. */ + msg_scroll = true; + need_wait_return = false; + // Save the current typeahead buffer and replace it with an empty one. + // This makes sure we get input from the user here and don't interfere + // with the commands being executed. Reset "ex_normal_busy" to avoid + // the side effects of using ":normal". Save the stuff buffer and make + // it empty. Set ignore_script to avoid reading from script input. save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; if (!debug_greedy) { save_typeahead(&typeaheadbuf); - typeahead_saved = TRUE; + typeahead_saved = true; save_ignore_script = ignore_script; - ignore_script = TRUE; + ignore_script = true; } xfree(cmdline); @@ -201,9 +198,9 @@ void do_debug(char_u *cmd) cmdline_row = msg_row; msg_starthere(); if (cmdline != NULL) { - /* If this is a debug command, set "last_cmd". - * If not, reset "last_cmd". - * For a blank line use previous command. */ + // If this is a debug command, set "last_cmd". + // If not, reset "last_cmd". + // For a blank line use previous command. p = skipwhite(cmdline); if (*p != NUL) { switch (*p) { @@ -255,11 +252,11 @@ void do_debug(char_u *cmd) default: last_cmd = 0; } if (last_cmd != 0) { - /* Check that the tail matches. */ - ++p; + // Check that the tail matches. + p++; while (*p != NUL && *p == *tail) { - ++p; - ++tail; + p++; + tail++; } if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) { last_cmd = 0; @@ -268,8 +265,7 @@ void do_debug(char_u *cmd) } if (last_cmd != 0) { - /* Execute debug command: decided where to break next and - * return. */ + // Execute debug command: decided where to break next and return. switch (last_cmd) { case CMD_CONT: debug_break_level = -1; @@ -284,13 +280,13 @@ void do_debug(char_u *cmd) debug_break_level = ex_nesting_level - 1; break; case CMD_QUIT: - got_int = TRUE; + got_int = true; debug_break_level = -1; break; case CMD_INTERRUPT: - got_int = TRUE; + got_int = true; debug_break_level = 9999; - /* Do not repeat ">interrupt" cmd, continue stepping. */ + // Do not repeat ">interrupt" cmd, continue stepping. last_cmd = CMD_STEP; break; case CMD_BACKTRACE: @@ -318,21 +314,21 @@ void do_debug(char_u *cmd) break; } - /* don't debug this command */ + // don't debug this command n = debug_break_level; debug_break_level = -1; (void)do_cmdline(cmdline, getexline, NULL, - DOCMD_VERBOSE|DOCMD_EXCRESET); + DOCMD_VERBOSE|DOCMD_EXCRESET); debug_break_level = n; } lines_left = (int)(Rows - 1); } xfree(cmdline); - --RedrawingDisabled; - --no_wait_return; + RedrawingDisabled--; + no_wait_return--; redraw_all_later(NOT_VALID); - need_wait_return = FALSE; + need_wait_return = false; msg_scroll = save_msg_scroll; lines_left = (int)(Rows - 1); State = save_State; @@ -342,9 +338,8 @@ void do_debug(char_u *cmd) emsg_silent = save_emsg_silent; redir_off = save_redir_off; - /* Only print the message again when typing a command before coming back - * here. */ - debug_did_msg = TRUE; + // Only print the message again when typing a command before coming back here. + debug_did_msg = true; } static int get_maxbacktrace_level(void) @@ -419,9 +414,8 @@ static void do_showbacktrace(char_u *cmd) } } -/* - * ":debug". - */ + +/// ":debug". void ex_debug(exarg_T *eap) { int debug_break_level_save = debug_break_level; @@ -434,88 +428,81 @@ void ex_debug(exarg_T *eap) static char_u *debug_breakpoint_name = NULL; static linenr_T debug_breakpoint_lnum; -/* - * When debugging or a breakpoint is set on a skipped command, no debug prompt - * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and - * debug_skipped_name is then set to the source name in the breakpoint case. If - * a skipped command decides itself that a debug prompt should be displayed, it - * can do so by calling dbg_check_skipped(). - */ +/// When debugging or a breakpoint is set on a skipped command, no debug prompt +/// is shown by do_one_cmd(). This situation is indicated by debug_skipped, and +/// debug_skipped_name is then set to the source name in the breakpoint case. If +/// a skipped command decides itself that a debug prompt should be displayed, it +/// can do so by calling dbg_check_skipped(). static int debug_skipped; static char_u *debug_skipped_name; -/* - * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is - * at or below the break level. But only when the line is actually - * executed. Return TRUE and set breakpoint_name for skipped commands that - * decide to execute something themselves. - * Called from do_one_cmd() before executing a command. - */ +/// Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is +/// at or below the break level. But only when the line is actually +/// executed. Return true and set breakpoint_name for skipped commands that +/// decide to execute something themselves. +/// Called from do_one_cmd() before executing a command. void dbg_check_breakpoint(exarg_T *eap) { char_u *p; - debug_skipped = FALSE; + debug_skipped = false; if (debug_breakpoint_name != NULL) { if (!eap->skip) { - /* replace K_SNR with "<SNR>" */ + // replace K_SNR with "<SNR>" if (debug_breakpoint_name[0] == K_SPECIAL && debug_breakpoint_name[1] == KS_EXTRA - && debug_breakpoint_name[2] == (int)KE_SNR) + && debug_breakpoint_name[2] == (int)KE_SNR) { p = (char_u *)"<SNR>"; - else + } else { p = (char_u *)""; + } smsg(_("Breakpoint in \"%s%s\" line %" PRId64), - p, - debug_breakpoint_name + (*p == NUL ? 0 : 3), - (int64_t)debug_breakpoint_lnum); + p, + debug_breakpoint_name + (*p == NUL ? 0 : 3), + (int64_t)debug_breakpoint_lnum); debug_breakpoint_name = NULL; do_debug(eap->cmd); } else { - debug_skipped = TRUE; + debug_skipped = true; debug_skipped_name = debug_breakpoint_name; debug_breakpoint_name = NULL; } } else if (ex_nesting_level <= debug_break_level) { - if (!eap->skip) + if (!eap->skip) { do_debug(eap->cmd); - else { - debug_skipped = TRUE; + } else { + debug_skipped = true; debug_skipped_name = NULL; } } } -/* - * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was - * set. Return TRUE when the debug mode is entered this time. - */ -int dbg_check_skipped(exarg_T *eap) +/// Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was +/// set. +/// +/// @return true when the debug mode is entered this time. +bool dbg_check_skipped(exarg_T *eap) { int prev_got_int; if (debug_skipped) { - /* - * Save the value of got_int and reset it. We don't want a previous - * interruption cause flushing the input buffer. - */ + // Save the value of got_int and reset it. We don't want a previous + // interruption cause flushing the input buffer. prev_got_int = got_int; - got_int = FALSE; + got_int = false; debug_breakpoint_name = debug_skipped_name; - /* eap->skip is TRUE */ - eap->skip = FALSE; + // eap->skip is true + eap->skip = false; dbg_check_breakpoint(eap); - eap->skip = TRUE; + eap->skip = true; got_int |= prev_got_int; - return TRUE; + return true; } - return FALSE; + return false; } -/* - * The list of breakpoints: dbg_breakp. - * This is a grow-array of structs. - */ +/// The list of breakpoints: dbg_breakp. +/// This is a grow-array of structs. struct debuggy { int dbg_nr; ///< breakpoint number int dbg_type; ///< DBG_FUNC or DBG_FILE @@ -525,33 +512,30 @@ struct debuggy { int dbg_forceit; ///< ! used }; -static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; +static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL }; #define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) #define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx]) -static int last_breakp = 0; /* nr of last defined breakpoint */ +static int last_breakp = 0; // nr of last defined breakpoint -/* Profiling uses file and func names similar to breakpoints. */ -static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; +// Profiling uses file and func names similar to breakpoints. +static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL }; #define DBG_FUNC 1 #define DBG_FILE 2 -/* - * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them - * in the entry just after the last one in dbg_breakp. Note that "dbg_name" - * is allocated. - * Returns FAIL for failure. - */ -static int -dbg_parsearg ( - char_u *arg, - garray_T *gap /* either &dbg_breakp or &prof_ga */ -) +/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them +/// in the entry just after the last one in dbg_breakp. Note that "dbg_name" +/// is allocated. +/// Returns FAIL for failure. +/// +/// @param arg +/// @param gap either &dbg_breakp or &prof_ga +static int dbg_parsearg(char_u *arg, garray_T *gap) { char_u *p = arg; char_u *q; struct debuggy *bp; - int here = FALSE; + bool here = false; ga_grow(gap, 1); @@ -568,7 +552,7 @@ dbg_parsearg ( return FAIL; } bp->dbg_type = DBG_FILE; - here = TRUE; + here = true; } else { EMSG2(_(e_invarg2), p); return FAIL; @@ -585,7 +569,7 @@ dbg_parsearg ( bp->dbg_lnum = 0; } - /* Find the function or file name. Don't accept a function name with (). */ + // Find the function or file name. Don't accept a function name with (). if ((!here && *p == NUL) || (here && *p != NUL) || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) { @@ -593,36 +577,38 @@ dbg_parsearg ( return FAIL; } - if (bp->dbg_type == DBG_FUNC) + if (bp->dbg_type == DBG_FUNC) { bp->dbg_name = vim_strsave(p); - else if (here) + } else if (here) { bp->dbg_name = vim_strsave(curbuf->b_ffname); - else { - /* Expand the file name in the same way as do_source(). This means - * doing it twice, so that $DIR/file gets expanded when $DIR is - * "~/dir". */ + } else { + // Expand the file name in the same way as do_source(). This means + // doing it twice, so that $DIR/file gets expanded when $DIR is + // "~/dir". q = expand_env_save(p); - if (q == NULL) + if (q == NULL) { return FAIL; + } p = expand_env_save(q); xfree(q); - if (p == NULL) + if (p == NULL) { return FAIL; + } if (*p != '*') { bp->dbg_name = (char_u *)fix_fname((char *)p); xfree(p); - } else + } else { bp->dbg_name = p; + } } - if (bp->dbg_name == NULL) + if (bp->dbg_name == NULL) { return FAIL; + } return OK; } -/* - * ":breakadd". - */ +/// ":breakadd". void ex_breakadd(exarg_T *eap) { struct debuggy *bp; @@ -630,52 +616,51 @@ void ex_breakadd(exarg_T *eap) garray_T *gap; gap = &dbg_breakp; - if (eap->cmdidx == CMD_profile) + if (eap->cmdidx == CMD_profile) { gap = &prof_ga; + } if (dbg_parsearg(eap->arg, gap) == OK) { bp = &DEBUGGY(gap, gap->ga_len); bp->dbg_forceit = eap->forceit; - pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE); + pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false); if (pat != NULL) { bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING); xfree(pat); } - if (pat == NULL || bp->dbg_prog == NULL) + if (pat == NULL || bp->dbg_prog == NULL) { xfree(bp->dbg_name); - else { - if (bp->dbg_lnum == 0) /* default line number is 1 */ + } else { + if (bp->dbg_lnum == 0) { // default line number is 1 bp->dbg_lnum = 1; + } if (eap->cmdidx != CMD_profile) { DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; - ++debug_tick; + debug_tick++; } - ++gap->ga_len; + gap->ga_len++; } } } -/* - * ":debuggreedy". - */ +/// ":debuggreedy". void ex_debuggreedy(exarg_T *eap) { - if (eap->addr_count == 0 || eap->line2 != 0) - debug_greedy = TRUE; - else - debug_greedy = FALSE; + if (eap->addr_count == 0 || eap->line2 != 0) { + debug_greedy = true; + } else { + debug_greedy = false; + } } -/* - * ":breakdel" and ":profdel". - */ +/// ":breakdel" and ":profdel". void ex_breakdel(exarg_T *eap) { struct debuggy *bp, *bpi; int nr; int todel = -1; - int del_all = FALSE; + bool del_all = false; linenr_T best_lnum = 0; garray_T *gap; @@ -687,7 +672,7 @@ void ex_breakdel(exarg_T *eap) if (ascii_isdigit(*eap->arg)) { // ":breakdel {nr}" nr = atoi((char *)eap->arg); - for (int i = 0; i < gap->ga_len; ++i) { + for (int i = 0; i < gap->ga_len; i++) { if (DEBUGGY(gap, i).dbg_nr == nr) { todel = i; break; @@ -695,13 +680,14 @@ void ex_breakdel(exarg_T *eap) } } else if (*eap->arg == '*') { todel = 0; - del_all = TRUE; + del_all = true; } else { - /* ":breakdel {func|file} [lnum] {name}" */ - if (dbg_parsearg(eap->arg, gap) == FAIL) + // ":breakdel {func|file} [lnum] {name}" + if (dbg_parsearg(eap->arg, gap) == FAIL) { return; + } bp = &DEBUGGY(gap, gap->ga_len); - for (int i = 0; i < gap->ga_len; ++i) { + for (int i = 0; i < gap->ga_len; i++) { bpi = &DEBUGGY(gap, i); if (bp->dbg_type == bpi->dbg_type && STRCMP(bp->dbg_name, bpi->dbg_name) == 0 @@ -716,90 +702,85 @@ void ex_breakdel(exarg_T *eap) xfree(bp->dbg_name); } - if (todel < 0) + if (todel < 0) { EMSG2(_("E161: Breakpoint not found: %s"), eap->arg); - else { + } else { while (!GA_EMPTY(gap)) { xfree(DEBUGGY(gap, todel).dbg_name); vim_regfree(DEBUGGY(gap, todel).dbg_prog); - --gap->ga_len; - if (todel < gap->ga_len) + gap->ga_len--; + if (todel < gap->ga_len) { memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), (size_t)(gap->ga_len - todel) * sizeof(struct debuggy)); + } if (eap->cmdidx == CMD_breakdel) { - ++debug_tick; + debug_tick++; } if (!del_all) { break; } } - /* If all breakpoints were removed clear the array. */ - if (GA_EMPTY(gap)) + // If all breakpoints were removed clear the array. + if (GA_EMPTY(gap)) { ga_clear(gap); + } } } -/* - * ":breaklist". - */ +/// ":breaklist". void ex_breaklist(exarg_T *eap) { struct debuggy *bp; - if (GA_EMPTY(&dbg_breakp)) + if (GA_EMPTY(&dbg_breakp)) { MSG(_("No breakpoints defined")); - else - for (int i = 0; i < dbg_breakp.ga_len; ++i) { + } else { + for (int i = 0; i < dbg_breakp.ga_len; i++) { bp = &BREAKP(i); - if (bp->dbg_type == DBG_FILE) - home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE); + if (bp->dbg_type == DBG_FILE) { + home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true); + } smsg(_("%3d %s %s line %" PRId64), - bp->dbg_nr, - bp->dbg_type == DBG_FUNC ? "func" : "file", - bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, - (int64_t)bp->dbg_lnum); - } -} - -/* - * Find a breakpoint for a function or sourced file. - * Returns line number at which to break; zero when no matching breakpoint. - */ -linenr_T -dbg_find_breakpoint ( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - linenr_T after /* after this line number */ + bp->dbg_nr, + bp->dbg_type == DBG_FUNC ? "func" : "file", + bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, + (int64_t)bp->dbg_lnum); + } + } +} + +/// Find a breakpoint for a function or sourced file. +/// Returns line number at which to break; zero when no matching breakpoint. +linenr_T +dbg_find_breakpoint( + bool file, // true for a file, false for a function + char_u *fname, // file or function name + linenr_T after // after this line number ) { return debuggy_find(file, fname, after, &dbg_breakp, NULL); } -/* - * Return TRUE if profiling is on for a function or sourced file. - */ -int -has_profiling ( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - int *fp /* return: forceit */ -) +/// @param file true for a file, false for a function +/// @param fname file or function name +/// @param fp[out] forceit +/// +/// @returns true if profiling is on for a function or sourced file. +bool has_profiling(bool file, char_u *fname, bool *fp) { return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp) != (linenr_T)0; } -/* - * Common code for dbg_find_breakpoint() and has_profiling(). - */ -static linenr_T -debuggy_find ( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - linenr_T after, /* after this line number */ - garray_T *gap, /* either &dbg_breakp or &prof_ga */ - int *fp /* if not NULL: return forceit */ +/// Common code for dbg_find_breakpoint() and has_profiling(). +static linenr_T +debuggy_find( + bool file, // true for a file, false for a function + char_u *fname, // file or function name + linenr_T after, // after this line number + garray_T *gap, // either &dbg_breakp or &prof_ga + bool *fp // if not NULL: return forceit ) { struct debuggy *bp; @@ -807,59 +788,57 @@ debuggy_find ( char_u *name = fname; int prev_got_int; - /* Return quickly when there are no breakpoints. */ - if (GA_EMPTY(gap)) + // Return quickly when there are no breakpoints. + if (GA_EMPTY(gap)) { return (linenr_T)0; + } - /* Replace K_SNR in function name with "<SNR>". */ + // Replace K_SNR in function name with "<SNR>". if (!file && fname[0] == K_SPECIAL) { name = xmalloc(STRLEN(fname) + 3); STRCPY(name, "<SNR>"); STRCPY(name + 5, fname + 3); } - for (int i = 0; i < gap->ga_len; ++i) { - /* Skip entries that are not useful or are for a line that is beyond - * an already found breakpoint. */ + for (int i = 0; i < gap->ga_len; i++) { + // Skip entries that are not useful or are for a line that is beyond + // an already found breakpoint. bp = &DEBUGGY(gap, i); - if (((bp->dbg_type == DBG_FILE) == file - && (gap == &prof_ga - || (bp->dbg_lnum > after - && (lnum == 0 || bp->dbg_lnum < lnum))))) { + if ((bp->dbg_type == DBG_FILE) == file + && (gap == &prof_ga + || (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) { // Save the value of got_int and reset it. We don't want a // previous interruption cancel matching, only hitting CTRL-C // while matching should abort it. prev_got_int = got_int; - got_int = FALSE; + got_int = false; if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) { lnum = bp->dbg_lnum; - if (fp != NULL) + if (fp != NULL) { *fp = bp->dbg_forceit; + } } got_int |= prev_got_int; } } - if (name != fname) + if (name != fname) { xfree(name); + } return lnum; } -/* - * Called when a breakpoint was encountered. - */ +/// Called when a breakpoint was encountered. void dbg_breakpoint(char_u *name, linenr_T lnum) { - /* We need to check if this line is actually executed in do_one_cmd() */ + // We need to check if this line is actually executed in do_one_cmd() debug_breakpoint_name = name; debug_breakpoint_lnum = lnum; } static char_u *profile_fname = NULL; -/* - * ":profile cmd args" - */ +/// ":profile cmd args" void ex_profile(exarg_T *eap) { static proftime_T pause_time; @@ -885,8 +864,9 @@ void ex_profile(exarg_T *eap) set_vim_var_nr(VV_PROFILING, 0L); profile_reset(); } else if (STRCMP(eap->arg, "pause") == 0) { - if (do_profiling == PROF_YES) + if (do_profiling == PROF_YES) { pause_time = profile_start(); + } do_profiling = PROF_PAUSED; } else if (STRCMP(eap->arg, "continue") == 0) { if (do_profiling == PROF_PAUSED) { @@ -897,7 +877,7 @@ void ex_profile(exarg_T *eap) } else if (STRCMP(eap->arg, "dump") == 0) { profile_dump(); } else { - /* The rest is similar to ":breakadd". */ + // The rest is similar to ":breakadd". ex_breakadd(eap); } } @@ -917,6 +897,21 @@ void ex_pydo(exarg_T *eap) script_host_do_range("python", eap); } +void ex_ruby(exarg_T *eap) +{ + script_host_execute("ruby", eap); +} + +void ex_rubyfile(exarg_T *eap) +{ + script_host_execute_file("ruby", eap); +} + +void ex_rubydo(exarg_T *eap) +{ + script_host_do_range("ruby", eap); +} + void ex_python3(exarg_T *eap) { script_host_execute("python3", eap); @@ -932,7 +927,7 @@ void ex_pydo3(exarg_T *eap) script_host_do_range("python3", eap); } -/* Command line expansion for :profile. */ +// Command line expansion for :profile. static enum { PEXP_SUBCMD, ///< expand :profile sub-commands PEXP_FUNC ///< expand :profile func {funcname} @@ -949,36 +944,33 @@ static char *pexpand_cmds[] = { NULL }; -/* - * Function given to ExpandGeneric() to obtain the profile command - * specific expansion. - */ +/// Function given to ExpandGeneric() to obtain the profile command +/// specific expansion. char_u *get_profile_name(expand_T *xp, int idx) { switch (pexpand_what) { case PEXP_SUBCMD: return (char_u *)pexpand_cmds[idx]; - /* case PEXP_FUNC: TODO */ + // case PEXP_FUNC: TODO default: return NULL; } } -/* - * Handle command line completion for :profile command. - */ +/// Handle command line completion for :profile command. void set_context_in_profile_cmd(expand_T *xp, char_u *arg) { char_u *end_subcmd; - /* Default: expand subcommands. */ + // Default: expand subcommands. xp->xp_context = EXPAND_PROFILE; pexpand_what = PEXP_SUBCMD; xp->xp_pattern = arg; end_subcmd = skiptowhite(arg); - if (*end_subcmd == NUL) + if (*end_subcmd == NUL) { return; + } if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) { xp->xp_context = EXPAND_FILES; @@ -986,22 +978,20 @@ void set_context_in_profile_cmd(expand_T *xp, char_u *arg) return; } - /* TODO: expand function names after "func" */ + // TODO(tarruda): expand function names after "func" xp->xp_context = EXPAND_NOTHING; } -/* - * Dump the profiling info. - */ +/// Dump the profiling info. void profile_dump(void) { FILE *fd; if (profile_fname != NULL) { fd = mch_fopen((char *)profile_fname, "w"); - if (fd == NULL) + if (fd == NULL) { EMSG2(_(e_notopen), profile_fname); - else { + } else { script_dump_profile(fd); func_dump_profile(fd); fclose(fd); @@ -1017,7 +1007,7 @@ static void profile_reset(void) scriptitem_T *si = &SCRIPT_ITEM(id); if (si->sn_prof_on) { si->sn_prof_on = false; - si->sn_pr_force = 0; + si->sn_pr_force = false; si->sn_pr_child = profile_zero(); si->sn_pr_nest = 0; si->sn_pr_count = 0; @@ -1077,26 +1067,23 @@ static void profile_init(scriptitem_T *si) si->sn_pr_nest = 0; } -/* - * save time when starting to invoke another script or function. - */ +/// save time when starting to invoke another script or function. void script_prof_save( - proftime_T *tm /* place to store wait time */ - ) + proftime_T *tm // place to store wait time +) { scriptitem_T *si; if (current_SID > 0 && current_SID <= script_items.ga_len) { si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && si->sn_pr_nest++ == 0) + if (si->sn_prof_on && si->sn_pr_nest++ == 0) { si->sn_pr_child = profile_start(); + } } *tm = profile_get_wait(); } -/* - * Count time spent in children after invoking another script or function. - */ +/// Count time spent in children after invoking another script or function. void script_prof_restore(proftime_T *tm) { scriptitem_T *si; @@ -1115,62 +1102,60 @@ void script_prof_restore(proftime_T *tm) static proftime_T inchar_time; -/* - * Called when starting to wait for the user to type a character. - */ +/// Called when starting to wait for the user to type a character. void prof_inchar_enter(void) { inchar_time = profile_start(); } -/* - * Called when finished waiting for the user to type a character. - */ +/// Called when finished waiting for the user to type a character. void prof_inchar_exit(void) { inchar_time = profile_end(inchar_time); profile_set_wait(profile_add(profile_get_wait(), inchar_time)); } -/* - * Dump the profiling results for all scripts in file "fd". - */ +/// Dump the profiling results for all scripts in file "fd". static void script_dump_profile(FILE *fd) { scriptitem_T *si; FILE *sfd; sn_prl_T *pp; - for (int id = 1; id <= script_items.ga_len; ++id) { + for (int id = 1; id <= script_items.ga_len; id++) { si = &SCRIPT_ITEM(id); if (si->sn_prof_on) { fprintf(fd, "SCRIPT %s\n", si->sn_name); - if (si->sn_pr_count == 1) + if (si->sn_pr_count == 1) { fprintf(fd, "Sourced 1 time\n"); - else + } else { fprintf(fd, "Sourced %d times\n", si->sn_pr_count); + } fprintf(fd, "Total time: %s\n", profile_msg(si->sn_pr_total)); fprintf(fd, " Self time: %s\n", profile_msg(si->sn_pr_self)); fprintf(fd, "\n"); fprintf(fd, "count total (s) self (s)\n"); sfd = mch_fopen((char *)si->sn_name, "r"); - if (sfd == NULL) + if (sfd == NULL) { fprintf(fd, "Cannot open file!\n"); - else { - for (int i = 0; i < si->sn_prl_ga.ga_len; ++i) { - if (vim_fgets(IObuff, IOSIZE, sfd)) + } else { + for (int i = 0; i < si->sn_prl_ga.ga_len; i++) { + if (vim_fgets(IObuff, IOSIZE, sfd)) { break; + } pp = &PRL_ITEM(si, i); if (pp->snp_count > 0) { fprintf(fd, "%5d ", pp->snp_count); - if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) + if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) { fprintf(fd, " "); - else + } else { fprintf(fd, "%s ", profile_msg(pp->sn_prl_total)); + } fprintf(fd, "%s ", profile_msg(pp->sn_prl_self)); - } else + } else { fprintf(fd, " "); + } fprintf(fd, "%s", IObuff); } fclose(sfd); @@ -1180,44 +1165,41 @@ static void script_dump_profile(FILE *fd) } } -/* - * Return TRUE when a function defined in the current script should be - * profiled. - */ -int prof_def_func(void) +/// Return true when a function defined in the current script should be +/// profiled. +bool prof_def_func(void) { - if (current_SID > 0) + if (current_SID > 0) { return SCRIPT_ITEM(current_SID).sn_pr_force; - return FALSE; + } + return false; } -/* - * If 'autowrite' option set, try to write the file. - * Careful: autocommands may make "buf" invalid! - * - * return FAIL for failure, OK otherwise - */ +/// If 'autowrite' option set, try to write the file. +/// Careful: autocommands may make "buf" invalid! +/// +/// @return FAIL for failure, OK otherwise int autowrite(buf_T *buf, int forceit) { int r; if (!(p_aw || p_awa) || !p_write - /* never autowrite a "nofile" or "nowrite" buffer */ + // never autowrite a "nofile" or "nowrite" buffer || bt_dontwrite(buf) - || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL) + || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL) { return FAIL; + } r = buf_write_all(buf, forceit); - /* Writing may succeed but the buffer still changed, e.g., when there is a - * conversion error. We do want to return FAIL then. */ - if (buf_valid(buf) && bufIsChanged(buf)) + // Writing may succeed but the buffer still changed, e.g., when there is a + // conversion error. We do want to return FAIL then. + if (buf_valid(buf) && bufIsChanged(buf)) { r = FAIL; + } return r; } -/* - * flush all buffers, except the ones that are readonly - */ +/// flush all buffers, except the ones that are readonly void autowrite_all(void) { if (!(p_aw || p_awa) || !p_write) { @@ -1226,123 +1208,125 @@ void autowrite_all(void) FOR_ALL_BUFFERS(buf) { if (bufIsChanged(buf) && !buf->b_p_ro) { - (void)buf_write_all(buf, FALSE); - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf)) + (void)buf_write_all(buf, false); + // an autocommand may have deleted the buffer + if (!buf_valid(buf)) { buf = firstbuf; + } } } } -/* - * Return TRUE if buffer was changed and cannot be abandoned. - * For flags use the CCGD_ values. - */ -int check_changed(buf_T *buf, int flags) +/// Return true if buffer was changed and cannot be abandoned. +/// For flags use the CCGD_ values. +bool check_changed(buf_T *buf, int flags) { int forceit = (flags & CCGD_FORCEIT); - if ( !forceit - && bufIsChanged(buf) - && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1) - && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL)) { + if (!forceit + && bufIsChanged(buf) + && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1) + && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL)) { if ((p_confirm || cmdmod.confirm) && p_write) { int count = 0; - if (flags & CCGD_ALLBUF) + if (flags & CCGD_ALLBUF) { FOR_ALL_BUFFERS(buf2) { if (bufIsChanged(buf2) && (buf2->b_ffname != NULL)) { - ++count; + count++; } } - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed now. */ - return FALSE; + } + if (!buf_valid(buf)) { + // Autocommand deleted buffer, oops! It's not changed now. + return false; + } dialog_changed(buf, count > 1); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed now. */ - return FALSE; + if (!buf_valid(buf)) { + // Autocommand deleted buffer, oops! It's not changed now. + return false; + } return bufIsChanged(buf); } - if (flags & CCGD_EXCMD) + if (flags & CCGD_EXCMD) { EMSG(_(e_nowrtmsg)); - else + } else { EMSG(_(e_nowrtmsg_nobang)); - return TRUE; + } + return true; } - return FALSE; + return false; } -/* - * Ask the user what to do when abandoning a changed buffer. - * Must check 'write' option first! - */ -void -dialog_changed ( - buf_T *buf, - int checkall /* may abandon all changed buffers */ -) +/// Ask the user what to do when abandoning a changed buffer. +/// Must check 'write' option first! +/// +/// @param buf +/// @param checkall may abandon all changed buffers +void dialog_changed(buf_T *buf, int checkall) { char_u buff[DIALOG_MSG_SIZE]; int ret; exarg_T ea; dialog_msg(buff, _("Save changes to \"%s\"?"), - (buf->b_fname != NULL) ? - buf->b_fname : (char_u *)_("Untitled")); - if (checkall) + (buf->b_fname != NULL) ? + buf->b_fname : (char_u *)_("Untitled")); + if (checkall) { ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1); - else + } else { ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1); + } - /* Init ea pseudo-structure, this is needed for the check_overwrite() - * function. */ - ea.append = ea.forceit = FALSE; + // Init ea pseudo-structure, this is needed for the check_overwrite() + // function. + ea.append = ea.forceit = false; if (ret == VIM_YES) { - if (buf->b_fname != NULL && check_overwrite(&ea, buf, - buf->b_fname, buf->b_ffname, FALSE) == OK) - /* didn't hit Cancel */ - (void)buf_write_all(buf, FALSE); + if (buf->b_fname != NULL + && check_overwrite(&ea, + buf, + buf->b_fname, + buf->b_ffname, + false) == OK) { + // didn't hit Cancel + (void)buf_write_all(buf, false); + } } else if (ret == VIM_NO) { - unchanged(buf, TRUE); + unchanged(buf, true); } else if (ret == VIM_ALL) { - /* - * Write all modified files that can be written. - * Skip readonly buffers, these need to be confirmed - * individually. - */ + // Write all modified files that can be written. + // Skip readonly buffers, these need to be confirmed + // individually. FOR_ALL_BUFFERS(buf2) { if (bufIsChanged(buf2) - && (buf2->b_ffname != NULL - ) + && (buf2->b_ffname != NULL) && !buf2->b_p_ro) { - if (buf2->b_fname != NULL && check_overwrite(&ea, buf2, - buf2->b_fname, buf2->b_ffname, FALSE) == OK) - /* didn't hit Cancel */ - (void)buf_write_all(buf2, FALSE); - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf2)) + if (buf2->b_fname != NULL + && check_overwrite(&ea, buf2, buf2->b_fname, + buf2->b_ffname, false) == OK) { + // didn't hit Cancel + (void)buf_write_all(buf2, false); + } + // an autocommand may have deleted the buffer + if (!buf_valid(buf2)) { buf2 = firstbuf; + } } } } else if (ret == VIM_DISCARDALL) { - /* - * mark all buffers as unchanged - */ + // mark all buffers as unchanged FOR_ALL_BUFFERS(buf2) { - unchanged(buf2, TRUE); + unchanged(buf2, true); } } } -/* - * Return TRUE if the buffer "buf" can be abandoned, either by making it - * hidden, autowriting it or unloading it. - */ -int can_abandon(buf_T *buf, int forceit) +/// Return true if the buffer "buf" can be abandoned, either by making it +/// hidden, autowriting it or unloading it. +bool can_abandon(buf_T *buf, int forceit) { return P_HID(buf) || !bufIsChanged(buf) @@ -1352,16 +1336,16 @@ int can_abandon(buf_T *buf, int forceit) } -/* - * Add a buffer number to "bufnrs", unless it's already there. - */ +/// Add a buffer number to "bufnrs", unless it's already there. static void add_bufnum(int *bufnrs, int *bufnump, int nr) { int i; - for (i = 0; i < *bufnump; ++i) - if (bufnrs[i] == nr) + for (i = 0; i < *bufnump; i++) { + if (bufnrs[i] == nr) { return; + } + } bufnrs[*bufnump] = nr; *bufnump = *bufnump + 1; } @@ -1375,7 +1359,7 @@ static void add_bufnum(int *bufnrs, int *bufnump, int nr) /// @param[in] unload specifies whether to unload, instead of hide, the buffer. /// /// @returns true if any buffer is changed and cannot be abandoned -int check_changed_any(bool hidden, bool unload) +bool check_changed_any(bool hidden, bool unload) { bool ret = false; int save; @@ -1385,7 +1369,7 @@ int check_changed_any(bool hidden, bool unload) int *bufnrs; FOR_ALL_BUFFERS(buf) { - ++bufcount; + bufcount++; } if (bufcount == 0) { @@ -1394,16 +1378,16 @@ int check_changed_any(bool hidden, bool unload) bufnrs = xmalloc(sizeof(*bufnrs) * bufcount); - /* curbuf */ + // curbuf bufnrs[bufnum++] = curbuf->b_fnum; - /* buf in curtab */ + // buf in curtab FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer != curbuf) { add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); } } - /* buf in other tab */ + // buf in other tab FOR_ALL_TABS(tp) { if (tp != curtab) { FOR_ALL_WINDOWS_IN_TAB(wp, tp) { @@ -1412,59 +1396,60 @@ int check_changed_any(bool hidden, bool unload) } } - /* any other buf */ + // any other buf FOR_ALL_BUFFERS(buf) { add_bufnum(bufnrs, &bufnum, buf->b_fnum); } buf_T *buf = NULL; - for (i = 0; i < bufnum; ++i) { + for (i = 0; i < bufnum; i++) { buf = buflist_findnr(bufnrs[i]); - if (buf == NULL) + if (buf == NULL) { continue; + } if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf)) { - /* Try auto-writing the buffer. If this fails but the buffer no - * longer exists it's not changed, that's OK. */ + // Try auto-writing the buffer. If this fails but the buffer no + // longer exists it's not changed, that's OK. if (check_changed(buf, (p_awa ? CCGD_AW : 0) - | CCGD_MULTWIN - | CCGD_ALLBUF) && buf_valid(buf)) - break; /* didn't save - still changes */ + | CCGD_MULTWIN + | CCGD_ALLBUF) && buf_valid(buf)) { + break; // didn't save - still changes + } } } - if (i >= bufnum) + if (i >= bufnum) { goto theend; + } - ret = TRUE; - exiting = FALSE; - /* - * When ":confirm" used, don't give an error message. - */ + ret = true; + exiting = false; + // When ":confirm" used, don't give an error message. if (!(p_confirm || cmdmod.confirm)) { - /* There must be a wait_return for this message, do_buffer() - * may cause a redraw. But wait_return() is a no-op when vgetc() - * is busy (Quit used from window menu), then make sure we don't - * cause a scroll up. */ + // There must be a wait_return for this message, do_buffer() + // may cause a redraw. But wait_return() is a no-op when vgetc() + // is busy (Quit used from window menu), then make sure we don't + // cause a scroll up. if (vgetc_busy > 0) { msg_row = cmdline_row; msg_col = 0; - msg_didout = FALSE; + msg_didout = false; } if (EMSG2(_("E162: No write since last change for buffer \"%s\""), - buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname)) { + buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname)) { save = no_wait_return; - no_wait_return = FALSE; - wait_return(FALSE); + no_wait_return = false; + wait_return(false); no_wait_return = save; } } - /* Try to find a window that contains the buffer. */ + // Try to find a window that contains the buffer. if (buf != curbuf) { FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { goto_tabpage_win(tp, wp); - /* Paranoia: did autocms wipe out the buffer with changes? */ + // Paranoia: did autocmds wipe out the buffer with changes? if (!buf_valid(buf)) { goto theend; } @@ -1484,10 +1469,8 @@ theend: return ret; } -/* - * return FAIL if there is no file name, OK if there is one - * give error message for FAIL - */ +/// Return FAIL if there is no file name, OK if there is one. +/// Give error message for FAIL. int check_fname(void) { if (curbuf->b_ffname == NULL) { @@ -1497,19 +1480,17 @@ int check_fname(void) return OK; } -/* - * flush the contents of a buffer, unless it has no file name - * - * return FAIL for failure, OK otherwise - */ +/// Flush the contents of a buffer, unless it has no file name. +/// +/// @return FAIL for failure, OK otherwise int buf_write_all(buf_T *buf, int forceit) { int retval; buf_T *old_curbuf = curbuf; retval = (buf_write(buf, buf->b_ffname, buf->b_fname, - (linenr_T)1, buf->b_ml.ml_line_count, NULL, - FALSE, forceit, TRUE, FALSE)); + (linenr_T)1, buf->b_ml.ml_line_count, NULL, + false, forceit, true, false)); if (curbuf != old_curbuf) { msg_source(hl_attr(HLF_W)); MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)")); @@ -1517,37 +1498,35 @@ int buf_write_all(buf_T *buf, int forceit) return retval; } -/* - * Code to handle the argument list. - */ +/// Code to handle the argument list. #define AL_SET 1 #define AL_ADD 2 #define AL_DEL 3 -/* - * Isolate one argument, taking backticks. - * Changes the argument in-place, puts a NUL after it. Backticks remain. - * Return a pointer to the start of the next argument. - */ +/// Isolate one argument, taking backticks. +/// Changes the argument in-place, puts a NUL after it. Backticks remain. +/// Return a pointer to the start of the next argument. static char_u *do_one_arg(char_u *str) { char_u *p; - int inbacktick; + bool inbacktick; - inbacktick = FALSE; - for (p = str; *str; ++str) { - /* When the backslash is used for escaping the special meaning of a - * character we need to keep it until wildcard expansion. */ + inbacktick = false; + for (p = str; *str; str++) { + // When the backslash is used for escaping the special meaning of a + // character we need to keep it until wildcard expansion. if (rem_backslash(str)) { *p++ = *str++; *p++ = *str; } else { - /* An item ends at a space not in backticks */ - if (!inbacktick && ascii_isspace(*str)) + // An item ends at a space not in backticks + if (!inbacktick && ascii_isspace(*str)) { break; - if (*str == '`') - inbacktick ^= TRUE; + } + if (*str == '`') { + inbacktick ^= true; + } *p++ = *str; } } @@ -1557,26 +1536,23 @@ static char_u *do_one_arg(char_u *str) return str; } -/* - * Separate the arguments in "str" and return a list of pointers in the - * growarray "gap". - */ +/// Separate the arguments in "str" and return a list of pointers in the +/// growarray "gap". void get_arglist(garray_T *gap, char_u *str) { ga_init(gap, (int)sizeof(char_u *), 20); while (*str != NUL) { GA_APPEND(char_u *, gap, str); - /* Isolate one argument, change it in-place, put a NUL after it. */ + // Isolate one argument, change it in-place, put a NUL after it. str = do_one_arg(str); } } -/* - * Parse a list of arguments (file names), expand them and return in - * "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'. - * Return FAIL or OK. - */ +/// Parse a list of arguments (file names), expand them and return in +/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'. +/// +/// @return FAIL or OK. int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) { garray_T ga; @@ -1584,31 +1560,29 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) get_arglist(&ga, str); - if (wig) + if (wig) { i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND); - else + fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + } else { i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + } ga_clear(&ga); return i; } -/* - * "what" == AL_SET: Redefine the argument list to 'str'. - * "what" == AL_ADD: add files in 'str' to the argument list after "after". - * "what" == AL_DEL: remove files in 'str' from the argument list. - * - * Return FAIL for failure, OK otherwise. - */ -static int -do_arglist ( - char_u *str, - int what, - int after /* 0 means before first one */ -) +/// @param str +/// @param what +/// AL_SET: Redefine the argument list to 'str'. +/// AL_ADD: add files in 'str' to the argument list after "after". +/// AL_DEL: remove files in 'str' from the argument list. +/// @param after +/// 0 means before first one +/// +/// @return FAIL for failure, OK otherwise. +static int do_arglist(char_u *str, int what, int after) { garray_T new_ga; int exp_count; @@ -1616,54 +1590,63 @@ do_arglist ( char_u *p; int match; - /* - * Collect all file name arguments in "new_ga". - */ + // Set default argument for ":argadd" command. + if (what == AL_ADD && *str == NUL) { + if (curbuf->b_ffname == NULL) { + return FAIL; + } + str = curbuf->b_fname; + } + + // Collect all file name arguments in "new_ga". get_arglist(&new_ga, str); if (what == AL_DEL) { regmatch_T regmatch; - int didone; - - /* - * Delete the items: use each item as a regexp and find a match in the - * argument list. - */ - regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */ - for (int i = 0; i < new_ga.ga_len && !got_int; ++i) { + bool didone; + + // Delete the items: use each item as a regexp and find a match in the + // argument list. + regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set + for (int i = 0; i < new_ga.ga_len && !got_int; i++) { p = ((char_u **)new_ga.ga_data)[i]; - p = file_pat_to_reg_pat(p, NULL, NULL, FALSE); - if (p == NULL) + p = file_pat_to_reg_pat(p, NULL, NULL, false); + if (p == NULL) { break; + } regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); if (regmatch.regprog == NULL) { xfree(p); break; } - didone = FALSE; - for (match = 0; match < ARGCOUNT; ++match) + didone = false; + for (match = 0; match < ARGCOUNT; match++) { if (vim_regexec(®match, alist_name(&ARGLIST[match]), - (colnr_T)0)) { - didone = TRUE; + (colnr_T)0)) { + didone = true; xfree(ARGLIST[match].ae_fname); memmove(ARGLIST + match, ARGLIST + match + 1, (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T)); - --ALIST(curwin)->al_ga.ga_len; - if (curwin->w_arg_idx > match) - --curwin->w_arg_idx; - --match; + ALIST(curwin)->al_ga.ga_len--; + if (curwin->w_arg_idx > match) { + curwin->w_arg_idx--; + } + match--; } + } vim_regfree(regmatch.regprog); xfree(p); - if (!didone) + if (!didone) { EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]); + } } ga_clear(&new_ga); } else { int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, - &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); + &exp_count, &exp_files, + EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); if (i == FAIL || exp_count == 0) { EMSG(_(e_nomatch)); @@ -1673,8 +1656,9 @@ do_arglist ( if (what == AL_ADD) { (void)alist_add_list(exp_count, exp_files, after); xfree(exp_files); - } else /* what == AL_SET */ - alist_set(ALIST(curwin), exp_count, exp_files, FALSE, NULL, 0); + } else { // what == AL_SET + alist_set(ALIST(curwin), exp_count, exp_files, false, NULL, 0); + } } alist_check_arg_idx(); @@ -1682,9 +1666,7 @@ do_arglist ( return OK; } -/* - * Check the validity of the arg_idx for each other window. - */ +/// Check the validity of the arg_idx for each other window. static void alist_check_arg_idx(void) { FOR_ALL_TAB_WINDOWS(tp, win) { @@ -1694,136 +1676,123 @@ static void alist_check_arg_idx(void) } } -/* - * Return TRUE if window "win" is editing the file at the current argument - * index. - */ -static int editing_arg_idx(win_T *win) +/// Return true if window "win" is editing the file at the current argument +/// index. +static bool editing_arg_idx(win_T *win) { return !(win->w_arg_idx >= WARGCOUNT(win) || (win->w_buffer->b_fnum != WARGLIST(win)[win->w_arg_idx].ae_fnum && (win->w_buffer->b_ffname == NULL || !(path_full_compare( - alist_name(&WARGLIST(win)[win->w_arg_idx]), - win->w_buffer->b_ffname, TRUE) & kEqualFiles)))); + alist_name(&WARGLIST(win)[win->w_arg_idx]), + win->w_buffer->b_ffname, true) & kEqualFiles)))); } -/* - * Check if window "win" is editing the w_arg_idx file in its argument list. - */ +/// Check if window "win" is editing the w_arg_idx file in its argument list. void check_arg_idx(win_T *win) { if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) { - /* We are not editing the current entry in the argument list. - * Set "arg_had_last" if we are editing the last one. */ - win->w_arg_idx_invalid = TRUE; + // We are not editing the current entry in the argument list. + // Set "arg_had_last" if we are editing the last one. + win->w_arg_idx_invalid = true; if (win->w_arg_idx != WARGCOUNT(win) - 1 - && arg_had_last == FALSE + && arg_had_last == false && ALIST(win) == &global_alist && GARGCOUNT > 0 && win->w_arg_idx < GARGCOUNT && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum || (win->w_buffer->b_ffname != NULL && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]), - win->w_buffer->b_ffname, TRUE) & kEqualFiles)))) - arg_had_last = TRUE; + win->w_buffer->b_ffname, true) + & kEqualFiles)))) { + arg_had_last = true; + } } else { - /* We are editing the current entry in the argument list. - * Set "arg_had_last" if it's also the last one */ - win->w_arg_idx_invalid = FALSE; + // We are editing the current entry in the argument list. + // Set "arg_had_last" if it's also the last one + win->w_arg_idx_invalid = false; if (win->w_arg_idx == WARGCOUNT(win) - 1 - && win->w_alist == &global_alist - ) - arg_had_last = TRUE; + && win->w_alist == &global_alist) { + arg_had_last = true; + } } } -/* - * ":args", ":argslocal" and ":argsglobal". - */ +/// ":args", ":argslocal" and ":argsglobal". void ex_args(exarg_T *eap) { if (eap->cmdidx != CMD_args) { alist_unlink(ALIST(curwin)); - if (eap->cmdidx == CMD_argglobal) + if (eap->cmdidx == CMD_argglobal) { ALIST(curwin) = &global_alist; - else /* eap->cmdidx == CMD_arglocal */ + } else { // eap->cmdidx == CMD_arglocal alist_new(); + } } if (!ends_excmd(*eap->arg)) { - /* - * ":args file ..": define new argument list, handle like ":next" - * Also for ":argslocal file .." and ":argsglobal file ..". - */ + // ":args file ..": define new argument list, handle like ":next" + // Also for ":argslocal file .." and ":argsglobal file ..". ex_next(eap); - } else if (eap->cmdidx == CMD_args) { - /* - * ":args": list arguments. - */ + } else if (eap->cmdidx == CMD_args) { + // ":args": list arguments. if (ARGCOUNT > 0) { - /* Overwrite the command, for a short list there is no scrolling - * required and no wait_return(). */ - gotocmdline(TRUE); - for (int i = 0; i < ARGCOUNT; ++i) { - if (i == curwin->w_arg_idx) + // Overwrite the command, for a short list there is no scrolling + // required and no wait_return(). + gotocmdline(true); + for (int i = 0; i < ARGCOUNT; i++) { + if (i == curwin->w_arg_idx) { msg_putchar('['); + } msg_outtrans(alist_name(&ARGLIST[i])); - if (i == curwin->w_arg_idx) + if (i == curwin->w_arg_idx) { msg_putchar(']'); + } msg_putchar(' '); } } } else if (eap->cmdidx == CMD_arglocal) { garray_T *gap = &curwin->w_alist->al_ga; - /* - * ":argslocal": make a local copy of the global argument list. - */ + // ":argslocal": make a local copy of the global argument list. ga_grow(gap, GARGCOUNT); - for (int i = 0; i < GARGCOUNT; ++i) + for (int i = 0; i < GARGCOUNT; i++) { if (GARGLIST[i].ae_fname != NULL) { AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname = vim_strsave(GARGLIST[i].ae_fname); AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum = GARGLIST[i].ae_fnum; - ++gap->ga_len; + gap->ga_len++; } + } } } -/* - * ":previous", ":sprevious", ":Next" and ":sNext". - */ +/// ":previous", ":sprevious", ":Next" and ":sNext". void ex_previous(exarg_T *eap) { - /* If past the last one already, go to the last one. */ - if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) + // If past the last one already, go to the last one. + if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) { do_argfile(eap, ARGCOUNT - 1); - else + } else { do_argfile(eap, curwin->w_arg_idx - (int)eap->line2); + } } -/* - * ":rewind", ":first", ":sfirst" and ":srewind". - */ +/// ":rewind", ":first", ":sfirst" and ":srewind". void ex_rewind(exarg_T *eap) { do_argfile(eap, 0); } -/* - * ":last" and ":slast". - */ +/// ":last" and ":slast". void ex_last(exarg_T *eap) { do_argfile(eap, ARGCOUNT - 1); } -/* - * ":argument" and ":sargument". - */ +/// ":argument" and ":sargument". void ex_argument(exarg_T *eap) { int i; @@ -1836,9 +1805,7 @@ void ex_argument(exarg_T *eap) do_argfile(eap, i); } -/* - * Edit file "argn" of the argument lists. - */ +/// Edit file "argn" of the argument lists. void do_argfile(exarg_T *eap, int argn) { int other; @@ -1846,26 +1813,26 @@ void do_argfile(exarg_T *eap, int argn) int old_arg_idx = curwin->w_arg_idx; if (argn < 0 || argn >= ARGCOUNT) { - if (ARGCOUNT <= 1) + if (ARGCOUNT <= 1) { EMSG(_("E163: There is only one file to edit")); - else if (argn < 0) + } else if (argn < 0) { EMSG(_("E164: Cannot go before first file")); - else + } else { EMSG(_("E165: Cannot go beyond last file")); + } } else { setpcmark(); - /* split window or create new tab page first */ + // split window or create new tab page first if (*eap->cmd == 's' || cmdmod.tab != 0) { - if (win_split(0, 0) == FAIL) + if (win_split(0, 0) == FAIL) { return; + } RESET_BINDING(curwin); } else { - /* - * if 'hidden' set, only check for changed file when re-editing - * the same buffer - */ - other = TRUE; + // if 'hidden' set, only check for changed file when re-editing + // the same buffer + other = true; if (P_HID(curbuf)) { p = (char_u *)fix_fname((char *)alist_name(&ARGLIST[argn])); other = otherfile(p); @@ -1873,101 +1840,97 @@ void do_argfile(exarg_T *eap, int argn) } if ((!P_HID(curbuf) || !other) && check_changed(curbuf, CCGD_AW - | (other ? 0 : CCGD_MULTWIN) - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) + | (other ? 0 : CCGD_MULTWIN) + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) { return; + } } curwin->w_arg_idx = argn; if (argn == ARGCOUNT - 1 - && curwin->w_alist == &global_alist - ) - arg_had_last = TRUE; + && curwin->w_alist == &global_alist) { + arg_had_last = true; + } - /* Edit the file; always use the last known line number. - * When it fails (e.g. Abort for already edited file) restore the - * argument index. */ + // Edit the file; always use the last known line number. + // When it fails (e.g. Abort for already edited file) restore the + // argument index. if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL, - eap, ECMD_LAST, - (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) - + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) + eap, ECMD_LAST, + (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) + + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) { curwin->w_arg_idx = old_arg_idx; - /* like Vi: set the mark where the cursor is in the file. */ - else if (eap->cmdidx != CMD_argdo) + } else if (eap->cmdidx != CMD_argdo) { + // like Vi: set the mark where the cursor is in the file. setmark('\''); + } } } -/* - * ":next", and commands that behave like it. - */ +/// ":next", and commands that behave like it. void ex_next(exarg_T *eap) { int i; - /* - * check for changed buffer now, if this fails the argument list is not - * redefined. - */ - if ( P_HID(curbuf) - || eap->cmdidx == CMD_snext - || !check_changed(curbuf, CCGD_AW - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) { - if (*eap->arg != NUL) { /* redefine file list */ - if (do_arglist(eap->arg, AL_SET, 0) == FAIL) + // check for changed buffer now, if this fails the argument list is not + // redefined. + if (P_HID(curbuf) + || eap->cmdidx == CMD_snext + || !check_changed(curbuf, CCGD_AW + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) { + if (*eap->arg != NUL) { // redefine file list + if (do_arglist(eap->arg, AL_SET, 0) == FAIL) { return; + } i = 0; - } else + } else { i = curwin->w_arg_idx + (int)eap->line2; + } do_argfile(eap, i); } } -/* - * ":argedit" - */ +/// ":argedit" void ex_argedit(exarg_T *eap) { int fnum; int i; char_u *s; - /* Add the argument to the buffer list and get the buffer number. */ + // Add the argument to the buffer list and get the buffer number. fnum = buflist_add(eap->arg, BLN_LISTED); - /* Check if this argument is already in the argument list. */ - for (i = 0; i < ARGCOUNT; ++i) - if (ARGLIST[i].ae_fnum == fnum) + // Check if this argument is already in the argument list. + for (i = 0; i < ARGCOUNT; i++) { + if (ARGLIST[i].ae_fnum == fnum) { break; + } + } if (i == ARGCOUNT) { - /* Can't find it, add it to the argument list. */ + // Can't find it, add it to the argument list. s = vim_strsave(eap->arg); - i = alist_add_list(1, &s, - eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1); + int after = eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1; + i = alist_add_list(1, &s, after); curwin->w_arg_idx = i; } alist_check_arg_idx(); - /* Edit the argument. */ + // Edit the argument. do_argfile(eap, i); } -/* - * ":argadd" - */ +/// ":argadd" void ex_argadd(exarg_T *eap) { do_arglist(eap->arg, AL_ADD, - eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1); + eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1); maketitle(); } -/* - * ":argdelete" - */ +/// ":argdelete" void ex_argdelete(exarg_T *eap) { if (eap->addr_count > 0) { @@ -1979,7 +1942,7 @@ void ex_argdelete(exarg_T *eap) if (*eap->arg != NUL || n <= 0) { EMSG(_(e_invarg)); } else { - for (linenr_T i = eap->line1; i <= eap->line2; ++i) { + for (linenr_T i = eap->line1; i <= eap->line2; i++) { xfree(ARGLIST[i - 1].ae_fname); } memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, @@ -1990,17 +1953,21 @@ void ex_argdelete(exarg_T *eap) } else if (curwin->w_arg_idx > eap->line1) { curwin->w_arg_idx = (int)eap->line1; } + if (ARGCOUNT == 0) { + curwin->w_arg_idx = 0; + } else if (curwin->w_arg_idx >= ARGCOUNT) { + curwin->w_arg_idx = ARGCOUNT - 1; + } } - } else if (*eap->arg == NUL) + } else if (*eap->arg == NUL) { EMSG(_(e_argreq)); - else + } else { do_arglist(eap->arg, AL_DEL, 0); + } maketitle(); } -/* - * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" - */ +/// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" void ex_listdo(exarg_T *eap) { int i; @@ -2010,10 +1977,11 @@ void ex_listdo(exarg_T *eap) char_u *save_ei = NULL; char_u *p_shm_save; - if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) - /* Don't do syntax HL autocommands. Skipping the syntax file is a - * great speed improvement. */ + if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) { + // Don't do syntax HL autocommands. Skipping the syntax file is a + // great speed improvement. save_ei = au_event_disable(",Syntax"); + } start_global_changes(); @@ -2021,36 +1989,36 @@ void ex_listdo(exarg_T *eap) || eap->cmdidx == CMD_tabdo || P_HID(curbuf) || !check_changed(curbuf, CCGD_AW - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) { + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) { i = 0; - /* start at the eap->line1 argument/window/buffer */ + // start at the eap->line1 argument/window/buffer wp = firstwin; tp = first_tabpage; switch (eap->cmdidx) { - case CMD_windo: - for (; wp != NULL && i + 1 < eap->line1; wp = wp->w_next) { - i++; - } - break; - case CMD_tabdo: - for (; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next) { - i++; - } - break; - case CMD_argdo: - i = (int)eap->line1 - 1; - break; - default: - break; + case CMD_windo: + for (; wp != NULL && i + 1 < eap->line1; wp = wp->w_next) { + i++; + } + break; + case CMD_tabdo: + for (; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next) { + i++; + } + break; + case CMD_argdo: + i = (int)eap->line1 - 1; + break; + default: + break; } buf_T *buf = curbuf; size_t qf_size = 0; - /* set pcmark now */ + // set pcmark now if (eap->cmdidx == CMD_bufdo) { - /* Advance to the first listed buffer after "eap->line1". */ + // Advance to the first listed buffer after "eap->line1". for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1 || !buf->b_p_bl); buf = buf->b_next) { @@ -2082,64 +2050,71 @@ void ex_listdo(exarg_T *eap) } else { setpcmark(); } - listcmd_busy = TRUE; /* avoids setting pcmark below */ + listcmd_busy = true; // avoids setting pcmark below while (!got_int && buf != NULL) { if (eap->cmdidx == CMD_argdo) { - /* go to argument "i" */ - if (i == ARGCOUNT) + // go to argument "i" + if (i == ARGCOUNT) { break; - /* Don't call do_argfile() when already there, it will try - * reloading the file. */ + } + // Don't call do_argfile() when already there, it will try + // reloading the file. if (curwin->w_arg_idx != i || !editing_arg_idx(curwin)) { - /* Clear 'shm' to avoid that the file message overwrites - * any output from the command. */ + // Clear 'shm' to avoid that the file message overwrites + // any output from the command. p_shm_save = vim_strsave(p_shm); set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); do_argfile(eap, i); set_option_value((char_u *)"shm", 0L, p_shm_save, 0); xfree(p_shm_save); } - if (curwin->w_arg_idx != i) + if (curwin->w_arg_idx != i) { break; + } } else if (eap->cmdidx == CMD_windo) { - /* go to window "wp" */ - if (!win_valid(wp)) + // go to window "wp" + if (!win_valid(wp)) { break; + } assert(wp); win_goto(wp); - if (curwin != wp) - break; /* something must be wrong */ + if (curwin != wp) { + break; // something must be wrong + } wp = curwin->w_next; } else if (eap->cmdidx == CMD_tabdo) { - /* go to window "tp" */ - if (!valid_tabpage(tp)) + // go to window "tp" + if (!valid_tabpage(tp)) { break; + } assert(tp); - goto_tabpage_tp(tp, TRUE, TRUE); + goto_tabpage_tp(tp, true, true); tp = tp->tp_next; } else if (eap->cmdidx == CMD_bufdo) { - /* Remember the number of the next listed buffer, in case - * ":bwipe" is used or autocommands do something strange. */ + // Remember the number of the next listed buffer, in case + // ":bwipe" is used or autocommands do something strange. next_fnum = -1; - for (buf_T *buf = curbuf->b_next; buf != NULL; buf = buf->b_next) + for (buf_T *buf = curbuf->b_next; buf != NULL; buf = buf->b_next) { if (buf->b_p_bl) { next_fnum = buf->b_fnum; break; } + } } - ++i; - /* execute the command */ + i++; + // execute the command do_cmdline(eap->arg, eap->getline, eap->cookie, - DOCMD_VERBOSE + DOCMD_NOWAIT); + DOCMD_VERBOSE + DOCMD_NOWAIT); if (eap->cmdidx == CMD_bufdo) { - /* Done? */ - if (next_fnum < 0 || next_fnum > eap->line2) + // Done? + if (next_fnum < 0 || next_fnum > eap->line2) { break; + } - /* Check if the buffer still exists. */ + // Check if the buffer still exists. bool buf_still_exists = false; FOR_ALL_BUFFERS(bp) { if (bp->b_fnum == next_fnum) { @@ -2151,8 +2126,8 @@ void ex_listdo(exarg_T *eap) break; } - /* Go to the next buffer. Clear 'shm' to avoid that the file - * message overwrites any output from the command. */ + // Go to the next buffer. Clear 'shm' to avoid that the file + // message overwrites any output from the command. p_shm_save = vim_strsave(p_shm); set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum); @@ -2178,15 +2153,16 @@ void ex_listdo(exarg_T *eap) // If jumping to the next quickfix entry fails, quit here. if (qf_get_cur_idx(eap) == qf_idx) { - break; + break; } } if (eap->cmdidx == CMD_windo) { - validate_cursor(); /* cursor may have moved */ - /* required when 'scrollbind' has been set */ - if (curwin->w_p_scb) - do_check_scrollbind(TRUE); + validate_cursor(); // cursor may have moved + // required when 'scrollbind' has been set + if (curwin->w_p_scb) { + do_check_scrollbind(true); + } } if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo) { if (i + 1 > eap->line2) { @@ -2197,54 +2173,53 @@ void ex_listdo(exarg_T *eap) break; } } - listcmd_busy = FALSE; + listcmd_busy = false; } if (save_ei != NULL) { au_event_restore(save_ei); apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, - curbuf->b_fname, TRUE, curbuf); + curbuf->b_fname, true, curbuf); } end_global_changes(); } -/* - * Add files[count] to the arglist of the current window after arg "after". - * The file names in files[count] must have been allocated and are taken over. - * Files[] itself is not taken over. - * Returns index of first added argument. - */ -static int -alist_add_list ( - int count, - char_u **files, - int after /* where to add: 0 = before first one */ -) +/// Add files[count] to the arglist of the current window after arg "after". +/// The file names in files[count] must have been allocated and are taken over. +/// Files[] itself is not taken over. +/// +/// @param after: where to add: 0 = before first one +/// +/// @return index of first added argument +static int alist_add_list(int count, char_u **files, int after) { + int old_argcount = ARGCOUNT; ga_grow(&ALIST(curwin)->al_ga, count); { - if (after < 0) + if (after < 0) { after = 0; - if (after > ARGCOUNT) + } + if (after > ARGCOUNT) { after = ARGCOUNT; - if (after < ARGCOUNT) + } + if (after < ARGCOUNT) { memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); - for (int i = 0; i < count; ++i) { + } + for (int i = 0; i < count; i++) { ARGLIST[after + i].ae_fname = files[i]; ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); } ALIST(curwin)->al_ga.ga_len += count; - if (curwin->w_arg_idx >= after) - ++curwin->w_arg_idx; + if (old_argcount > 0 && curwin->w_arg_idx >= after) { + curwin->w_arg_idx += count; + } return after; } } -/* - * ":compiler[!] {name}" - */ +/// ":compiler[!] {name}" void ex_compiler(exarg_T *eap) { char_u *buf; @@ -2252,134 +2227,148 @@ void ex_compiler(exarg_T *eap) char_u *p; if (*eap->arg == NUL) { - /* List all compiler scripts. */ - do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.vim')"); - /* ) keep the indenter happy... */ + // List all compiler scripts. + do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.vim')"); // NOLINT } else { - buf = xmalloc(STRLEN(eap->arg) + 14); + size_t bufsize = STRLEN(eap->arg) + 14; + buf = xmalloc(bufsize); if (eap->forceit) { - /* ":compiler! {name}" sets global options */ + // ":compiler! {name}" sets global options do_cmdline_cmd("command -nargs=* CompilerSet set <args>"); } else { - /* ":compiler! {name}" sets local options. - * To remain backwards compatible "current_compiler" is always - * used. A user's compiler plugin may set it, the distributed - * plugin will then skip the settings. Afterwards set - * "b:current_compiler" and restore "current_compiler". - * Explicitly prepend "g:" to make it work in a function. */ + // ":compiler! {name}" sets local options. + // To remain backwards compatible "current_compiler" is always + // used. A user's compiler plugin may set it, the distributed + // plugin will then skip the settings. Afterwards set + // "b:current_compiler" and restore "current_compiler". + // Explicitly prepend "g:" to make it work in a function. old_cur_comp = get_var_value((char_u *)"g:current_compiler"); - if (old_cur_comp != NULL) + if (old_cur_comp != NULL) { old_cur_comp = vim_strsave(old_cur_comp); + } do_cmdline_cmd("command -nargs=* CompilerSet setlocal <args>"); } - do_unlet((char_u *)"g:current_compiler", TRUE); - do_unlet((char_u *)"b:current_compiler", TRUE); + do_unlet((char_u *)"g:current_compiler", true); + do_unlet((char_u *)"b:current_compiler", true); - sprintf((char *)buf, "compiler/%s.vim", eap->arg); - if (source_runtime(buf, TRUE) == FAIL) + snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); + if (source_runtime(buf, DIP_ALL) == FAIL) { EMSG2(_("E666: compiler not supported: %s"), eap->arg); + } xfree(buf); do_cmdline_cmd(":delcommand CompilerSet"); - /* Set "b:current_compiler" from "current_compiler". */ + // Set "b:current_compiler" from "current_compiler". p = get_var_value((char_u *)"g:current_compiler"); - if (p != NULL) + if (p != NULL) { set_internal_string_var((char_u *)"b:current_compiler", p); + } - /* Restore "current_compiler" for ":compiler {name}". */ + // Restore "current_compiler" for ":compiler {name}". if (!eap->forceit) { if (old_cur_comp != NULL) { set_internal_string_var((char_u *)"g:current_compiler", - old_cur_comp); + old_cur_comp); xfree(old_cur_comp); - } else - do_unlet((char_u *)"g:current_compiler", TRUE); + } else { + do_unlet((char_u *)"g:current_compiler", true); + } } } } -/* - * ":runtime {name}" - */ +/// ":runtime [what] {name}" void ex_runtime(exarg_T *eap) { - source_runtime(eap->arg, eap->forceit); + char_u *arg = eap->arg; + char_u *p = skiptowhite(arg); + ptrdiff_t len = p - arg; + int flags = eap->forceit ? DIP_ALL : 0; + + if (STRNCMP(arg, "START", len) == 0) { + flags += DIP_START + DIP_NORTP; + arg = skipwhite(arg + len); + } else if (STRNCMP(arg, "OPT", len) == 0) { + flags += DIP_OPT + DIP_NORTP; + arg = skipwhite(arg + len); + } else if (STRNCMP(arg, "PACK", len) == 0) { + flags += DIP_START + DIP_OPT + DIP_NORTP; + arg = skipwhite(arg + len); + } else if (STRNCMP(arg, "ALL", len) == 0) { + flags += DIP_START + DIP_OPT; + arg = skipwhite(arg + len); + } + + source_runtime(arg, flags); } static void source_callback(char_u *fname, void *cookie) { - (void)do_source(fname, FALSE, DOSO_NONE); + (void)do_source(fname, false, DOSO_NONE); } -/* - * Source the file "name" from all directories in 'runtimepath'. - * "name" can contain wildcards. - * When "all" is TRUE, source all files, otherwise only the first one. - * return FAIL when no file could be sourced, OK otherwise. - */ -int source_runtime(char_u *name, int all) +/// Source the file "name" from all directories in 'runtimepath'. +/// "name" can contain wildcards. +/// When "flags" has DIP_ALL: source all files, otherwise only the first one. +/// +/// return FAIL when no file could be sourced, OK otherwise. +int source_runtime(char_u *name, int flags) { - return do_in_runtimepath(name, all, source_callback, NULL); + return do_in_runtimepath(name, flags, source_callback, NULL); } -/* - * Find "name" in 'runtimepath'. When found, invoke the callback function for - * it: callback(fname, "cookie") - * When "all" is TRUE repeat for all matches, otherwise only the first one is - * used. - * Returns OK when at least one match found, FAIL otherwise. - * - * If "name" is NULL calls callback for each entry in runtimepath. Cookie is - * passed by reference in this case, setting it to NULL indicates that callback - * has done its job. - */ -int do_in_runtimepath(char_u *name, int all, DoInRuntimepathCB callback, - void *cookie) +/// Find the file "name" in all directories in "path" and invoke +/// "callback(fname, cookie)". +/// "name" can contain wildcards. +/// When "flags" has DIP_ALL: source all files, otherwise only the first one. +/// When "flags" has DIP_DIR: find directories instead of files. +/// When "flags" has DIP_ERR: give an error message if there is no match. +/// +/// return FAIL when no file could be sourced, OK otherwise. +int do_in_path(char_u *path, char_u *name, int flags, + DoInRuntimepathCB callback, void *cookie) { - char_u *rtp; - char_u *np; - char_u *buf; - char_u *rtp_copy; char_u *tail; int num_files; char_u **files; int i; - int did_one = FALSE; + bool did_one = false; - /* Make a copy of 'runtimepath'. Invoking the callback may change the - * value. */ - rtp_copy = vim_strsave(p_rtp); - buf = xmallocz(MAXPATHL); + // Make a copy of 'runtimepath'. Invoking the callback may change the + // value. + char_u *rtp_copy = vim_strsave(path); + char_u *buf = xmallocz(MAXPATHL); { if (p_verbose > 1 && name != NULL) { verbose_enter(); smsg(_("Searching for \"%s\" in \"%s\""), - (char *)name, (char *)p_rtp); + (char *)name, (char *)path); verbose_leave(); } - /* Loop over all entries in 'runtimepath'. */ - rtp = rtp_copy; - while (*rtp != NUL && (all || !did_one)) { - /* Copy the path from 'runtimepath' to buf[]. */ + // Loop over all entries in 'runtimepath'. + char_u *rtp = rtp_copy; + while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) { + // Copy the path from 'runtimepath' to buf[]. copy_option_part(&rtp, buf, MAXPATHL, ","); if (name == NULL) { - (*callback)(buf, (void *) &cookie); - if (!did_one) + (*callback)(buf, (void *)&cookie); + if (!did_one) { did_one = (cookie == NULL); + } } else if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL) { add_pathsep((char *)buf); tail = buf + STRLEN(buf); - /* Loop over all patterns in "name" */ - np = name; - while (*np != NUL && (all || !did_one)) { - /* Append the pattern from "name" to buf[]. */ + // Loop over all patterns in "name" + char_u *np = name; + while (*np != NUL && ((flags & DIP_ALL) || !did_one)) { + // Append the pattern from "name" to buf[]. assert(MAXPATHL >= (tail - buf)); copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)), - "\t "); + "\t "); if (p_verbose > 2) { verbose_enter(); @@ -2387,14 +2376,16 @@ int do_in_runtimepath(char_u *name, int all, DoInRuntimepathCB callback, verbose_leave(); } - /* Expand wildcards, invoke the callback for each match. */ + // Expand wildcards, invoke the callback for each match. if (gen_expand_wildcards(1, &buf, &num_files, &files, - EW_FILE) == OK) { - for (i = 0; i < num_files; ++i) { + (flags & DIP_DIR) ? EW_DIR + : EW_FILE) == OK) { + for (i = 0; i < num_files; i++) { (*callback)(files[i], cookie); - did_one = TRUE; - if (!all) + did_one = true; + if (!(flags & DIP_ALL)) { break; + } } FreeWild(num_files, files); } @@ -2404,27 +2395,235 @@ int do_in_runtimepath(char_u *name, int all, DoInRuntimepathCB callback, } xfree(buf); xfree(rtp_copy); - if (p_verbose > 0 && !did_one && name != NULL) { - verbose_enter(); - smsg(_("not found in 'runtimepath': \"%s\""), name); - verbose_leave(); + if (!did_one && name != NULL) { + char *basepath = path == p_rtp ? "runtimepath" : "packpath"; + + if (flags & DIP_ERR) { + EMSG3(_(e_dirnotf), basepath, name); + } else if (p_verbose > 0) { + verbose_enter(); + smsg(_("not found in '%s': \"%s\""), basepath, name); + verbose_leave(); + } } return did_one ? OK : FAIL; } -/* - * ":options" - */ +/// Find "name" in 'runtimepath'. When found, invoke the callback function for +/// it: callback(fname, "cookie") +/// When "flags" has DIP_ALL repeat for all matches, otherwise only the first +/// one is used. +/// Returns OK when at least one match found, FAIL otherwise. +/// If "name" is NULL calls callback for each entry in runtimepath. Cookie is +/// passed by reference in this case, setting it to NULL indicates that callback +/// has done its job. +int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, + void *cookie) +{ + int done = FAIL; + + if ((flags & DIP_NORTP) == 0) { + done = do_in_path(p_rtp, name, flags, callback, cookie); + } + + if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) { + char *start_dir = "pack/*/start/*/%s"; // NOLINT + size_t len = STRLEN(start_dir) + STRLEN(name); + char_u *s = xmallocz(len); + + vim_snprintf((char *)s, len, start_dir, name); + done = do_in_path(p_pp, s, flags, callback, cookie); + + xfree(s); + } + + if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT)) { + char *opt_dir = "pack/*/opt/*/%s"; // NOLINT + size_t len = STRLEN(opt_dir) + STRLEN(name); + char_u *s = xmallocz(len); + + vim_snprintf((char *)s, len, opt_dir, name); + done = do_in_path(p_pp, s, flags, callback, cookie); + + xfree(s); + } + + return done; +} + +// Expand wildcards in "pat" and invoke do_source() for each match. +static void source_all_matches(char_u *pat) +{ + int num_files; + char_u **files; + + if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) { + for (int i = 0; i < num_files; i++) { + (void)do_source(files[i], false, DOSO_NONE); + } + FreeWild(num_files, files); + } +} + +// used for "cookie" of add_pack_plugin() +static int APP_ADD_DIR; +static int APP_LOAD; +static int APP_BOTH; + +static void add_pack_plugin(char_u *fname, void *cookie) +{ + char_u *p4, *p3, *p2, *p1, *p; + char_u *new_rtp; + char_u *ffname = (char_u *)fix_fname((char *)fname); + + if (ffname == NULL) { + return; + } + + if (cookie != &APP_LOAD && strstr((char *)p_rtp, (char *)ffname) == NULL) { + // directory is not yet in 'runtimepath', add it + p4 = p3 = p2 = p1 = get_past_head(ffname); + for (p = p1; *p; mb_ptr_adv(p)) { + if (vim_ispathsep_nocolon(*p)) { + p4 = p3; p3 = p2; p2 = p1; p1 = p; + } + } + + // now we have: + // rtp/pack/name/start/name + // p4 p3 p2 p1 + // + // find the part up to "pack" in 'runtimepath' + char_u c = *p4; + *p4 = NUL; + + // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences + size_t fname_len = STRLEN(ffname); + char_u *insp = p_rtp; + for (;;) { + if (vim_fnamencmp(insp, ffname, fname_len) == 0) { + break; + } + insp = vim_strchr(insp, ','); + if (insp == NULL) { + break; + } + insp++; + } + + if (insp == NULL) { + // not found, append at the end + insp = p_rtp + STRLEN(p_rtp); + } else { + // append after the matching directory. + insp += STRLEN(ffname); + while (*insp != NUL && *insp != ',') { + insp++; + } + } + *p4 = c; + + // check if rtp/pack/name/start/name/after exists + char *afterdir = concat_fnames((char *)ffname, "after", true); + size_t afterlen = 0; + if (os_isdir((char_u *)afterdir)) { + afterlen = STRLEN(afterdir) + 1; // add one for comma + } + + size_t oldlen = STRLEN(p_rtp); + size_t addlen = STRLEN(ffname) + 1; // add one for comma + new_rtp = try_malloc(oldlen + addlen + afterlen + 1); // add one for NUL + if (new_rtp == NULL) { + goto theend; + } + uintptr_t keep = (uintptr_t)(insp - p_rtp); + memmove(new_rtp, p_rtp, keep); + new_rtp[keep] = ','; + memmove(new_rtp + keep + 1, ffname, addlen); + if (p_rtp[keep] != NUL) { + memmove(new_rtp + keep + addlen, p_rtp + keep, + oldlen - keep + 1); + } + if (afterlen > 0) { + STRCAT(new_rtp, ","); + STRCAT(new_rtp, afterdir); + } + set_option_value((char_u *)"rtp", 0L, new_rtp, 0); + xfree(new_rtp); + xfree(afterdir); + } + + if (cookie != &APP_ADD_DIR) { + static const char *plugpat = "%s/plugin/*.vim"; // NOLINT + static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT + + size_t len = STRLEN(ffname) + STRLEN(ftpat); + char_u *pat = try_malloc(len + 1); + if (pat == NULL) { + goto theend; + } + vim_snprintf((char *)pat, len, plugpat, ffname); + source_all_matches(pat); + + char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes"); + + // If runtime/filetype.vim wasn't loaded yet, the scripts will be + // found when it loads. + if (eval_to_number(cmd) > 0) { + do_cmdline_cmd("augroup filetypedetect"); + vim_snprintf((char *)pat, len, ftpat, ffname); + source_all_matches(pat); + do_cmdline_cmd("augroup END"); + } + xfree(cmd); + xfree(pat); + } + +theend: + xfree(ffname); +} + +static bool did_source_packages = false; + +// ":packloadall" +// Find plugins in the package directories and source them. +void ex_packloadall(exarg_T *eap) +{ + if (!did_source_packages || (eap != NULL && eap->forceit)) { + did_source_packages = true; + + // First do a round to add all directories to 'runtimepath', then load + // the plugins. This allows for plugins to use an autoload directory + // of another plugin. + do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT + add_pack_plugin, &APP_ADD_DIR); + do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT + add_pack_plugin, &APP_LOAD); + } +} + +/// ":packadd[!] {name}" +void ex_packadd(exarg_T *eap) +{ + static const char *plugpat = "pack/*/opt/%s"; // NOLINT + + size_t len = STRLEN(plugpat) + STRLEN(eap->arg); + char *pat = (char *)xmallocz(len); + vim_snprintf(pat, len, plugpat, eap->arg); + do_in_path(p_pp, (char_u *)pat, DIP_ALL + DIP_DIR + DIP_ERR, add_pack_plugin, + eap->forceit ? &APP_ADD_DIR : &APP_BOTH); + xfree(pat); +} + +/// ":options" void ex_options(exarg_T *eap) { cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); } -/* - * ":source {fname}" - */ +/// ":source {fname}" void ex_source(exarg_T *eap) { cmd_source(eap->arg, eap); @@ -2432,96 +2631,82 @@ void ex_source(exarg_T *eap) static void cmd_source(char_u *fname, exarg_T *eap) { - if (*fname == NUL) + if (*fname == NUL) { EMSG(_(e_argreq)); - - else if (eap != NULL && eap->forceit) - /* ":source!": read Normal mode commands - * Need to execute the commands directly. This is required at least - * for: - * - ":g" command busy - * - after ":argdo", ":windo" or ":bufdo" - * - another command follows - * - inside a loop - */ + } else if (eap != NULL && eap->forceit) { + // ":source!": read Normal mode commands + // Need to execute the commands directly. This is required at least + // for: + // - ":g" command busy + // - after ":argdo", ":windo" or ":bufdo" + // - another command follows + // - inside a loop openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL - || eap->cstack->cs_idx >= 0 - ); + || eap->cstack->cs_idx >= 0); - /* ":source" read ex commands */ - else if (do_source(fname, FALSE, DOSO_NONE) == FAIL) + // ":source" read ex commands + } else if (do_source(fname, false, DOSO_NONE) == FAIL) { EMSG2(_(e_notopen), fname); + } } -/* - * ":source" and associated commands. - */ - -/* - * Return the address holding the next breakpoint line for a source cookie. - */ +/// ":source" and associated commands. +/// +/// @return address holding the next breakpoint line for a source cookie linenr_T *source_breakpoint(void *cookie) { return &((struct source_cookie *)cookie)->breakpoint; } -/* - * Return the address holding the debug tick for a source cookie. - */ +/// Return the address holding the debug tick for a source cookie. int *source_dbg_tick(void *cookie) { return &((struct source_cookie *)cookie)->dbg_tick; } -/* - * Return the nesting level for a source cookie. - */ +/// Return the nesting level for a source cookie. int source_level(void *cookie) { return ((struct source_cookie *)cookie)->level; } - -#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC) -# define USE_FOPEN_NOINH -/* - * Special function to open a file without handle inheritance. - * When possible the handle is closed on exec(). - */ +/// Special function to open a file without handle inheritance. +/// If possible the handle is closed on exec(). static FILE *fopen_noinh_readbin(char *filename) { +#ifdef WIN32 + int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0); +#else int fd_tmp = os_open(filename, O_RDONLY, 0); +#endif - if (fd_tmp < 0) + if (fd_tmp < 0) { return NULL; + } -# ifdef HAVE_FD_CLOEXEC +#ifdef HAVE_FD_CLOEXEC { int fdflags = fcntl(fd_tmp, F_GETFD); if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); } } -# endif +#endif return fdopen(fd_tmp, READBIN); } -#endif -/* - * do_source: Read the file "fname" and execute its lines as EX commands. - * - * This function may be called recursively! - * - * return FAIL if file could not be opened, OK otherwise - */ -int -do_source ( - char_u *fname, - int check_other, /* check for .vimrc and _vimrc */ - int is_vimrc /* DOSO_ value */ -) +/// Read the file "fname" and execute its lines as EX commands. +/// +/// This function may be called recursively! +/// +/// @param fname +/// @param check_other check for .vimrc and _vimrc +/// @param is_vimrc DOSO_ value +/// +/// @return FAIL if file could not be opened, OK otherwise +int do_source(char_u *fname, int check_other, int is_vimrc) { struct source_cookie cookie; char_u *save_sourcing_name; @@ -2538,125 +2723,113 @@ do_source ( proftime_T wait_start; p = expand_env_save(fname); - if (p == NULL) + if (p == NULL) { return retval; + } fname_exp = (char_u *)fix_fname((char *)p); xfree(p); - if (fname_exp == NULL) + if (fname_exp == NULL) { return retval; + } if (os_isdir(fname_exp)) { smsg(_("Cannot source a directory: \"%s\""), fname); goto theend; } - /* Apply SourceCmd autocommands, they should get the file and source it. */ + // Apply SourceCmd autocommands, they should get the file and source it. if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL) && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp, - FALSE, curbuf)) { + false, curbuf)) { retval = aborting() ? FAIL : OK; goto theend; } - /* Apply SourcePre autocommands, they may get the file. */ - apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf); + // Apply SourcePre autocommands, they may get the file. + apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf); -#ifdef USE_FOPEN_NOINH cookie.fp = fopen_noinh_readbin((char *)fname_exp); -#else - cookie.fp = mch_fopen((char *)fname_exp, READBIN); -#endif if (cookie.fp == NULL && check_other) { - /* - * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, - * and ".exrc" by "_exrc" or vice versa. - */ + // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, + // and ".exrc" by "_exrc" or vice versa. p = path_tail(fname_exp); if ((*p == '.' || *p == '_') && (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) { - if (*p == '_') - *p = '.'; - else - *p = '_'; -#ifdef USE_FOPEN_NOINH + *p = (*p == '_') ? '.' : '_'; cookie.fp = fopen_noinh_readbin((char *)fname_exp); -#else - cookie.fp = mch_fopen((char *)fname_exp, READBIN); -#endif } } if (cookie.fp == NULL) { if (p_verbose > 0) { verbose_enter(); - if (sourcing_name == NULL) + if (sourcing_name == NULL) { smsg(_("could not source \"%s\""), fname); - else + } else { smsg(_("line %" PRId64 ": could not source \"%s\""), - (int64_t)sourcing_lnum, fname); + (int64_t)sourcing_lnum, fname); + } verbose_leave(); } goto theend; } - /* - * The file exists. - * - In verbose mode, give a message. - * - For a vimrc file, may want to call vimrc_found(). - */ + // The file exists. + // - In verbose mode, give a message. + // - For a vimrc file, may want to call vimrc_found(). if (p_verbose > 1) { verbose_enter(); - if (sourcing_name == NULL) + if (sourcing_name == NULL) { smsg(_("sourcing \"%s\""), fname); - else + } else { smsg(_("line %" PRId64 ": sourcing \"%s\""), - (int64_t)sourcing_lnum, fname); + (int64_t)sourcing_lnum, fname); + } verbose_leave(); } - if (is_vimrc == DOSO_VIMRC) + if (is_vimrc == DOSO_VIMRC) { vimrc_found(fname_exp, (char_u *)"MYVIMRC"); - else if (is_vimrc == DOSO_GVIMRC) + } else if (is_vimrc == DOSO_GVIMRC) { vimrc_found(fname_exp, (char_u *)"MYGVIMRC"); + } #ifdef USE_CRNL - /* If no automatic file format: Set default to CR-NL. */ - if (*p_ffs == NUL) + // If no automatic file format: Set default to CR-NL. + if (*p_ffs == NUL) { cookie.fileformat = EOL_DOS; - else + } else { cookie.fileformat = EOL_UNKNOWN; - cookie.error = FALSE; + } + cookie.error = false; #endif cookie.nextline = NULL; - cookie.finished = FALSE; + cookie.finished = false; - /* - * Check if this script has a breakpoint. - */ - cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0); + // Check if this script has a breakpoint. + cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, (linenr_T)0); cookie.fname = fname_exp; cookie.dbg_tick = debug_tick; cookie.level = ex_nesting_level; - /* - * Keep the sourcing name/lnum, for recursive calls. - */ + // Keep the sourcing name/lnum, for recursive calls. save_sourcing_name = sourcing_name; sourcing_name = fname_exp; save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 0; - cookie.conv.vc_type = CONV_NONE; /* no conversion */ + cookie.conv.vc_type = CONV_NONE; // no conversion - /* Read the first line so we can check for a UTF-8 BOM. */ + // Read the first line so we can check for a UTF-8 BOM. firstline = getsourceline(0, (void *)&cookie, 0); if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef && firstline[1] == 0xbb && firstline[2] == 0xbf) { - /* Found BOM; setup conversion, skip over BOM and recode the line. */ + // Found BOM; setup conversion, skip over BOM and recode the line. convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); p = string_convert(&cookie.conv, firstline + 3, NULL); - if (p == NULL) + if (p == NULL) { p = vim_strsave(firstline + 3); + } xfree(firstline); firstline = p; } @@ -2671,22 +2844,21 @@ do_source ( } const int l_do_profiling = do_profiling; - if (l_do_profiling == PROF_YES) - prof_child_enter(&wait_start); /* entering a child now */ + if (l_do_profiling == PROF_YES) { + prof_child_enter(&wait_start); // entering a child now + } - /* Don't use local function variables, if called from a function. - * Also starts profiling timer for nested script. */ + // Don't use local function variables, if called from a function. + // Also starts profiling timer for nested script. save_funccalp = save_funccal(); - /* - * Check if this script was sourced before to finds its SID. - * If it's new, generate a new SID. - */ + // Check if this script was sourced before to finds its SID. + // If it's new, generate a new SID. save_current_SID = current_SID; FileID file_id; bool file_id_ok = os_fileid((char *)fname_exp, &file_id); assert(script_items.ga_len >= 0); - for (current_SID = script_items.ga_len; current_SID > 0; --current_SID) { + for (current_SID = script_items.ga_len; current_SID > 0; current_SID--) { si = &SCRIPT_ITEM(current_SID); // Compare dev/ino when possible, it catches symbolic links. // Also compare file names, the inode may change when the file was edited. @@ -2701,7 +2873,7 @@ do_source ( current_SID = ++last_current_SID; ga_grow(&script_items, (int)(current_SID - script_items.ga_len)); while (script_items.ga_len < current_SID) { - ++script_items.ga_len; + script_items.ga_len++; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; } @@ -2715,53 +2887,53 @@ do_source ( si->file_id_valid = false; } - /* Allocate the local script variables to use for this script. */ + // Allocate the local script variables to use for this script. new_script_vars(current_SID); } if (l_do_profiling == PROF_YES) { - int forceit; + bool forceit; - /* Check if we do profiling for this script. */ + // Check if we do profiling for this script. if (!si->sn_prof_on && has_profiling(true, si->sn_name, &forceit)) { profile_init(si); si->sn_pr_force = forceit; } if (si->sn_prof_on) { - ++si->sn_pr_count; + si->sn_pr_count++; si->sn_pr_start = profile_start(); si->sn_pr_children = profile_zero(); } } - /* - * Call do_cmdline, which will call getsourceline() to get the lines. - */ + // Call do_cmdline, which will call getsourceline() to get the lines. do_cmdline(firstline, getsourceline, (void *)&cookie, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); retval = OK; if (l_do_profiling == PROF_YES) { - /* Get "si" again, "script_items" may have been reallocated. */ + // Get "si" again, "script_items" may have been reallocated. si = &SCRIPT_ITEM(current_SID); if (si->sn_prof_on) { si->sn_pr_start = profile_end(si->sn_pr_start); si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start); si->sn_pr_total = profile_add(si->sn_pr_total, si->sn_pr_start); si->sn_pr_self = profile_self(si->sn_pr_self, si->sn_pr_start, - si->sn_pr_children); + si->sn_pr_children); } } - if (got_int) + if (got_int) { EMSG(_(e_interr)); + } sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; if (p_verbose > 1) { verbose_enter(); smsg(_("finished sourcing %s"), fname); - if (sourcing_name != NULL) + if (sourcing_name != NULL) { smsg(_("continuing in %s"), sourcing_name); + } verbose_leave(); } @@ -2771,18 +2943,18 @@ do_source ( time_pop(rel_time); } - /* - * After a "finish" in debug mode, need to break at first command of next - * sourced file. - */ + // After a "finish" in debug mode, need to break at first command of next + // sourced file. if (save_debug_break_level > ex_nesting_level - && debug_break_level == ex_nesting_level) - ++debug_break_level; + && debug_break_level == ex_nesting_level) { + debug_break_level++; + } current_SID = save_current_SID; restore_funccal(save_funccalp); - if (l_do_profiling == PROF_YES) - prof_child_exit(&wait_start); /* leaving a child now */ + if (l_do_profiling == PROF_YES) { + prof_child_exit(&wait_start); // leaving a child now + } fclose(cookie.fp); xfree(cookie.nextline); xfree(firstline); @@ -2794,26 +2966,23 @@ theend: } -/* - * ":scriptnames" - */ +/// ":scriptnames" void ex_scriptnames(exarg_T *eap) { - for (int i = 1; i <= script_items.ga_len && !got_int; ++i) + for (int i = 1; i <= script_items.ga_len && !got_int; i++) { if (SCRIPT_ITEM(i).sn_name != NULL) { home_replace(NULL, SCRIPT_ITEM(i).sn_name, - NameBuff, MAXPATHL, TRUE); + NameBuff, MAXPATHL, true); smsg("%3d: %s", i, NameBuff); } + } } # if defined(BACKSLASH_IN_FILENAME) -/* - * Fix slashes in the list of script names for 'shellslash'. - */ +/// Fix slashes in the list of script names for 'shellslash'. void scriptnames_slash_adjust(void) { - for (int i = 1; i <= script_items.ga_len; ++i) { + for (int i = 1; i <= script_items.ga_len; i++) { if (SCRIPT_ITEM(i).sn_name != NULL) { slash_adjust(SCRIPT_ITEM(i).sn_name); } @@ -2822,21 +2991,24 @@ void scriptnames_slash_adjust(void) # endif -/* - * Get a pointer to a script name. Used for ":verbose set". - */ +/// Get a pointer to a script name. Used for ":verbose set". char_u *get_scriptname(scid_T id) { - if (id == SID_MODELINE) + if (id == SID_MODELINE) { return (char_u *)_("modeline"); - if (id == SID_CMDARG) + } + if (id == SID_CMDARG) { return (char_u *)_("--cmd argument"); - if (id == SID_CARG) + } + if (id == SID_CARG) { return (char_u *)_("-c argument"); - if (id == SID_ENV) + } + if (id == SID_ENV) { return (char_u *)_("environment variable"); - if (id == SID_ERROR) + } + if (id == SID_ERROR) { return (char_u *)_("error handler"); + } return SCRIPT_ITEM(id).sn_name; } @@ -2849,51 +3021,49 @@ void free_scriptnames(void) # endif -/* - * Get one full line from a sourced file. - * Called by do_cmdline() when it's called from do_source(). - * - * Return a pointer to the line in allocated memory. - * Return NULL for end-of-file or some error. - */ +/// Get one full line from a sourced file. +/// Called by do_cmdline() when it's called from do_source(). +/// +/// @return pointer to the line in allocated memory, or NULL for end-of-file or +/// some error. char_u *getsourceline(int c, void *cookie, int indent) { struct source_cookie *sp = (struct source_cookie *)cookie; - char_u *line; - char_u *p; + char_u *line; + char_u *p; - /* If breakpoints have been added/deleted need to check for it. */ + // If breakpoints have been added/deleted need to check for it. if (sp->dbg_tick < debug_tick) { - sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); + sp->breakpoint = dbg_find_breakpoint(true, sp->fname, sourcing_lnum); sp->dbg_tick = debug_tick; } - if (do_profiling == PROF_YES) + if (do_profiling == PROF_YES) { script_line_end(); - /* - * Get current line. If there is a read-ahead line, use it, otherwise get - * one now. - */ - if (sp->finished) + } + // Get current line. If there is a read-ahead line, use it, otherwise get + // one now. + if (sp->finished) { line = NULL; - else if (sp->nextline == NULL) + } else if (sp->nextline == NULL) { line = get_one_sourceline(sp); - else { + } else { line = sp->nextline; sp->nextline = NULL; - ++sourcing_lnum; + sourcing_lnum++; } - if (line != NULL && do_profiling == PROF_YES) + if (line != NULL && do_profiling == PROF_YES) { script_line_start(); + } - /* Only concatenate lines starting with a \ when 'cpoptions' doesn't - * contain the 'C' flag. */ + // Only concatenate lines starting with a \ when 'cpoptions' doesn't + // contain the 'C' flag. if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) { - /* compensate for the one line read-ahead */ - --sourcing_lnum; + // compensate for the one line read-ahead + sourcing_lnum--; - /* Get the next line and concatenate it when it starts with a - * backslash. We always need to read the next line, keep it in - * sp->nextline. */ + // Get the next line and concatenate it when it starts with a + // backslash. We always need to read the next line, keep it in + // sp->nextline. sp->nextline = get_one_sourceline(sp); if (sp->nextline != NULL && *(p = skipwhite(sp->nextline)) == '\\') { garray_T ga; @@ -2904,13 +3074,15 @@ char_u *getsourceline(int c, void *cookie, int indent) for (;; ) { xfree(sp->nextline); sp->nextline = get_one_sourceline(sp); - if (sp->nextline == NULL) + if (sp->nextline == NULL) { break; + } p = skipwhite(sp->nextline); - if (*p != '\\') + if (*p != '\\') { break; - /* Adjust the growsize to the current length to speed up - * concatenating many lines. */ + } + // Adjust the growsize to the current length to speed up + // concatenating many lines. if (ga.ga_len > 400) { ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len); } @@ -2925,7 +3097,7 @@ char_u *getsourceline(int c, void *cookie, int indent) if (line != NULL && sp->conv.vc_type != CONV_NONE) { char_u *s; - /* Convert the encoding of the script line. */ + // Convert the encoding of the script line. s = string_convert(&sp->conv, line, NULL); if (s != NULL) { xfree(line); @@ -2933,11 +3105,11 @@ char_u *getsourceline(int c, void *cookie, int indent) } } - /* Did we encounter a breakpoint? */ + // Did we encounter a breakpoint? if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) { dbg_breakpoint(sp->fname, sourcing_lnum); - /* Find next breakpoint. */ - sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); + // Find next breakpoint. + sp->breakpoint = dbg_find_breakpoint(true, sp->fname, sourcing_lnum); sp->dbg_tick = debug_tick; } @@ -2951,175 +3123,170 @@ static char_u *get_one_sourceline(struct source_cookie *sp) int c; char_u *buf; #ifdef USE_CRNL - int has_cr; /* CR-LF found */ + int has_cr; // CR-LF found #endif - int have_read = FALSE; + bool have_read = false; - /* use a growarray to store the sourced line */ + // use a growarray to store the sourced line ga_init(&ga, 1, 250); - /* - * Loop until there is a finished line (or end-of-file). - */ + // Loop until there is a finished line (or end-of-file). sourcing_lnum++; for (;; ) { - /* make room to read at least 120 (more) characters */ + // make room to read at least 120 (more) characters ga_grow(&ga, 120); buf = (char_u *)ga.ga_data; if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, - sp->fp) == NULL) + sp->fp) == NULL) { break; + } len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); #ifdef USE_CRNL - /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the - * CTRL-Z by its own, or after a NL. */ - if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n')) - && sp->fileformat == EOL_DOS - && buf[len - 1] == Ctrl_Z) { + // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the + // CTRL-Z by its own, or after a NL. + if ((len == 1 || (len >= 2 && buf[len - 2] == '\n')) + && sp->fileformat == EOL_DOS + && buf[len - 1] == Ctrl_Z) { buf[len - 1] = NUL; break; } #endif - have_read = TRUE; + have_read = true; ga.ga_len = len; - /* If the line was longer than the buffer, read more. */ - if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') + // If the line was longer than the buffer, read more. + if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') { continue; + } - if (len >= 1 && buf[len - 1] == '\n') { /* remove trailing NL */ + if (len >= 1 && buf[len - 1] == '\n') { // remove trailing NL #ifdef USE_CRNL has_cr = (len >= 2 && buf[len - 2] == '\r'); if (sp->fileformat == EOL_UNKNOWN) { - if (has_cr) + if (has_cr) { sp->fileformat = EOL_DOS; - else + } else { sp->fileformat = EOL_UNIX; + } } if (sp->fileformat == EOL_DOS) { - if (has_cr) { /* replace trailing CR */ + if (has_cr) { // replace trailing CR buf[len - 2] = '\n'; - --len; - --ga.ga_len; - } else { /* lines like ":map xx yy^M" will have failed */ + len--; + ga.ga_len--; + } else { // lines like ":map xx yy^M" will have failed if (!sp->error) { msg_source(hl_attr(HLF_W)); EMSG(_("W15: Warning: Wrong line separator, ^M may be missing")); } - sp->error = TRUE; + sp->error = true; sp->fileformat = EOL_UNIX; } } #endif - /* The '\n' is escaped if there is an odd number of ^V's just - * before it, first set "c" just before the 'V's and then check - * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */ - for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) - ; - if ((len & 1) != (c & 1)) { /* escaped NL, read more */ + // The '\n' is escaped if there is an odd number of ^V's just + // before it, first set "c" just before the 'V's and then check + // len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo + for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) {} + if ((len & 1) != (c & 1)) { // escaped NL, read more sourcing_lnum++; continue; } - buf[len - 1] = NUL; /* remove the NL */ + buf[len - 1] = NUL; // remove the NL } - /* - * Check for ^C here now and then, so recursive :so can be broken. - */ + // Check for ^C here now and then, so recursive :so can be broken. line_breakcheck(); break; } - if (have_read) + if (have_read) { return (char_u *)ga.ga_data; + } xfree(ga.ga_data); return NULL; } -/* - * Called when starting to read a script line. - * "sourcing_lnum" must be correct! - * When skipping lines it may not actually be executed, but we won't find out - * until later and we need to store the time now. - */ +/// Called when starting to read a script line. +/// "sourcing_lnum" must be correct! +/// When skipping lines it may not actually be executed, but we won't find out +/// until later and we need to store the time now. void script_line_start(void) { scriptitem_T *si; sn_prl_T *pp; - if (current_SID <= 0 || current_SID > script_items.ga_len) + if (current_SID <= 0 || current_SID > script_items.ga_len) { return; + } si = &SCRIPT_ITEM(current_SID); if (si->sn_prof_on && sourcing_lnum >= 1) { - /* Grow the array before starting the timer, so that the time spent - * here isn't counted. */ + // Grow the array before starting the timer, so that the time spent + // here isn't counted. ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); si->sn_prl_idx = sourcing_lnum - 1; while (si->sn_prl_ga.ga_len <= si->sn_prl_idx && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) { - /* Zero counters for a line that was not used before. */ + // Zero counters for a line that was not used before. pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len); pp->snp_count = 0; pp->sn_prl_total = profile_zero(); pp->sn_prl_self = profile_zero(); - ++si->sn_prl_ga.ga_len; + si->sn_prl_ga.ga_len++; } - si->sn_prl_execed = FALSE; + si->sn_prl_execed = false; si->sn_prl_start = profile_start(); si->sn_prl_children = profile_zero(); si->sn_prl_wait = profile_get_wait(); } } -/* - * Called when actually executing a function line. - */ +/// Called when actually executing a function line. void script_line_exec(void) { scriptitem_T *si; - if (current_SID <= 0 || current_SID > script_items.ga_len) + if (current_SID <= 0 || current_SID > script_items.ga_len) { return; + } si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && si->sn_prl_idx >= 0) - si->sn_prl_execed = TRUE; + if (si->sn_prof_on && si->sn_prl_idx >= 0) { + si->sn_prl_execed = true; + } } -/* - * Called when done with a function line. - */ +/// Called when done with a function line. void script_line_end(void) { scriptitem_T *si; sn_prl_T *pp; - if (current_SID <= 0 || current_SID > script_items.ga_len) + if (current_SID <= 0 || current_SID > script_items.ga_len) { return; + } si = &SCRIPT_ITEM(current_SID); if (si->sn_prof_on && si->sn_prl_idx >= 0 && si->sn_prl_idx < si->sn_prl_ga.ga_len) { if (si->sn_prl_execed) { pp = &PRL_ITEM(si, si->sn_prl_idx); - ++pp->snp_count; + pp->snp_count++; si->sn_prl_start = profile_end(si->sn_prl_start); si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start); pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start); pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start, - si->sn_prl_children); + si->sn_prl_children); } si->sn_prl_idx = -1; } } -/* - * ":scriptencoding": Set encoding conversion for a sourced script. - * Without the multi-byte feature it's simply ignored. - */ +/// ":scriptencoding": Set encoding conversion for a sourced script. +/// Without the multi-byte feature it's simply ignored. void ex_scriptencoding(exarg_T *eap) { struct source_cookie *sp; @@ -3132,84 +3299,80 @@ void ex_scriptencoding(exarg_T *eap) if (*eap->arg != NUL) { name = enc_canonize(eap->arg); - } else + } else { name = eap->arg; + } - /* Setup for conversion from the specified encoding to 'encoding'. */ + // Setup for conversion from the specified encoding to 'encoding'. sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie); convert_setup(&sp->conv, name, p_enc); - if (name != eap->arg) + if (name != eap->arg) { xfree(name); + } } -/* - * ":finish": Mark a sourced file as finished. - */ +/// ":finish": Mark a sourced file as finished. void ex_finish(exarg_T *eap) { - if (getline_equal(eap->getline, eap->cookie, getsourceline)) - do_finish(eap, FALSE); - else + if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + do_finish(eap, false); + } else { EMSG(_("E168: :finish used outside of a sourced file")); + } } -/* - * Mark a sourced file as finished. Possibly makes the ":finish" pending. - * Also called for a pending finish at the ":endtry" or after returning from - * an extra do_cmdline(). "reanimate" is used in the latter case. - */ +/// Mark a sourced file as finished. Possibly makes the ":finish" pending. +/// Also called for a pending finish at the ":endtry" or after returning from +/// an extra do_cmdline(). "reanimate" is used in the latter case. void do_finish(exarg_T *eap, int reanimate) { int idx; - if (reanimate) + if (reanimate) { ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = FALSE; - - /* - * Cleanup (and inactivate) conditionals, but stop when a try conditional - * not in its finally clause (which then is to be executed next) is found. - * In this case, make the ":finish" pending for execution at the ":endtry". - * Otherwise, finish normally. - */ - idx = cleanup_conditionals(eap->cstack, 0, TRUE); + eap->cookie))->finished = false; + } + + // Cleanup (and inactivate) conditionals, but stop when a try conditional + // not in its finally clause (which then is to be executed next) is found. + // In this case, make the ":finish" pending for execution at the ":endtry". + // Otherwise, finish normally. + idx = cleanup_conditionals(eap->cstack, 0, true); if (idx >= 0) { eap->cstack->cs_pending[idx] = CSTP_FINISH; report_make_pending(CSTP_FINISH, NULL); - } else + } else { ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = TRUE; + eap->cookie))->finished = true; + } } -/* - * Return TRUE when a sourced file had the ":finish" command: Don't give error - * message for missing ":endif". - * Return FALSE when not sourcing a file. - */ -int source_finished(LineGetter fgetline, void *cookie) +/// Return true when a sourced file had the ":finish" command: Don't give error +/// message for missing ":endif". +/// Return false when not sourcing a file. +bool source_finished(LineGetter fgetline, void *cookie) { return getline_equal(fgetline, cookie, getsourceline) && ((struct source_cookie *)getline_cookie( - fgetline, cookie))->finished; + fgetline, cookie))->finished; } -/* - * ":checktime [buffer]" - */ +/// ":checktime [buffer]" void ex_checktime(exarg_T *eap) { buf_T *buf; int save_no_check_timestamps = no_check_timestamps; no_check_timestamps = 0; - if (eap->addr_count == 0) /* default is all buffers */ - check_timestamps(FALSE); - else { + if (eap->addr_count == 0) { // default is all buffers + check_timestamps(false); + } else { buf = buflist_findnr((int)eap->line2); - if (buf != NULL) /* cannot happen? */ - (void)buf_check_timestamp(buf, FALSE); + if (buf != NULL) { // cannot happen? + (void)buf_check_timestamp(buf, false); + } } no_check_timestamps = save_no_check_timestamps; } @@ -3228,10 +3391,8 @@ static char *get_locale_val(int what) -/* - * Obtain the current messages language. Used to set the default for - * 'helplang'. May return NULL or an empty string. - */ +/// Obtain the current messages language. Used to set the default for +/// 'helplang'. May return NULL or an empty string. char *get_mess_lang(void) { char *p; @@ -3240,10 +3401,10 @@ char *get_mess_lang(void) # if defined(LC_MESSAGES) p = get_locale_val(LC_MESSAGES); # else - /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG - * may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME - * and LC_MONETARY may be set differently for a Japanese working in the - * US. */ + // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG + // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME + // and LC_MONETARY may be set differently for a Japanese working in the + // US. p = get_locale_val(LC_COLLATE); # endif # else @@ -3258,11 +3419,9 @@ char *get_mess_lang(void) return p; } -/* Complicated #if; matches with where get_mess_env() is used below. */ +// Complicated #if; matches with where get_mess_env() is used below. #ifdef HAVE_WORKING_LIBINTL -/* - * Get the language used for messages from the environment. - */ +/// Get the language used for messages from the environment. static char_u *get_mess_env(void) { char_u *p; @@ -3273,7 +3432,7 @@ static char_u *get_mess_env(void) if (p == NULL) { p = (char_u *)os_getenv("LANG"); if (p != NULL && ascii_isdigit(*p)) { - p = NULL; /* ignore something like "1043" */ + p = NULL; // ignore something like "1043" } # ifdef HAVE_GET_LOCALE_VAL if (p == NULL) { @@ -3288,10 +3447,8 @@ static char_u *get_mess_env(void) #endif -/* - * Set the "v:lang" variable according to the current locale setting. - * Also do "v:lc_time"and "v:ctype". - */ +/// Set the "v:lang" variable according to the current locale setting. +/// Also do "v:lc_time"and "v:ctype". void set_lang_var(void) { const char *loc; @@ -3304,10 +3461,10 @@ void set_lang_var(void) # endif set_vim_var_string(VV_CTYPE, loc, -1); - /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall - * back to LC_CTYPE if it's empty. */ + // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall + // back to LC_CTYPE if it's empty. # ifdef HAVE_WORKING_LIBINTL - loc = (char *) get_mess_env(); + loc = (char *)get_mess_env(); # elif defined(LC_MESSAGES) loc = get_locale_val(LC_MESSAGES); # else @@ -3323,9 +3480,11 @@ void set_lang_var(void) } #ifdef HAVE_WORKING_LIBINTL -/* - * ":language": Set the language (locale). - */ +/// +/// ":language": Set the language (locale). +/// +/// @param eap +/// void ex_language(exarg_T *eap) { char *loc; @@ -3341,9 +3500,9 @@ void ex_language(exarg_T *eap) name = eap->arg; - /* Check for "messages {name}", "ctype {name}" or "time {name}" argument. - * Allow abbreviation, but require at least 3 characters to avoid - * confusion with a two letter language name "me" or "ct". */ + // Check for "messages {name}", "ctype {name}" or "time {name}" argument. + // Allow abbreviation, but require at least 3 characters to avoid + // confusion with a two letter language name "me" or "ct". p = skiptowhite(eap->arg); if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) { if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) { @@ -3363,47 +3522,52 @@ void ex_language(exarg_T *eap) if (*name == NUL) { #ifdef HAVE_WORKING_LIBINTL - if (what == VIM_LC_MESSAGES) + if (what == VIM_LC_MESSAGES) { p = get_mess_env(); - else + } else { +#endif + p = (char_u *)setlocale(what, NULL); +#ifdef HAVE_WORKING_LIBINTL + } #endif - p = (char_u *)setlocale(what, NULL); - if (p == NULL || *p == NUL) + if (p == NULL || *p == NUL) { p = (char_u *)"Unknown"; + } smsg(_("Current %slanguage: \"%s\""), whatstr, p); } else { #ifndef LC_MESSAGES - if (what == VIM_LC_MESSAGES) + if (what == VIM_LC_MESSAGES) { loc = ""; - else + } else { #endif - { loc = setlocale(what, (char *)name); #ifdef LC_NUMERIC - /* Make sure strtod() uses a decimal point, not a comma. */ + // Make sure strtod() uses a decimal point, not a comma. setlocale(LC_NUMERIC, "C"); #endif +#ifndef LC_MESSAGES } - if (loc == NULL) +#endif + if (loc == NULL) { EMSG2(_("E197: Cannot set language to \"%s\""), name); - else { + } else { #ifdef HAVE_NL_MSG_CAT_CNTR - /* Need to do this for GNU gettext, otherwise cached translations - * will be used again. */ + // Need to do this for GNU gettext, otherwise cached translations + // will be used again. extern int _nl_msg_cat_cntr; - ++_nl_msg_cat_cntr; + _nl_msg_cat_cntr++; #endif - /* Reset $LC_ALL, otherwise it would overrule everything. */ + // Reset $LC_ALL, otherwise it would overrule everything. vim_setenv("LC_ALL", ""); if (what != LC_TIME) { - /* Tell gettext() what to translate to. It apparently doesn't - * use the currently effective locale. */ + // Tell gettext() what to translate to. It apparently doesn't + // use the currently effective locale. if (what == LC_ALL) { vim_setenv("LANG", (char *)name); - /* Clear $LANGUAGE because GNU gettext uses it. */ + // Clear $LANGUAGE because GNU gettext uses it. vim_setenv("LANGUAGE", ""); } if (what != LC_CTYPE) { @@ -3412,7 +3576,7 @@ void ex_language(exarg_T *eap) } } - /* Set v:lang, v:lc_time and v:ctype to the final result. */ + // Set v:lang, v:lc_time and v:ctype to the final result. set_lang_var(); maketitle(); } @@ -3420,43 +3584,43 @@ void ex_language(exarg_T *eap) } -static char_u **locales = NULL; /* Array of all available locales */ -static int did_init_locales = FALSE; +static char_u **locales = NULL; // Array of all available locales +static bool did_init_locales = false; -/* - * Lazy initialization of all available locales. - */ +/// Lazy initialization of all available locales. static void init_locales(void) { if (!did_init_locales) { - did_init_locales = TRUE; + did_init_locales = true; locales = find_locales(); } } -/* Return an array of strings for all available locales + NULL for the - * last element. Return NULL in case of error. */ +// Return an array of strings for all available locales + NULL for the +/// last element. Return NULL in case of error. static char_u **find_locales(void) { garray_T locales_ga; char_u *loc; + char *saveptr = NULL; - /* Find all available locales by running command "locale -a". If this - * doesn't work we won't have completion. */ + // Find all available locales by running command "locale -a". If this + // doesn't work we won't have completion. char_u *locale_a = get_cmd_output((char_u *)"locale -a", NULL, kShellOptSilent, NULL); - if (locale_a == NULL) + if (locale_a == NULL) { return NULL; + } ga_init(&locales_ga, sizeof(char_u *), 20); - /* Transform locale_a string where each locale is separated by "\n" - * into an array of locale strings. */ - loc = (char_u *)strtok((char *)locale_a, "\n"); + // Transform locale_a string where each locale is separated by "\n" + // into an array of locale strings. + loc = (char_u *)os_strtok((char *)locale_a, "\n", &saveptr); while (loc != NULL) { loc = vim_strsave(loc); GA_APPEND(char_u *, &locales_ga, loc); - loc = (char_u *)strtok(NULL, "\n"); + loc = (char_u *)os_strtok(NULL, "\n", &saveptr); } xfree(locale_a); // Guarantee that .ga_data is NULL terminated @@ -3470,8 +3634,9 @@ void free_locales(void) { int i; if (locales != NULL) { - for (i = 0; locales[i] != NULL; i++) + for (i = 0; locales[i] != NULL; i++) { xfree(locales[i]); + } xfree(locales); locales = NULL; } @@ -3479,33 +3644,34 @@ void free_locales(void) # endif -/* - * Function given to ExpandGeneric() to obtain the possible arguments of the - * ":language" command. - */ +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":language" command. char_u *get_lang_arg(expand_T *xp, int idx) { - if (idx == 0) + if (idx == 0) { return (char_u *)"messages"; - if (idx == 1) + } + if (idx == 1) { return (char_u *)"ctype"; - if (idx == 2) + } + if (idx == 2) { return (char_u *)"time"; + } init_locales(); - if (locales == NULL) + if (locales == NULL) { return NULL; + } return locales[idx - 3]; } -/* - * Function given to ExpandGeneric() to obtain the available locales. - */ +/// Function given to ExpandGeneric() to obtain the available locales. char_u *get_locales(expand_T *xp, int idx) { init_locales(); - if (locales == NULL) + if (locales == NULL) { return NULL; + } return locales[idx]; } @@ -3552,79 +3718,65 @@ static void script_host_do_range(char *name, exarg_T *eap) (void)eval_call_provider(name, "do_range", args); } -/* - * ":drop" - * Opens the first argument in a window. When there are two or more arguments - * the argument list is redefined. - */ +/// ":drop" +/// Opens the first argument in a window. When there are two or more arguments +/// the argument list is redefined. void ex_drop(exarg_T *eap) { - int split = FALSE; - buf_T *buf; - - /* - * Check if the first argument is already being edited in a window. If - * so, jump to that window. - * We would actually need to check all arguments, but that's complicated - * and mostly only one file is dropped. - * This also ignores wildcards, since it is very unlikely the user is - * editing a file name with a wildcard character. - */ - do_arglist(eap->arg, AL_SET, 0); - - /* - * Expanding wildcards may result in an empty argument list. E.g. when - * editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we - * already did an error message for this. - */ - if (ARGCOUNT == 0) - return; - - if (cmdmod.tab) - { - /* ":tab drop file ...": open a tab for each argument that isn't - * edited in a window yet. It's like ":tab all" but without closing - * windows or tabs. */ - ex_all(eap); - } - else - { - /* ":drop file ...": Edit the first argument. Jump to an existing - * window if possible, edit in current window if the current buffer - * can be abandoned, otherwise open a new window. */ - buf = buflist_findnr(ARGLIST[0].ae_fnum); - - FOR_ALL_TAB_WINDOWS(tp, wp) - { - if (wp->w_buffer == buf) - { - goto_tabpage_win(tp, wp); - curwin->w_arg_idx = 0; - return; - } - } - - /* - * Check whether the current buffer is changed. If so, we will need - * to split the current window or data could be lost. - * Skip the check if the 'hidden' option is set, as in this case the - * buffer won't be lost. - */ - if (!P_HID(curbuf)) - { - ++emsg_off; - split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD); - --emsg_off; - } - - /* Fake a ":sfirst" or ":first" command edit the first argument. */ - if (split) - { - eap->cmdidx = CMD_sfirst; - eap->cmd[0] = 's'; - } - else - eap->cmdidx = CMD_first; - ex_rewind(eap); + bool split = false; + buf_T *buf; + + // Check if the first argument is already being edited in a window. If + // so, jump to that window. + // We would actually need to check all arguments, but that's complicated + // and mostly only one file is dropped. + // This also ignores wildcards, since it is very unlikely the user is + // editing a file name with a wildcard character. + do_arglist(eap->arg, AL_SET, 0); + + // Expanding wildcards may result in an empty argument list. E.g. when + // editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we + // already did an error message for this. + if (ARGCOUNT == 0) { + return; + } + + if (cmdmod.tab) { + // ":tab drop file ...": open a tab for each argument that isn't + // edited in a window yet. It's like ":tab all" but without closing + // windows or tabs. + ex_all(eap); + } else { + // ":drop file ...": Edit the first argument. Jump to an existing + // window if possible, edit in current window if the current buffer + // can be abandoned, otherwise open a new window. + buf = buflist_findnr(ARGLIST[0].ae_fnum); + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + goto_tabpage_win(tp, wp); + curwin->w_arg_idx = 0; + return; + } } + + // Check whether the current buffer is changed. If so, we will need + // to split the current window or data could be lost. + // Skip the check if the 'hidden' option is set, as in this case the + // buffer won't be lost. + if (!P_HID(curbuf)) { + emsg_off++; + split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD); + emsg_off--; + } + + // Fake a ":sfirst" or ":first" command edit the first argument. + if (split) { + eap->cmdidx = CMD_sfirst; + eap->cmd[0] = 's'; + } else { + eap->cmdidx = CMD_first; + } + ex_rewind(eap); + } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 59962c153b..9bc7ec39da 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1536,8 +1536,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, } ea.cmd = skipwhite(ea.cmd); lnum = get_address(&ea, &ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0); - if (ea.cmd == NULL) /* error detected */ + if (ea.cmd == NULL) { // error detected goto doend; + } if (lnum == MAXLNUM) { if (*ea.cmd == '%') { /* '%' - all lines */ ++ea.cmd; @@ -3315,6 +3316,11 @@ set_one_cmd_context ( xp->xp_pattern = arg; break; + case CMD_packadd: + xp->xp_context = EXPAND_PACKADD; + xp->xp_pattern = arg; + break; + #ifdef HAVE_WORKING_LIBINTL case CMD_language: p = skiptowhite(arg); @@ -4640,14 +4646,14 @@ static struct { char *name; } addr_type_complete[] = { - {ADDR_ARGUMENTS, "arguments"}, - {ADDR_LINES, "lines"}, - {ADDR_LOADED_BUFFERS, "loaded_buffers"}, - {ADDR_TABS, "tabs"}, - {ADDR_BUFFERS, "buffers"}, - {ADDR_WINDOWS, "windows"}, - {ADDR_QUICKFIX, "quickfix"}, - {-1, NULL} + { ADDR_ARGUMENTS, "arguments" }, + { ADDR_LINES, "lines" }, + { ADDR_LOADED_BUFFERS, "loaded_buffers" }, + { ADDR_TABS, "tabs" }, + { ADDR_BUFFERS, "buffers" }, + { ADDR_WINDOWS, "windows" }, + { ADDR_QUICKFIX, "quickfix" }, + { -1, NULL } }; /* @@ -4659,41 +4665,42 @@ static struct { char *name; } command_complete[] = { - {EXPAND_AUGROUP, "augroup"}, - {EXPAND_BEHAVE, "behave"}, - {EXPAND_BUFFERS, "buffer"}, - {EXPAND_COLORS, "color"}, - {EXPAND_COMMANDS, "command"}, - {EXPAND_COMPILER, "compiler"}, - {EXPAND_CSCOPE, "cscope"}, - {EXPAND_USER_DEFINED, "custom"}, - {EXPAND_USER_LIST, "customlist"}, - {EXPAND_DIRECTORIES, "dir"}, - {EXPAND_ENV_VARS, "environment"}, - {EXPAND_EVENTS, "event"}, - {EXPAND_EXPRESSION, "expression"}, - {EXPAND_FILES, "file"}, - {EXPAND_FILES_IN_PATH, "file_in_path"}, - {EXPAND_FILETYPE, "filetype"}, - {EXPAND_FUNCTIONS, "function"}, - {EXPAND_HELP, "help"}, - {EXPAND_HIGHLIGHT, "highlight"}, - {EXPAND_HISTORY, "history"}, + { EXPAND_AUGROUP, "augroup" }, + { EXPAND_BEHAVE, "behave" }, + { EXPAND_BUFFERS, "buffer" }, + { EXPAND_COLORS, "color" }, + { EXPAND_COMMANDS, "command" }, + { EXPAND_COMPILER, "compiler" }, + { EXPAND_CSCOPE, "cscope" }, + { EXPAND_USER_DEFINED, "custom" }, + { EXPAND_USER_LIST, "customlist" }, + { EXPAND_DIRECTORIES, "dir" }, + { EXPAND_ENV_VARS, "environment" }, + { EXPAND_EVENTS, "event" }, + { EXPAND_EXPRESSION, "expression" }, + { EXPAND_FILES, "file" }, + { EXPAND_FILES_IN_PATH, "file_in_path" }, + { EXPAND_FILETYPE, "filetype" }, + { EXPAND_FUNCTIONS, "function" }, + { EXPAND_HELP, "help" }, + { EXPAND_HIGHLIGHT, "highlight" }, + { EXPAND_HISTORY, "history" }, #ifdef HAVE_WORKING_LIBINTL - {EXPAND_LOCALES, "locale"}, + { EXPAND_LOCALES, "locale" }, #endif - {EXPAND_MAPPINGS, "mapping"}, - {EXPAND_MENUS, "menu"}, - {EXPAND_OWNSYNTAX, "syntax"}, - {EXPAND_SYNTIME, "syntime"}, - {EXPAND_SETTINGS, "option"}, - {EXPAND_SHELLCMD, "shellcmd"}, - {EXPAND_SIGN, "sign"}, - {EXPAND_TAGS, "tag"}, - {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, - {EXPAND_USER, "user"}, - {EXPAND_USER_VARS, "var"}, - {0, NULL} + { EXPAND_MAPPINGS, "mapping" }, + { EXPAND_MENUS, "menu" }, + { EXPAND_OWNSYNTAX, "syntax" }, + { EXPAND_SYNTIME, "syntime" }, + { EXPAND_SETTINGS, "option" }, + { EXPAND_PACKADD, "packadd" }, + { EXPAND_SHELLCMD, "shellcmd" }, + { EXPAND_SIGN, "sign" }, + { EXPAND_TAGS, "tag" }, + { EXPAND_TAGS_LISTFILES, "tag_listfiles" }, + { EXPAND_USER, "user" }, + { EXPAND_USER_VARS, "var" }, + { 0, NULL } }; static void uc_list(char_u *name, size_t name_len) @@ -6865,6 +6872,9 @@ void post_chdir(CdScope scope) curwin->w_localdir = vim_strsave(NameBuff); } break; + case kCdScopeInvalid: + // We should never get here + assert(false); } shorten_fnames(TRUE); @@ -6992,7 +7002,7 @@ void do_sleep(long msec) ui_flush(); // flush before waiting for (long left = msec; !got_int && left > 0; left -= 1000L) { int next = left > 1000l ? 1000 : (int)left; - LOOP_PROCESS_EVENTS_UNTIL(&loop, loop.events, (int)next, got_int); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, (int)next, got_int); os_breakcheck(); } } @@ -7122,8 +7132,8 @@ static void ex_put(exarg_T *eap) eap->forceit = TRUE; } curwin->w_cursor.lnum = eap->line2; - do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1L, - PUT_LINE|PUT_CURSLINE); + do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1, + PUT_LINE|PUT_CURSLINE); } /* @@ -7132,7 +7142,7 @@ static void ex_put(exarg_T *eap) static void ex_copymove(exarg_T *eap) { long n = get_address(eap, &eap->arg, eap->addr_type, false, false); - if (eap->arg == NULL) { /* error detected */ + if (eap->arg == NULL) { // error detected eap->nextcmd = NULL; return; } @@ -7351,10 +7361,11 @@ static void ex_redir(exarg_T *eap) /* Can use both "@a" and "@a>". */ if (*arg == '>') arg++; - /* Make register empty when not using @A-@Z and the - * command is valid. */ - if (*arg == NUL && !isupper(redir_reg)) - write_reg_contents(redir_reg, (char_u *)"", -1, FALSE); + // Make register empty when not using @A-@Z and the + // command is valid. + if (*arg == NUL && !isupper(redir_reg)) { + write_reg_contents(redir_reg, (char_u *)"", 0, false); + } } } if (*arg != NUL) { @@ -7645,7 +7656,7 @@ open_exfile ( return NULL; } #endif - if (!forceit && *mode != 'a' && os_file_exists(fname)) { + if (!forceit && *mode != 'a' && os_path_exists(fname)) { EMSG2(_("E189: \"%s\" exists (add ! to override)"), fname); return NULL; } @@ -7855,19 +7866,26 @@ static void ex_stopinsert(exarg_T *eap) */ void exec_normal_cmd(char_u *cmd, int remap, bool silent) { + // Stuff the argument into the typeahead buffer. + ins_typebuf(cmd, remap, 0, true, silent); + exec_normal(false); +} + +/// Execute normal_cmd() until there is no typeahead left. +/// +/// @param was_typed whether or not something was typed +void exec_normal(bool was_typed) +{ oparg_T oa; - /* - * Stuff the argument into the typeahead buffer. - * Execute normal_cmd() until there is no typeahead left. - */ clear_oparg(&oa); - finish_op = FALSE; - ins_typebuf(cmd, remap, 0, TRUE, silent); - while ((!stuff_empty() || (!typebuf_typed() && typebuf.tb_len > 0)) + finish_op = false; + while ((!stuff_empty() + || ((was_typed || !typebuf_typed()) + && typebuf.tb_len > 0)) && !got_int) { update_topline_cursor(); - normal_cmd(&oa, TRUE); /* execute a Normal mode cmd */ + normal_cmd(&oa, true); // execute a Normal mode cmd } } @@ -9318,14 +9336,14 @@ static void ex_filetype(exarg_T *eap) } if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0) { if (*arg == 'o' || !filetype_detect) { - source_runtime((char_u *)FILETYPE_FILE, true); + source_runtime((char_u *)FILETYPE_FILE, DIP_ALL); filetype_detect = kTrue; if (plugin) { - source_runtime((char_u *)FTPLUGIN_FILE, true); + source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL); filetype_plugin = kTrue; } if (indent) { - source_runtime((char_u *)INDENT_FILE, true); + source_runtime((char_u *)INDENT_FILE, DIP_ALL); filetype_indent = kTrue; } } @@ -9336,15 +9354,15 @@ static void ex_filetype(exarg_T *eap) } else if (STRCMP(arg, "off") == 0) { if (plugin || indent) { if (plugin) { - source_runtime((char_u *)FTPLUGOF_FILE, true); + source_runtime((char_u *)FTPLUGOF_FILE, DIP_ALL); filetype_plugin = kFalse; } if (indent) { - source_runtime((char_u *)INDOFF_FILE, true); + source_runtime((char_u *)INDOFF_FILE, DIP_ALL); filetype_indent = kFalse; } } else { - source_runtime((char_u *)FTOFF_FILE, true); + source_runtime((char_u *)FTOFF_FILE, DIP_ALL); filetype_detect = kFalse; } } else diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index dbfc64e2f1..bafad20169 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -26,6 +26,7 @@ /// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes /// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead. typedef enum { + kCdScopeInvalid = -1, kCdScopeWindow, ///< Affects one window. kCdScopeTab, ///< Affects one tab page. kCdScopeGlobal, ///< Affects the entire instance of Neovim. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index db21fddedb..cd28554970 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -358,7 +358,7 @@ static int command_line_execute(VimState *state, int key) s->c = key; if (s->c == K_EVENT) { - queue_process_events(loop.events); + queue_process_events(main_loop.events); redrawcmdline(); return 1; } @@ -3440,6 +3440,7 @@ addstar ( || context == EXPAND_COMPILER || context == EXPAND_OWNSYNTAX || context == EXPAND_FILETYPE + || context == EXPAND_PACKADD || (context == EXPAND_TAGS && fname[0] == '/')) retval = vim_strnsave(fname, len); else { @@ -3794,23 +3795,27 @@ ExpandFromContext ( || xp->xp_context == EXPAND_TAGS_LISTFILES) return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file); if (xp->xp_context == EXPAND_COLORS) { - char *directories[] = {"colors", NULL}; - return ExpandRTDir(pat, num_file, file, directories); + char *directories[] = { "colors", NULL }; + return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories); } if (xp->xp_context == EXPAND_COMPILER) { - char *directories[] = {"compiler", NULL}; - return ExpandRTDir(pat, num_file, file, directories); + char *directories[] = { "compiler", NULL }; + return ExpandRTDir(pat, 0, num_file, file, directories); } if (xp->xp_context == EXPAND_OWNSYNTAX) { - char *directories[] = {"syntax", NULL}; - return ExpandRTDir(pat, num_file, file, directories); + char *directories[] = { "syntax", NULL }; + return ExpandRTDir(pat, 0, num_file, file, directories); } if (xp->xp_context == EXPAND_FILETYPE) { - char *directories[] = {"syntax", "indent", "ftplugin", NULL}; - return ExpandRTDir(pat, num_file, file, directories); + char *directories[] = { "syntax", "indent", "ftplugin", NULL }; + return ExpandRTDir(pat, 0, num_file, file, directories); } - if (xp->xp_context == EXPAND_USER_LIST) + if (xp->xp_context == EXPAND_USER_LIST) { return ExpandUserList(xp, num_file, file); + } + if (xp->xp_context == EXPAND_PACKADD) { + return ExpandPackAddDir(pat, num_file, file); + } regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); if (regmatch.regprog == NULL) @@ -4190,12 +4195,16 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) return OK; } -/* - * Expand color scheme, compiler or filetype names: - * 'runtimepath'/{dirnames}/{pat}.vim - * "dirnames" is an array with one or more directory names. - */ -static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirnames[]) +/// Expand color scheme, compiler or filetype names. +/// Search from 'runtimepath': +/// 'runtimepath'/{dirnames}/{pat}.vim +/// When "flags" has DIP_START: search also from 'start' of 'packpath': +/// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim +/// When "flags" has DIP_OPT: search also from 'opt' of 'packpath': +/// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim +/// "dirnames" is an array with one or more directory names. +static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, + char *dirnames[]) { *num_file = 0; *file = NULL; @@ -4212,6 +4221,26 @@ static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirname xfree(s); } + if (flags & DIP_START) { + for (int i = 0; dirnames[i] != NULL; i++) { + size_t size = STRLEN(dirnames[i]) + pat_len + 22; + char_u *s = xmalloc(size); + snprintf((char *)s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT + globpath(p_pp, s, &ga, 0); + xfree(s); + } + } + + if (flags & DIP_OPT) { + for (int i = 0; dirnames[i] != NULL; i++) { + size_t size = STRLEN(dirnames[i]) + pat_len + 20; + char_u *s = xmalloc(size); + snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT + globpath(p_pp, s, &ga, 0); + xfree(s); + } + } + for (int i = 0; i < ga.ga_len; i++) { char_u *match = ((char_u **)ga.ga_data)[i]; char_u *s = match; @@ -4241,6 +4270,43 @@ static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirname return OK; } +/// Expand loadplugin names: +/// 'packpath'/pack/ * /opt/{pat} +static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file) +{ + garray_T ga; + + *num_file = 0; + *file = NULL; + size_t pat_len = STRLEN(pat); + ga_init(&ga, (int)sizeof(char *), 10); + + size_t buflen = pat_len + 26; + char_u *s = xmalloc(buflen); + snprintf((char *)s, buflen, "pack/*/opt/%s*", pat); // NOLINT + globpath(p_pp, s, &ga, 0); + xfree(s); + + for (int i = 0; i < ga.ga_len; i++) { + char_u *match = ((char_u **)ga.ga_data)[i]; + s = path_tail(match); + char_u *e = s + STRLEN(s); + memmove(match, s, e - s + 1); + } + + if (GA_EMPTY(&ga)) { + return FAIL; + } + + // Sort and remove duplicates which can happen when specifying multiple + // directories in dirnames. + ga_remove_duplicate_strings(&ga); + + *file = ga.ga_data; + *num_file = ga.ga_len; + return OK; +} + /// Expand `file` for all comma-separated directories in `path`. /// Adds matches to `ga`. diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index beefc4238e..78b224f04c 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -791,7 +791,7 @@ char_u *vim_findfile(void *search_ctx_arg) for (;; ) { /* if file exists and we didn't already find it */ if ((path_with_url((char *)file_path) - || (os_file_exists(file_path) + || (os_path_exists(file_path) && (search_ctx->ffsc_find_what == FINDFILE_BOTH || ((search_ctx->ffsc_find_what @@ -1378,7 +1378,7 @@ find_file_in_path_option ( /* copy file name into NameBuff, expanding environment variables */ save_char = ptr[len]; ptr[len] = NUL; - expand_env(ptr, NameBuff, MAXPATHL); + expand_env_esc(ptr, NameBuff, MAXPATHL, false, true, NULL); ptr[len] = save_char; xfree(ff_file_to_find); @@ -1442,12 +1442,12 @@ find_file_in_path_option ( buf = suffixes; for (;; ) { if ( - (os_file_exists(NameBuff) - && (find_what == FINDFILE_BOTH - || ((find_what == FINDFILE_DIR) - == os_isdir(NameBuff))))) { - file_name = vim_strsave(NameBuff); - goto theend; + (os_path_exists(NameBuff) + && (find_what == FINDFILE_BOTH + || ((find_what == FINDFILE_DIR) + == os_isdir(NameBuff))))) { + file_name = vim_strsave(NameBuff); + goto theend; } if (*buf == NUL) break; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index db1469db97..154558b332 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1749,8 +1749,9 @@ failed: #ifdef HAVE_FD_CLOEXEC else { int fdflags = fcntl(fd, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + (void)fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC); + } } #endif xfree(buffer); @@ -2989,14 +2990,15 @@ nobackup: * delete an existing one, try to use another name. * Change one character, just before the extension. */ - if (!p_bk && os_file_exists(backup)) { + if (!p_bk && os_path_exists(backup)) { p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext); if (p < backup) /* empty file name ??? */ p = backup; *p = 'z'; - while (*p > 'a' && os_file_exists(backup)) - --*p; - /* They all exist??? Must be something wrong! */ + while (*p > 'a' && os_path_exists(backup)) { + (*p)--; + } + // They all exist??? Must be something wrong! if (*p == 'a') { xfree(backup); backup = NULL; @@ -3223,12 +3225,12 @@ restore_backup: * This may not work if the vim_rename() fails. * In that case we leave the copy around. */ - /* If file does not exist, put the copy in its place */ - if (!os_file_exists(fname)) { + // If file does not exist, put the copy in its place + if (!os_path_exists(fname)) { vim_rename(backup, fname); } - /* if original file does exist throw away the copy */ - if (os_file_exists(fname)) { + // if original file does exist throw away the copy + if (os_path_exists(fname)) { os_remove((char *)backup); } } else { @@ -3237,8 +3239,8 @@ restore_backup: } } - /* if original file no longer exists give an extra warning */ - if (!newfile && !os_file_exists(fname)) { + // if original file no longer exists give an extra warning + if (!newfile && !os_path_exists(fname)) { end = 0; } } @@ -3596,9 +3598,9 @@ restore_backup: * If the original file does not exist yet * the current backup file becomes the original file */ - if (org == NULL) + if (org == NULL) { EMSG(_("E205: Patchmode: can't save original file")); - else if (!os_file_exists((char_u *)org)) { + } else if (!os_path_exists((char_u *)org)) { vim_rename(backup, (char_u *)org); xfree(backup); /* don't delete the file */ backup = NULL; @@ -4513,9 +4515,11 @@ int vim_rename(char_u *from, char_u *to) if (STRLEN(from) >= MAXPATHL - 5) return -1; STRCPY(tempname, from); - for (n = 123; n < 99999; ++n) { - sprintf((char *)path_tail(tempname), "%d", n); - if (!os_file_exists(tempname)) { + for (n = 123; n < 99999; n++) { + char * tail = (char *)path_tail(tempname); + snprintf(tail, (MAXPATHL + 1) - (tail - (char *)tempname - 1), "%d", n); + + if (!os_path_exists(tempname)) { if (os_rename(from, tempname) == OK) { if (os_rename(tempname, to) == OK) return 0; @@ -4862,7 +4866,7 @@ buf_check_timestamp ( } } else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W) - && os_file_exists(buf->b_ffname)) { + && os_path_exists(buf->b_ffname)) { retval = 1; mesg = _("W13: Warning: File \"%s\" has been created after editing started"); buf->b_flags |= BF_NEW_W; diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index af8558d40d..ea017ab0c8 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -41,169 +41,174 @@ // $ gcc -E -dM - </dev/null // $ echo | clang -dM -E - +#ifndef NVIM_FUNC_ATTR_H +#define NVIM_FUNC_ATTR_H +#undef NVIM_FUNC_ATTR_H + #ifdef FUNC_ATTR_MALLOC - #undef FUNC_ATTR_MALLOC +# undef FUNC_ATTR_MALLOC #endif #ifdef FUNC_ATTR_ALLOC_SIZE - #undef FUNC_ATTR_ALLOC_SIZE +# undef FUNC_ATTR_ALLOC_SIZE #endif #ifdef FUNC_ATTR_ALLOC_SIZE_PROD - #undef FUNC_ATTR_ALLOC_SIZE_PROD +# undef FUNC_ATTR_ALLOC_SIZE_PROD #endif #ifdef FUNC_ATTR_ALLOC_ALIGN - #undef FUNC_ATTR_ALLOC_ALIGN +# undef FUNC_ATTR_ALLOC_ALIGN #endif #ifdef FUNC_ATTR_PURE - #undef FUNC_ATTR_PURE +# undef FUNC_ATTR_PURE #endif #ifdef FUNC_ATTR_CONST - #undef FUNC_ATTR_CONST +# undef FUNC_ATTR_CONST #endif #ifdef FUNC_ATTR_WARN_UNUSED_RESULT - #undef FUNC_ATTR_WARN_UNUSED_RESULT +# undef FUNC_ATTR_WARN_UNUSED_RESULT #endif #ifdef FUNC_ATTR_ALWAYS_INLINE - #undef FUNC_ATTR_ALWAYS_INLINE +# undef FUNC_ATTR_ALWAYS_INLINE #endif #ifdef FUNC_ATTR_UNUSED - #undef FUNC_ATTR_UNUSED +# undef FUNC_ATTR_UNUSED #endif #ifdef FUNC_ATTR_NONNULL_ALL - #undef FUNC_ATTR_NONNULL_ALL +# undef FUNC_ATTR_NONNULL_ALL #endif #ifdef FUNC_ATTR_NONNULL_ARG - #undef FUNC_ATTR_NONNULL_ARG +# undef FUNC_ATTR_NONNULL_ARG #endif #ifdef FUNC_ATTR_NONNULL_RET - #undef FUNC_ATTR_NONNULL_RET +# undef FUNC_ATTR_NONNULL_RET #endif #ifndef DID_REAL_ATTR - #define DID_REAL_ATTR - #ifdef __GNUC__ - // place defines for all gnulikes here, for now that's gcc, clang and - // intel. - - // place these after the argument list of the function declaration - // (not definition), like so: - // void myfunc(void) REAL_FATTR_ALWAYS_INLINE; - #define REAL_FATTR_MALLOC __attribute__((malloc)) - #define REAL_FATTR_ALLOC_ALIGN(x) __attribute__((alloc_align(x))) - #define REAL_FATTR_PURE __attribute__ ((pure)) - #define REAL_FATTR_CONST __attribute__((const)) - #define REAL_FATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) - #define REAL_FATTR_ALWAYS_INLINE __attribute__((always_inline)) - #define REAL_FATTR_UNUSED __attribute__((unused)) - #define REAL_FATTR_NONNULL_ALL __attribute__((nonnull)) - #define REAL_FATTR_NONNULL_ARG(...) __attribute__((nonnull(__VA_ARGS__))) - - #ifdef __clang__ - // clang only - #elif defined(__INTEL_COMPILER) - // intel only - #else - #define GCC_VERSION \ +# define DID_REAL_ATTR +# ifdef __GNUC__ +// place defines for all gnulikes here, for now that's gcc, clang and +// intel. + +// place these after the argument list of the function declaration +// (not definition), like so: +// void myfunc(void) REAL_FATTR_ALWAYS_INLINE; +# define REAL_FATTR_MALLOC __attribute__((malloc)) +# define REAL_FATTR_ALLOC_ALIGN(x) __attribute__((alloc_align(x))) +# define REAL_FATTR_PURE __attribute__ ((pure)) +# define REAL_FATTR_CONST __attribute__((const)) +# define REAL_FATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# define REAL_FATTR_ALWAYS_INLINE __attribute__((always_inline)) +# define REAL_FATTR_UNUSED __attribute__((unused)) +# define REAL_FATTR_NONNULL_ALL __attribute__((nonnull)) +# define REAL_FATTR_NONNULL_ARG(...) __attribute__((nonnull(__VA_ARGS__))) + +# ifdef __clang__ +// clang only +# elif defined(__INTEL_COMPILER) +// intel only +# else +# define GCC_VERSION \ (__GNUC__ * 10000 + \ - __GNUC_MINOR__ * 100 + \ - __GNUC_PATCHLEVEL__) - // gcc only - #define REAL_FATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x))) - #define REAL_FATTR_ALLOC_SIZE_PROD(x,y) __attribute__((alloc_size(x,y))) - #if GCC_VERSION >= 40900 - #define REAL_FATTR_NONNULL_RET __attribute__((returns_nonnull)) - #endif - #endif - #endif - - // define function attributes that haven't been defined for this specific - // compiler. - - #ifndef REAL_FATTR_MALLOC - #define REAL_FATTR_MALLOC - #endif - - #ifndef REAL_FATTR_ALLOC_SIZE - #define REAL_FATTR_ALLOC_SIZE(x) - #endif - - #ifndef REAL_FATTR_ALLOC_SIZE_PROD - #define REAL_FATTR_ALLOC_SIZE_PROD(x,y) - #endif - - #ifndef REAL_FATTR_ALLOC_ALIGN - #define REAL_FATTR_ALLOC_ALIGN(x) - #endif - - #ifndef REAL_FATTR_PURE - #define REAL_FATTR_PURE - #endif - - #ifndef REAL_FATTR_CONST - #define REAL_FATTR_CONST - #endif - - #ifndef REAL_FATTR_WARN_UNUSED_RESULT - #define REAL_FATTR_WARN_UNUSED_RESULT - #endif - - #ifndef REAL_FATTR_ALWAYS_INLINE - #define REAL_FATTR_ALWAYS_INLINE - #endif - - #ifndef REAL_FATTR_UNUSED - #define REAL_FATTR_UNUSED - #endif - - #ifndef REAL_FATTR_NONNULL_ALL - #define REAL_FATTR_NONNULL_ALL - #endif - - #ifndef REAL_FATTR_NONNULL_ARG - #define REAL_FATTR_NONNULL_ARG(...) - #endif - - #ifndef REAL_FATTR_NONNULL_RET - #define REAL_FATTR_NONNULL_RET - #endif + __GNUC_MINOR__ * 100 + \ + __GNUC_PATCHLEVEL__) +// gcc only +# define REAL_FATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x))) +# define REAL_FATTR_ALLOC_SIZE_PROD(x, y) __attribute__((alloc_size(x, y))) +# if GCC_VERSION >= 40900 +# define REAL_FATTR_NONNULL_RET __attribute__((returns_nonnull)) +# endif +# endif +# endif + +// define function attributes that haven't been defined for this specific +// compiler. + +# ifndef REAL_FATTR_MALLOC +# define REAL_FATTR_MALLOC +# endif + +# ifndef REAL_FATTR_ALLOC_SIZE +# define REAL_FATTR_ALLOC_SIZE(x) +# endif + +# ifndef REAL_FATTR_ALLOC_SIZE_PROD +# define REAL_FATTR_ALLOC_SIZE_PROD(x, y) +# endif + +# ifndef REAL_FATTR_ALLOC_ALIGN +# define REAL_FATTR_ALLOC_ALIGN(x) +# endif + +# ifndef REAL_FATTR_PURE +# define REAL_FATTR_PURE +# endif + +# ifndef REAL_FATTR_CONST +# define REAL_FATTR_CONST +# endif + +# ifndef REAL_FATTR_WARN_UNUSED_RESULT +# define REAL_FATTR_WARN_UNUSED_RESULT +# endif + +# ifndef REAL_FATTR_ALWAYS_INLINE +# define REAL_FATTR_ALWAYS_INLINE +# endif + +# ifndef REAL_FATTR_UNUSED +# define REAL_FATTR_UNUSED +# endif + +# ifndef REAL_FATTR_NONNULL_ALL +# define REAL_FATTR_NONNULL_ALL +# endif + +# ifndef REAL_FATTR_NONNULL_ARG +# define REAL_FATTR_NONNULL_ARG(...) +# endif + +# ifndef REAL_FATTR_NONNULL_RET +# define REAL_FATTR_NONNULL_RET +# endif #endif #ifdef DEFINE_FUNC_ATTRIBUTES - #define FUNC_API_ASYNC - #define FUNC_API_NOEXPORT - #define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC - #define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x) - #define FUNC_ATTR_ALLOC_SIZE_PROD(x,y) REAL_FATTR_ALLOC_SIZE_PROD(x,y) - #define FUNC_ATTR_ALLOC_ALIGN(x) REAL_FATTR_ALLOC_ALIGN(x) - #define FUNC_ATTR_PURE REAL_FATTR_PURE - #define FUNC_ATTR_CONST REAL_FATTR_CONST - #define FUNC_ATTR_WARN_UNUSED_RESULT REAL_FATTR_WARN_UNUSED_RESULT - #define FUNC_ATTR_ALWAYS_INLINE REAL_FATTR_ALWAYS_INLINE - #define FUNC_ATTR_UNUSED REAL_FATTR_UNUSED - #define FUNC_ATTR_NONNULL_ALL REAL_FATTR_NONNULL_ALL - #define FUNC_ATTR_NONNULL_ARG(...) REAL_FATTR_NONNULL_ARG(__VA_ARGS__) - #define FUNC_ATTR_NONNULL_RET REAL_FATTR_NONNULL_RET +# define FUNC_API_ASYNC +# define FUNC_API_NOEXPORT +# define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC +# define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x) +# define FUNC_ATTR_ALLOC_SIZE_PROD(x, y) REAL_FATTR_ALLOC_SIZE_PROD(x, y) +# define FUNC_ATTR_ALLOC_ALIGN(x) REAL_FATTR_ALLOC_ALIGN(x) +# define FUNC_ATTR_PURE REAL_FATTR_PURE +# define FUNC_ATTR_CONST REAL_FATTR_CONST +# define FUNC_ATTR_WARN_UNUSED_RESULT REAL_FATTR_WARN_UNUSED_RESULT +# define FUNC_ATTR_ALWAYS_INLINE REAL_FATTR_ALWAYS_INLINE +# define FUNC_ATTR_UNUSED REAL_FATTR_UNUSED +# define FUNC_ATTR_NONNULL_ALL REAL_FATTR_NONNULL_ALL +# define FUNC_ATTR_NONNULL_ARG(...) REAL_FATTR_NONNULL_ARG(__VA_ARGS__) +# define FUNC_ATTR_NONNULL_RET REAL_FATTR_NONNULL_RET #elif !defined(DO_NOT_DEFINE_EMPTY_ATTRIBUTES) - #define FUNC_ATTR_MALLOC - #define FUNC_ATTR_ALLOC_SIZE(x) - #define FUNC_ATTR_ALLOC_SIZE_PROD(x,y) - #define FUNC_ATTR_ALLOC_ALIGN(x) - #define FUNC_ATTR_PURE - #define FUNC_ATTR_CONST - #define FUNC_ATTR_WARN_UNUSED_RESULT - #define FUNC_ATTR_ALWAYS_INLINE - #define FUNC_ATTR_UNUSED - #define FUNC_ATTR_NONNULL_ALL - #define FUNC_ATTR_NONNULL_ARG(...) - #define FUNC_ATTR_NONNULL_RET +# define FUNC_ATTR_MALLOC +# define FUNC_ATTR_ALLOC_SIZE(x) +# define FUNC_ATTR_ALLOC_SIZE_PROD(x, y) +# define FUNC_ATTR_ALLOC_ALIGN(x) +# define FUNC_ATTR_PURE +# define FUNC_ATTR_CONST +# define FUNC_ATTR_WARN_UNUSED_RESULT +# define FUNC_ATTR_ALWAYS_INLINE +# define FUNC_ATTR_UNUSED +# define FUNC_ATTR_NONNULL_ALL +# define FUNC_ATTR_NONNULL_ARG(...) +# define FUNC_ATTR_NONNULL_RET #endif +#endif // NVIM_FUNC_ATTR_H diff --git a/src/nvim/garray.h b/src/nvim/garray.h index 642eaf54f0..5d7806bbfa 100644 --- a/src/nvim/garray.h +++ b/src/nvim/garray.h @@ -21,10 +21,10 @@ typedef struct growarray { #define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0) -#define GA_APPEND(item_type, gap, item) \ - do { \ - ga_grow(gap, 1); \ - ((item_type *)(gap)->ga_data)[(gap)->ga_len++] = (item); \ +#define GA_APPEND(item_type, gap, item) \ + do { \ + ga_grow(gap, 1); \ + ((item_type *)(gap)->ga_data)[(gap)->ga_len++] = (item); \ } while (0) #define GA_APPEND_VIA_PTR(item_type, gap) \ @@ -49,16 +49,16 @@ static inline void *ga_append_via_ptr(garray_T *gap, size_t item_size) /// @param gap the garray to be freed /// @param item_type type of the item in the garray /// @param free_item_fn free function that takes (*item_type) as parameter -#define GA_DEEP_CLEAR(gap, item_type, free_item_fn) \ - do { \ - garray_T *_gap = (gap); \ - if (_gap->ga_data != NULL) { \ - for (int i = 0; i < _gap->ga_len; i++) { \ - item_type *_item = &(((item_type *)_gap->ga_data)[i]); \ - free_item_fn(_item); \ - } \ - } \ - ga_clear(_gap); \ +#define GA_DEEP_CLEAR(gap, item_type, free_item_fn) \ + do { \ + garray_T *_gap = (gap); \ + if (_gap->ga_data != NULL) { \ + for (int i = 0; i < _gap->ga_len; i++) { \ + item_type *_item = &(((item_type *)_gap->ga_data)[i]); \ + free_item_fn(_item); \ + } \ + } \ + ga_clear(_gap); \ } while (false) #define FREE_PTR_PTR(ptr) xfree(*(ptr)) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index dbf0322d78..ae1857f318 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1754,10 +1754,11 @@ static int vgetorpeek(int advance) || ((compl_cont_status & CONT_LOCAL) && (c1 == Ctrl_N || c1 == Ctrl_P))) ) { - if (c1 == K_SPECIAL) + if (c1 == K_SPECIAL) { nolmaplen = 2; - else { - LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0); + } else { + LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0 + && get_real_state() != SELECTMODE); nolmaplen = 0; } /* First try buffer-local mappings. */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index dafb75ca87..f2610ac868 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -414,7 +414,7 @@ EXTERN int provider_call_nesting INIT(= 0); EXTERN char_u hash_removed; -EXTERN int t_colors INIT(= 0); /* int value of T_CCO */ +EXTERN int t_colors INIT(= 256); // int value of T_CCO /* * When highlight_match is TRUE, highlight a match, starting at the cursor @@ -836,8 +836,8 @@ EXTERN int* (*iconv_errno)(void); EXTERN int State INIT(= NORMAL); /* This is the current state of the * command interpreter. */ -EXTERN int finish_op INIT(= FALSE); /* TRUE while an operator is pending */ -EXTERN long opcount INIT(= 0); /* count for pending operator */ +EXTERN bool finish_op INIT(= false); // true while an operator is pending +EXTERN long opcount INIT(= 0); // count for pending operator /* * ex mode (Q) state @@ -914,9 +914,6 @@ EXTERN int KeyTyped; // TRUE if user typed current char EXTERN int KeyStuffed; // TRUE if current char from stuffbuf EXTERN int maptick INIT(= 0); // tick for each non-mapped char -EXTERN uint8_t chartab[256]; // table used in charset.c; See - // init_chartab() for explanation - EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */ EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */ EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */ @@ -985,10 +982,11 @@ EXTERN int keep_help_flag INIT(= FALSE); /* doing :ta from help file */ */ EXTERN char_u *empty_option INIT(= (char_u *)""); -EXTERN int redir_off INIT(= FALSE); /* no redirection for a moment */ -EXTERN FILE *redir_fd INIT(= NULL); /* message redirection file */ -EXTERN int redir_reg INIT(= 0); /* message redirection register */ -EXTERN int redir_vname INIT(= 0); /* message redirection variable */ +EXTERN int redir_off INIT(= false); // no redirection for a moment +EXTERN FILE *redir_fd INIT(= NULL); // message redirection file +EXTERN int redir_reg INIT(= 0); // message redirection register +EXTERN int redir_vname INIT(= 0); // message redirection variable +EXTERN garray_T *capture_ga INIT(= NULL); // capture() buffer EXTERN char_u langmap_mapchar[256]; /* mapping for language keys */ @@ -1222,6 +1220,8 @@ EXTERN char_u e_invalpat[] INIT(= N_( EXTERN char_u e_bufloaded[] INIT(= N_("E139: File is loaded in another buffer")); EXTERN char_u e_notset[] INIT(= N_("E764: Option '%s' is not set")); EXTERN char_u e_invalidreg[] INIT(= N_("E850: Invalid register name")); +EXTERN char_u e_dirnotf[] INIT(= N_( + "E919: Directory not found in '%s': \"%s\"")); EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); @@ -1243,7 +1243,6 @@ EXTERN char *ignoredp; // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); -EXTERN Loop loop; /// Used to track the status of external functions. /// Currently only used for iconv(). diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 916d27a964..1c9b8e18ef 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -1526,8 +1526,7 @@ static int prt_find_resource(char *name, struct prt_ps_resource_S *resource) vim_strcat(buffer, (char_u *)name, MAXPATHL); vim_strcat(buffer, (char_u *)".ps", MAXPATHL); resource->filename[0] = NUL; - retval = (do_in_runtimepath(buffer, FALSE, prt_resource_name, - resource->filename) + retval = (do_in_runtimepath(buffer, 0, prt_resource_name, resource->filename) && resource->filename[0] != NUL); xfree(buffer); return retval; diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 2f9ec0b3ff..f2432dd71d 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -480,8 +480,9 @@ staterr: if (arg2 != NULL) { ppath = xmalloc(MAXPATHL + 1); expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL); - if (!os_file_exists((char_u *)ppath)) + if (!os_path_exists((char_u *)ppath)) { goto staterr; + } } int i; diff --git a/src/nvim/lib/klist.h b/src/nvim/lib/klist.h index 1280a927e8..7ee100ab8c 100644 --- a/src/nvim/lib/klist.h +++ b/src/nvim/lib/klist.h @@ -33,35 +33,37 @@ #include "nvim/func_attr.h" -#define KMEMPOOL_INIT(name, kmptype_t, kmpfree_f) \ - typedef struct { \ - size_t cnt, n, max; \ - kmptype_t **buf; \ - } kmp_##name##_t; \ - static inline kmp_##name##_t *kmp_init_##name(void) { \ - return xcalloc(1, sizeof(kmp_##name##_t)); \ - } \ - static inline void kmp_destroy_##name(kmp_##name##_t *mp) \ - REAL_FATTR_UNUSED; \ - static inline void kmp_destroy_##name(kmp_##name##_t *mp) { \ - size_t k; \ - for (k = 0; k < mp->n; ++k) { \ - kmpfree_f(mp->buf[k]); xfree(mp->buf[k]); \ - } \ - xfree(mp->buf); xfree(mp); \ - } \ - static inline kmptype_t *kmp_alloc_##name(kmp_##name##_t *mp) { \ - ++mp->cnt; \ - if (mp->n == 0) return xcalloc(1, sizeof(kmptype_t)); \ - return mp->buf[--mp->n]; \ - } \ +#define KMEMPOOL_INIT(name, kmptype_t, kmpfree_f) \ + typedef struct { \ + size_t cnt, n, max; \ + kmptype_t **buf; \ + } kmp_##name##_t; \ + static inline kmp_##name##_t *kmp_init_##name(void) { \ + return xcalloc(1, sizeof(kmp_##name##_t)); \ + } \ + static inline void kmp_destroy_##name(kmp_##name##_t *mp) \ + REAL_FATTR_UNUSED; \ + static inline void kmp_destroy_##name(kmp_##name##_t *mp) { \ + size_t k; \ + for (k = 0; k < mp->n; k++) { \ + kmpfree_f(mp->buf[k]); xfree(mp->buf[k]); \ + } \ + xfree(mp->buf); xfree(mp); \ + } \ + static inline kmptype_t *kmp_alloc_##name(kmp_##name##_t *mp) { \ + mp->cnt++; \ + if (mp->n == 0) { \ + return xcalloc(1, sizeof(kmptype_t)); \ + } \ + return mp->buf[--mp->n]; \ + } \ static inline void kmp_free_##name(kmp_##name##_t *mp, kmptype_t *p) { \ - --mp->cnt; \ - if (mp->n == mp->max) { \ - mp->max = mp->max? mp->max<<1 : 16; \ + mp->cnt--; \ + if (mp->n == mp->max) { \ + mp->max = mp->max ? (mp->max << 1) : 16; \ mp->buf = xrealloc(mp->buf, sizeof(kmptype_t *) * mp->max); \ - } \ - mp->buf[mp->n++] = p; \ + } \ + mp->buf[mp->n++] = p; \ } #define kmempool_t(name) kmp_##name##_t @@ -70,54 +72,56 @@ #define kmp_alloc(name, mp) kmp_alloc_##name(mp) #define kmp_free(name, mp, p) kmp_free_##name(mp, p) -#define KLIST_INIT(name, kltype_t, kmpfree_t) \ - struct __kl1_##name { \ - kltype_t data; \ - struct __kl1_##name *next; \ - }; \ - typedef struct __kl1_##name kl1_##name; \ - KMEMPOOL_INIT(name, kl1_##name, kmpfree_t) \ - typedef struct { \ - kl1_##name *head, *tail; \ - kmp_##name##_t *mp; \ - size_t size; \ - } kl_##name##_t; \ - static inline kl_##name##_t *kl_init_##name(void) { \ - kl_##name##_t *kl = xcalloc(1, sizeof(kl_##name##_t)); \ - kl->mp = kmp_init(name); \ - kl->head = kl->tail = kmp_alloc(name, kl->mp); \ - kl->head->next = 0; \ - return kl; \ - } \ - static inline void kl_destroy_##name(kl_##name##_t *kl) \ - REAL_FATTR_UNUSED; \ - static inline void kl_destroy_##name(kl_##name##_t *kl) { \ - kl1_##name *p; \ - for (p = kl->head; p != kl->tail; p = p->next) \ - kmp_free(name, kl->mp, p); \ - kmp_free(name, kl->mp, p); \ - kmp_destroy(name, kl->mp); \ - xfree(kl); \ - } \ - static inline void kl_push_##name(kl_##name##_t *kl, kltype_t d) { \ - kl1_##name *q, *p = kmp_alloc(name, kl->mp); \ - q = kl->tail; p->next = 0; kl->tail->next = p; kl->tail = p; \ - ++kl->size; \ - q->data = d; \ - } \ - static inline kltype_t kl_shift_at_##name(kl_##name##_t *kl, \ - kl1_##name **n) { \ - assert((*n)->next); \ - kl1_##name *p; \ - --kl->size; \ - p = *n; \ - *n = (*n)->next; \ - if (p == kl->head) kl->head = *n; \ - kltype_t d = p->data; \ - kmp_free(name, kl->mp, p); \ - return d; \ - } \ - +#define KLIST_INIT(name, kltype_t, kmpfree_t) \ + struct __kl1_##name { \ + kltype_t data; \ + struct __kl1_##name *next; \ + }; \ + typedef struct __kl1_##name kl1_##name; \ + KMEMPOOL_INIT(name, kl1_##name, kmpfree_t) \ + typedef struct { \ + kl1_##name *head, *tail; \ + kmp_##name##_t *mp; \ + size_t size; \ + } kl_##name##_t; \ + static inline kl_##name##_t *kl_init_##name(void) { \ + kl_##name##_t *kl = xcalloc(1, sizeof(kl_##name##_t)); \ + kl->mp = kmp_init(name); \ + kl->head = kl->tail = kmp_alloc(name, kl->mp); \ + kl->head->next = 0; \ + return kl; \ + } \ + static inline void kl_destroy_##name(kl_##name##_t *kl) \ + REAL_FATTR_UNUSED; \ + static inline void kl_destroy_##name(kl_##name##_t *kl) { \ + kl1_##name *p; \ + for (p = kl->head; p != kl->tail; p = p->next) { \ + kmp_free(name, kl->mp, p); \ + } \ + kmp_free(name, kl->mp, p); \ + kmp_destroy(name, kl->mp); \ + xfree(kl); \ + } \ + static inline void kl_push_##name(kl_##name##_t *kl, kltype_t d) { \ + kl1_##name *q, *p = kmp_alloc(name, kl->mp); \ + q = kl->tail; p->next = 0; kl->tail->next = p; kl->tail = p; \ + kl->size++; \ + q->data = d; \ + } \ + static inline kltype_t kl_shift_at_##name(kl_##name##_t *kl, \ + kl1_##name **n) { \ + assert((*n)->next); \ + kl1_##name *p; \ + kl->size--; \ + p = *n; \ + *n = (*n)->next; \ + if (p == kl->head) { \ + kl->head = *n; \ + } \ + kltype_t d = p->data; \ + kmp_free(name, kl->mp, p); \ + return d; \ + } #define kliter_t(name) kl1_##name #define klist_t(name) kl_##name##_t diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index b41ef0cc9f..584282d773 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -1,59 +1,61 @@ -/* The MIT License - - Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk> - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -/* - An example: - -#include "kvec.h" -int main() { - kvec_t(int) array; - kv_init(array); - kv_push(int, array, 10); // append - kv_a(int, array, 20) = 5; // dynamic - kv_A(array, 20) = 4; // static - kv_destroy(array); - return 0; -} -*/ - -/* - 2008-09-22 (0.1.0): +// The MIT License +// +// Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk> +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// An example: +// +// #include "kvec.h" +// int main() { +// kvec_t(int) array = KV_INITIAL_VALUE; +// kv_push(array, 10); // append +// kv_a(array, 20) = 5; // dynamic +// kv_A(array, 20) = 4; // static +// kv_destroy(array); +// return 0; +// } + +#ifndef NVIM_LIB_KVEC_H +#define NVIM_LIB_KVEC_H - * The initial version. +#include <stdlib.h> +#include <string.h> -*/ +#include "nvim/memory.h" -#ifndef AC_KVEC_H -#define AC_KVEC_H +#define kv_roundup32(x) \ + ((--(x)), \ + ((x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16), \ + (++(x))) -#include <stdlib.h> -#include "nvim/memory.h" +#define KV_INITIAL_VALUE { .size = 0, .capacity = 0, .items = NULL } -#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#define kvec_t(type) \ + struct { \ + size_t size; \ + size_t capacity; \ + type *items; \ + } -#define kvec_t(type) struct { size_t size, capacity; type *items; } #define kv_init(v) ((v).size = (v).capacity = 0, (v).items = 0) #define kv_destroy(v) xfree((v).items) #define kv_A(v, i) ((v).items[(i)]) @@ -62,31 +64,133 @@ int main() { #define kv_max(v) ((v).capacity) #define kv_last(v) kv_A(v, kv_size(v) - 1) -#define kv_resize(type, v, s) ((v).capacity = (s), (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity)) - -#define kv_copy(type, v1, v0) do { \ - if ((v1).capacity < (v0).size) kv_resize(type, v1, (v0).size); \ - (v1).size = (v0).size; \ - memcpy((v1).items, (v0).items, sizeof(type) * (v0).size); \ - } while (0) \ - -#define kv_push(type, v, x) do { \ - if ((v).size == (v).capacity) { \ - (v).capacity = (v).capacity? (v).capacity<<1 : 8; \ - (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity); \ - } \ - (v).items[(v).size++] = (x); \ - } while (0) - -#define kv_pushp(type, v) ((((v).size == (v).capacity)? \ - ((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \ - (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \ - : 0), ((v).items + ((v).size++))) - -#define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \ - ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \ - (v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \ - : (v).size <= (size_t)(i)? (v).size = (i) + 1 \ - : 0), (v).items[(i)]) - -#endif +#define kv_resize(v, s) \ + ((v).capacity = (s), \ + (v).items = xrealloc((v).items, sizeof((v).items[0]) * (v).capacity)) + +#define kv_resize_full(v) \ + kv_resize(v, (v).capacity ? (v).capacity << 1 : 8) + +#define kv_copy(v1, v0) \ + do { \ + if ((v1).capacity < (v0).size) { \ + kv_resize(v1, (v0).size); \ + } \ + (v1).size = (v0).size; \ + memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \ + } while (0) \ + +#define kv_pushp(v) \ + ((((v).size == (v).capacity) ? (kv_resize_full(v), 0) : 0), \ + ((v).items + ((v).size++))) + +#define kv_push(v, x) \ + (*kv_pushp(v) = (x)) + +#define kv_a(v, i) \ + (((v).capacity <= (size_t) (i) \ + ? ((v).capacity = (v).size = (i) + 1, \ + kv_roundup32((v).capacity), \ + kv_resize((v), (v).capacity), 0) \ + : ((v).size <= (size_t) (i) \ + ? (v).size = (i) + 1 \ + : 0)), \ + (v).items[(i)]) + +/// Type of a vector with a few first members allocated on stack +/// +/// Is compatible with #kv_A, #kv_pop, #kv_size, #kv_max, #kv_last. +/// Is not compatible with #kv_resize, #kv_resize_full, #kv_copy, #kv_push, +/// #kv_pushp, #kv_a, #kv_destroy. +/// +/// @param[in] type Type of vector elements. +/// @param[in] init_size Number of the elements in the initial array. +#define kvec_withinit_t(type, INIT_SIZE) \ + struct { \ + size_t size; \ + size_t capacity; \ + type *items; \ + type init_array[INIT_SIZE]; \ + } + +/// Initialize vector with preallocated array +/// +/// @param[out] v Vector to initialize. +#define kvi_init(v) \ + ((v).capacity = ARRAY_SIZE((v).init_array), \ + (v).size = 0, \ + (v).items = (v).init_array) + +/// Move data to a new destination and free source +static inline void *_memcpy_free(void *const restrict dest, + void *const restrict src, + const size_t size) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_ALWAYS_INLINE +{ + memcpy(dest, src, size); + xfree(src); + return dest; +} + +/// Resize vector with preallocated array +/// +/// @note May not resize to an array smaller then init_array: if requested, +/// init_array will be used. +/// +/// @param[out] v Vector to resize. +/// @param[in] s New size. +#define kvi_resize(v, s) \ + ((v).capacity = ((s) > ARRAY_SIZE((v).init_array) \ + ? (s) \ + : ARRAY_SIZE((v).init_array)), \ + (v).items = ((v).capacity == ARRAY_SIZE((v).init_array) \ + ? ((v).items == (v).init_array \ + ? (v).items \ + : _memcpy_free((v).init_array, (v).items, \ + (v).size * sizeof((v).items[0]))) \ + : ((v).items == (v).init_array \ + ? memcpy(xmalloc((v).capacity * sizeof((v).items[0])), \ + (v).items, \ + (v).size * sizeof((v).items[0])) \ + : xrealloc((v).items, \ + (v).capacity * sizeof((v).items[0]))))) + +/// Resize vector with preallocated array when it is full +/// +/// @param[out] v Vector to resize. +#define kvi_resize_full(v) \ + /* ARRAY_SIZE((v).init_array) is the minimal capacity of this vector. */ \ + /* Thus when vector is full capacity may not be zero and it is safe */ \ + /* not to bother with checking whether (v).capacity is 0. But now */ \ + /* capacity is not guaranteed to have size that is a power of 2, it is */ \ + /* hard to fix this here and is not very necessary if users will use */ \ + /* 2^x initial array size. */ \ + kvi_resize(v, (v).capacity << 1) + +/// Get location where to store new element to a vector with preallocated array +/// +/// @param[in,out] v Vector to push to. +/// +/// @return Pointer to the place where new value should be stored. +#define kvi_pushp(v) \ + ((((v).size == (v).capacity) ? (kvi_resize_full(v), 0) : 0), \ + ((v).items + ((v).size++))) + +/// Push value to a vector with preallocated array +/// +/// @param[out] v Vector to push to. +/// @param[in] x Value to push. +#define kvi_push(v, x) \ + (*kvi_pushp(v) = (x)) + +/// Free array of elements of a vector with preallocated array if needed +/// +/// @param[out] v Vector to free. +#define kvi_destroy(v) \ + do { \ + if ((v).items != (v).init_array) { \ + xfree((v).items); \ + } \ + } while (0) + +#endif // NVIM_LIB_KVEC_H diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue.h index fe02b454ea..9fcedf298f 100644 --- a/src/nvim/lib/queue.h +++ b/src/nvim/lib/queue.h @@ -1,92 +1,94 @@ -/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ +// Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -#ifndef QUEUE_H_ -#define QUEUE_H_ +#ifndef NVIM_LIB_QUEUE_H +#define NVIM_LIB_QUEUE_H -typedef void *QUEUE[2]; +#include <stddef.h> -/* Private macros. */ -#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) -#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) -#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) -#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) +#include "nvim/func_attr.h" -/* Public macros. */ -#define QUEUE_DATA(ptr, type, field) \ - ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) +typedef struct _queue { + struct _queue *next; + struct _queue *prev; +} QUEUE; -#define QUEUE_FOREACH(q, h) \ - for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) +// Public macros. +#define QUEUE_DATA(ptr, type, field) \ + ((type *)((char *)(ptr) - offsetof(type, field))) -#define QUEUE_EMPTY(q) \ - ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) +#define QUEUE_FOREACH(q, h) \ + for ( /* NOLINT(readability/braces) */ \ + (q) = (h)->next; (q) != (h); (q) = (q)->next) -#define QUEUE_HEAD(q) \ - (QUEUE_NEXT(q)) +// ffi.cdef is unable to swallow `bool` in place of `int` here. +static inline int QUEUE_EMPTY(const QUEUE *const q) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return q == q->next; +} -#define QUEUE_INIT(q) \ - do { \ - QUEUE_NEXT(q) = (q); \ - QUEUE_PREV(q) = (q); \ - } \ - while (0) +#define QUEUE_HEAD(q) (q)->next -#define QUEUE_ADD(h, n) \ - do { \ - QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ - QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ - QUEUE_PREV(h) = QUEUE_PREV(n); \ - QUEUE_PREV_NEXT(h) = (h); \ - } \ - while (0) +static inline void QUEUE_INIT(QUEUE *const q) FUNC_ATTR_ALWAYS_INLINE +{ + q->next = q; + q->prev = q; +} -#define QUEUE_SPLIT(h, q, n) \ - do { \ - QUEUE_PREV(n) = QUEUE_PREV(h); \ - QUEUE_PREV_NEXT(n) = (n); \ - QUEUE_NEXT(n) = (q); \ - QUEUE_PREV(h) = QUEUE_PREV(q); \ - QUEUE_PREV_NEXT(h) = (h); \ - QUEUE_PREV(q) = (n); \ - } \ - while (0) +static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n) + FUNC_ATTR_ALWAYS_INLINE +{ + h->prev->next = n->next; + n->next->prev = h->prev; + h->prev = n->prev; + h->prev->next = h; +} -#define QUEUE_INSERT_HEAD(h, q) \ - do { \ - QUEUE_NEXT(q) = QUEUE_NEXT(h); \ - QUEUE_PREV(q) = (h); \ - QUEUE_NEXT_PREV(q) = (q); \ - QUEUE_NEXT(h) = (q); \ - } \ - while (0) +static inline void QUEUE_SPLIT(QUEUE *const h, QUEUE *const q, QUEUE *const n) + FUNC_ATTR_ALWAYS_INLINE +{ + n->prev = h->prev; + n->prev->next = n; + n->next = q; + h->prev = q->prev; + h->prev->next = h; + q->prev = n; +} -#define QUEUE_INSERT_TAIL(h, q) \ - do { \ - QUEUE_NEXT(q) = (h); \ - QUEUE_PREV(q) = QUEUE_PREV(h); \ - QUEUE_PREV_NEXT(q) = (q); \ - QUEUE_PREV(h) = (q); \ - } \ - while (0) +static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q) + FUNC_ATTR_ALWAYS_INLINE +{ + q->next = h->next; + q->prev = h; + q->next->prev = q; + h->next = q; +} -#define QUEUE_REMOVE(q) \ - do { \ - QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ - QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ - } \ - while (0) +static inline void QUEUE_INSERT_TAIL(QUEUE *const h, QUEUE *const q) + FUNC_ATTR_ALWAYS_INLINE +{ + q->next = h; + q->prev = h->prev; + q->prev->next = q; + h->prev = q; +} -#endif /* QUEUE_H_ */ +static inline void QUEUE_REMOVE(QUEUE *const q) FUNC_ATTR_ALWAYS_INLINE +{ + q->prev->next = q->next; + q->next->prev = q->prev; +} + +#endif // NVIM_LIB_QUEUE_H diff --git a/src/nvim/lib/ringbuf.h b/src/nvim/lib/ringbuf.h index cb71500bb7..12b75ec65a 100644 --- a/src/nvim/lib/ringbuf.h +++ b/src/nvim/lib/ringbuf.h @@ -65,12 +65,12 @@ /// @param TypeName Ring buffer type name. Actual type name will be /// `{TypeName}RingBuffer`. /// @param RBType Type of the single ring buffer element. -#define RINGBUF_TYPEDEF(TypeName, RBType) \ -typedef struct { \ - RBType *buf; \ - RBType *next; \ - RBType *first; \ - RBType *buf_end; \ +#define RINGBUF_TYPEDEF(TypeName, RBType) \ +typedef struct { \ + RBType *buf; \ + RBType *next; \ + RBType *first; \ + RBType *buf_end; \ } TypeName##RingBuffer; /// Initialize a new ring buffer @@ -84,198 +84,196 @@ typedef struct { \ /// a macros like `#define RBFREE(item)` (to skip freeing). /// /// Intended function signature: `void *rbfree(RBType *)`; -#define RINGBUF_INIT(TypeName, funcprefix, RBType, rbfree) \ - \ - \ -static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \ - REAL_FATTR_WARN_UNUSED_RESULT; \ -static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \ -{ \ - assert(size != 0); \ - RBType *buf = xmalloc(size * sizeof(RBType)); \ - return (TypeName##RingBuffer) { \ - .buf = buf, \ - .next = buf, \ - .first = NULL, \ - .buf_end = buf + size - 1, \ - }; \ -} \ - \ -static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \ - REAL_FATTR_UNUSED; \ -static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \ -{ \ - if (rb == NULL) { \ - return; \ - } \ - RINGBUF_FORALL(rb, RBType, rbitem) { \ - rbfree(rbitem); \ - } \ - xfree(rb->buf); \ -} \ - \ -static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \ - REAL_FATTR_UNUSED; \ -static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \ -{ \ - xfree(rb->buf); \ -} \ - \ -static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \ - RBType item) \ - REAL_FATTR_NONNULL_ARG(1); \ -static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \ - RBType item) \ -{ \ - if (rb->next == rb->first) { \ - rbfree(rb->first); \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } else if (rb->first == NULL) { \ - rb->first = rb->next; \ - } \ - *rb->next = item; \ - rb->next = _RINGBUF_NEXT(rb, rb->next); \ -} \ - \ -static inline ptrdiff_t funcprefix##_rb_find_idx( \ - const TypeName##RingBuffer *const rb, const RBType *const item_p) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \ -static inline ptrdiff_t funcprefix##_rb_find_idx( \ - const TypeName##RingBuffer *const rb, const RBType *const item_p) \ -{ \ - assert(rb->buf <= item_p); \ - assert(rb->buf_end >= item_p); \ - if (rb->first == NULL) { \ - return -1; \ - } else if (item_p >= rb->first) { \ - return item_p - rb->first; \ - } else { \ - return item_p - rb->buf + rb->buf_end - rb->first + 1; \ - } \ -} \ - \ -static inline size_t funcprefix##_rb_size( \ - const TypeName##RingBuffer *const rb) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ -static inline size_t funcprefix##_rb_size( \ - const TypeName##RingBuffer *const rb) \ -{ \ - return (size_t) (rb->buf_end - rb->buf) + 1; \ -} \ - \ -static inline size_t funcprefix##_rb_length( \ - const TypeName##RingBuffer *const rb) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ -static inline size_t funcprefix##_rb_length( \ - const TypeName##RingBuffer *const rb) \ -{ \ - return _RINGBUF_LENGTH(rb); \ -} \ - \ -static inline RBType *funcprefix##_rb_idx_p( \ - const TypeName##RingBuffer *const rb, const size_t idx) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ -static inline RBType *funcprefix##_rb_idx_p( \ - const TypeName##RingBuffer *const rb, const size_t idx) \ -{ \ - assert(idx <= funcprefix##_rb_size(rb)); \ - assert(idx <= funcprefix##_rb_length(rb)); \ - if (rb->first + idx > rb->buf_end) { \ - return rb->buf + ((rb->first + idx) - (rb->buf_end + 1)); \ - } else { \ - return rb->first + idx; \ - } \ -} \ - \ +#define RINGBUF_INIT(TypeName, funcprefix, RBType, rbfree) \ +static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \ + REAL_FATTR_WARN_UNUSED_RESULT; \ +static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \ +{ \ + assert(size != 0); \ + RBType *buf = xmalloc(size * sizeof(RBType)); \ + return (TypeName##RingBuffer) { \ + .buf = buf, \ + .next = buf, \ + .first = NULL, \ + .buf_end = buf + size - 1, \ + }; \ +} \ +\ +static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \ + REAL_FATTR_UNUSED; \ +static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \ +{ \ + if (rb == NULL) { \ + return; \ + } \ + RINGBUF_FORALL(rb, RBType, rbitem) { \ + rbfree(rbitem); \ + } \ + xfree(rb->buf); \ +} \ +\ +static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \ + REAL_FATTR_UNUSED; \ +static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \ +{ \ + xfree(rb->buf); \ +} \ +\ +static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \ + RBType item) \ + REAL_FATTR_NONNULL_ARG(1); \ +static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \ + RBType item) \ +{ \ + if (rb->next == rb->first) { \ + rbfree(rb->first); \ + rb->first = _RINGBUF_NEXT(rb, rb->first); \ + } else if (rb->first == NULL) { \ + rb->first = rb->next; \ + } \ + *rb->next = item; \ + rb->next = _RINGBUF_NEXT(rb, rb->next); \ +} \ +\ +static inline ptrdiff_t funcprefix##_rb_find_idx( \ + const TypeName##RingBuffer *const rb, const RBType *const item_p) \ + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \ +static inline ptrdiff_t funcprefix##_rb_find_idx( \ + const TypeName##RingBuffer *const rb, const RBType *const item_p) \ +{ \ + assert(rb->buf <= item_p); \ + assert(rb->buf_end >= item_p); \ + if (rb->first == NULL) { \ + return -1; \ + } else if (item_p >= rb->first) { \ + return item_p - rb->first; \ + } else { \ + return item_p - rb->buf + rb->buf_end - rb->first + 1; \ + } \ +} \ +\ +static inline size_t funcprefix##_rb_size( \ + const TypeName##RingBuffer *const rb) \ + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ +static inline size_t funcprefix##_rb_size( \ + const TypeName##RingBuffer *const rb) \ +{ \ + return (size_t) (rb->buf_end - rb->buf) + 1; \ +} \ +\ +static inline size_t funcprefix##_rb_length( \ + const TypeName##RingBuffer *const rb) \ + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ +static inline size_t funcprefix##_rb_length( \ + const TypeName##RingBuffer *const rb) \ +{ \ + return _RINGBUF_LENGTH(rb); \ +} \ +\ +static inline RBType *funcprefix##_rb_idx_p( \ + const TypeName##RingBuffer *const rb, const size_t idx) \ + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \ +static inline RBType *funcprefix##_rb_idx_p( \ + const TypeName##RingBuffer *const rb, const size_t idx) \ +{ \ + assert(idx <= funcprefix##_rb_size(rb)); \ + assert(idx <= funcprefix##_rb_length(rb)); \ + if (rb->first + idx > rb->buf_end) { \ + return rb->buf + ((rb->first + idx) - (rb->buf_end + 1)); \ + } else { \ + return rb->first + idx; \ + } \ +} \ +\ static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \ - const size_t idx) \ - REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \ + const size_t idx) \ + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \ static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \ - const size_t idx) \ -{ \ - return *funcprefix##_rb_idx_p(rb, idx); \ -} \ - \ -static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \ - const size_t idx, \ - RBType item) \ - REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \ -static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \ - const size_t idx, \ - RBType item) \ -{ \ - assert(idx <= funcprefix##_rb_size(rb)); \ - assert(idx <= funcprefix##_rb_length(rb)); \ - const size_t length = funcprefix##_rb_length(rb); \ - if (idx == length) { \ - funcprefix##_rb_push(rb, item); \ - return; \ - } \ - RBType *const insertpos = funcprefix##_rb_idx_p(rb, idx); \ - if (insertpos == rb->next) { \ - funcprefix##_rb_push(rb, item); \ - return; \ - } \ - if (length == funcprefix##_rb_size(rb)) { \ - rbfree(rb->first); \ - } \ - if (insertpos < rb->next) { \ - memmove(insertpos + 1, insertpos, \ - (size_t) ((uintptr_t) rb->next - (uintptr_t) insertpos)); \ - } else { \ - assert(insertpos > rb->first); \ - assert(rb->next <= rb->first); \ - memmove(rb->buf + 1, rb->buf, \ - (size_t) ((uintptr_t) rb->next - (uintptr_t) rb->buf)); \ - *rb->buf = *rb->buf_end; \ - memmove(insertpos + 1, insertpos, \ + const size_t idx) \ +{ \ + return *funcprefix##_rb_idx_p(rb, idx); \ +} \ +\ +static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \ + const size_t idx, \ + RBType item) \ + REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \ +static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \ + const size_t idx, \ + RBType item) \ +{ \ + assert(idx <= funcprefix##_rb_size(rb)); \ + assert(idx <= funcprefix##_rb_length(rb)); \ + const size_t length = funcprefix##_rb_length(rb); \ + if (idx == length) { \ + funcprefix##_rb_push(rb, item); \ + return; \ + } \ + RBType *const insertpos = funcprefix##_rb_idx_p(rb, idx); \ + if (insertpos == rb->next) { \ + funcprefix##_rb_push(rb, item); \ + return; \ + } \ + if (length == funcprefix##_rb_size(rb)) { \ + rbfree(rb->first); \ + } \ + if (insertpos < rb->next) { \ + memmove(insertpos + 1, insertpos, \ + (size_t) ((uintptr_t) rb->next - (uintptr_t) insertpos)); \ + } else { \ + assert(insertpos > rb->first); \ + assert(rb->next <= rb->first); \ + memmove(rb->buf + 1, rb->buf, \ + (size_t) ((uintptr_t) rb->next - (uintptr_t) rb->buf)); \ + *rb->buf = *rb->buf_end; \ + memmove(insertpos + 1, insertpos, \ (size_t) ((uintptr_t) (rb->buf_end + 1) - (uintptr_t) insertpos)); \ - } \ - *insertpos = item; \ - if (length == funcprefix##_rb_size(rb)) { \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } \ - rb->next = _RINGBUF_NEXT(rb, rb->next); \ -} \ - \ -static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \ - const size_t idx) \ - REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \ -static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \ - const size_t idx) \ -{ \ - assert(idx < funcprefix##_rb_size(rb)); \ - assert(idx < funcprefix##_rb_length(rb)); \ - RBType *const rmpos = funcprefix##_rb_idx_p(rb, idx); \ - rbfree(rmpos); \ - if (rmpos == rb->next - 1) { \ - rb->next--; \ - if (rb->first == rb->next) { \ - rb->first = NULL; \ - rb->next = rb->buf; \ - } \ - } else if (rmpos == rb->first) { \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - if (rb->first == rb->next) { \ - rb->first = NULL; \ - rb->next = rb->buf; \ - } \ - } else if (rb->first < rb->next || rb->next == rb->buf) { \ - assert(rmpos > rb->first); \ - assert(rmpos <= _RINGBUF_PREV(rb, rb->next)); \ - memmove(rb->first + 1, rb->first, \ - (size_t) ((uintptr_t) rmpos - (uintptr_t) rb->first)); \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } else if (rmpos < rb->next) { \ - memmove(rmpos, rmpos + 1, \ - (size_t) ((uintptr_t) rb->next - (uintptr_t) rmpos)); \ - rb->next = _RINGBUF_PREV(rb, rb->next); \ - } else { \ - assert(rb->first < rb->buf_end); \ - memmove(rb->first + 1, rb->first, \ - (size_t) ((uintptr_t) rmpos - (uintptr_t) rb->first)); \ - rb->first = _RINGBUF_NEXT(rb, rb->first); \ - } \ + } \ + *insertpos = item; \ + if (length == funcprefix##_rb_size(rb)) { \ + rb->first = _RINGBUF_NEXT(rb, rb->first); \ + } \ + rb->next = _RINGBUF_NEXT(rb, rb->next); \ +} \ +\ +static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \ + const size_t idx) \ + REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \ +static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \ + const size_t idx) \ +{ \ + assert(idx < funcprefix##_rb_size(rb)); \ + assert(idx < funcprefix##_rb_length(rb)); \ + RBType *const rmpos = funcprefix##_rb_idx_p(rb, idx); \ + rbfree(rmpos); \ + if (rmpos == rb->next - 1) { \ + rb->next--; \ + if (rb->first == rb->next) { \ + rb->first = NULL; \ + rb->next = rb->buf; \ + } \ + } else if (rmpos == rb->first) { \ + rb->first = _RINGBUF_NEXT(rb, rb->first); \ + if (rb->first == rb->next) { \ + rb->first = NULL; \ + rb->next = rb->buf; \ + } \ + } else if (rb->first < rb->next || rb->next == rb->buf) { \ + assert(rmpos > rb->first); \ + assert(rmpos <= _RINGBUF_PREV(rb, rb->next)); \ + memmove(rb->first + 1, rb->first, \ + (size_t) ((uintptr_t) rmpos - (uintptr_t) rb->first)); \ + rb->first = _RINGBUF_NEXT(rb, rb->first); \ + } else if (rmpos < rb->next) { \ + memmove(rmpos, rmpos + 1, \ + (size_t) ((uintptr_t) rb->next - (uintptr_t) rmpos)); \ + rb->next = _RINGBUF_PREV(rb, rb->next); \ + } else { \ + assert(rb->first < rb->buf_end); \ + memmove(rb->first + 1, rb->first, \ + (size_t) ((uintptr_t) rmpos - (uintptr_t) rb->first)); \ + rb->first = _RINGBUF_NEXT(rb, rb->first); \ + } \ } #endif // NVIM_LIB_RINGBUF_H diff --git a/src/nvim/log.c b/src/nvim/log.c index 773d497881..c31af6b287 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -10,7 +10,14 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" -#define USR_LOG_FILE "$HOME" _PATHSEPSTR ".nvimlog" +/// First location of the log file used by log_path_init() +#define USR_LOG_FILE "$NVIM_LOG_FILE" + +/// Fall back location of the log file used by log_path_init() +#define USR_LOG_FILE_2 "$HOME" _PATHSEPSTR ".nvimlog" + +/// Cached location of the log file set by log_path_init() +static char expanded_log_file_path[MAXPATHL + 1] = { 0 }; static uv_mutex_t mutex; @@ -18,6 +25,35 @@ static uv_mutex_t mutex; # include "log.c.generated.h" #endif +/// Initialize path to log file +/// +/// Tries to use #USR_LOG_FILE, then falls back #USR_LOG_FILE_2. Path to log +/// file is cached, so only the first call has effect, unless first call was not +/// successful. To make initialization not succeed either a bug in expand_env() +/// is needed or both `$NVIM_LOG_FILE` and `$HOME` environment variables +/// undefined. +/// +/// @return true if path was initialized, false otherwise. +static bool log_path_init(void) +{ + if (expanded_log_file_path[0]) { + return true; + } + expand_env((char_u *)USR_LOG_FILE, (char_u *)expanded_log_file_path, + sizeof(expanded_log_file_path) - 1); + // if the log file path expansion failed then fall back to stderr + if (strcmp(USR_LOG_FILE, expanded_log_file_path) == 0) { + memset(expanded_log_file_path, 0, sizeof(expanded_log_file_path)); + expand_env((char_u *)USR_LOG_FILE_2, (char_u *)expanded_log_file_path, + sizeof(expanded_log_file_path) - 1); + if (strcmp(USR_LOG_FILE_2, expanded_log_file_path) == 0) { + memset(expanded_log_file_path, 0, sizeof(expanded_log_file_path)); + return false; + } + } + return true; +} + void log_init(void) { uv_mutex_init(&mutex); @@ -73,30 +109,17 @@ FILE *open_log_file(void) return stderr; } - // expand USR_LOG_FILE and open the file - FILE *log_file; + // expand USR_LOG_FILE if needed and open the file + FILE *log_file = NULL; opening_log_file = true; - { - static char expanded_log_file_path[MAXPATHL + 1]; - - expand_env((char_u *)USR_LOG_FILE, (char_u *)expanded_log_file_path, - MAXPATHL); - // if the log file path expansion failed then fall back to stderr - if (strcmp(USR_LOG_FILE, expanded_log_file_path) == 0) { - goto open_log_file_error; - } - + if (log_path_init()) { log_file = fopen(expanded_log_file_path, "a"); - if (log_file == NULL) { - goto open_log_file_error; - } } opening_log_file = false; - return log_file; - -open_log_file_error: - opening_log_file = false; + if (log_file != NULL) { + return log_file; + } do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, "Couldn't open USR_LOG_FILE, logging to stderr! This may be " diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 5f69fa2f6a..503daa9648 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -73,11 +73,6 @@ /* Returns empty string if it is NULL. */ #define EMPTY_IF_NULL(x) ((x) ? (x) : (char_u *)"") -/* macro version of chartab(). - * Only works with values 0-255! - * Doesn't work for UTF-8 mode with chars >= 0x80. */ -#define CHARSIZE(c) (chartab[c] & CT_CELL_MASK) - /* * Adjust chars in a language according to 'langmap' option. * NOTE that there is no noticeable overhead if 'langmap' is not set. diff --git a/src/nvim/main.c b/src/nvim/main.c index 71a972e8f6..e052d0d315 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -120,6 +120,8 @@ typedef struct { # include "main.c.generated.h" #endif +Loop main_loop; + static char *argv0; // Error messages @@ -133,7 +135,7 @@ static const char *err_extra_cmd = void event_init(void) { - loop_init(&loop, NULL); + loop_init(&main_loop, NULL); // early msgpack-rpc initialization msgpack_rpc_init_method_table(); msgpack_rpc_helpers_init(); @@ -151,19 +153,20 @@ void event_init(void) void event_teardown(void) { - if (!loop.events) { + if (!main_loop.events) { return; } - queue_process_events(loop.events); + queue_process_events(main_loop.events); input_stop(); channel_teardown(); - process_teardown(&loop); + process_teardown(&main_loop); + timer_teardown(); server_teardown(); signal_teardown(); terminal_teardown(); - loop_close(&loop); + loop_close(&main_loop); } /// Performs early initialization. @@ -273,7 +276,6 @@ int main(int argc, char **argv) printf(_("%d files to edit\n"), GARGCOUNT); full_screen = true; - t_colors = 256; check_tty(¶ms); /* @@ -504,6 +506,9 @@ int main(int argc, char **argv) no_wait_return = FALSE; starting = 0; + // 'autochdir' has been postponed. + do_autochdir(); + /* start in insert mode */ if (p_im) need_start_insertmode = TRUE; @@ -665,8 +670,8 @@ static void init_locale(void) { char_u *p; - /* expand_env() doesn't work yet, because chartab[] is not initialized - * yet, call vim_getenv() directly */ + // expand_env() doesn't work yet, because g_chartab[] is not + // initialized yet, call vim_getenv() directly p = (char_u *)vim_getenv("VIMRUNTIME"); if (p != NULL && *p != NUL) { vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); @@ -1236,8 +1241,11 @@ static void set_window_layout(mparm_T *paramp) static void load_plugins(void) { if (p_lpl) { - source_runtime((char_u *)"plugin/**/*.vim", TRUE); + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL); // NOLINT TIME_MSG("loading plugins"); + + ex_packloadall(NULL); + TIME_MSG("loading packages"); } } @@ -1665,8 +1673,6 @@ static bool do_user_initialization(void) } /// Source startup scripts -/// -/// @param[in] static void source_startup_scripts(const mparm_T *const parmp) FUNC_ATTR_NONNULL_ALL { diff --git a/src/nvim/main.h b/src/nvim/main.h index 084e247b7e..86d25fe657 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -2,6 +2,9 @@ #define NVIM_MAIN_H #include "nvim/normal.h" +#include "nvim/event/loop.h" + +extern Loop main_loop; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "main.h.generated.h" diff --git a/src/nvim/map.c b/src/nvim/map.c index d4262ae9a8..398e74268f 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -34,88 +34,88 @@ #define INITIALIZER_DECLARE(T, U, ...) const U INITIALIZER(T, U) = __VA_ARGS__ #define DEFAULT_INITIALIZER {0} -#define MAP_IMPL(T, U, ...) \ - INITIALIZER_DECLARE(T, U, __VA_ARGS__); \ - __KHASH_IMPL(T##_##U##_map,, T, U, 1, T##_hash, T##_eq) \ - \ - Map(T, U) *map_##T##_##U##_new() \ - { \ - Map(T, U) *rv = xmalloc(sizeof(Map(T, U))); \ - rv->table = kh_init(T##_##U##_map); \ - return rv; \ - } \ - \ - void map_##T##_##U##_free(Map(T, U) *map) \ - { \ - kh_destroy(T##_##U##_map, map->table); \ - xfree(map); \ - } \ - \ - U map_##T##_##U##_get(Map(T, U) *map, T key) \ - { \ - khiter_t k; \ - \ +#define MAP_IMPL(T, U, ...) \ + INITIALIZER_DECLARE(T, U, __VA_ARGS__); \ + __KHASH_IMPL(T##_##U##_map,, T, U, 1, T##_hash, T##_eq) \ + \ + Map(T, U) *map_##T##_##U##_new() \ + { \ + Map(T, U) *rv = xmalloc(sizeof(Map(T, U))); \ + rv->table = kh_init(T##_##U##_map); \ + return rv; \ + } \ + \ + void map_##T##_##U##_free(Map(T, U) *map) \ + { \ + kh_destroy(T##_##U##_map, map->table); \ + xfree(map); \ + } \ + \ + U map_##T##_##U##_get(Map(T, U) *map, T key) \ + { \ + khiter_t k; \ + \ if ((k = kh_get(T##_##U##_map, map->table, key)) == kh_end(map->table)) { \ - return INITIALIZER(T, U); \ - } \ - \ - return kh_val(map->table, k); \ - } \ - \ - bool map_##T##_##U##_has(Map(T, U) *map, T key) \ - { \ - return kh_get(T##_##U##_map, map->table, key) != kh_end(map->table); \ - } \ - \ - U map_##T##_##U##_put(Map(T, U) *map, T key, U value) \ - { \ - int ret; \ - U rv = INITIALIZER(T, U); \ - khiter_t k = kh_put(T##_##U##_map, map->table, key, &ret); \ - \ - if (!ret) { \ - rv = kh_val(map->table, k); \ - } \ - \ - kh_val(map->table, k) = value; \ - return rv; \ - } \ - \ - U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \ - { \ - int ret; \ - khiter_t k; \ - if (put) { \ - k = kh_put(T##_##U##_map, map->table, key, &ret); \ - if (ret) { \ - kh_val(map->table, k) = INITIALIZER(T, U); \ - } \ - } else { \ - k = kh_get(T##_##U##_map, map->table, key); \ - if (k == kh_end(map->table)) { \ - return NULL; \ - } \ - } \ - \ - return &kh_val(map->table, k); \ - } \ - \ - U map_##T##_##U##_del(Map(T, U) *map, T key) \ - { \ - U rv = INITIALIZER(T, U); \ - khiter_t k; \ - \ + return INITIALIZER(T, U); \ + } \ + \ + return kh_val(map->table, k); \ + } \ + \ + bool map_##T##_##U##_has(Map(T, U) *map, T key) \ + { \ + return kh_get(T##_##U##_map, map->table, key) != kh_end(map->table); \ + } \ + \ + U map_##T##_##U##_put(Map(T, U) *map, T key, U value) \ + { \ + int ret; \ + U rv = INITIALIZER(T, U); \ + khiter_t k = kh_put(T##_##U##_map, map->table, key, &ret); \ + \ + if (!ret) { \ + rv = kh_val(map->table, k); \ + } \ + \ + kh_val(map->table, k) = value; \ + return rv; \ + } \ + \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \ + { \ + int ret; \ + khiter_t k; \ + if (put) { \ + k = kh_put(T##_##U##_map, map->table, key, &ret); \ + if (ret) { \ + kh_val(map->table, k) = INITIALIZER(T, U); \ + } \ + } else { \ + k = kh_get(T##_##U##_map, map->table, key); \ + if (k == kh_end(map->table)) { \ + return NULL; \ + } \ + } \ + \ + return &kh_val(map->table, k); \ + } \ + \ + U map_##T##_##U##_del(Map(T, U) *map, T key) \ + { \ + U rv = INITIALIZER(T, U); \ + khiter_t k; \ + \ if ((k = kh_get(T##_##U##_map, map->table, key)) != kh_end(map->table)) { \ - rv = kh_val(map->table, k); \ - kh_del(T##_##U##_map, map->table, k); \ - } \ - \ - return rv; \ - } \ - \ - void map_##T##_##U##_clear(Map(T, U) *map) \ - { \ - kh_clear(T##_##U##_map, map->table); \ + rv = kh_val(map->table, k); \ + kh_del(T##_##U##_map, map->table, k); \ + } \ + \ + return rv; \ + } \ + \ + void map_##T##_##U##_clear(Map(T, U) *map) \ + { \ + kh_clear(T##_##U##_map, map->table); \ } static inline khint_t String_hash(String s) @@ -129,7 +129,10 @@ static inline khint_t String_hash(String s) static inline bool String_eq(String a, String b) { - return strncmp(a.data, b.data, MIN(a.size, b.size)) == 0; + if (a.size != b.size) { + return false; + } + return memcmp(a.data, b.data, a.size) == 0; } diff --git a/src/nvim/map.h b/src/nvim/map.h index e90cc360ce..c7d9894bd1 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -8,20 +8,20 @@ #include "nvim/msgpack_rpc/defs.h" #include "nvim/bufhl_defs.h" -#define MAP_DECLS(T, U) \ - KHASH_DECLARE(T##_##U##_map, T, U) \ - \ - typedef struct { \ - khash_t(T##_##U##_map) *table; \ - } Map(T, U); \ - \ - Map(T, U) *map_##T##_##U##_new(void); \ - void map_##T##_##U##_free(Map(T, U) *map); \ - U map_##T##_##U##_get(Map(T, U) *map, T key); \ - bool map_##T##_##U##_has(Map(T, U) *map, T key); \ - U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \ - U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \ - U map_##T##_##U##_del(Map(T, U) *map, T key); \ +#define MAP_DECLS(T, U) \ + KHASH_DECLARE(T##_##U##_map, T, U) \ + \ + typedef struct { \ + khash_t(T##_##U##_map) *table; \ + } Map(T, U); \ + \ + Map(T, U) *map_##T##_##U##_new(void); \ + void map_##T##_##U##_free(Map(T, U) *map); \ + U map_##T##_##U##_get(Map(T, U) *map, T key); \ + bool map_##T##_##U##_has(Map(T, U) *map, T key); \ + U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \ + U map_##T##_##U##_del(Map(T, U) *map, T key); \ void map_##T##_##U##_clear(Map(T, U) *map); MAP_DECLS(int, int) diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 3495203c43..0ba9f8b076 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1304,7 +1304,7 @@ int utfc_ptr2char(const char_u *p, int *pcc) */ int utfc_ptr2char_len(const char_u *p, int *pcc, int maxlen) { -#define IS_COMPOSING(s1, s2, s3) \ +#define IS_COMPOSING(s1, s2, s3) \ (i == 0 ? UTF_COMPOSINGLIKE((s1), (s2)) : utf_iscomposing((s3))) assert(maxlen > 0); diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 6599db787f..9fb03c4ac7 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -913,7 +913,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags) #ifdef HAVE_FD_CLOEXEC int fdflags = fcntl(mfp->mf_fd, F_GETFD); if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { - fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); } #endif #ifdef HAVE_SELINUX diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 4e35dd481f..673205f08f 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -443,8 +443,9 @@ void ml_setname(buf_T *buf) #ifdef HAVE_FD_CLOEXEC { int fdflags = fcntl(mfp->mf_fd, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + } } #endif } @@ -1358,7 +1359,7 @@ recover_names ( if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) { char_u *swapname = (char_u *)modname((char *)fname_res, ".swp", TRUE); if (swapname != NULL) { - if (os_file_exists(swapname)) { + if (os_path_exists(swapname)) { files = xmalloc(sizeof(char_u *)); files[0] = swapname; swapname = NULL; @@ -3425,11 +3426,11 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, break; } - /* If the file was deleted this fname can be used. */ - if (!os_file_exists((char_u *) fname)) + // If the file was deleted this fname can be used. + if (!os_path_exists((char_u *)fname)) { break; - } else - { + } + } else { MSG_PUTS("\n"); if (msg_silent == 0) /* call wait_return() later */ diff --git a/src/nvim/message.c b/src/nvim/message.c index 521db85cf0..77e8f0e4f2 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -493,10 +493,11 @@ int emsg(char_u *s) * when the message should be ignored completely (used for the * interrupt message). */ - if (cause_errthrow(s, severe, &ignore) == TRUE) { - if (!ignore) - did_emsg = TRUE; - return TRUE; + if (cause_errthrow(s, severe, &ignore) == true) { + if (!ignore) { + did_emsg = true; + } + return true; } // set "v:errmsg", also when using ":silent! cmd" @@ -511,41 +512,43 @@ int emsg(char_u *s) p = get_emsg_source(); if (p != NULL) { STRCAT(p, "\n"); - redir_write(p, -1); + redir_write(p, STRLEN(p)); xfree(p); } p = get_emsg_lnum(); if (p != NULL) { STRCAT(p, "\n"); - redir_write(p, -1); + redir_write(p, STRLEN(p)); xfree(p); } - redir_write(s, -1); - return TRUE; + redir_write(s, STRLEN(s)); + return true; } - /* Reset msg_silent, an error causes messages to be switched back on. */ + // Reset msg_silent, an error causes messages to be switched back on. msg_silent = 0; cmd_silent = FALSE; - if (global_busy) /* break :global command */ - ++global_busy; + if (global_busy) { // break :global command + global_busy++; + } - if (p_eb) - beep_flush(); /* also includes flush_buffers() */ - else - flush_buffers(FALSE); /* flush internal buffers */ - did_emsg = TRUE; /* flag for DoOneCmd() */ + if (p_eb) { + beep_flush(); // also includes flush_buffers() + } else { + flush_buffers(false); // flush internal buffers + } + did_emsg = true; // flag for DoOneCmd() } - emsg_on_display = TRUE; /* remember there is an error message */ - ++msg_scroll; /* don't overwrite a previous message */ - attr = hl_attr(HLF_E); /* set highlight mode for error messages */ - if (msg_scrolled != 0) - need_wait_return = TRUE; /* needed in case emsg() is called after - * wait_return has reset need_wait_return - * and a redraw is expected because - * msg_scrolled is non-zero */ + emsg_on_display = true; // remember there is an error message + msg_scroll++; // don't overwrite a previous message + attr = hl_attr(HLF_E); // set highlight mode for error messages + if (msg_scrolled != 0) { + need_wait_return = true; // needed in case emsg() is called after + } // wait_return has reset need_wait_return + // and a redraw is expected because + // msg_scrolled is non-zero /* * Display name and line number for the source of the error. @@ -2389,6 +2392,19 @@ static void redir_write(char_u *str, int maxlen) char_u *s = str; static int cur_col = 0; + if (maxlen == 0) { + return; + } + + // Append output to capture(). + if (capture_ga) { + size_t len = 0; + while (str[len] && (maxlen < 0 ? 1 : (len < (size_t)maxlen))) { + len++; + } + ga_concat_len(capture_ga, (const char *)str, len); + } + /* Don't do anything for displaying prompts and the like. */ if (redir_off) return; @@ -2401,22 +2417,27 @@ static void redir_write(char_u *str, int maxlen) /* If the string doesn't start with CR or NL, go to msg_col */ if (*s != '\n' && *s != '\r') { while (cur_col < msg_col) { - if (redir_reg) - write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE); - else if (redir_vname) + if (redir_reg) { + write_reg_contents(redir_reg, (char_u *)" ", 1, true); + } else if (redir_vname) { var_redir_str((char_u *)" ", -1); - else if (redir_fd != NULL) + } else if (redir_fd != NULL) { fputs(" ", redir_fd); - if (verbose_fd != NULL) + } + if (verbose_fd != NULL) { fputs(" ", verbose_fd); - ++cur_col; + } + cur_col++; } } - if (redir_reg) - write_reg_contents(redir_reg, s, maxlen, TRUE); - if (redir_vname) + if (redir_reg) { + size_t len = maxlen == -1 ? STRLEN(s) : (size_t)maxlen; + write_reg_contents(redir_reg, s, len, true); + } + if (redir_vname) { var_redir_str(s, maxlen); + } /* Write and adjust the current column. */ while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) { diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 48791384a6..d72d8e8513 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -178,12 +178,11 @@ open_line ( if (curbuf->b_p_ai || do_si ) { - /* - * count white space on current line - */ - newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts, FALSE); - if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) - newindent = second_line_indent; /* for ^^D command in insert mode */ + // count white space on current line + newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts, false); + if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) { + newindent = second_line_indent; // for ^^D command in insert mode + } /* * Do smart indenting. @@ -612,7 +611,7 @@ open_line ( if (curbuf->b_p_ai || do_si ) - newindent = get_indent_str(leader, (int)curbuf->b_p_ts, FALSE); + newindent = get_indent_str(leader, (int)curbuf->b_p_ts, false); /* Add the indent offset */ if (newindent + off < 0) { diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 3a6d7c1434..5b249ee1c7 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -16,6 +16,7 @@ #include "nvim/event/socket.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/vim.h" +#include "nvim/main.h" #include "nvim/ascii.h" #include "nvim/memory.h" #include "nvim/os_unix.h" @@ -119,7 +120,7 @@ void channel_teardown(void) uint64_t channel_from_process(char **argv) { Channel *channel = register_channel(kChannelTypeProc); - channel->data.process.uvproc = libuv_process_init(&loop, channel); + channel->data.process.uvproc = libuv_process_init(&main_loop, channel); Process *proc = &channel->data.process.uvproc.process; proc->argv = argv; proc->in = &channel->data.process.in; @@ -127,7 +128,7 @@ uint64_t channel_from_process(char **argv) proc->err = &channel->data.process.err; proc->cb = process_exit; if (!process_spawn(proc)) { - loop_poll_events(&loop, 0); + loop_poll_events(&main_loop, 0); decref(channel); return 0; } @@ -179,7 +180,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) // Pending request, queue the notification for later sending. String method = cstr_as_string(name); WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); - kv_push(WBuffer *, channel->delayed_notifications, buffer); + kv_push(channel->delayed_notifications, buffer); } else { send_event(channel, name, args); } @@ -217,10 +218,10 @@ Object channel_send_call(uint64_t id, send_request(channel, request_id, method_name, args); // Push the frame - ChannelCallFrame frame = {request_id, false, false, NIL}; - kv_push(ChannelCallFrame *, channel->call_stack, &frame); + ChannelCallFrame frame = { request_id, false, false, NIL }; + kv_push(channel->call_stack, &frame); channel->pending_requests++; - LOOP_PROCESS_EVENTS_UNTIL(&loop, channel->events, -1, frame.returned); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned); (void)kv_pop(channel->call_stack); channel->pending_requests--; @@ -316,11 +317,11 @@ void channel_from_stdio(void) Channel *channel = register_channel(kChannelTypeStdio); incref(channel); // stdio channels are only closed on exit // read stream - rstream_init_fd(&loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE, - channel); + rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE, + channel); rstream_start(&channel->data.std.in, parse_msgpack); // write stream - wstream_init_fd(&loop, &channel->data.std.out, 1, 0, NULL); + wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0, NULL); } static void forward_stderr(Stream *stream, RBuffer *rbuf, size_t count, @@ -574,13 +575,12 @@ static void send_event(Channel *channel, static void broadcast_event(char *name, Array args) { - kvec_t(Channel *) subscribed; - kv_init(subscribed); + kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; map_foreach_value(channels, channel, { if (pmap_has(cstr_t)(channel->subscribed_events, name)) { - kv_push(Channel *, subscribed, channel); + kv_push(subscribed, channel); } }); @@ -600,7 +600,7 @@ static void broadcast_event(char *name, Array args) for (size_t i = 0; i < kv_size(subscribed); i++) { Channel *channel = kv_A(subscribed, i); if (channel->pending_requests) { - kv_push(WBuffer *, channel->delayed_notifications, buffer); + kv_push(channel->delayed_notifications, buffer); } else { channel_write(channel, buffer); } @@ -647,7 +647,7 @@ static void close_channel(Channel *channel) case kChannelTypeStdio: stream_close(&channel->data.std.in, NULL); stream_close(&channel->data.std.out, NULL); - queue_put(loop.fast_events, exit_event, 1, channel); + queue_put(main_loop.fast_events, exit_event, 1, channel); return; default: abort(); @@ -692,7 +692,7 @@ static void close_cb(Stream *stream, void *data) static Channel *register_channel(ChannelType type) { Channel *rv = xmalloc(sizeof(Channel)); - rv->events = queue_new_child(loop.events); + rv->events = queue_new_child(main_loop.events); rv->type = type; rv->refcount = 1; rv->closed = false; @@ -816,20 +816,55 @@ static void decref(Channel *channel) #define REQ "[request] " #define RES "[response] " #define NOT "[notification] " +#define ERR "[error] " + +// Cannot define array with negative offsets, so this one is needed to be added +// to MSGPACK_UNPACK_\* values. +#define MUR_OFF 2 + +static const char *const msgpack_error_messages[] = { + [MSGPACK_UNPACK_EXTRA_BYTES + MUR_OFF] = "extra bytes found", + [MSGPACK_UNPACK_CONTINUE + MUR_OFF] = "incomplete string", + [MSGPACK_UNPACK_PARSE_ERROR + MUR_OFF] = "parse error", + [MSGPACK_UNPACK_NOMEM_ERROR + MUR_OFF] = "not enough memory", +}; static void log_server_msg(uint64_t channel_id, msgpack_sbuffer *packed) { msgpack_unpacked unpacked; msgpack_unpacked_init(&unpacked); - msgpack_unpack_next(&unpacked, packed->data, packed->size, NULL); - uint64_t type = unpacked.data.via.array.ptr[0].via.u64; DLOGN("[msgpack-rpc] nvim -> client(%" PRIu64 ") ", channel_id); - log_lock(); - FILE *f = open_log_file(); - fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); - log_msg_close(f, unpacked.data); - msgpack_unpacked_destroy(&unpacked); + const msgpack_unpack_return result = + msgpack_unpack_next(&unpacked, packed->data, packed->size, NULL); + switch (result) { + case MSGPACK_UNPACK_SUCCESS: { + uint64_t type = unpacked.data.via.array.ptr[0].via.u64; + log_lock(); + FILE *f = open_log_file(); + fprintf(f, type ? (type == 1 ? RES : NOT) : REQ); + log_msg_close(f, unpacked.data); + msgpack_unpacked_destroy(&unpacked); + break; + } + case MSGPACK_UNPACK_EXTRA_BYTES: + case MSGPACK_UNPACK_CONTINUE: + case MSGPACK_UNPACK_PARSE_ERROR: + case MSGPACK_UNPACK_NOMEM_ERROR: { + log_lock(); + FILE *f = open_log_file(); + fprintf(f, ERR); + log_msg_close(f, (msgpack_object) { + .type = MSGPACK_OBJECT_STR, + .via.str = { + .ptr = (char *)msgpack_error_messages[result + MUR_OFF], + .size = (uint32_t)strlen( + msgpack_error_messages[result + MUR_OFF]), + }, + }); + break; + } + } } static void log_client_msg(uint64_t channel_id, diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 0049ae6b95..9195b10614 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -7,9 +7,11 @@ #include "nvim/api/private/helpers.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/defs.h" +#include "nvim/lib/kvec.h" #include "nvim/vim.h" #include "nvim/log.h" #include "nvim/memory.h" +#include "nvim/assert.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/helpers.c.generated.h" @@ -18,39 +20,39 @@ static msgpack_zone zone; static msgpack_sbuffer sbuffer; -#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ - FUNC_ATTR_NONNULL_ALL \ - { \ - if (obj->type != MSGPACK_OBJECT_EXT \ - || obj->via.ext.type != kObjectType##t) { \ - return false; \ - } \ - \ - msgpack_object data; \ - msgpack_unpack_return ret = msgpack_unpack(obj->via.ext.ptr, \ - obj->via.ext.size, \ - NULL, \ - &zone, \ - &data); \ - \ - if (ret != MSGPACK_UNPACK_SUCCESS) { \ - return false; \ - } \ - \ - *arg = data.via.u64; \ - return true; \ - } \ - \ - void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \ - FUNC_ATTR_NONNULL_ARG(2) \ - { \ - msgpack_packer pac; \ - msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ - msgpack_pack_uint64(&pac, o); \ - msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \ - msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ - msgpack_sbuffer_clear(&sbuffer); \ +#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ + bool msgpack_rpc_to_##lt(const msgpack_object *const obj, t *const arg) \ + FUNC_ATTR_NONNULL_ALL \ + { \ + if (obj->type != MSGPACK_OBJECT_EXT \ + || obj->via.ext.type != kObjectType##t) { \ + return false; \ + } \ + \ + msgpack_object data; \ + msgpack_unpack_return ret = msgpack_unpack(obj->via.ext.ptr, \ + obj->via.ext.size, \ + NULL, \ + &zone, \ + &data); \ + \ + if (ret != MSGPACK_UNPACK_SUCCESS) { \ + return false; \ + } \ + \ + *arg = data.via.u64; \ + return true; \ + } \ + \ + void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \ + FUNC_ATTR_NONNULL_ARG(2) \ + { \ + msgpack_packer pac; \ + msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ + msgpack_pack_uint64(&pac, o); \ + msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \ + msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ + msgpack_sbuffer_clear(&sbuffer); \ } void msgpack_rpc_helpers_init(void) @@ -63,34 +65,182 @@ HANDLE_TYPE_CONVERSION_IMPL(Buffer, buffer) HANDLE_TYPE_CONVERSION_IMPL(Window, window) HANDLE_TYPE_CONVERSION_IMPL(Tabpage, tabpage) -bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) +typedef struct { + const msgpack_object *mobj; + Object *aobj; + bool container; + size_t idx; +} MPToAPIObjectStackItem; + +/// Convert type used by msgpack parser to Neovim own API type +/// +/// @param[in] obj Msgpack value to convert. +/// @param[out] arg Location where result of conversion will be saved. +/// +/// @return true in case of success, false otherwise. +bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) FUNC_ATTR_NONNULL_ALL { - *arg = obj->via.boolean; - return obj->type == MSGPACK_OBJECT_BOOLEAN; -} - -bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) - FUNC_ATTR_NONNULL_ALL -{ - if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER - && obj->via.u64 <= INT64_MAX) { - *arg = (int64_t)obj->via.u64; - return true; + bool ret = true; + kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((MPToAPIObjectStackItem) { obj, arg, false, 0 })); + while (ret && kv_size(stack)) { + MPToAPIObjectStackItem cur = kv_last(stack); + if (!cur.container) { + *cur.aobj = NIL; + } + switch (cur.mobj->type) { + case MSGPACK_OBJECT_NIL: { + break; + } + case MSGPACK_OBJECT_BOOLEAN: { + *cur.aobj = BOOLEAN_OBJ(cur.mobj->via.boolean); + break; + } + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + STATIC_ASSERT(sizeof(Integer) == sizeof(cur.mobj->via.i64), + "Msgpack integer size does not match API integer"); + *cur.aobj = INTEGER_OBJ(cur.mobj->via.i64); + break; + } + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + STATIC_ASSERT(sizeof(Integer) == sizeof(cur.mobj->via.u64), + "Msgpack integer size does not match API integer"); + if (cur.mobj->via.u64 > API_INTEGER_MAX) { + ret = false; + } else { + *cur.aobj = INTEGER_OBJ((Integer)cur.mobj->via.u64); + } + break; + } + case MSGPACK_OBJECT_FLOAT: { + STATIC_ASSERT(sizeof(Float) == sizeof(cur.mobj->via.f64), + "Msgpack floating-point size does not match API integer"); + *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); + break; + } +#define STR_CASE(type, attr, obj, dest, conv) \ + case type: { \ + dest = conv(((String) { \ + .size = obj->via.attr.size, \ + .data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \ + ? NULL \ + : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \ + })); \ + break; \ + } + STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ) + STR_CASE(MSGPACK_OBJECT_BIN, bin, cur.mobj, *cur.aobj, STRING_OBJ) + case MSGPACK_OBJECT_ARRAY: { + const size_t size = cur.mobj->via.array.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + kv_push(stack, ((MPToAPIObjectStackItem) { + .mobj = &cur.mobj->via.array.ptr[idx], + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); + } + } else { + *cur.aobj = ARRAY_OBJ(((Array) { + .size = size, + .capacity = size, + .items = (size > 0 + ? xcalloc(size, sizeof(*cur.aobj->data.array.items)) + : NULL), + })); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case MSGPACK_OBJECT_MAP: { + const size_t size = cur.mobj->via.map.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key; + switch (key->type) { +#define ID(x) x + STR_CASE(MSGPACK_OBJECT_STR, str, key, + cur.aobj->data.dictionary.items[idx].key, ID) + STR_CASE(MSGPACK_OBJECT_BIN, bin, key, + cur.aobj->data.dictionary.items[idx].key, ID) +#undef ID + case MSGPACK_OBJECT_NIL: + case MSGPACK_OBJECT_BOOLEAN: + case MSGPACK_OBJECT_POSITIVE_INTEGER: + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + case MSGPACK_OBJECT_FLOAT: + case MSGPACK_OBJECT_EXT: + case MSGPACK_OBJECT_MAP: + case MSGPACK_OBJECT_ARRAY: { + ret = false; + break; + } + } + if (ret) { + kv_push(stack, ((MPToAPIObjectStackItem) { + .mobj = &cur.mobj->via.map.ptr[idx].val, + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); + } + } + } else { + *cur.aobj = DICTIONARY_OBJ(((Dictionary) { + .size = size, + .capacity = size, + .items = (size > 0 + ? xcalloc(size, sizeof(*cur.aobj->data.dictionary.items)) + : NULL), + })); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case MSGPACK_OBJECT_EXT: { + switch (cur.mobj->via.ext.type) { + case kObjectTypeBuffer: { + cur.aobj->type = kObjectTypeBuffer; + ret = msgpack_rpc_to_buffer(cur.mobj, &cur.aobj->data.buffer); + break; + } + case kObjectTypeWindow: { + cur.aobj->type = kObjectTypeWindow; + ret = msgpack_rpc_to_window(cur.mobj, &cur.aobj->data.window); + break; + } + case kObjectTypeTabpage: { + cur.aobj->type = kObjectTypeTabpage; + ret = msgpack_rpc_to_tabpage(cur.mobj, &cur.aobj->data.tabpage); + break; + } + } + break; + } +#undef STR_CASE + } + if (!cur.container) { + (void)kv_pop(stack); + } } - - *arg = obj->via.i64; - return obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER; + kv_destroy(stack); + return ret; } -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) - FUNC_ATTR_NONNULL_ALL -{ - *arg = obj->via.f64; - return obj->type == MSGPACK_OBJECT_FLOAT; -} - -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) +static bool msgpack_rpc_to_string(const msgpack_object *const obj, + String *const arg) FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { @@ -103,58 +253,7 @@ bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) return false; } -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) - FUNC_ATTR_NONNULL_ALL -{ - switch (obj->type) { - case MSGPACK_OBJECT_NIL: - arg->type = kObjectTypeNil; - return true; - - case MSGPACK_OBJECT_BOOLEAN: - arg->type = kObjectTypeBoolean; - return msgpack_rpc_to_boolean(obj, &arg->data.boolean); - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - arg->type = kObjectTypeInteger; - return msgpack_rpc_to_integer(obj, &arg->data.integer); - - case MSGPACK_OBJECT_FLOAT: - arg->type = kObjectTypeFloat; - return msgpack_rpc_to_float(obj, &arg->data.floating); - - case MSGPACK_OBJECT_BIN: - case MSGPACK_OBJECT_STR: - arg->type = kObjectTypeString; - return msgpack_rpc_to_string(obj, &arg->data.string); - - case MSGPACK_OBJECT_ARRAY: - arg->type = kObjectTypeArray; - return msgpack_rpc_to_array(obj, &arg->data.array); - - case MSGPACK_OBJECT_MAP: - arg->type = kObjectTypeDictionary; - return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); - - case MSGPACK_OBJECT_EXT: - switch (obj->via.ext.type) { - case kObjectTypeBuffer: - arg->type = kObjectTypeBuffer; - return msgpack_rpc_to_buffer(obj, &arg->data.buffer); - case kObjectTypeWindow: - arg->type = kObjectTypeWindow; - return msgpack_rpc_to_window(obj, &arg->data.window); - case kObjectTypeTabpage: - arg->type = kObjectTypeTabpage; - return msgpack_rpc_to_tabpage(obj, &arg->data.tabpage); - } - default: - return false; - } -} - -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) +bool msgpack_rpc_to_array(const msgpack_object *const obj, Array *const arg) FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_ARRAY) { @@ -173,7 +272,8 @@ bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) return true; } -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) +bool msgpack_rpc_to_dictionary(const msgpack_object *const obj, + Dictionary *const arg) FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_MAP) { @@ -228,50 +328,108 @@ void msgpack_rpc_from_string(String result, msgpack_packer *res) msgpack_pack_str_body(res, result.data, result.size); } -void msgpack_rpc_from_object(Object result, msgpack_packer *res) +typedef struct { + const Object *aobj; + bool container; + size_t idx; +} APIToMPObjectStackItem; + +/// Convert type used by Neovim API to msgpack +/// +/// @param[in] result Object to convert. +/// @param[out] res Structure that defines where conversion results are saved. +/// +/// @return true in case of success, false otherwise. +void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) FUNC_ATTR_NONNULL_ARG(2) { - switch (result.type) { - case kObjectTypeNil: - msgpack_pack_nil(res); - break; - - case kObjectTypeBoolean: - msgpack_rpc_from_boolean(result.data.boolean, res); - break; - - case kObjectTypeInteger: - msgpack_rpc_from_integer(result.data.integer, res); - break; - - case kObjectTypeFloat: - msgpack_rpc_from_float(result.data.floating, res); - break; - - case kObjectTypeString: - msgpack_rpc_from_string(result.data.string, res); - break; - - case kObjectTypeArray: - msgpack_rpc_from_array(result.data.array, res); - break; - - case kObjectTypeBuffer: - msgpack_rpc_from_buffer(result.data.buffer, res); - break; - - case kObjectTypeWindow: - msgpack_rpc_from_window(result.data.window, res); - break; - - case kObjectTypeTabpage: - msgpack_rpc_from_tabpage(result.data.tabpage, res); - break; - - case kObjectTypeDictionary: - msgpack_rpc_from_dictionary(result.data.dictionary, res); - break; + kvec_t(APIToMPObjectStackItem) stack = KV_INITIAL_VALUE; + kv_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); + while (kv_size(stack)) { + APIToMPObjectStackItem cur = kv_last(stack); + switch (cur.aobj->type) { + case kObjectTypeNil: { + msgpack_pack_nil(res); + break; + } + case kObjectTypeBoolean: { + msgpack_rpc_from_boolean(cur.aobj->data.boolean, res); + break; + } + case kObjectTypeInteger: { + msgpack_rpc_from_integer(cur.aobj->data.integer, res); + break; + } + case kObjectTypeFloat: { + msgpack_rpc_from_float(cur.aobj->data.floating, res); + break; + } + case kObjectTypeString: { + msgpack_rpc_from_string(cur.aobj->data.string, res); + break; + } + case kObjectTypeBuffer: { + msgpack_rpc_from_buffer(cur.aobj->data.buffer, res); + break; + } + case kObjectTypeWindow: { + msgpack_rpc_from_window(cur.aobj->data.window, res); + break; + } + case kObjectTypeTabpage: { + msgpack_rpc_from_tabpage(cur.aobj->data.tabpage, res); + break; + } + case kObjectTypeArray: { + const size_t size = cur.aobj->data.array.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + kv_push(stack, ((APIToMPObjectStackItem) { + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); + } + } else { + msgpack_pack_array(res, size); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case kObjectTypeDictionary: { + const size_t size = cur.aobj->data.dictionary.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, + res); + kv_push(stack, ((APIToMPObjectStackItem) { + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); + } + } else { + msgpack_pack_map(res, size); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + } + if (!cur.container) { + (void)kv_pop(stack); + } } + kv_destroy(stack); } void msgpack_rpc_from_array(Array result, msgpack_packer *res) diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 6cc56ba3dd..abbd3e8aff 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -12,6 +12,7 @@ #include "nvim/eval.h" #include "nvim/garray.h" #include "nvim/vim.h" +#include "nvim/main.h" #include "nvim/memory.h" #include "nvim/log.h" #include "nvim/fileio.h" @@ -108,7 +109,7 @@ int server_start(const char *endpoint) } SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher)); - socket_watcher_init(&loop, watcher, endpoint, NULL); + socket_watcher_init(&main_loop, watcher, endpoint, NULL); // Check if a watcher for the endpoint already exists for (int i = 0; i < watchers.ga_len; i++) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 382c4943ff..c95e5e1a15 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -974,7 +974,7 @@ static int normal_execute(VimState *state, int key) s->old_col = curwin->w_curswant; s->c = key; - LANGMAP_ADJUST(s->c, true); + LANGMAP_ADJUST(s->c, get_real_state() != SELECTMODE); // If a mapping was started in Visual or Select mode, remember the length // of the mapping. This is used below to not return to Insert mode for as @@ -1795,10 +1795,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (oap->line_count < 2) oap->line_count = 2; if (curwin->w_cursor.lnum + oap->line_count - 1 > - curbuf->b_ml.ml_line_count) + curbuf->b_ml.ml_line_count) { beep_flush(); - else { - do_join(oap->line_count, oap->op_type == OP_JOIN, true, true, true); + } else { + do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, + true, true, true); auto_format(false, true); } break; @@ -7666,7 +7667,7 @@ static void nv_join(cmdarg_T *cap) prep_redo(cap->oap->regname, cap->count0, NUL, cap->cmdchar, NUL, NUL, cap->nchar); - do_join(cap->count0, cap->nchar == NUL, true, true, true); + do_join((size_t)cap->count0, cap->nchar == NUL, true, true, true); } } @@ -7879,7 +7880,7 @@ static void nv_event(cmdarg_T *cap) // not safe to perform garbage collection because there could be unreferenced // lists or dicts being used. may_garbage_collect = false; - queue_process_events(loop.events); + queue_process_events(main_loop.events); cap->retval |= CA_COMMAND_BUSY; // don't call edit() now } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index adfd0424f0..25b3b85497 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1,6 +1,6 @@ /* * ops.c: implementation of various operators: op_shift, op_delete, op_tilde, - * op_change, op_yank, do_put, do_join + * op_change, op_yank, do_put, do_join */ #include <assert.h> @@ -257,8 +257,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) * shift the current line one shiftwidth left (if left != 0) or right * leaves cursor on first blank in the line */ -void -shift_line ( +void shift_line( int left, int round, int amount, @@ -369,18 +368,18 @@ static void shift_block(oparg_T *oap, int amount) memset(newp + bd.textcol + i, ' ', (size_t)j); /* the end */ memmove(newp + bd.textcol + i + j, bd.textstart, (size_t)len); - } else { /* left */ - colnr_T destination_col; /* column to which text in block will - be shifted */ - char_u *verbatim_copy_end; /* end of the part of the line which is - copied verbatim */ - colnr_T verbatim_copy_width; /* the (displayed) width of this part - of line */ - unsigned fill; /* nr of spaces that replace a TAB */ - unsigned new_line_len; /* the length of the line after the - block shift */ - size_t block_space_width; - size_t shift_amount; + } else { // left + colnr_T destination_col; // column to which text in block will + // be shifted + char_u *verbatim_copy_end; // end of the part of the line which is + // copied verbatim + colnr_T verbatim_copy_width; // the (displayed) width of this part + // of line + size_t fill; // nr of spaces that replace a TAB + size_t new_line_len; // the length of the line after the + // block shift + colnr_T block_space_width; + colnr_T shift_amount; char_u *non_white = bd.textstart; colnr_T non_white_col; @@ -409,11 +408,10 @@ static void shift_block(oparg_T *oap, int amount) block_space_width = non_white_col - oap->start_vcol; /* We will shift by "total" or "block_space_width", whichever is less. */ - shift_amount = (block_space_width < (size_t)total - ? block_space_width : (size_t)total); + shift_amount = (block_space_width < total ? block_space_width : total); - /* The column to which we will shift the text. */ - destination_col = (colnr_T)(non_white_col - shift_amount); + // The column to which we will shift the text. + destination_col = non_white_col - shift_amount; /* Now let's find out how much of the beginning of the line we can * reuse without modification. */ @@ -439,20 +437,21 @@ static void shift_block(oparg_T *oap, int amount) /* If "destination_col" is different from the width of the initial * part of the line that will be copied, it means we encountered a tab * character, which we will have to partly replace with spaces. */ - fill = destination_col - verbatim_copy_width; - - /* The replacement line will consist of: - * - the beginning of the original line up to "verbatim_copy_end", - * - "fill" number of spaces, - * - the rest of the line, pointed to by non_white. */ - new_line_len = (unsigned)(verbatim_copy_end - oldp) - + fill - + (unsigned)STRLEN(non_white) + 1; - - newp = (char_u *) xmalloc((size_t)(new_line_len)); - memmove(newp, oldp, (size_t)(verbatim_copy_end - oldp)); - memset(newp + (verbatim_copy_end - oldp), ' ', (size_t)fill); - STRMOVE(newp + (verbatim_copy_end - oldp) + fill, non_white); + assert(destination_col - verbatim_copy_width >= 0); + fill = (size_t)(destination_col - verbatim_copy_width); + + assert(verbatim_copy_end - oldp >= 0); + size_t verbatim_diff = (size_t)(verbatim_copy_end - oldp); + // The replacement line will consist of: + // - the beginning of the original line up to "verbatim_copy_end", + // - "fill" number of spaces, + // - the rest of the line, pointed to by non_white. + new_line_len = verbatim_diff + fill + STRLEN(non_white) + 1; + + newp = (char_u *) xmalloc(new_line_len); + memmove(newp, oldp, verbatim_diff); + memset(newp + verbatim_diff, ' ', fill); + STRMOVE(newp + verbatim_diff + fill, non_white); } /* replace the line */ ml_replace(curwin->w_cursor.lnum, newp, FALSE); @@ -469,21 +468,20 @@ static void shift_block(oparg_T *oap, int amount) static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def *bdp) { int p_ts; - int count = 0; /* extra spaces to replace a cut TAB */ - int spaces = 0; /* non-zero if cutting a TAB */ - colnr_T offset; /* pointer along new line */ - unsigned s_len; /* STRLEN(s) */ - char_u *newp, *oldp; /* new, old lines */ - linenr_T lnum; /* loop var */ + int count = 0; // extra spaces to replace a cut TAB + int spaces = 0; // non-zero if cutting a TAB + colnr_T offset; // pointer along new line + size_t s_len = STRLEN(s); + char_u *newp, *oldp; // new, old lines + linenr_T lnum; // loop var int oldstate = State; - - State = INSERT; /* don't want REPLACE for State */ - s_len = (unsigned)STRLEN(s); + State = INSERT; // don't want REPLACE for State for (lnum = oap->start.lnum + 1; lnum <= oap->end.lnum; lnum++) { - block_prep(oap, bdp, lnum, TRUE); - if (bdp->is_short && b_insert) - continue; /* OP_INSERT, line ends before block start */ + block_prep(oap, bdp, lnum, true); + if (bdp->is_short && b_insert) { + continue; // OP_INSERT, line ends before block start + } oldp = ml_get(lnum); @@ -523,25 +521,26 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def count -= off; } - newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + s_len + count + 1)); + assert(count >= 0); + newp = (char_u *)xmalloc(STRLEN(oldp) + s_len + (size_t)count + 1); - /* copy up to shifted part */ - memmove(newp, oldp, (size_t)(offset)); + // copy up to shifted part + memmove(newp, oldp, (size_t)offset); oldp += offset; - /* insert pre-padding */ + // insert pre-padding memset(newp + offset, ' ', (size_t)spaces); - /* copy the new text */ - memmove(newp + offset + spaces, s, (size_t)s_len); - offset += s_len; + // copy the new text + memmove(newp + offset + spaces, s, s_len); + offset += (int)s_len; if (spaces && !bdp->is_short) { - /* insert post-padding */ + // insert post-padding memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces)); - /* We're splitting a TAB, don't copy it. */ + // We're splitting a TAB, don't copy it. oldp++; - /* We allowed for that TAB, remember this now */ + // We allowed for that TAB, remember this now count++; } @@ -804,7 +803,7 @@ yankreg_T *copy_register(int name) copy->y_array = NULL; } else { copy->y_array = xcalloc(copy->y_size, sizeof(char_u *)); - for (linenr_T i = 0; i < copy->y_size; i++) { + for (size_t i = 0; i < copy->y_size; i++) { copy->y_array[i] = vim_strsave(reg->y_array[i]); } } @@ -888,7 +887,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) /* * Stuff string "p" into yank register "regname" as a single line (append if - * uppercase). "p" must have been alloced. + * uppercase). "p" must have been alloced. * * return FAIL for failure, OK otherwise */ @@ -940,10 +939,8 @@ do_execreg ( int silent /* set "silent" flag in typeahead buffer */ ) { - long i; - char_u *p; + char_u *p; int retval = OK; - int remap; if (regname == '@') { /* repeat previous one */ if (execreg_lastc == NUL) { @@ -1001,21 +998,20 @@ do_execreg ( if (reg->y_array == NULL) return FAIL; - /* Disallow remaping for ":@r". */ - remap = colon ? REMAP_NONE : REMAP_YES; + // Disallow remaping for ":@r". + int remap = colon ? REMAP_NONE : REMAP_YES; /* * Insert lines into typeahead buffer, from last one to first one. */ put_reedit_in_typebuf(silent); - for (i = reg->y_size - 1; i >= 0; i--) { - char_u *escaped; - + char_u *escaped; + for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included // insert NL between lines and after last line if type is kMTLineWise - if (reg->y_type == kMTLineWise || i < reg->y_size - 1 - || addcr) { - if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL) + if (reg->y_type == kMTLineWise || i < reg->y_size - 1 || addcr) { + if (ins_typebuf((char_u *)"\n", remap, 0, true, silent) == FAIL) { return FAIL; + } } escaped = vim_strsave_escape_csi(reg->y_array[i]); retval = ins_typebuf(escaped, remap, 0, TRUE, silent); @@ -1045,7 +1041,7 @@ static void put_reedit_in_typebuf(int silent) buf[1] = 'R'; buf[2] = NUL; } else { - buf[0] = restart_edit == 'I' ? 'i' : restart_edit; + buf[0] = (char_u)(restart_edit == 'I' ? 'i' : restart_edit); buf[1] = NUL; } if (ins_typebuf(buf, REMAP_NONE, 0, TRUE, silent) == OK) @@ -1059,8 +1055,7 @@ static void put_reedit_in_typebuf(int silent) * When "esc" is TRUE it is to be taken literally: Escape CSI characters and * no remapping. */ -static int -put_in_typebuf ( +static int put_in_typebuf( char_u *s, int esc, int colon, /* add ':' before the line */ @@ -1098,13 +1093,11 @@ put_in_typebuf ( * * return FAIL for failure, OK otherwise */ -int -insert_reg ( +int insert_reg( int regname, int literally /* insert literally, not as if typed */ ) { - long i; int retval = OK; char_u *arg; int allocated; @@ -1132,10 +1125,10 @@ insert_reg ( xfree(arg); } else { /* name or number register */ yankreg_T *reg = get_yank_register(regname, YREG_PASTE); - if (reg->y_array == NULL) + if (reg->y_array == NULL) { retval = FAIL; - else { - for (i = 0; i < reg->y_size; i++) { + } else { + for (size_t i = 0; i < reg->y_size; i++) { stuffescaped(reg->y_array[i], literally); // Insert a newline between lines and after last line if // y_type is kMTLineWise. @@ -1185,8 +1178,7 @@ static void stuffescaped(char_u *arg, int literally) * If "regname" is a special register, return TRUE and store a pointer to its * value in "argp". */ -int -get_spec_reg ( +int get_spec_reg( int regname, char_u **argp, int *allocated, /* return: TRUE when value was allocated */ @@ -1273,13 +1265,11 @@ get_spec_reg ( /// @returns FAIL for failure, OK otherwise bool cmdline_paste_reg(int regname, bool literally, bool remcr) { - long i; - yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array == NULL) return FAIL; - for (i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { cmdline_paste_str(reg->y_array[i], literally); // Insert ^M between lines and after last line if type is kMTLineWise. @@ -1311,12 +1301,14 @@ int op_delete(oparg_T *oap) struct block_def bd; linenr_T old_lcount = curbuf->b_ml.ml_line_count; - if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to do */ + if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to do return OK; + } - /* Nothing to delete, return here. Do prepare undo, for op_change(). */ - if (oap->empty) + // Nothing to delete, return here. Do prepare undo, for op_change(). + if (oap->empty) { return u_save_cursor(); + } if (!MODIFIABLE(curbuf)) { EMSG(_(e_modifiable)); @@ -1431,23 +1423,21 @@ int op_delete(oparg_T *oap) curwin->w_cursor.coladd = 0; } - /* n == number of chars deleted - * If we delete a TAB, it may be replaced by several characters. - * Thus the number of characters may increase! - */ + // n == number of chars deleted + // If we delete a TAB, it may be replaced by several characters. + // Thus the number of characters may increase! n = bd.textlen - bd.startspaces - bd.endspaces; oldp = ml_get(lnum); - newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + 1 - n)); - /* copy up to deleted part */ + newp = (char_u *)xmalloc(STRLEN(oldp) - (size_t)n + 1); + // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); - /* insert spaces */ - memset(newp + bd.textcol, ' ', - (size_t)(bd.startspaces + bd.endspaces)); - /* copy the part after the deleted part */ + // insert spaces + memset(newp + bd.textcol, ' ', (size_t)(bd.startspaces + bd.endspaces)); + // copy the part after the deleted part oldp += bd.textcol + bd.textlen; STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp); - /* replace the line */ - ml_replace(lnum, newp, FALSE); + // replace the line + ml_replace(lnum, newp, false); } check_cursor_col(); @@ -1552,7 +1542,7 @@ int op_delete(oparg_T *oap) curwin->w_cursor.coladd = 0; } - (void)del_bytes((long)n, !virtual_op, + (void)del_bytes((colnr_T)n, !virtual_op, oap->op_type == OP_DELETE && !oap->is_VIsual); } else { // delete characters between lines @@ -1572,7 +1562,7 @@ int op_delete(oparg_T *oap) // delete from start of line until op_end n = (oap->end.col + 1 - !oap->inclusive); curwin->w_cursor.col = 0; - (void)del_bytes((long)n, !virtual_op, + (void)del_bytes((colnr_T)n, !virtual_op, oap->op_type == OP_DELETE && !oap->is_VIsual); curwin->w_cursor = curpos; // restore curwin->w_cursor (void)do_join(2, false, false, false, false); @@ -1611,7 +1601,8 @@ static void mb_adjust_opend(oparg_T *oap) */ static inline void pchar(pos_T lp, int c) { - *(ml_get_buf(curbuf, lp.lnum, TRUE) + lp.col) = c;; + assert(c <= UCHAR_MAX); + *(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char_u)c; } /* @@ -1622,7 +1613,7 @@ int op_replace(oparg_T *oap, int c) int n, numc; int num_chars; char_u *newp, *oldp; - size_t oldlen; + colnr_T oldlen; struct block_def bd; char_u *after_p = NULL; int had_ctrl_v_cr = (c == -1 || c == -2); @@ -1690,42 +1681,56 @@ int op_replace(oparg_T *oap, int c) /* Compute bytes needed, move character count to num_chars. */ num_chars = numc; numc *= (*mb_char2len)(c); - /* oldlen includes textlen, so don't double count */ - n += numc - bd.textlen; oldp = get_cursor_line_ptr(); - oldlen = STRLEN(oldp); - newp = (char_u *) xmalloc((size_t)(oldlen + 1 + n)); - memset(newp, NUL, (size_t)(oldlen + 1 + n)); - /* copy up to deleted part */ + oldlen = (int)STRLEN(oldp); + + size_t newp_size = (size_t)(bd.textcol + bd.startspaces); + if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { + newp_size += (size_t)numc; + if (!bd.is_short) { + newp_size += (size_t)(bd.endspaces + oldlen + - bd.textcol - bd.textlen); + } + } + newp = xmallocz(newp_size); + // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); oldp += bd.textcol + bd.textlen; - /* insert pre-spaces */ + // insert pre-spaces memset(newp + bd.textcol, ' ', (size_t)bd.startspaces); - /* insert replacement chars CHECK FOR ALLOCATED SPACE */ - /* -1/-2 is used for entering CR literally. */ + // insert replacement chars CHECK FOR ALLOCATED SPACE + // -1/-2 is used for entering CR literally. + size_t after_p_len = 0; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { - if (has_mbyte) { - n = (int)STRLEN(newp); - while (--num_chars >= 0) - n += (*mb_char2bytes)(c, newp + n); - } else - memset(newp + STRLEN(newp), c, (size_t)numc); - if (!bd.is_short) { - /* insert post-spaces */ - memset(newp + STRLEN(newp), ' ', (size_t)bd.endspaces); - /* copy the part after the changed part */ - STRMOVE(newp + STRLEN(newp), oldp); + // strlen(newp) at this point + int newp_len = bd.textcol + bd.startspaces; + if (has_mbyte) { + while (--num_chars >= 0) { + newp_len += (*mb_char2bytes)(c, newp + newp_len); + } + } else { + memset(newp + newp_len, c, (size_t)numc); + newp_len += numc; + } + if (!bd.is_short) { + // insert post-spaces + memset(newp + newp_len, ' ', (size_t)bd.endspaces); + newp_len += bd.endspaces; + // copy the part after the changed part + memmove(newp + newp_len, oldp, + (size_t)(oldlen - bd.textcol - bd.textlen + 1)); } } else { - /* Replacing with \r or \n means splitting the line. */ - after_p = (char_u *) xmalloc((size_t)(oldlen + 1 + n - STRLEN(newp))); - STRMOVE(after_p, oldp); + // Replacing with \r or \n means splitting the line. + after_p_len = (size_t)(oldlen - bd.textcol - bd.textlen + 1); + after_p = (char_u *)xmalloc(after_p_len); + memmove(after_p, oldp, after_p_len); } /* replace the line */ ml_replace(curwin->w_cursor.lnum, newp, FALSE); if (after_p != NULL) { - ml_append(curwin->w_cursor.lnum++, after_p, 0, FALSE); + ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false); appended_lines_mark(curwin->w_cursor.lnum, 1L); oap->end.lnum++; xfree(after_p); @@ -1992,7 +1997,7 @@ void op_insert(oparg_T *oap, long count1) // already disabled, but still need it when calling // coladvance_force(). if (curwin->w_cursor.coladd > 0) { - int old_ve_flags = ve_flags; + unsigned old_ve_flags = ve_flags; ve_flags = VE_ALL; if (u_save_cursor() == FAIL) @@ -2066,8 +2071,8 @@ void op_insert(oparg_T *oap, long count1) if (oap->op_type == OP_INSERT && oap->start.col + oap->start.coladd != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { - size_t t = getviscol2(curbuf->b_op_start_orig.col, - curbuf->b_op_start_orig.coladd); + int t = getviscol2(curbuf->b_op_start_orig.col, + curbuf->b_op_start_orig.coladd); oap->start.col = curbuf->b_op_start_orig.col; pre_textlen -= t - oap->start_vcol; oap->start_vcol = t; @@ -2075,8 +2080,8 @@ void op_insert(oparg_T *oap, long count1) && oap->end.col + oap->end.coladd >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { - size_t t = getviscol2(curbuf->b_op_start_orig.col, - curbuf->b_op_start_orig.coladd); + int t = getviscol2(curbuf->b_op_start_orig.col, + curbuf->b_op_start_orig.coladd); oap->start.col = curbuf->b_op_start_orig.col; /* reset pre_textlen to the value of OP_INSERT */ pre_textlen += bd.textlen; @@ -2109,14 +2114,13 @@ void op_insert(oparg_T *oap, long count1) firstline = ml_get(oap->start.lnum) + bd.textcol; if (oap->op_type == OP_APPEND) firstline += bd.textlen; - if (pre_textlen >= 0 - && (ins_len = (long)STRLEN(firstline) - pre_textlen) > 0) { - ins_text = vim_strnsave(firstline, (int)ins_len); - /* block handled here */ - if (u_save(oap->start.lnum, - (linenr_T)(oap->end.lnum + 1)) == OK) - block_insert(oap, ins_text, (oap->op_type == OP_INSERT), - &bd); + ins_len = (long)STRLEN(firstline) - pre_textlen; + if (pre_textlen >= 0 && ins_len > 0) { + ins_text = vim_strnsave(firstline, (size_t)ins_len); + // block handled here + if (u_save(oap->start.lnum, (linenr_T)(oap->end.lnum + 1)) == OK) { + block_insert(oap, ins_text, (oap->op_type == OP_INSERT), &bd); + } curwin->w_cursor.col = oap->start.col; check_cursor(); @@ -2139,8 +2143,10 @@ int op_change(oparg_T *oap) long ins_len; long pre_textlen = 0; long pre_indent = 0; - char_u *firstline; - char_u *ins_text, *newp, *oldp; + char_u *newp; + char_u *firstline; + char_u *ins_text; + char_u *oldp; struct block_def bd; l = oap->start.col; @@ -2198,14 +2204,14 @@ int op_change(oparg_T *oap) long new_indent = (long)(skipwhite(firstline) - firstline); pre_textlen += new_indent - pre_indent; - bd.textcol += new_indent - pre_indent; + bd.textcol += (colnr_T)(new_indent - pre_indent); } ins_len = (long)STRLEN(firstline) - pre_textlen; if (ins_len > 0) { /* Subsequent calls to ml_get() flush the firstline data - take a * copy of the inserted text. */ - ins_text = (char_u *) xmalloc((size_t)(ins_len + 1)); + ins_text = (char_u *)xmalloc((size_t)(ins_len + 1)); STRLCPY(ins_text, firstline + bd.textcol, ins_len + 1); for (linenr = oap->start.lnum + 1; linenr <= oap->end.lnum; linenr++) { @@ -2218,11 +2224,13 @@ int op_change(oparg_T *oap) if (bd.is_short) { vpos.lnum = linenr; (void)getvpos(&vpos, oap->start_vcol); - } else + } else { vpos.coladd = 0; + } oldp = ml_get(linenr); - newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + vpos.coladd + ins_len + 1)); - /* copy up to block start */ + newp = xmalloc(STRLEN(oldp) + (size_t)vpos.coladd + + (size_t)ins_len + 1); + // copy up to block start memmove(newp, oldp, (size_t)bd.textcol); offset = bd.textcol; memset(newp + offset, ' ', (size_t)vpos.coladd); @@ -2272,9 +2280,7 @@ void free_register(yankreg_T *reg) { set_yreg_additional_data(reg, NULL); if (reg->y_array != NULL) { - long i; - - for (i = reg->y_size - 1; i >= 0; i--) { + for (size_t i = reg->y_size; i-- > 0;) { // from y_size - 1 to 0 included xfree(reg->y_array[i]); } xfree(reg->y_array); @@ -2311,30 +2317,27 @@ bool op_yank(oparg_T *oap, bool message) static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) { - long y_idx; /* index in y_array[] */ - yankreg_T *curr; /* copy of current register */ - yankreg_T newreg; /* new yank register when appending */ - char_u **new_ptr; - linenr_T lnum; /* current line number */ - long j; + yankreg_T newreg; // new yank register when appending + char_u **new_ptr; + linenr_T lnum; // current line number + size_t j; MotionType yank_type = oap->motion_type; - long yanklines = oap->line_count; + size_t yanklines = (size_t)oap->line_count; linenr_T yankendlnum = oap->end.lnum; - char_u *p; - char_u *pnew; + char_u *p; + char_u *pnew; struct block_def bd; - curr = reg; - /* append to existing contents */ - if (append && reg->y_array != NULL) + yankreg_T *curr = reg; // copy of current register + // append to existing contents + if (append && reg->y_array != NULL) { reg = &newreg; - else - free_register(reg); /* free previously yanked lines */ + } else { + free_register(reg); // free previously yanked lines + } - /* - * If the cursor was in column 1 before and after the movement, and the - * operator is not inclusive, the yank is always linewise. - */ + // If the cursor was in column 1 before and after the movement, and the + // operator is not inclusive, the yank is always linewise. if (oap->motion_type == kMTCharWise && oap->start.col == 0 && !oap->inclusive @@ -2353,7 +2356,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) reg->additional_data = NULL; reg->timestamp = os_time(); - y_idx = 0; + size_t y_idx = 0; // index in y_array[] lnum = oap->start.lnum; if (yank_type == kMTBlockWise) { @@ -2481,7 +2484,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) yanklines = 0; } // Some versions of Vi use ">=" here, some don't... - if (yanklines > p_report) { + if (yanklines > (size_t)p_report) { // redisplay now, so message is not deleted update_topline_redraw(); if (yanklines == 1) { @@ -2512,10 +2515,10 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) return; } -static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx) +static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx) { - char_u *pnew = xmallocz(bd->startspaces + bd->endspaces + bd->textlen); - + char_u *pnew = xmallocz((size_t)(bd->startspaces + bd->endspaces + + bd->textlen)); reg->y_array[y_idx] = pnew; memset(pnew, ' ', (size_t)bd->startspaces); pnew += bd->startspaces; @@ -2548,7 +2551,7 @@ static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) // the yanked text list_T *list = list_alloc(); - for (linenr_T i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { list_append_string(list, reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; @@ -2565,7 +2568,7 @@ static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) dict_add_nr_str(dict, "regname", 0, (char_u *)buf); // kind of operation (yank/delete/change) - buf[0] = get_op_char(oap->op_type); + buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; dict_add_nr_str(dict, "operator", 0, (char_u *)buf); @@ -2582,23 +2585,24 @@ static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) /* * Put contents of register "regname" into the text. * Caller must check "regname" to be valid! - * "flags": PUT_FIXINDENT make indent look nice - * PUT_CURSEND leave cursor after end of new text - * PUT_LINE force linewise put (":put") + * "flags": PUT_FIXINDENT make indent look nice + * PUT_CURSEND leave cursor after end of new text + * PUT_LINE force linewise put (":put") dir: BACKWARD for 'P', FORWARD for 'p' */ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) { - char_u *ptr; - char_u *newp, *oldp; + char_u *ptr; + char_u *newp; + char_u *oldp; int yanklen; - int totlen = 0; /* init for gcc */ + size_t totlen = 0; // init for gcc linenr_T lnum; colnr_T col; - long i; // index in y_array[] + size_t i; // index in y_array[] MotionType y_type; - long y_size; - int oldlen; - long y_width = 0; + size_t y_size; + size_t oldlen; + int y_width = 0; colnr_T vcol; int delcount; int incr = 0; @@ -2705,7 +2709,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (curbuf->terminal) { for (int i = 0; i < count; i++) { // feed the lines to the terminal - for (int j = 0; j < y_size; j++) { + for (size_t j = 0; j < y_size; j++) { if (j) { // terminate the previous line terminal_send(curbuf->terminal, "\n", 1); @@ -2736,7 +2740,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (dir == FORWARD && *p != NUL) { mb_ptr_adv(p); } - ptr = vim_strnsave(oldp, p - oldp); + ptr = vim_strnsave(oldp, (size_t)(p - oldp)); ml_replace(curwin->w_cursor.lnum, ptr, false); nr_lines++; dir = FORWARD; @@ -2761,11 +2765,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } if (y_type == kMTBlockWise) { - lnum = curwin->w_cursor.lnum + y_size + 1; - if (lnum > curbuf->b_ml.ml_line_count) + lnum = curwin->w_cursor.lnum + (linenr_T)y_size + 1; + if (lnum > curbuf->b_ml.ml_line_count) { lnum = curbuf->b_ml.ml_line_count + 1; - if (u_save(curwin->w_cursor.lnum - 1, lnum) == FAIL) + } + if (u_save(curwin->w_cursor.lnum - 1, lnum) == FAIL) { goto end; + } } else if (y_type == kMTLineWise) { lnum = curwin->w_cursor.lnum; /* Correct line number for closed fold. Don't move the cursor yet, @@ -2811,7 +2817,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) * Block mode */ if (y_type == kMTBlockWise) { - char c = gchar_cursor(); + int c = gchar_cursor(); colnr_T endcol2 = 0; if (dir == FORWARD && c != NUL) { @@ -2864,7 +2870,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } /* get the old line and advance to the position to insert at */ oldp = get_cursor_line_ptr(); - oldlen = (int)STRLEN(oldp); + oldlen = STRLEN(oldp); for (ptr = oldp; vcol < col && *ptr; ) { /* Count a tab for what it's worth (if list mode not on) */ incr = lbr_chartabsize_adv(oldp, &ptr, (colnr_T)vcol); @@ -2901,10 +2907,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (spaces < 0) spaces = 0; - /* insert the new text */ - totlen = count * (yanklen + spaces) + bd.startspaces + bd.endspaces; - newp = (char_u *) xmalloc((size_t)(totlen + oldlen + 1)); - /* copy part up to cursor to new line */ + // insert the new text + totlen = (size_t)(count * (yanklen + spaces) + + bd.startspaces + bd.endspaces); + newp = (char_u *) xmalloc(totlen + oldlen + 1); + // copy part up to cursor to new line ptr = newp; memmove(ptr, oldp, (size_t)bd.textcol); ptr += bd.textcol; @@ -2925,10 +2932,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* may insert some spaces after the new text */ memset(ptr, ' ', (size_t)bd.endspaces); ptr += bd.endspaces; - /* move the text after the cursor to the end of the line. */ + // move the text after the cursor to the end of the line. memmove(ptr, oldp + bd.textcol + delcount, - (size_t)(oldlen - bd.textcol - delcount + 1)); - ml_replace(curwin->w_cursor.lnum, newp, FALSE); + (size_t)((int)oldlen - bd.textcol - delcount + 1)); + ml_replace(curwin->w_cursor.lnum, newp, false); ++curwin->w_cursor.lnum; if (i == 0) @@ -2943,7 +2950,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* adjust '] mark */ curbuf->b_op_end.lnum = curwin->w_cursor.lnum - 1; - curbuf->b_op_end.col = bd.textcol + totlen - 1; + curbuf->b_op_end.col = bd.textcol + (colnr_T)totlen - 1; curbuf->b_op_end.coladd = 0; if (flags & PUT_CURSEND) { colnr_T len; @@ -2994,13 +3001,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) */ if (y_type == kMTCharWise && y_size == 1) { do { - totlen = count * yanklen; + totlen = (size_t)(count * yanklen); if (totlen > 0) { oldp = ml_get(lnum); newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + totlen + 1)); memmove(newp, oldp, (size_t)col); ptr = newp + col; - for (i = 0; i < count; i++) { + for (i = 0; i < (size_t)count; i++) { memmove(ptr, y_array[0], (size_t)yanklen); ptr += yanklen; } @@ -3037,7 +3044,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // Then append y_array[0] to first line. lnum = new_cursor.lnum; ptr = ml_get(lnum) + col; - totlen = (int)STRLEN(y_array[y_size - 1]); + totlen = STRLEN(y_array[y_size - 1]); newp = (char_u *) xmalloc((size_t)(STRLEN(ptr) + totlen + 1)); STRCPY(newp, y_array[y_size - 1]); STRCAT(newp, ptr); @@ -3217,22 +3224,19 @@ int get_register_name(int num) */ void ex_display(exarg_T *eap) { - int i, n; - long j; - char_u *p; - yankreg_T *yb; + char_u *p; + yankreg_T *yb; int name; - int attr; - char_u *arg = eap->arg; + char_u *arg = eap->arg; int clen; if (arg != NULL && *arg == NUL) arg = NULL; - attr = hl_attr(HLF_8); + int attr = hl_attr(HLF_8); /* Highlight title */ MSG_PUTS_TITLE(_("\n--- Registers ---")); - for (i = -1; i < NUM_REGISTERS && !got_int; i++) { + for (int i = -1; i < NUM_REGISTERS && !got_int; i++) { name = get_register_name(i); if (arg != NULL && vim_strchr(arg, name) == NULL) { @@ -3261,8 +3265,8 @@ void ex_display(exarg_T *eap) msg_putchar(name); MSG_PUTS(" "); - n = (int)Columns - 6; - for (j = 0; j < yb->y_size && n > 1; ++j) { + int n = (int)Columns - 6; + for (size_t j = 0; j < yb->y_size && n > 1; j++) { if (j) { MSG_PUTS_ATTR("^J", attr); n -= 2; @@ -3438,7 +3442,7 @@ static char_u *skip_comment(char_u *line, int process, int include_space, int *i // to set those marks. // // return FAIL for failure, OK otherwise -int do_join(long count, +int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions, @@ -3461,24 +3465,21 @@ int do_join(long count, && has_format_option(FO_REMOVE_COMS); int prev_was_comment; - assert(count > 1); - if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1), - (linenr_T)(curwin->w_cursor.lnum + count)) == FAIL) + if (save_undo && u_save(curwin->w_cursor.lnum - 1, + curwin->w_cursor.lnum + (linenr_T)count) == FAIL) { return FAIL; - - /* Allocate an array to store the number of spaces inserted before each - * line. We will use it to pre-compute the length of the new line and the - * proper placement of each original line in the new one. */ + } + // Allocate an array to store the number of spaces inserted before each + // line. We will use it to pre-compute the length of the new line and the + // proper placement of each original line in the new one. spaces = xcalloc(count, 1); if (remove_comments) { comments = xcalloc(count, sizeof(*comments)); } - /* - * Don't move anything, just compute the final line length - * and setup the array of space strings lengths - */ - for (t = 0; t < count; ++t) { + // Don't move anything, just compute the final line length + // and setup the array of space strings lengths + for (t = 0; t < (linenr_T)count; t++) { curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t)); if (t == 0 && setmark) { // Set the '[ mark. @@ -3558,7 +3559,7 @@ int do_join(long count, * column. This is not Vi compatible, but Vi deletes the marks, thus that * should not really be a problem. */ - for (t = count - 1;; --t) { + for (t = (linenr_T)count - 1;; t--) { cend -= currsize; memmove(cend, curr, (size_t)currsize); if (spaces[t] > 0) { @@ -3595,8 +3596,8 @@ int do_join(long count, * have moved up (last line deleted), so the current lnum is kept in t. */ t = curwin->w_cursor.lnum; - ++curwin->w_cursor.lnum; - del_lines(count - 1, FALSE); + curwin->w_cursor.lnum++; + del_lines((long)count - 1, false); curwin->w_cursor.lnum = t; /* @@ -3778,8 +3779,8 @@ fex_format ( * Set v:lnum to the first line number and v:count to the number of lines. * Set v:char to the character to be inserted (can be NUL). */ - set_vim_var_nr(VV_LNUM, lnum); - set_vim_var_nr(VV_COUNT, count); + set_vim_var_nr(VV_LNUM, (varnumber_T)lnum); + set_vim_var_nr(VV_COUNT, (varnumber_T)count); set_vim_var_char(c); /* @@ -4473,7 +4474,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) firstdigit = 'a'; } } else { - firstdigit -= Prenum1; + firstdigit -= (int)Prenum1; } } else { if (26 - CharOrd(firstdigit) - 1 < Prenum1) { @@ -4483,7 +4484,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) firstdigit = 'z'; } } else { - firstdigit += Prenum1; + firstdigit += (int)Prenum1; } } curwin->w_cursor.col = col; @@ -4591,7 +4592,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // Prepare the leading characters in buf1[]. // When there are many leading zeros it could be very long. // Allocate a bit too much. - buf1 = xmalloc(length + NUMBUFLEN); + buf1 = xmalloc((size_t)length + NUMBUFLEN); if (buf1 == NULL) { goto theend; } @@ -4604,7 +4605,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) length--; } if (pre == 'b' || pre == 'B' || pre == 'x' || pre == 'X') { - *ptr++ = pre; + *ptr++ = (char_u)pre; length--; } @@ -4767,8 +4768,6 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) /// @returns NULL for error. void *get_reg_contents(int regname, int flags) { - long i; - // Don't allow using an expression register inside an expression. if (regname == '=') { if (flags & kGRegNoExpr) { @@ -4804,7 +4803,7 @@ void *get_reg_contents(int regname, int flags) if (flags & kGRegList) { list_T *list = list_alloc(); - for (int i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { list_append_string(list, reg->y_array[i], -1); } @@ -4815,7 +4814,7 @@ void *get_reg_contents(int regname, int flags) * Compute length of resulting string. */ size_t len = 0; - for (i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { len += STRLEN(reg->y_array[i]); /* * Insert a newline between lines and after last line if @@ -4832,7 +4831,7 @@ void *get_reg_contents(int regname, int flags) * Copy the lines of the yank register into the string. */ len = 0; - for (i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { STRCPY(retval + len, reg->y_array[i]); len += STRLEN(retval + len); @@ -4888,7 +4887,7 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, void write_reg_contents_lst(int name, char_u **strings, int maxlen, bool must_append, MotionType yank_type, - long block_len) + colnr_T block_len) { if (name == '/' || name == '=') { char_u *s = strings[0]; @@ -4913,7 +4912,8 @@ void write_reg_contents_lst(int name, char_u **strings, int maxlen, return; } - str_to_reg(reg, yank_type, (char_u *) strings, -1, block_len, true); + str_to_reg(reg, yank_type, (char_u *)strings, STRLEN((char_u *)strings), + block_len, true); finish_write_reg(name, reg, old_y_previous); } @@ -4941,7 +4941,7 @@ void write_reg_contents_ex(int name, ssize_t len, bool must_append, MotionType yank_type, - long block_len) + colnr_T block_len) { if (len < 0) { len = (ssize_t) STRLEN(str); @@ -4991,7 +4991,7 @@ void write_reg_contents_ex(int name, // Copy the input string into the adjusted memory at the specified // offset. expr_line = xrealloc(expr_line, totlen + 1); - memcpy(expr_line + offset, str, (size_t) len); + memcpy(expr_line + offset, str, (size_t)len); expr_line[totlen] = NUL; return; @@ -5005,7 +5005,7 @@ void write_reg_contents_ex(int name, if (!(reg = init_write_reg(name, &old_y_previous, must_append))) { return; } - str_to_reg(reg, yank_type, str, len, block_len, false); + str_to_reg(reg, yank_type, str, (size_t)len, block_len, false); finish_write_reg(name, reg, old_y_previous); } @@ -5061,7 +5061,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, (y_ptr->y_size + newlines) * sizeof(char_u *)); y_ptr->y_array = pp; - linenr_T lnum = y_ptr->y_size; // The current line number. + size_t lnum = y_ptr->y_size; // The current line number. // If called with `blocklen < 0`, we have to update the yank reg's width. size_t maxlen = 0; @@ -5080,7 +5080,9 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, for (const char_u *start = str, *end = str + len; start < end + extraline; start += line_len + 1, lnum++) { - line_len = (const char_u *) xmemscan(start, '\n', end - start) - start; + assert(end - start >= 0); + line_len = (size_t)((char_u *)xmemscan(start, '\n', + (size_t)(end - start)) - start); if (line_len > maxlen) { maxlen = line_len; } @@ -5090,7 +5092,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, char_u *s = xmallocz(line_len + extra); memcpy(s, pp[lnum], extra); memcpy(s + extra, start, line_len); - ssize_t s_len = extra + line_len; + size_t s_len = extra + line_len; if (append) { xfree(pp[lnum]); @@ -5450,7 +5452,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) yankreg_T *target; if (cb_flags & CB_UNNAMEDPLUS) { - *name = cb_flags & CB_UNNAMED ? '"': '+'; + *name = (cb_flags & CB_UNNAMED && writing) ? '"': '+'; target = &y_regs[PLUS_REGISTER]; } else { *name = '*'; @@ -5474,7 +5476,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) free_register(reg); list_T *args = list_alloc(); - char_u regname = name; + char_u regname = (char_u)name; list_append_string(args, ®name, 1); typval_T result = eval_call_provider("clipboard", "get", args); @@ -5519,8 +5521,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) reg->y_type = kMTUnknown; } - reg->y_array = xcalloc(lines->lv_len, sizeof(uint8_t *)); - reg->y_size = lines->lv_len; + reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *)); + reg->y_size = (size_t)lines->lv_len; reg->additional_data = NULL; reg->timestamp = 0; // Timestamp is not saved for clipboard registers because clipboard registers @@ -5551,14 +5553,15 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } if (reg->y_type == kMTBlockWise) { - int maxlen = 0; - for (int i = 0; i < reg->y_size; i++) { - int rowlen = STRLEN(reg->y_array[i]); + size_t maxlen = 0; + for (size_t i = 0; i < reg->y_size; i++) { + size_t rowlen = STRLEN(reg->y_array[i]); if (rowlen > maxlen) { maxlen = rowlen; } } - reg->y_width = maxlen-1; + assert(maxlen <= INT_MAX); + reg->y_width = (int)maxlen - 1; } *target = reg; @@ -5566,7 +5569,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) err: if (reg->y_array) { - for (int i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { xfree(reg->y_array[i]); } xfree(reg->y_array); @@ -5590,7 +5593,7 @@ static void set_clipboard(int name, yankreg_T *reg) list_T *lines = list_alloc(); - for (int i = 0; i < reg->y_size; i++) { + for (size_t i = 0; i < reg->y_size; i++) { list_append_string(lines, reg->y_array[i], -1); } @@ -5615,7 +5618,7 @@ static void set_clipboard(int name, yankreg_T *reg) } list_append_string(args, ®type, 1); - char_u regname = name; + char_u regname = (char_u)name; list_append_string(args, ®name, 1); (void)eval_call_provider("clipboard", "set", args); @@ -5678,8 +5681,8 @@ const void *op_register_iter(const void *const iter, char *const name, if (iter_reg - &(y_regs[0]) == NUM_SAVED_REGISTERS || reg_empty(iter_reg)) { return NULL; } - size_t iter_off = iter_reg - &(y_regs[0]); - *name = (char) get_register_name(iter_off); + int iter_off = (int)(iter_reg - &(y_regs[0])); + *name = (char)get_register_name(iter_off); *reg = *iter_reg; while (++iter_reg - &(y_regs[0]) < NUM_SAVED_REGISTERS) { if (!reg_empty(iter_reg)) { diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 8c8a586957..44df2e9e0c 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -79,7 +79,7 @@ enum GRegFlags { /// Definition of one register typedef struct yankreg { char_u **y_array; ///< Pointer to an array of line pointers. - linenr_T y_size; ///< Number of lines in y_array. + size_t y_size; ///< Number of lines in y_array. MotionType y_type; ///< Register type colnr_T y_width; ///< Register width (only valid for y_type == kBlockWise). Timestamp timestamp; ///< Time when register was last modified. diff --git a/src/nvim/option.c b/src/nvim/option.c index 45ebb4fa4c..de53b0b1f4 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2129,6 +2129,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_nf); check_string_option(&buf->b_p_qe); check_string_option(&buf->b_p_syn); + check_string_option(&buf->b_s.b_syn_isk); check_string_option(&buf->b_s.b_p_spc); check_string_option(&buf->b_s.b_p_spf); check_string_option(&buf->b_s.b_p_spl); @@ -2313,50 +2314,46 @@ set_string_option_global ( } } -/* - * Set a string option to a new value, and handle the effects. - * - * Returns NULL on success or error message on error. - */ -static char_u * -set_string_option ( - int opt_idx, - char_u *value, - int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */ -) +/// Set a string option to a new value, handling the effects +/// +/// @param[in] opt_idx Option to set. +/// @param[in] value New value. +/// @param[in] opt_flags Option flags: expected to contain #OPT_LOCAL and/or +/// #OPT_GLOBAL. +/// +/// @return NULL on success, error message on error. +static char *set_string_option(const int opt_idx, const char *const value, + const int opt_flags) + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *s; - char_u **varp; - char_u *oldval; - char *saved_oldval = NULL; - char_u *r = NULL; - - if (options[opt_idx].var == NULL) /* don't set hidden option */ + if (options[opt_idx].var == NULL) { // don't set hidden option return NULL; + } - s = vim_strsave(value); - varp = (char_u **)get_varp_scope(&(options[opt_idx]), - (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 - ? (((int)options[opt_idx].indir & PV_BOTH) - ? OPT_GLOBAL : OPT_LOCAL) - : opt_flags); - oldval = *varp; + char *const s = xstrdup(value); + char **const varp = (char **)get_varp_scope( + &(options[opt_idx]), + ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 + ? (((int)options[opt_idx].indir & PV_BOTH) + ? OPT_GLOBAL : OPT_LOCAL) + : opt_flags)); + char *const oldval = *varp; *varp = s; - if (!starting) { - saved_oldval = xstrdup((char *) oldval); - } + char *const saved_oldval = (starting ? NULL : xstrdup(oldval)); - if ((r = did_set_string_option(opt_idx, varp, (int)true, oldval, NULL, - opt_flags)) == NULL) - did_set_option(opt_idx, opt_flags, TRUE); + char *const r = (char *)did_set_string_option( + opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags); + if (r == NULL) { + did_set_option(opt_idx, opt_flags, true); + } // call autocommand after handling side effects if (saved_oldval != NULL) { char buf_type[7]; vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, (char *) (*varp), -1); + set_vim_var_string(VV_OPTION_NEW, (char *)(*varp), -1); set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); apply_autocmds(EVENT_OPTIONSET, @@ -2442,16 +2439,13 @@ did_set_string_option ( else if (varp == &curwin->w_p_briopt) { if (briopt_check(curwin) == FAIL) errmsg = e_invarg; - } - /* - * 'isident', 'iskeyword', 'isprint or 'isfname' option: refill chartab[] - * If the new option is invalid, use old value. 'lisp' option: refill - * chartab[] for '-' char - */ - else if ( varp == &p_isi + } else if (varp == &p_isi || varp == &(curbuf->b_p_isk) || varp == &p_isp || varp == &p_isf) { + // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[] + // If the new option is invalid, use old value. 'lisp' option: refill + // g_chartab[] for '-' char if (init_chartab() == FAIL) { did_chartab = TRUE; /* need to restore it below */ errmsg = e_invarg; /* error in value */ @@ -3190,8 +3184,9 @@ did_set_string_option ( for (p = q; *p != NUL; ++p) if (vim_strchr((char_u *)"_.,", *p) != NULL) break; - vim_snprintf((char *)fname, 200, "spell/%.*s.vim", (int)(p - q), q); - source_runtime(fname, TRUE); + vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim", + (int)(p - q), q); + source_runtime(fname, DIP_ALL); } } @@ -3402,9 +3397,10 @@ char_u *check_stl_option(char_u *s) if (!*s) break; s++; - if (*s != '%' && *s != ')') - ++itemcnt; - if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_MIDDLEMARK) { + if (*s != '%' && *s != ')') { + itemcnt++; + } + if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) { s++; continue; } @@ -4029,15 +4025,16 @@ set_num_option ( errmsg = e_invarg; curwin->w_p_fdc = 12; } - } - /* 'shiftwidth' or 'tabstop' */ - else if (pp == &curbuf->b_p_sw || pp == &curbuf->b_p_ts) { - if (foldmethodIsIndent(curwin)) + // 'shiftwidth' or 'tabstop' + } else if (pp == &curbuf->b_p_sw || pp == (long *)&curbuf->b_p_ts) { + if (foldmethodIsIndent(curwin)) { foldUpdateAll(curwin); - /* When 'shiftwidth' changes, or it's zero and 'tabstop' changes: - * parse 'cinoptions'. */ - if (pp == &curbuf->b_p_sw || curbuf->b_p_sw == 0) + } + // When 'shiftwidth' changes, or it's zero and 'tabstop' changes: + // parse 'cinoptions'. + if (pp == &curbuf->b_p_sw || curbuf->b_p_sw == 0) { parse_cino(curbuf); + } } /* 'maxcombine' */ else if (pp == &p_mco) { @@ -4405,6 +4402,7 @@ bool get_tty_option(char *name, char **value) if (is_tty_option(name)) { if (value) { + // XXX: All other t_* options were removed in 3baba1e7. *value = xstrdup(""); } return true; @@ -4652,26 +4650,28 @@ set_option_value ( EMSG(_(e_sandbox)); return NULL; } - if (flags & P_STRING) - return set_string_option(opt_idx, string, opt_flags); - else { + if (flags & P_STRING) { + const char *s = (const char *)string; + if (s == NULL) { + s = ""; + } + return (char_u *)set_string_option(opt_idx, s, opt_flags); + } else { varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (varp != NULL) { /* hidden option is not changed */ if (number == 0 && string != NULL) { int idx; - /* Either we are given a string or we are setting option - * to zero. */ - for (idx = 0; string[idx] == '0'; ++idx) - ; + // Either we are given a string or we are setting option + // to zero. + for (idx = 0; string[idx] == '0'; idx++) {} if (string[idx] != NUL || idx == 0) { - /* There's another character after zeros or the string - * is empty. In both cases, we are trying to set a - * num option using a string. */ + // There's another character after zeros or the string + // is empty. In both cases, we are trying to set a + // num option using a string. EMSG3(_("E521: Number required: &%s = '%s'"), - name, string); - return NULL; /* do nothing as we hit an error */ - + name, string); + return NULL; // do nothing as we hit an error } } if (flags & P_NUM) @@ -5606,6 +5606,7 @@ void buf_copy_options(buf_T *buf, int flags) /* Don't copy 'syntax', it must be set */ buf->b_p_syn = empty_option; buf->b_p_smc = p_smc; + buf->b_s.b_syn_isk = empty_option; buf->b_s.b_p_spc = vim_strsave(p_spc); (void)compile_cap_prog(&buf->b_s); buf->b_s.b_p_spf = vim_strsave(p_spf); @@ -5655,7 +5656,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_isk = save_p_isk; else { buf->b_p_isk = vim_strsave(p_isk); - did_isk = TRUE; + did_isk = true; buf->b_p_ts = p_ts; buf->b_help = false; if (buf->b_p_bt[0] == 'h') @@ -5836,6 +5837,7 @@ set_context_in_set_cmd ( if (p == (char_u *)&p_bdir || p == (char_u *)&p_dir || p == (char_u *)&p_path + || p == (char_u *)&p_pp || p == (char_u *)&p_rtp || p == (char_u *)&p_cdpath || p == (char_u *)&p_vdir diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 904e97f8ca..8d6f42e088 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -258,7 +258,7 @@ enum { STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y). STL_PAGENUM = 'N', ///< Page number (when printing). STL_VIM_EXPR = '{', ///< Start of expression to substitute. - STL_MIDDLEMARK = '=', ///< Separation between left and right. + STL_SEPARATE = '=', ///< Separation between alignment sections. STL_TRUNCMARK = '<', ///< Truncation mark if line is too long. STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0. STL_HIGHLIGHT = '#', ///< Highlight name. @@ -274,7 +274,7 @@ enum { STL_HELPFLAG, STL_HELPFLAG_ALT, STL_FILETYPE, STL_FILETYPE_ALT, \ STL_PREVIEWFLAG, STL_PREVIEWFLAG_ALT, STL_MODIFIED, STL_MODIFIED_ALT, \ STL_QUICKFIX, STL_PERCENTAGE, STL_ALTPERCENT, STL_ARGLISTSTAT, STL_PAGENUM, \ - STL_VIM_EXPR, STL_MIDDLEMARK, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \ + STL_VIM_EXPR, STL_SEPARATE, STL_TRUNCMARK, STL_USER_HL, STL_HIGHLIGHT, \ STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \ 0, \ }) @@ -456,80 +456,81 @@ EXTERN int p_hid; // 'hidden' // Use P_HID to check if a buffer is to be hidden when it is no longer // visible in a window. # define P_HID(buf) (buf_hide(buf)) -EXTERN char_u *p_hl; /* 'highlight' */ -EXTERN int p_hls; /* 'hlsearch' */ -EXTERN long p_hi; /* 'history' */ -EXTERN int p_hkmap; /* 'hkmap' */ -EXTERN int p_hkmapp; /* 'hkmapp' */ -EXTERN int p_fkmap; /* 'fkmap' */ -EXTERN int p_altkeymap; /* 'altkeymap' */ -EXTERN int p_arshape; /* 'arabicshape' */ -EXTERN int p_icon; /* 'icon' */ -EXTERN char_u *p_iconstring; /* 'iconstring' */ -EXTERN int p_ic; /* 'ignorecase' */ -EXTERN int p_is; /* 'incsearch' */ -EXTERN int p_im; /* 'insertmode' */ -EXTERN char_u *p_isf; /* 'isfname' */ -EXTERN char_u *p_isi; /* 'isident' */ -EXTERN char_u *p_isp; /* 'isprint' */ -EXTERN int p_js; /* 'joinspaces' */ -EXTERN char_u *p_kp; /* 'keywordprg' */ -EXTERN char_u *p_km; /* 'keymodel' */ -EXTERN char_u *p_langmap; /* 'langmap'*/ -EXTERN int p_lnr; /* 'langnoremap'*/ -EXTERN char_u *p_lm; /* 'langmenu' */ -EXTERN char_u *p_lispwords; /* 'lispwords' */ -EXTERN long p_ls; /* 'laststatus' */ -EXTERN long p_stal; /* 'showtabline' */ -EXTERN char_u *p_lcs; /* 'listchars' */ - -EXTERN int p_lz; /* 'lazyredraw' */ -EXTERN int p_lpl; /* 'loadplugins' */ -EXTERN int p_magic; /* 'magic' */ -EXTERN char_u *p_mef; /* 'makeef' */ -EXTERN char_u *p_mp; /* 'makeprg' */ -EXTERN char_u *p_cc; /* 'colorcolumn' */ -EXTERN int p_cc_cols[256]; /* array for 'colorcolumn' columns */ -EXTERN long p_mat; /* 'matchtime' */ -EXTERN long p_mco; /* 'maxcombine' */ -EXTERN long p_mfd; /* 'maxfuncdepth' */ -EXTERN long p_mmd; /* 'maxmapdepth' */ -EXTERN long p_mm; /* 'maxmem' */ -EXTERN long p_mmp; /* 'maxmempattern' */ -EXTERN long p_mmt; /* 'maxmemtot' */ -EXTERN long p_mis; /* 'menuitems' */ -EXTERN char_u *p_msm; /* 'mkspellmem' */ -EXTERN long p_mls; /* 'modelines' */ -EXTERN char_u *p_mouse; /* 'mouse' */ -EXTERN char_u *p_mousem; /* 'mousemodel' */ -EXTERN long p_mouset; /* 'mousetime' */ -EXTERN int p_more; /* 'more' */ -EXTERN char_u *p_opfunc; /* 'operatorfunc' */ -EXTERN char_u *p_para; /* 'paragraphs' */ -EXTERN int p_paste; /* 'paste' */ -EXTERN char_u *p_pt; /* 'pastetoggle' */ -EXTERN char_u *p_pex; /* 'patchexpr' */ -EXTERN char_u *p_pm; /* 'patchmode' */ -EXTERN char_u *p_path; /* 'path' */ -EXTERN char_u *p_cdpath; /* 'cdpath' */ -EXTERN long p_rdt; /* 'redrawtime' */ -EXTERN int p_remap; /* 'remap' */ -EXTERN long p_re; /* 'regexpengine' */ -EXTERN long p_report; /* 'report' */ -EXTERN long p_pvh; /* 'previewheight' */ -EXTERN int p_ari; /* 'allowrevins' */ -EXTERN int p_ri; /* 'revins' */ -EXTERN int p_ru; /* 'ruler' */ -EXTERN char_u *p_ruf; /* 'rulerformat' */ -EXTERN char_u *p_rtp; /* 'runtimepath' */ -EXTERN long p_sj; /* 'scrolljump' */ -EXTERN long p_so; /* 'scrolloff' */ -EXTERN char_u *p_sbo; /* 'scrollopt' */ -EXTERN char_u *p_sections; /* 'sections' */ -EXTERN int p_secure; /* 'secure' */ -EXTERN char_u *p_sel; /* 'selection' */ -EXTERN char_u *p_slm; /* 'selectmode' */ -EXTERN char_u *p_ssop; /* 'sessionoptions' */ +EXTERN char_u *p_hl; // 'highlight' +EXTERN int p_hls; // 'hlsearch' +EXTERN long p_hi; // 'history' +EXTERN int p_hkmap; // 'hkmap' +EXTERN int p_hkmapp; // 'hkmapp' +EXTERN int p_fkmap; // 'fkmap' +EXTERN int p_altkeymap; // 'altkeymap' +EXTERN int p_arshape; // 'arabicshape' +EXTERN int p_icon; // 'icon' +EXTERN char_u *p_iconstring; // 'iconstring' +EXTERN int p_ic; // 'ignorecase' +EXTERN int p_is; // 'incsearch' +EXTERN int p_im; // 'insertmode' +EXTERN char_u *p_isf; // 'isfname' +EXTERN char_u *p_isi; // 'isident' +EXTERN char_u *p_isp; // 'isprint' +EXTERN int p_js; // 'joinspaces' +EXTERN char_u *p_kp; // 'keywordprg' +EXTERN char_u *p_km; // 'keymodel' +EXTERN char_u *p_langmap; // 'langmap'*/ +EXTERN int p_lnr; // 'langnoremap'*/ +EXTERN char_u *p_lm; // 'langmenu' +EXTERN char_u *p_lispwords; // 'lispwords' +EXTERN long p_ls; // 'laststatus' +EXTERN long p_stal; // 'showtabline' +EXTERN char_u *p_lcs; // 'listchars' + +EXTERN int p_lz; // 'lazyredraw' +EXTERN int p_lpl; // 'loadplugins' +EXTERN int p_magic; // 'magic' +EXTERN char_u *p_mef; // 'makeef' +EXTERN char_u *p_mp; // 'makeprg' +EXTERN char_u *p_cc; // 'colorcolumn' +EXTERN int p_cc_cols[256]; // array for 'colorcolumn' columns +EXTERN long p_mat; // 'matchtime' +EXTERN long p_mco; // 'maxcombine' +EXTERN long p_mfd; // 'maxfuncdepth' +EXTERN long p_mmd; // 'maxmapdepth' +EXTERN long p_mm; // 'maxmem' +EXTERN long p_mmp; // 'maxmempattern' +EXTERN long p_mmt; // 'maxmemtot' +EXTERN long p_mis; // 'menuitems' +EXTERN char_u *p_msm; // 'mkspellmem' +EXTERN long p_mls; // 'modelines' +EXTERN char_u *p_mouse; // 'mouse' +EXTERN char_u *p_mousem; // 'mousemodel' +EXTERN long p_mouset; // 'mousetime' +EXTERN int p_more; // 'more' +EXTERN char_u *p_opfunc; // 'operatorfunc' +EXTERN char_u *p_para; // 'paragraphs' +EXTERN int p_paste; // 'paste' +EXTERN char_u *p_pt; // 'pastetoggle' +EXTERN char_u *p_pex; // 'patchexpr' +EXTERN char_u *p_pm; // 'patchmode' +EXTERN char_u *p_path; // 'path' +EXTERN char_u *p_cdpath; // 'cdpath' +EXTERN long p_rdt; // 'redrawtime' +EXTERN int p_remap; // 'remap' +EXTERN long p_re; // 'regexpengine' +EXTERN long p_report; // 'report' +EXTERN long p_pvh; // 'previewheight' +EXTERN int p_ari; // 'allowrevins' +EXTERN int p_ri; // 'revins' +EXTERN int p_ru; // 'ruler' +EXTERN char_u *p_ruf; // 'rulerformat' +EXTERN char_u *p_pp; // 'packpath' +EXTERN char_u *p_rtp; // 'runtimepath' +EXTERN long p_sj; // 'scrolljump' +EXTERN long p_so; // 'scrolloff' +EXTERN char_u *p_sbo; // 'scrollopt' +EXTERN char_u *p_sections; // 'sections' +EXTERN int p_secure; // 'secure' +EXTERN char_u *p_sel; // 'selection' +EXTERN char_u *p_slm; // 'selectmode' +EXTERN char_u *p_ssop; // 'sessionoptions' EXTERN unsigned ssop_flags; # ifdef IN_OPTION_C /* Also used for 'viewoptions'! */ diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 218e34f595..d19af4f73f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1640,6 +1640,16 @@ return { defaults={if_true={vi=""}} }, { + full_name='packpath', abbreviation='pp', + type='string', list='onecomma', scope={'global'}, + deny_duplicates=true, + secure=true, + vi_def=true, + expand=true, + varname='p_pp', + defaults={if_true={vi=''}} + }, + { full_name='paragraphs', abbreviation='para', type='string', scope={'global'}, vi_def=true, diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c new file mode 100644 index 0000000000..6cee102305 --- /dev/null +++ b/src/nvim/os/fileio.c @@ -0,0 +1,319 @@ +/// @file fileio.c +/// +/// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with +/// Neovim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite +/// replacement. + +#include <unistd.h> +#include <assert.h> +#include <stddef.h> +#include <stdbool.h> +#include <fcntl.h> + +#include "auto/config.h" + +#ifdef HAVE_SYS_UIO_H +# include <sys/uio.h> +#endif + +#include <uv.h> + +#include "nvim/os/fileio.h" +#include "nvim/memory.h" +#include "nvim/os/os.h" +#include "nvim/globals.h" +#include "nvim/rbuffer.h" +#include "nvim/macros.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/fileio.c.generated.h" +#endif + +/// Open file +/// +/// @param[out] ret_fp Address where information needed for reading from or +/// writing to a file is saved +/// @param[in] fname File name to open. +/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and +/// writing to the file at once is not supported, so either +/// FILE_WRITE_ONLY or FILE_READ_ONLY is required. +/// @param[in] mode Permissions for the newly created file (ignored if flags +/// does not have FILE_CREATE\*). +/// +/// @return Error code (@see os_strerror()) or 0. +int file_open(FileDescriptor *const ret_fp, const char *const fname, + const int flags, const int mode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + int os_open_flags = 0; + int fd; + TriState wr = kNone; +#define FLAG(flags, flag, fcntl_flags, wrval, cond) \ + do { \ + if (flags & flag) { \ + os_open_flags |= fcntl_flags; \ + assert(cond); \ + if (wrval != kNone) { \ + wr = wrval; \ + } \ + } \ + } while (0) + FLAG(flags, kFileWriteOnly, O_WRONLY, kTrue, true); + FLAG(flags, kFileCreateOnly, O_CREAT|O_EXCL|O_WRONLY, kTrue, true); + FLAG(flags, kFileCreate, O_CREAT|O_WRONLY, kTrue, !(flags & kFileCreateOnly)); + FLAG(flags, kFileTruncate, O_TRUNC|O_WRONLY, kTrue, + !(flags & kFileCreateOnly)); + FLAG(flags, kFileReadOnly, O_RDONLY, kFalse, wr != kTrue); +#ifdef O_NOFOLLOW + FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true); +#endif +#undef FLAG + + fd = os_open(fname, os_open_flags, mode); + + if (fd < 0) { + return fd; + } + + ret_fp->wr = (wr == kTrue); + ret_fp->fd = fd; + ret_fp->eof = false; + ret_fp->rv = rbuffer_new(kRWBufferSize); + ret_fp->_error = 0; + if (ret_fp->wr) { + ret_fp->rv->data = ret_fp; + ret_fp->rv->full_cb = (rbuffer_callback)&file_rb_write_full_cb; + } + return 0; +} + +/// Like file_open(), but allocate and return ret_fp +/// +/// @param[out] error Error code, @see os_strerror(). Is set to zero on +/// success. +/// @param[in] fname File name to open. +/// @param[in] flags Flags, @see FileOpenFlags. +/// @param[in] mode Permissions for the newly created file (ignored if flags +/// does not have FILE_CREATE\*). +/// +/// @return [allocated] Opened file or NULL in case of error. +FileDescriptor *file_open_new(int *const error, const char *const fname, + const int flags, const int mode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + FileDescriptor *const fp = xmalloc(sizeof(*fp)); + if ((*error = file_open(fp, fname, flags, mode)) != 0) { + xfree(fp); + return NULL; + } + return fp; +} + +/// Close file and free its buffer +/// +/// @param[in,out] fp File to close. +/// +/// @return 0 or error code. +int file_close(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +{ + const int error = file_fsync(fp); + const int error2 = os_close(fp->fd); + rbuffer_free(fp->rv); + if (error2 != 0) { + return error2; + } + return error; +} + +/// Close and free file obtained using file_open_new() +/// +/// @param[in,out] fp File to close. +/// +/// @return 0 or error code. +int file_free(FileDescriptor *const fp) FUNC_ATTR_NONNULL_ALL +{ + const int ret = file_close(fp); + xfree(fp); + return ret; +} + +/// Flush file modifications to disk +/// +/// @param[in,out] fp File to work with. +/// +/// @return 0 or error code. +int file_fsync(FileDescriptor *const fp) + FUNC_ATTR_NONNULL_ALL +{ + if (!fp->wr) { + return 0; + } + file_rb_write_full_cb(fp->rv, fp); + if (fp->_error != 0) { + const int error = fp->_error; + fp->_error = 0; + return error; + } + return os_fsync(fp->fd); +} + +/// Buffer used for writing +/// +/// Like IObuff, but allows file_\* callers not to care about spoiling it. +static char writebuf[kRWBufferSize]; + +/// Function run when RBuffer is full when writing to a file +/// +/// Actually does writing to the file, may also be invoked directly. +/// +/// @param[in,out] rv RBuffer instance used. +/// @param[in,out] fp File to work with. +static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp) + FUNC_ATTR_NONNULL_ALL +{ + assert(fp->wr); + assert(rv->data == (void *)fp); + if (rbuffer_size(rv) == 0) { + return; + } + const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize); + const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes); + if (wres != (ptrdiff_t)read_bytes) { + if (wres >= 0) { + fp->_error = UV_EIO; + } else { + fp->_error = (int)wres; + } + } +} + +/// Read from file +/// +/// @param[in,out] fp File to work with. +/// @param[out] ret_buf Buffer to read to. Must not be NULL. +/// @param[in] size Number of bytes to read. Buffer must have at least ret_buf +/// bytes. +/// +/// @return error_code (< 0) or number of bytes read. +ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, + const size_t size) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(!fp->wr); + char *buf = ret_buf; + size_t read_remaining = size; + RBuffer *const rv = fp->rv; + while (read_remaining) { + const size_t rv_size = rbuffer_size(rv); + if (rv_size > 0) { + const size_t rsize = rbuffer_read(rv, buf, MIN(rv_size, read_remaining)); + buf += rsize; + read_remaining -= rsize; + } + if (fp->eof) { + break; + } + if (read_remaining) { + assert(rbuffer_size(rv) == 0); + rbuffer_reset(rv); +#ifdef HAVE_READV + // If there is readv() syscall, then take an opportunity to populate + // both target buffer and RBuffer at once, … + size_t write_count; + struct iovec iov[] = { + { .iov_base = buf, .iov_len = read_remaining }, + { .iov_base = rbuffer_write_ptr(rv, &write_count), + .iov_len = kRWBufferSize }, + }; + assert(write_count == kRWBufferSize); + const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov, + ARRAY_SIZE(iov)); + if (r_ret > 0) { + if (r_ret > (ptrdiff_t)read_remaining) { + rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining)); + read_remaining = 0; + } else { + buf += (size_t)r_ret; + read_remaining -= (size_t)r_ret; + } + } else if (r_ret < 0) { + return r_ret; + } +#else + if (read_remaining >= kRWBufferSize) { + // …otherwise leave RBuffer empty and populate only target buffer, + // because filtering information through rbuffer will be more syscalls. + const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining); + if (r_ret >= 0) { + read_remaining -= (size_t)r_ret; + return (ptrdiff_t)(size - read_remaining); + } else if (r_ret < 0) { + return r_ret; + } + } else { + size_t write_count; + const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, + rbuffer_write_ptr(rv, &write_count), + kRWBufferSize); + assert(write_count == kRWBufferSize); + if (r_ret > 0) { + rbuffer_produced(rv, (size_t)r_ret); + } else if (r_ret < 0) { + return r_ret; + } + } +#endif + } + } + return (ptrdiff_t)(size - read_remaining); +} + +/// Write to a file +/// +/// @param[in] fd File descriptor to write to. +/// @param[in] buf Data to write. May be NULL if size is zero. +/// @param[in] size Amount of bytes to write. +/// +/// @return Number of bytes written or libuv error code (< 0). +ptrdiff_t file_write(FileDescriptor *const fp, const char *const buf, + const size_t size) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + assert(fp->wr); + const size_t written = rbuffer_write(fp->rv, buf, size); + if (fp->_error != 0) { + const int error = fp->_error; + fp->_error = 0; + return error; + } else if (written != size) { + return UV_EIO; + } + return (ptrdiff_t)written; +} + +/// Buffer used for skipping. Its contents is undefined and should never be +/// used. +static char skipbuf[kRWBufferSize]; + +/// Skip some bytes +/// +/// This is like `fseek(fp, size, SEEK_CUR)`, but actual implementation simply +/// reads to a buffer and discards the result. +ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size) + FUNC_ATTR_NONNULL_ALL +{ + assert(!fp->wr); + size_t read_bytes = 0; + do { + const ptrdiff_t new_read_bytes = file_read( + fp, skipbuf, MIN(size - read_bytes, sizeof(skipbuf))); + if (new_read_bytes < 0) { + return new_read_bytes; + } else if (new_read_bytes == 0) { + break; + } + read_bytes += (size_t)new_read_bytes; + } while (read_bytes < size && !file_eof(fp)); + + return (ptrdiff_t)read_bytes; +} diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h new file mode 100644 index 0000000000..2cffd5c851 --- /dev/null +++ b/src/nvim/os/fileio.h @@ -0,0 +1,72 @@ +#ifndef NVIM_OS_FILEIO_H +#define NVIM_OS_FILEIO_H + +#include <stdbool.h> +#include <stddef.h> + +#include "nvim/func_attr.h" +#include "nvim/rbuffer.h" + +/// Structure used to read from/write to file +typedef struct { + int fd; ///< File descriptor. + int _error; ///< Error code for use with RBuffer callbacks or zero. + RBuffer *rv; ///< Read or write buffer. + bool wr; ///< True if file is in write mode. + bool eof; ///< True if end of file was encountered. +} FileDescriptor; + +/// file_open() flags +typedef enum { + kFileReadOnly = 1, ///< Open file read-only. Default. + kFileCreate = 2, ///< Create file if it does not exist yet. + ///< Implies kFileWriteOnly. + kFileWriteOnly = 4, ///< Open file for writing only. + ///< Cannot be used with kFileReadOnly. + kFileNoSymlink = 8, ///< Do not allow symbolic links. + kFileCreateOnly = 16, ///< Only create the file, failing if it already + ///< exists. Implies kFileWriteOnly. Cannot be used + ///< with kFileCreate. + kFileTruncate = 32, ///< Truncate the file if it exists. + ///< Implies kFileWriteOnly. Cannot be used with + ///< kFileCreateOnly. +} FileOpenFlags; + +static inline bool file_eof(const FileDescriptor *const fp) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Check whether end of file was encountered +/// +/// @param[in] fp File to check. +/// +/// @return true if it was, false if it was not or read operation was never +/// performed. +static inline bool file_eof(const FileDescriptor *const fp) +{ + return fp->eof && rbuffer_size(fp->rv) == 0; +} + +static inline int file_fd(const FileDescriptor *const fp) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Return the file descriptor associated with the FileDescriptor structure +/// +/// @param[in] fp File to check. +/// +/// @return File descriptor. +static inline int file_fd(const FileDescriptor *const fp) +{ + return fp->fd; +} + +enum { + /// Read or write buffer size + /// + /// Currently equal to (IOSIZE - 1), but they do not need to be connected. + kRWBufferSize = 1024 +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/fileio.h.generated.h" +#endif +#endif // NVIM_OS_FILEIO_H diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 143a7160b0..d12d34d595 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -1,14 +1,26 @@ // fs.c -- filesystem access #include <stdbool.h> - +#include <stddef.h> #include <assert.h> +#include <limits.h> +#include <unistd.h> #include <fcntl.h> +#include <errno.h> + +#include "auto/config.h" + +#ifdef HAVE_SYS_UIO_H +# include <sys/uio.h> +#endif + +#include <uv.h> #include "nvim/os/os.h" #include "nvim/os/os_defs.h" #include "nvim/ascii.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/assert.h" #include "nvim/misc1.h" #include "nvim/misc2.h" #include "nvim/path.h" @@ -18,6 +30,20 @@ # include "os/fs.c.generated.h" #endif +#define RUN_UV_FS_FUNC(ret, func, ...) \ + do { \ + bool did_try_to_free = false; \ +uv_call_start: {} \ + uv_fs_t req; \ + ret = func(&fs_loop, &req, __VA_ARGS__); \ + uv_fs_req_cleanup(&req); \ + if (ret == UV_ENOMEM && !did_try_to_free) { \ + try_to_free_memory(); \ + did_try_to_free = true; \ + goto uv_call_start; \ + } \ + } while (0) + // Many fs functions from libuv return that value on success. static const int kLibuvSuccess = 0; static uv_loop_t fs_loop; @@ -325,13 +351,190 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) int os_open(const char* path, int flags, int mode) FUNC_ATTR_NONNULL_ALL { - uv_fs_t open_req; - int r = uv_fs_open(&fs_loop, &open_req, path, flags, mode, NULL); - uv_fs_req_cleanup(&open_req); - // r is the same as open_req.result (except for OOM: then only r is set). + int r; + RUN_UV_FS_FUNC(r, uv_fs_open, path, flags, mode, NULL); + return r; +} + +/// Close a file +/// +/// @return 0 or libuv error code on failure. +int os_close(const int fd) +{ + int r; + RUN_UV_FS_FUNC(r, uv_fs_close, fd, NULL); return r; } +/// Read from a file +/// +/// Handles EINTR and ENOMEM, but not other errors. +/// +/// @param[in] fd File descriptor to read from. +/// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set +/// to false. Initial value is ignored. +/// @param[out] ret_buf Buffer to write to. May be NULL if size is zero. +/// @param[in] size Amount of bytes to read. +/// +/// @return Number of bytes read or libuv error code (< 0). +ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, + const size_t size) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + *ret_eof = false; + if (ret_buf == NULL) { + assert(size == 0); + return 0; + } + size_t read_bytes = 0; + bool did_try_to_free = false; + while (read_bytes != size) { + const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes, + size - read_bytes); + if (cur_read_bytes > 0) { + read_bytes += (size_t)cur_read_bytes; + assert(read_bytes <= size); + } + if (cur_read_bytes < 0) { +#ifdef HAVE_UV_TRANSLATE_SYS_ERROR + const int error = uv_translate_sys_error(errno); +#else + const int error = -errno; + STATIC_ASSERT(-EINTR == UV_EINTR, "Need to translate error codes"); + STATIC_ASSERT(-EAGAIN == UV_EAGAIN, "Need to translate error codes"); + STATIC_ASSERT(-ENOMEM == UV_ENOMEM, "Need to translate error codes"); +#endif + errno = 0; + if (error == UV_EINTR || error == UV_EAGAIN) { + continue; + } else if (error == UV_ENOMEM && !did_try_to_free) { + try_to_free_memory(); + did_try_to_free = true; + continue; + } else { + return (ptrdiff_t)error; + } + } + if (cur_read_bytes == 0) { + *ret_eof = true; + break; + } + } + return (ptrdiff_t)read_bytes; +} + +#ifdef HAVE_READV +/// Read from a file to multiple buffers at once +/// +/// Wrapper for readv(). +/// +/// @param[in] fd File descriptor to read from. +/// @param[out] ret_eof Is set to true if EOF was encountered, otherwise set +/// to false. Initial value is ignored. +/// @param[out] iov Description of buffers to write to. Note: this description +/// may change, it is incorrect to use data it points to after +/// os_readv(). +/// @param[in] iov_size Number of buffers in iov. +ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) + FUNC_ATTR_NONNULL_ALL +{ + *ret_eof = false; + size_t read_bytes = 0; + bool did_try_to_free = false; + size_t toread = 0; + for (size_t i = 0; i < iov_size; i++) { + // Overflow, trying to read too much data + assert(toread <= SIZE_MAX - iov[i].iov_len); + toread += iov[i].iov_len; + } + while (read_bytes < toread && iov_size && !*ret_eof) { + ptrdiff_t cur_read_bytes = readv(fd, iov, (int)iov_size); + if (toread && cur_read_bytes == 0) { + *ret_eof = true; + } + if (cur_read_bytes > 0) { + read_bytes += (size_t)cur_read_bytes; + while (iov_size && cur_read_bytes) { + if (cur_read_bytes < (ptrdiff_t)iov->iov_len) { + iov->iov_len -= (size_t)cur_read_bytes; + iov->iov_base = (char *)iov->iov_base + cur_read_bytes; + cur_read_bytes = 0; + } else { + cur_read_bytes -= (ptrdiff_t)iov->iov_len; + iov_size--; + iov++; + } + } + } else if (cur_read_bytes < 0) { +#ifdef HAVE_UV_TRANSLATE_SYS_ERROR + const int error = uv_translate_sys_error(errno); +#else + const int error = -errno; + STATIC_ASSERT(-EINTR == UV_EINTR, "Need to translate error codes"); + STATIC_ASSERT(-EAGAIN == UV_EAGAIN, "Need to translate error codes"); + STATIC_ASSERT(-ENOMEM == UV_ENOMEM, "Need to translate error codes"); +#endif + errno = 0; + if (error == UV_EINTR || error == UV_EAGAIN) { + continue; + } else if (error == UV_ENOMEM && !did_try_to_free) { + try_to_free_memory(); + did_try_to_free = true; + continue; + } else { + return (ptrdiff_t)error; + } + } + } + return (ptrdiff_t)read_bytes; +} +#endif // HAVE_READV + +/// Write to a file +/// +/// @param[in] fd File descriptor to write to. +/// @param[in] buf Data to write. May be NULL if size is zero. +/// @param[in] size Amount of bytes to write. +/// +/// @return Number of bytes written or libuv error code (< 0). +ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (buf == NULL) { + assert(size == 0); + return 0; + } + size_t written_bytes = 0; + while (written_bytes != size) { + const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes, + size - written_bytes); + if (cur_written_bytes > 0) { + written_bytes += (size_t)cur_written_bytes; + } + if (cur_written_bytes < 0) { +#ifdef HAVE_UV_TRANSLATE_SYS_ERROR + const int error = uv_translate_sys_error(errno); +#else + const int error = -errno; + STATIC_ASSERT(-EINTR == UV_EINTR, "Need to translate error codes"); + STATIC_ASSERT(-EAGAIN == UV_EAGAIN, "Need to translate error codes"); + // According to the man page open() may fail with ENOMEM, but write() + // can’t. +#endif + errno = 0; + if (error == UV_EINTR || error == UV_EAGAIN) { + continue; + } else { + return error; + } + } + if (cur_written_bytes == 0) { + return UV_UNKNOWN; + } + } + return (ptrdiff_t)written_bytes; +} + /// Flushes file modifications to disk. /// /// @param fd the file descriptor of the file to flush to disk. @@ -339,9 +542,8 @@ int os_open(const char* path, int flags, int mode) /// @return `0` on success, a libuv error code on failure. int os_fsync(int fd) { - uv_fs_t fsync_req; - int r = uv_fs_fsync(&fs_loop, &fsync_req, fd, NULL); - uv_fs_req_cleanup(&fsync_req); + int r; + RUN_UV_FS_FUNC(r, uv_fs_fsync, fd, NULL); return r; } @@ -379,16 +581,9 @@ int32_t os_getperm(const char_u *name) int os_setperm(const char_u *name, int perm) FUNC_ATTR_NONNULL_ALL { - uv_fs_t request; - int result = uv_fs_chmod(&fs_loop, &request, - (const char*)name, perm, NULL); - uv_fs_req_cleanup(&request); - - if (result == kLibuvSuccess) { - return OK; - } - - return FAIL; + int r; + RUN_UV_FS_FUNC(r, uv_fs_chmod, (const char *)name, perm, NULL); + return (r == kLibuvSuccess ? OK : FAIL); } /// Changes the ownership of the file referred to by the open file descriptor. @@ -397,24 +592,21 @@ int os_setperm(const char_u *name, int perm) /// /// @note If the `owner` or `group` is specified as `-1`, then that ID is not /// changed. -int os_fchown(int file_descriptor, uv_uid_t owner, uv_gid_t group) - FUNC_ATTR_NONNULL_ALL +int os_fchown(int fd, uv_uid_t owner, uv_gid_t group) { - uv_fs_t request; - int result = uv_fs_fchown(&fs_loop, &request, file_descriptor, - owner, group, NULL); - uv_fs_req_cleanup(&request); - return result; + int r; + RUN_UV_FS_FUNC(r, uv_fs_fchown, fd, owner, group, NULL); + return r; } -/// Check if a file exists. +/// Check if a path exists. /// -/// @return `true` if `name` exists. -bool os_file_exists(const char_u *name) +/// @return `true` if `path` exists +bool os_path_exists(const char_u *path) FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; - return os_stat((char *)name, &statbuf) == kLibuvSuccess; + return os_stat((char *)path, &statbuf) == kLibuvSuccess; } /// Check if a file is readable. @@ -423,9 +615,8 @@ bool os_file_exists(const char_u *name) bool os_file_is_readable(const char *name) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - uv_fs_t req; - int r = uv_fs_access(&fs_loop, &req, name, R_OK, NULL); - uv_fs_req_cleanup(&req); + int r; + RUN_UV_FS_FUNC(r, uv_fs_access, name, R_OK, NULL); return (r == 0); } @@ -437,9 +628,8 @@ bool os_file_is_readable(const char *name) int os_file_is_writable(const char *name) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - uv_fs_t req; - int r = uv_fs_access(&fs_loop, &req, name, W_OK, NULL); - uv_fs_req_cleanup(&req); + int r; + RUN_UV_FS_FUNC(r, uv_fs_access, name, W_OK, NULL); if (r == 0) { return os_isdir((char_u *)name) ? 2 : 1; } @@ -452,16 +642,10 @@ int os_file_is_writable(const char *name) int os_rename(const char_u *path, const char_u *new_path) FUNC_ATTR_NONNULL_ALL { - uv_fs_t request; - int result = uv_fs_rename(&fs_loop, &request, - (const char *)path, (const char *)new_path, NULL); - uv_fs_req_cleanup(&request); - - if (result == kLibuvSuccess) { - return OK; - } - - return FAIL; + int r; + RUN_UV_FS_FUNC(r, uv_fs_rename, (const char *)path, (const char *)new_path, + NULL); + return (r == kLibuvSuccess ? OK : FAIL); } /// Make a directory. @@ -470,10 +654,9 @@ int os_rename(const char_u *path, const char_u *new_path) int os_mkdir(const char *path, int32_t mode) FUNC_ATTR_NONNULL_ALL { - uv_fs_t request; - int result = uv_fs_mkdir(&fs_loop, &request, path, mode, NULL); - uv_fs_req_cleanup(&request); - return result; + int r; + RUN_UV_FS_FUNC(r, uv_fs_mkdir, path, mode, NULL); + return r; } /// Make a directory, with higher levels when needed @@ -555,10 +738,9 @@ int os_mkdtemp(const char *template, char *path) int os_rmdir(const char *path) FUNC_ATTR_NONNULL_ALL { - uv_fs_t request; - int result = uv_fs_rmdir(&fs_loop, &request, path, NULL); - uv_fs_req_cleanup(&request); - return result; + int r; + RUN_UV_FS_FUNC(r, uv_fs_rmdir, path, NULL); + return r; } /// Opens a directory. @@ -600,10 +782,9 @@ void os_closedir(Directory *dir) int os_remove(const char *path) FUNC_ATTR_NONNULL_ALL { - uv_fs_t request; - int result = uv_fs_unlink(&fs_loop, &request, path, NULL); - uv_fs_req_cleanup(&request); - return result; + int r; + RUN_UV_FS_FUNC(r, uv_fs_unlink, path, NULL); + return r; } /// Get the file information for a given path diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 7687b14f02..0c46dc96ee 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -60,7 +60,7 @@ void input_start(int fd) } global_fd = fd; - rstream_init_fd(&loop, &read_stream, fd, READ_BUFFER_SIZE, NULL); + rstream_init_fd(&main_loop, &read_stream, fd, READ_BUFFER_SIZE, NULL); rstream_start(&read_stream, read_cb); } @@ -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(loop.events)); - queue_put(loop.events, cursorhold_event, 0); + assert(!events_enabled || queue_empty(main_loop.events)); + queue_put(main_loop.events, cursorhold_event, 0); } // Low level input function @@ -147,7 +147,7 @@ bool os_char_avail(void) void os_breakcheck(void) { if (!got_int) { - loop_poll_events(&loop, 0); + loop_poll_events(&main_loop, 0); } } @@ -322,7 +322,7 @@ static bool input_poll(int ms) prof_inchar_enter(); } - LOOP_PROCESS_EVENTS_UNTIL(&loop, NULL, ms, input_ready() || input_eof); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof); if (do_profiling == PROF_YES && ms) { prof_inchar_exit(); @@ -419,5 +419,5 @@ static void read_error_exit(void) static bool pending_events(void) { - return events_enabled && !queue_empty(loop.events); + return events_enabled && !queue_empty(main_loop.events); } diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index eee0cdd10b..5e164b54a5 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -46,4 +46,10 @@ /// negative libuv error codes are returned by a number of os functions. #define os_strerror uv_strerror +#ifdef WIN32 +# define os_strtok strtok_s +#else +# define os_strtok strtok_r +#endif + #endif // NVIM_OS_OS_DEFS_H diff --git a/src/nvim/os/pty_process.h b/src/nvim/os/pty_process.h new file mode 100644 index 0000000000..94923499ca --- /dev/null +++ b/src/nvim/os/pty_process.h @@ -0,0 +1,9 @@ +#ifndef NVIM_OS_PTY_PROCESS_H +#define NVIM_OS_PTY_PROCESS_H + +#ifdef WIN32 +# include "nvim/os/pty_process_win.h" +#else +# include "nvim/os/pty_process_unix.h" +#endif +#endif // NVIM_OS_PTY_PROCESS_H diff --git a/src/nvim/event/pty_process.c b/src/nvim/os/pty_process_unix.c index 8eef72f12f..436de030ba 100644 --- a/src/nvim/event/pty_process.c +++ b/src/nvim/os/pty_process_unix.c @@ -26,11 +26,12 @@ #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" #include "nvim/event/process.h" -#include "nvim/event/pty_process.h" +#include "nvim/os/pty_process_unix.h" #include "nvim/log.h" +#include "nvim/os/os.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/pty_process.c.generated.h" +# include "os/pty_process_unix.c.generated.h" #endif bool pty_process_spawn(PtyProcess *ptyproc) @@ -44,7 +45,7 @@ bool pty_process_spawn(PtyProcess *ptyproc) Process *proc = (Process *)ptyproc; assert(!proc->err); uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); - ptyproc->winsize = (struct winsize){ptyproc->height, ptyproc->width, 0, 0}; + ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; uv_disable_stdio_inheritance(); int master; int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize); @@ -86,11 +87,10 @@ error: return false; } -void pty_process_resize(PtyProcess *ptyproc, uint16_t width, - uint16_t height) +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) FUNC_ATTR_NONNULL_ALL { - ptyproc->winsize = (struct winsize){height, width, 0, 0}; + ptyproc->winsize = (struct winsize){ height, width, 0, 0 }; ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize); } @@ -132,6 +132,12 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL signal(SIGTERM, SIG_DFL); signal(SIGALRM, SIG_DFL); + Process *proc = (Process *)ptyproc; + if (proc->cwd && os_chdir(proc->cwd) != 0) { + fprintf(stderr, "chdir failed: %s\n", strerror(errno)); + return; + } + setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); execvp(ptyproc->process.argv[0], ptyproc->process.argv); fprintf(stderr, "execvp failed: %s\n", strerror(errno)); diff --git a/src/nvim/event/pty_process.h b/src/nvim/os/pty_process_unix.h index 446d7fd3c8..f7c57b3839 100644 --- a/src/nvim/event/pty_process.h +++ b/src/nvim/os/pty_process_unix.h @@ -1,5 +1,5 @@ -#ifndef NVIM_EVENT_PTY_PROCESS_H -#define NVIM_EVENT_PTY_PROCESS_H +#ifndef NVIM_OS_PTY_PROCESS_UNIX_H +#define NVIM_OS_PTY_PROCESS_UNIX_H #include <sys/ioctl.h> @@ -25,6 +25,7 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) } #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/pty_process.h.generated.h" +# include "os/pty_process_unix.h.generated.h" #endif -#endif // NVIM_EVENT_PTY_PROCESS_H + +#endif // NVIM_OS_PTY_PROCESS_UNIX_H diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h new file mode 100644 index 0000000000..20cc589925 --- /dev/null +++ b/src/nvim/os/pty_process_win.h @@ -0,0 +1,28 @@ +#ifndef NVIM_OS_PTY_PROCESS_WIN_H +#define NVIM_OS_PTY_PROCESS_WIN_H + +#include "nvim/event/libuv_process.h" + +typedef struct pty_process { + Process process; + char *term_name; + uint16_t width, height; +} PtyProcess; + +#define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) +#define pty_process_close(job) libuv_process_close((LibuvProcess *)job) +#define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) +#define pty_process_resize(job, width, height) +#define pty_process_teardown(loop) + +static inline PtyProcess pty_process_init(Loop *loop, void *data) +{ + PtyProcess rv; + rv.process = process_init(loop, kProcessTypePty, data); + rv.term_name = NULL; + rv.width = 80; + rv.height = 24; + return rv; +} + +#endif // NVIM_OS_PTY_PROCESS_WIN_H diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index f5a1637c94..64c673930a 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -14,6 +14,7 @@ #include "nvim/os/shell.h" #include "nvim/os/signal.h" #include "nvim/types.h" +#include "nvim/main.h" #include "nvim/vim.h" #include "nvim/message.h" #include "nvim/memory.h" @@ -205,16 +206,16 @@ static int do_os_system(char **argv, xstrlcpy(prog, argv[0], MAXPATHL); Stream in, out, err; - LibuvProcess uvproc = libuv_process_init(&loop, &buf); + LibuvProcess uvproc = libuv_process_init(&main_loop, &buf); Process *proc = &uvproc.process; - Queue *events = queue_new_child(loop.events); + Queue *events = queue_new_child(main_loop.events); proc->events = events; proc->argv = argv; proc->in = input != NULL ? &in : NULL; proc->out = &out; proc->err = &err; if (!process_spawn(proc)) { - loop_poll_events(&loop, 0); + loop_poll_events(&main_loop, 0); // Failed, probably due to `sh` not being executable if (!silent) { MSG_PUTS(_("\nCannot execute ")); @@ -309,25 +310,70 @@ static void system_data_cb(Stream *stream, RBuffer *buf, size_t count, dbuf->len += nread; } +/// Continue to append data to last screen line. +/// +/// @param output Data to append to screen lines. +/// @param remaining Size of data. +/// @param new_line If true, next data output will be on a new line. +static void append_to_screen_end(char *output, size_t remaining, bool new_line) +{ + // Column of last row to start appending data to. + static colnr_T last_col = 0; + + size_t off = 0; + int last_row = (int)Rows - 1; + + while (off < remaining) { + // Found end of line? + if (output[off] == NL) { + // Can we start a new line or do we need to continue the last one? + if (last_col == 0) { + screen_del_lines(0, 0, 1, (int)Rows, NULL); + } + screen_puts_len((char_u *)output, (int)off, last_row, last_col, 0); + last_col = 0; + + size_t skip = off + 1; + output += skip; + remaining -= skip; + off = 0; + continue; + } + + // Translate NUL to SOH + if (output[off] == NUL) { + output[off] = 1; + } + + off++; + } + + if (remaining) { + if (last_col == 0) { + screen_del_lines(0, 0, 1, (int)Rows, NULL); + } + screen_puts_len((char_u *)output, (int)remaining, last_row, last_col, 0); + last_col += (colnr_T)remaining; + } + + if (new_line) { + last_col = 0; + } + + ui_flush(); +} + static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { + // We always output the whole buffer, so the buffer can never + // wrap around. size_t cnt; char *ptr = rbuffer_read_ptr(buf, &cnt); - if (!cnt) { - return; - } - - size_t written = write_output(ptr, cnt, false, eof); - // No output written, force emptying the Rbuffer if it is full. - if (!written && rbuffer_size(buf) == rbuffer_capacity(buf)) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - screen_puts_len((char_u *)ptr, (int)cnt, (int)Rows - 1, 0, 0); - written = cnt; - } - if (written) { - rbuffer_consumed(buf, written); + append_to_screen_end(ptr, cnt, eof); + if (cnt) { + rbuffer_consumed(buf, cnt); } } diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 0ff6016e32..4abc9cfc36 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -8,6 +8,7 @@ #include "nvim/globals.h" #include "nvim/memline.h" #include "nvim/eval.h" +#include "nvim/main.h" #include "nvim/memory.h" #include "nvim/misc1.h" #include "nvim/misc2.h" @@ -28,10 +29,10 @@ static bool rejecting_deadly; void signal_init(void) { - signal_watcher_init(&loop, &spipe, NULL); - signal_watcher_init(&loop, &shup, NULL); - signal_watcher_init(&loop, &squit, NULL); - signal_watcher_init(&loop, &sterm, NULL); + signal_watcher_init(&main_loop, &spipe, NULL); + signal_watcher_init(&main_loop, &shup, NULL); + signal_watcher_init(&main_loop, &squit, NULL); + signal_watcher_init(&main_loop, &sterm, NULL); #ifdef SIGPIPE signal_watcher_start(&spipe, on_signal, SIGPIPE); #endif @@ -41,7 +42,7 @@ void signal_init(void) #endif signal_watcher_start(&sterm, on_signal, SIGTERM); #ifdef SIGPWR - signal_watcher_init(&loop, &spwr, NULL); + signal_watcher_init(&main_loop, &spwr, NULL); signal_watcher_start(&spwr, on_signal, SIGPWR); #endif } diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 188f0802c9..2205ad0958 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -9,6 +9,7 @@ #include "nvim/os/time.h" #include "nvim/event/loop.h" #include "nvim/vim.h" +#include "nvim/main.h" static uv_mutex_t delay_mutex; static uv_cond_t delay_cond; @@ -43,7 +44,7 @@ void os_delay(uint64_t milliseconds, bool ignoreinput) if (milliseconds > INT_MAX) { milliseconds = INT_MAX; } - LOOP_PROCESS_EVENTS_UNTIL(&loop, NULL, (int)milliseconds, got_int); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int); } else { os_microdelay(milliseconds * 1000); } diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 2ed0c2c856..def7e3b0e5 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -566,10 +566,11 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, /* * Move the file names to allocated memory. */ - for (j = 0, i = 0; i < *num_file; ++i) { - /* Require the files to exist. Helps when using /bin/sh */ - if (!(flags & EW_NOTFOUND) && !os_file_exists((*file)[i])) + for (j = 0, i = 0; i < *num_file; i++) { + // Require the files to exist. Helps when using /bin/sh + if (!(flags & EW_NOTFOUND) && !os_path_exists((*file)[i])) { continue; + } /* check if this entry should be included */ dir = (os_isdir((*file)[i])); diff --git a/src/nvim/path.c b/src/nvim/path.c index 41fd69f238..57499429ec 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -684,7 +684,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, } // add existing file or symbolic link if ((flags & EW_ALLLINKS) ? os_fileinfo_link((char *)buf, &file_info) - : os_file_exists(buf)) { + : os_path_exists(buf)) { addfile(gap, buf, flags); } } @@ -1205,10 +1205,11 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, /* When EW_NOTFOUND is used, always add files and dirs. Makes * "vim c:/" work. */ - if (flags & EW_NOTFOUND) + if (flags & EW_NOTFOUND) { addfile(&ga, t, flags | EW_DIR | EW_FILE); - else if (os_file_exists(t)) + } else if (os_path_exists(t)) { addfile(&ga, t, flags); + } xfree(t); } @@ -1327,7 +1328,7 @@ void addfile( if (!(flags & EW_NOTFOUND) && ((flags & EW_ALLLINKS) ? !os_fileinfo_link((char *)f, &file_info) - : !os_file_exists(f))) { + : !os_path_exists(f))) { return; } diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po index 5b0cb2260b..6bc76506ae 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Esperanto)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-30 17:54+0200\n" -"PO-Revision-Date: 2015-07-30 18:00+0200\n" +"POT-Creation-Date: 2016-02-13 23:42+0100\n" +"PO-Revision-Date: 2016-02-13 23:45+0100\n" "Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: eo\n" @@ -504,10 +504,6 @@ msgstr "E686: Argumento de %s devas esti Listo" msgid "E712: Argument of %s must be a List or Dictionary" msgstr "E712: Argumento de %s devas esti Listo aÅ Vortaro" -#: ../eval.c:144 -msgid "E713: Cannot use empty key for Dictionary" -msgstr "E713: Ne eblas uzi malplenan Ålosilon de Vortaro" - #: ../eval.c:145 msgid "E714: List required" msgstr "E714: Listo bezonata" @@ -657,6 +653,9 @@ msgstr "E110: Mankas ')'" msgid "E695: Cannot index a Funcref" msgstr "E695: Ne eblas indeksi Funcref" +msgid "E909: Cannot index a special variable" +msgstr "E909: Ne eblas indeksi specialan variablon" + #: ../eval.c:4839 #, c-format msgid "E112: Option name missing: %s" @@ -689,7 +688,7 @@ msgstr "E697: Mankas fino de Listo ']': %s" #: ../eval.c:5750 msgid "Not enough memory to set references, garbage collection aborted!" -msgstr "Ne sufiĉa memory por valorigi referencojn, senrubigado ĉesigita!" +msgstr "Ne sufiĉa memoro por valorigi referencojn, senrubigado ĉesigita!" #: ../eval.c:6475 #, c-format @@ -874,6 +873,18 @@ msgstr "E745: Uzo de Listo kiel Nombro" msgid "E728: Using a Dictionary as a Number" msgstr "E728: Uzo de Vortaro kiel Nombro" +msgid "E891: Using a Funcref as a Float" +msgstr "E891: Uzo de Funcref kiel Glitpunktnombro" + +msgid "E892: Using a String as a Float" +msgstr "E892: Uzo de Ĉeno kiel Glitpunktnombro" + +msgid "E893: Using a List as a Float" +msgstr "E893: Uzo de Listo kiel Glitpunktnombro" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: Uzo de Vortaro kiel Glitpunktnombro" + #: ../eval.c:16259 msgid "E729: using Funcref as a String" msgstr "E729: uzo de Funcref kiel Ĉeno" @@ -886,6 +897,9 @@ msgstr "E730: uzo de Listo kiel Ĉeno" msgid "E731: using Dictionary as a String" msgstr "E731: uzo de Vortaro kiel Ĉeno" +msgid "E908: using an invalid value as a String" +msgstr "E908: uzo de nevalida valoro kiel Ĉeno" + #: ../eval.c:16619 #, c-format msgid "E706: Variable type mismatch for: %s" @@ -1391,6 +1405,13 @@ msgstr "linio %<PRId64>: %s" msgid "cmd: %s" msgstr "kmd: %s" +msgid "frame is zero" +msgstr "kadro estas nul" + +#, c-format +msgid "frame at highest level: %d" +msgstr "kadro je la plej alta nivelo: %d" + #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" @@ -2861,6 +2882,9 @@ msgstr "E46: Ne eblas ÅanÄi nurlegeblan variablon \"%s\"" msgid "E794: Cannot set variable in the sandbox: \"%s\"" msgstr "E794: Ne eblas agordi variablon en la sabloludejo: \"%s\"" +msgid "E713: Cannot use empty key for Dictionary" +msgstr "E713: Ne eblas uzi malplenan Ålosilon de Vortaro" + #: ../globals.h:1076 msgid "E47: Error while reading errorfile" msgstr "E47: Eraro dum legado de erardosiero" @@ -4090,12 +4114,12 @@ msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" " be careful not to end up with two different instances of the same\n" -" file when making changes." +" file when making changes. Quit, or continue with caution.\n" msgstr "" "\n" -"(1) Alia programo eble redaktas la saman dosieron.\n" -" Se jes, estu singarda por ne havi du malsamajn\n" -" aperojn de la sama dosiero, kiam vi faros ÅanÄojn." +"(1) Alia programo eble redaktas la saman dosieron. Se jes, estu singarda\n" +" por ne havi du malsamajn aperojn de la sama dosiero, kiam vi faros\n" +" ÅanÄojn. Eliru aÅ daÅrigu singarde.\n" #: ../memline.c:3245 msgid " Quit, or continue with caution.\n" @@ -4792,6 +4816,15 @@ msgstr "" "\n" "Ne povis Åalti kuntekston de sekureco por " +#, c-format +msgid "Could not set security context %s for %s" +msgstr "Ne povis Åalti kuntekston de sekureco %s por %s" + +#, c-format +msgid "Could not get security context %s for %s. Removing it!" +msgstr "" +"Ne povis akiri kuntekston de sekureco %s por %s. Gi nun estas forigata!" + #: ../os_unix.c:1558 ../os_unix.c:1647 #, c-format msgid "dlerror = \"%s\"" @@ -5717,6 +5750,9 @@ msgstr "Neniu sintaksa elemento difinita por tiu bufro" msgid "E390: Illegal argument: %s" msgstr "E390: Nevalida argumento: %s" +msgid "syntax iskeyword " +msgstr "sintakso iskeyword " + #: ../syntax.c:3299 #, c-format msgid "E391: No such syntax cluster: %s" @@ -5813,6 +5849,10 @@ msgstr "E847: Tro da sintaksaj inkluzivoj" msgid "E789: Missing ']': %s" msgstr "E789: Mankas ']': %s" +#, c-format +msgid "E890: trailing char after ']': %s]%s" +msgstr "E890: vosta signo post ']': %s]%s" + #: ../syntax.c:4531 #, c-format msgid "E398: Missing '=': %s" diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po index 171e155689..084102da60 100644 --- a/src/nvim/po/it.po +++ b/src/nvim/po/it.po @@ -13,13 +13,13 @@ msgid "" msgstr "" "Project-Id-Version: vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-11 20:58+0200\n" -"PO-Revision-Date: 2015-08-11 22:02+0200\n" -"Last-Translator: Vlad Sandrini <vlad.gently@gmail.com>\n" -"Language-Team: Italian Antonio Colombo <azc100@gmail." -"com> Vlad Sandrini <vlad.gently@gmail." -"com> Luciano Montanaro <mikelima@cirulla.net>\n" -"Language: \n" +"POT-Creation-Date: 2016-02-11 12:10+0100\n" +"PO-Revision-Date: 2016-02-11 14:42+0200\n" +"Last-Translator: Antonio Colombo <azc100@gmail.com>\n" +"Language-Team: Antonio Colombo <azc100@gmail.com>" +" Vlad Sandrini <vlad.gently@gmail.com" +" Luciano Montanaro <mikelima@cirulla.net>\n" +"Language: Italian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO_8859-1\n" "Content-Transfer-Encoding: 8-bit\n" @@ -491,10 +491,6 @@ msgstr "E686: L'argomento di %s deve essere una Lista" msgid "E712: Argument of %s must be a List or Dictionary" msgstr "E712: L'argomento di %s deve essere una Lista o un Dizionario" -#: ../eval.c:144 -msgid "E713: Cannot use empty key for Dictionary" -msgstr "E713: Non posso usare una chiave nulla per il Dizionario" - #: ../eval.c:145 msgid "E714: List required" msgstr "E714: È necessaria una Lista" @@ -548,7 +544,7 @@ msgstr "E461: Nome di variabile non ammesso: %s" # nuovo #: ../eval.c:157 msgid "E806: using Float as a String" -msgstr "E806: uso di un numero con virgola come stringa" +msgstr "E806: uso di un Numero-a-virgola-mobile come Stringa" #: ../eval.c:1830 msgid "E687: Less targets than List items" @@ -635,7 +631,7 @@ msgstr "E694: Operazione non valida per Funcref" #: ../eval.c:4277 msgid "E804: Cannot use '%' with Float" -msgstr "E804: Non si può usare '%' con un numero con virgola" +msgstr "E804: Non si può usare '%' con un Numero-a-virgola-mobile" #: ../eval.c:4478 msgid "E110: Missing ')'" @@ -645,6 +641,9 @@ msgstr "E110: Manca ')'" msgid "E695: Cannot index a Funcref" msgstr "E695: Non posso indicizzare un Funcref" +msgid "E909: Cannot index a special variable" +msgstr "E909: Non posso indicizzare una variabile speciale" + #: ../eval.c:4839 #, c-format msgid "E112: Option name missing: %s" @@ -736,7 +735,7 @@ msgstr "E725: Chiamata di funzione dict in assenza di Dizionario: %s" #: ../eval.c:7453 msgid "E808: Number or Float required" -msgstr "E808: Ci vuole un numero intero o con virgola" +msgstr "E808: Ci vuole un Numero o un Numero-a-virgola-mobile" #: ../eval.c:7503 msgid "add() argument" @@ -847,7 +846,7 @@ msgstr "E677: Errore in scrittura su file temporaneo" #: ../eval.c:16159 msgid "E805: Using a Float as a Number" -msgstr "E805: Uso di un numero con virgola come intero" +msgstr "E805: Uso di un Numero-a-virgola-mobile come Numero" #: ../eval.c:16162 msgid "E703: Using a Funcref as a Number" @@ -861,6 +860,18 @@ msgstr "E745: Uso di Lista come Numero" msgid "E728: Using a Dictionary as a Number" msgstr "E728: Uso di Dizionario come Numero" +msgid "E891: Using a Funcref as a Float" +msgstr "E891: Uso di Funcref come Numero-a-virgola-mobile" + +msgid "E892: Using a String as a Float" +msgstr "E892: Uso di Stringa come Numero-a-virgola-mobile" + +msgid "E893: Using a List as a Float" +msgstr "E893: Uso di Lista come Numero-a-virgola-mobile" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: Uso di Dizionario come Numero-a-virgola-mobile" + #: ../eval.c:16259 msgid "E729: using Funcref as a String" msgstr "E729: uso di Funcref come Stringa" @@ -873,6 +884,10 @@ msgstr "E730: uso di Lista come Stringa" msgid "E731: using Dictionary as a String" msgstr "E731: uso di Dizionario come Stringa" +# nuovo +msgid "E908: using an invalid value as a String" +msgstr "E908: uso di un valore non valido come Stringa" + #: ../eval.c:16619 #, c-format msgid "E706: Variable type mismatch for: %s" @@ -960,12 +975,14 @@ msgid "E129: Function name required" msgstr "E129: Nome funzione necessario" #: ../eval.c:17824 +#, c-format msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: Il nome funzione deve iniziare con una maiuscola o \"s:\": %s" +msgstr "E128: Il nome funzione deve iniziare con maiuscola o \"s:\": %s" #: ../eval.c:17833 +#, c-format msgid "E884: Function name cannot contain a colon: %s" -msgstr "E884: Il nome funzione non può contenere una virgola: %s" +msgstr "E884: Il nome della funzione non può contenere un due punti: %s" #: ../eval.c:18336 #, c-format @@ -1382,6 +1399,13 @@ msgstr "riga %<PRId64>: %s" msgid "cmd: %s" msgstr "com: %s" +msgid "frame is zero" +msgstr "al livello zero" + +#, c-format +msgid "frame at highest level: %d" +msgstr "al livello più alto: %d" + #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" @@ -1422,8 +1446,7 @@ msgstr "E162: Buffer \"%s\" non salvato dopo modifica" #: ../ex_cmds2.c:1480 msgid "Warning: Entered other buffer unexpectedly (check autocommands)" msgstr "" -"Avviso: Entrato in altro buffer inaspettatamente (controllare " -"autocomandi)" +"Avviso: Entrato in altro buffer inaspettatamente (controllare autocomandi)" #: ../ex_cmds2.c:1826 msgid "E163: There is only one file to edit" @@ -2301,19 +2324,19 @@ msgstr "[in formato DOS]" #: ../fileio.c:3801 msgid "[mac]" -msgstr "[MAC]" +msgstr "[Mac]" #: ../fileio.c:3801 msgid "[mac format]" -msgstr "[in formato MAC]" +msgstr "[in formato Mac]" #: ../fileio.c:3807 msgid "[unix]" -msgstr "[UNIX]" +msgstr "[Unix]" #: ../fileio.c:3807 msgid "[unix format]" -msgstr "[in formato UNIX]" +msgstr "[in formato Unix]" #: ../fileio.c:3831 msgid "1 line, " @@ -2864,6 +2887,9 @@ msgstr "E46: Non posso cambiare la variabile read-only \"%s\"" msgid "E794: Cannot set variable in the sandbox: \"%s\"" msgstr "E794: Non posso impostare la variabile read-only in ambiente protetto: \"%s\"" +msgid "E713: Cannot use empty key for Dictionary" +msgstr "E713: Non posso usare una chiave nulla per il Dizionario" + #: ../globals.h:1076 msgid "E47: Error while reading errorfile" msgstr "E47: Errore leggendo il file errori" @@ -4087,12 +4113,12 @@ msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" " be careful not to end up with two different instances of the same\n" -" file when making changes." +" file when making changes. Quit, or continue with caution.\n" msgstr "" "\n" -"(1) Un altro programma può essere in edit sullo stesso file.\n" -" Se è così, attenzione a non trovarti con due versioni\n" -" differenti dello stesso file a cui vengono apportate modifiche." +"(1) Un altro programma può essere in edit sullo stesso file. Se è così,\n" +" attenzione a non finire con due sessioni differenti che modificano lo\n" +" stesso file. Uscire da Vim, o continuare con cautela.\n" #: ../memline.c:3245 msgid " Quit, or continue with caution.\n" @@ -4335,7 +4361,7 @@ msgstr "E766: Argomenti non sufficienti per printf()" #: ../message.c:3119 msgid "E807: Expected Float argument for printf()" -msgstr "E807: Numero con virgola atteso come argomento per printf()" +msgstr "E807: Numero-a-virgola-mobile atteso come argomento per printf()" #: ../message.c:3873 msgid "E767: Too many arguments to printf()" @@ -4526,7 +4552,8 @@ msgstr "E574: Tipo di registro sconosciuto: %d" msgid "" "E883: search pattern and expression register may not contain two or more " "lines" -msgstr "E883: espressione di ricerca e registro dell'espressione non possono " +msgstr "" +"E883: espressione di ricerca e registro dell'espressione non possono " "contenere due o più righe" #: ../ops.c:5089 @@ -4794,6 +4821,14 @@ msgstr "" "\n" "Non posso impostare il contesto di sicurezza per " +#, c-format +msgid "Could not set security context %s for %s" +msgstr "Non posso impostare il contesto di sicurezza %s per %s" + +#, c-format +msgid "Could not get security context %s for %s. Removing it!" +msgstr "Non posso ottenere il contesto di sicurezza %s per %s. Lo rimuovo!" + #: ../os_unix.c:1558 ../os_unix.c:1647 #, c-format msgid "dlerror = \"%s\"" @@ -4890,6 +4925,7 @@ msgid "E777: String or List expected" msgstr "E777: aspettavo Stringa o Lista" #: ../regexp.c:359 +#, c-format msgid "E369: invalid item in %s%%[]" msgstr "E369: elemento non valido in %s%%[]" @@ -5005,6 +5041,7 @@ msgid "External submatches:\n" msgstr "Sotto-corrispondenze esterne:\n" #: ../regexp.c:2470 +#, c-format msgid "E888: (NFA regexp) cannot repeat %s" msgstr "E888: (NFA regexp) non riesco a ripetere %s" @@ -5399,8 +5436,7 @@ msgstr "Valore errato per CHECKCOMPOUNDPATTERN in %s riga %d: %s" #: ../spell.c:4847 #, c-format msgid "Different combining flag in continued affix block in %s line %d: %s" -msgstr "" -"Flag combinazione diverso in blocco affissi continuo in %s riga %d: %s" +msgstr "Flag combinazione diverso in blocco affissi continuo in %s riga %d: %s" #: ../spell.c:4850 #, c-format @@ -5639,10 +5675,12 @@ msgid "E765: 'spellfile' does not have %<PRId64> entries" msgstr "E765: 'spellfile' non ha %<PRId64> elementi" #: ../spell.c:8074 +#, c-format msgid "Word '%.*s' removed from %s" msgstr "Parola '%.*s' rimossa da %s" #: ../spell.c:8117 +#, c-format msgid "Word '%.*s' added to %s" msgstr "Parola '%.*s' aggiunta a %s" @@ -5720,6 +5758,9 @@ msgstr "Nessun elemento sintattico definito per questo buffer" msgid "E390: Illegal argument: %s" msgstr "E390: Argomento non ammesso: %s" +msgid "syntax iskeyword " +msgstr "syntax iskeyword " + #: ../syntax.c:3299 #, c-format msgid "E391: No such syntax cluster: %s" @@ -5816,6 +5857,9 @@ msgstr "E847: Troppe inclusioni di sintassi" msgid "E789: Missing ']': %s" msgstr "E789: Manca ']': %s" +msgid "E890: trailing char after ']': %s]%s" +msgstr "E890: Caratteri in più dopo ']': %s]%s" + #: ../syntax.c:4531 #, c-format msgid "E398: Missing '=': %s" diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po index d3061d3c5a..85042e3506 100644 --- a/src/nvim/po/ja.euc-jp.po +++ b/src/nvim/po/ja.euc-jp.po @@ -1,11 +1,11 @@ -# Japanese translation for Vim vim:set foldmethod=marker: +# Japanese translation for Vim # # Do ":help uganda" in Vim to read copying and usage conditions. # Do ":help credits" in Vim to see a list of people who contributed. # -# Last Change: 2013 Jul 06 +# Copyright (C) 2001-2016 MURAOKA Taro <koron.kaoriya@gmail.com>, +# vim-jp (http://vim-jp.org/) # -# Copyright (C) 2001-13 MURAOKA Taro <koron.kaoriya@gmail.com> # THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. # # Generated from ja.po, DO NOT EDIT. @@ -14,10 +14,10 @@ msgid "" msgstr "" "Project-Id-Version: Vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-07-06 15:00+0900\n" +"POT-Creation-Date: 2016-02-01 09:02+0900\n" +"PO-Revision-Date: 2016-02-01 09:08+0900\n" "Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n" -"Language-Team: MURAOKA Taro <koron.kaoriya@gmail.com>\n" +"Language-Team: vim-jp (https://github.com/vim-jp/lang-ja)\n" "Language: Japanese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=euc-jp\n" @@ -34,7 +34,7 @@ msgstr "ÆâÉô¥¨¥é¡¼: ̤ÃΤΥª¥×¥·¥ç¥ó·¿¤Ç¤¹" #: ../buffer.c:92 msgid "[Location List]" -msgstr "[¾ì½ê¥ê¥¹¥È]" +msgstr "[¥í¥±¡¼¥·¥ç¥ó¥ê¥¹¥È]" #: ../buffer.c:93 msgid "[Quickfix List]" @@ -277,7 +277,7 @@ msgstr "E810: °ì»þ¥Õ¥¡¥¤¥ë¤ÎÆÉ¹þ¤â¤·¤¯¤Ï½ñ¹þ¤¬¤Ç¤¤Þ¤»¤ó" #: ../diff.c:755 msgid "E97: Cannot create diffs" -msgstr "E97: º¹Ê¬¤òºîÀ®¤Ç¤¤Þ¤»¤ó " +msgstr "E97: º¹Ê¬¤òºîÀ®¤Ç¤¤Þ¤»¤ó" #: ../diff.c:966 msgid "E816: Cannot read patch output" @@ -293,7 +293,7 @@ msgstr "E99: ¸½ºß¤Î¥Ð¥Ã¥Õ¥¡¤Ïº¹Ê¬¥â¡¼¥É¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó" #: ../diff.c:2100 msgid "E793: No other buffer in diff mode is modifiable" -msgstr "E793: º¹Ê¬¥â¡¼¥É¤Ç¤¢¤ë¾¤Î¥Ð¥Ã¥Õ¥¡¤ÏÊѹ¹²Äǽ¤Ç¤¹" +msgstr "E793: º¹Ê¬¥â¡¼¥É¤Ç¤¢¤ë¾¤Î¥Ð¥Ã¥Õ¥¡¤ÏÊѹ¹¤Ç¤¤Þ¤»¤ó" #: ../diff.c:2102 msgid "E100: No other buffer in diff mode" @@ -349,7 +349,7 @@ msgstr " ¹Ô(Á´ÂÎ)Êä´° (^L^N^P)" #: ../edit.c:86 msgid " File name completion (^F^N^P)" -msgstr "¥Õ¥¡¥¤¥ë̾Êä´° (^F^N^P)" +msgstr " ¥Õ¥¡¥¤¥ë̾Êä´° (^F^N^P)" #: ../edit.c:87 msgid " Tag completion (^]^N^P)" @@ -377,7 +377,7 @@ msgstr " ¥³¥Þ¥ó¥É¥é¥¤¥óÊä´° (^V^N^P)" #: ../edit.c:94 msgid " User defined completion (^U^N^P)" -msgstr " ¥æ¡¼¥¶ÄêµÁÊä´° (^U^N^P)" +msgstr " ¥æ¡¼¥¶¡¼ÄêµÁÊä´° (^U^N^P)" #: ../edit.c:95 msgid " Omni completion (^O^N^P)" @@ -679,6 +679,11 @@ msgstr "E696: ¥ê¥¹¥È·¿¤Ë¥«¥ó¥Þ¤¬¤¢¤ê¤Þ¤»¤ó: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: ¥ê¥¹¥È·¿¤ÎºÇ¸å¤Ë ']' ¤¬¤¢¤ê¤Þ¤»¤ó: %s" +#: ../eval.c:5807 +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "" +"¥¬¡¼¥Ù¥Ã¥¸¥³¥ì¥¯¥·¥ç¥ó¤òÃæ»ß¤·¤Þ¤·¤¿! »²¾È¤òºîÀ®¤¹¤ë¤Î¤Ë¥á¥â¥ê¤¬ÉÔ¤·¤Þ¤·¤¿" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -721,7 +726,7 @@ msgstr "E117: ̤ÃΤδؿô¤Ç¤¹: %s" #: ../eval.c:7383 #, c-format msgid "E119: Not enough arguments for function: %s" -msgstr "E119: ´Ø¿ô¤Î°ú¿ô¤¬¾¯¤Ê²á¤®¤Þ¤¹: %s" +msgstr "E119: ´Ø¿ô¤Î°ú¿ô¤¬Â¤ê¤Þ¤»¤ó: %s" #: ../eval.c:7387 #, c-format @@ -826,18 +831,16 @@ msgid "sort() argument" msgstr "sort() ¤Î°ú¿ô" #: ../eval.c:13721 -#, fuzzy msgid "uniq() argument" -msgstr "add() ¤Î°ú¿ô" +msgstr "uniq() ¤Î°ú¿ô" #: ../eval.c:13776 msgid "E702: Sort compare function failed" msgstr "E702: ¥½¡¼¥È¤ÎÈæ³Ó´Ø¿ô¤¬¼ºÇÔ¤·¤Þ¤·¤¿" #: ../eval.c:13806 -#, fuzzy msgid "E882: Uniq compare function failed" -msgstr "E702: ¥½¡¼¥È¤ÎÈæ³Ó´Ø¿ô¤¬¼ºÇÔ¤·¤Þ¤·¤¿" +msgstr "E882: Uniq ¤ÎÈæ³Ó´Ø¿ô¤¬¼ºÇÔ¤·¤Þ¤·¤¿" #: ../eval.c:14085 msgid "(Invalid)" @@ -863,6 +866,18 @@ msgstr "E745: ¥ê¥¹¥È·¿¤ò¿ôÃͤȤ·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹" msgid "E728: Using a Dictionary as a Number" msgstr "E728: ¼½ñ·¿¤ò¿ôÃͤȤ·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹" +msgid "E891: Using a Funcref as a Float" +msgstr "E891: ´Ø¿ô»²¾È·¿¤òÉâÆ°¾®¿ôÅÀ¿ô¤È¤·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹¡£" + +msgid "E892: Using a String as a Float" +msgstr "E892: ʸ»úÎó¤òÉâÆ°¾®¿ôÅÀ¿ô¤È¤·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹" + +msgid "E893: Using a List as a Float" +msgstr "E893: ¥ê¥¹¥È·¿¤òÉâÆ°¾®¿ôÅÀ¿ô¤È¤·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: ¼½ñ·¿¤òÉâÆ°¾®¿ôÅÀ¿ô¤È¤·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹" + #: ../eval.c:16259 msgid "E729: using Funcref as a String" msgstr "E729: ´Ø¿ô»²¾È·¿¤òʸ»úÎó¤È¤·¤Æ°·¤Ã¤Æ¤¤¤Þ¤¹" @@ -961,14 +976,14 @@ msgid "E129: Function name required" msgstr "E129: ´Ø¿ô̾¤¬Í׵ᤵ¤ì¤Þ¤¹" #: ../eval.c:17824 -#, fuzzy, c-format +#, c-format msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: ´Ø¿ô̾¤ÏÂçʸ»ú¤Ç»Ï¤Þ¤ë¤«¥³¥í¥ó¤ò´Þ¤Þ¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó: %s" +msgstr "E128: ´Ø¿ô̾¤ÏÂçʸ»ú¤« \"s:\" ¤Ç»Ï¤Þ¤é¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó: %s" #: ../eval.c:17833 -#, fuzzy, c-format +#, c-format msgid "E884: Function name cannot contain a colon: %s" -msgstr "E128: ´Ø¿ô̾¤ÏÂçʸ»ú¤Ç»Ï¤Þ¤ë¤«¥³¥í¥ó¤ò´Þ¤Þ¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó: %s" +msgstr "E884: ´Ø¿ô̾¤Ë¤Ï¥³¥í¥ó¤Ï´Þ¤á¤é¤ì¤Þ¤»¤ó: %s" #: ../eval.c:18336 #, c-format @@ -1081,7 +1096,7 @@ msgstr "E136: viminfo: ¥¨¥é¡¼¤¬Â¿²á¤®¤ë¤Î¤Ç, °Ê¹ß¤Ï¥¹¥¥Ã¥×¤·¤Þ¤¹" #: ../ex_cmds.c:1458 #, c-format msgid "Reading viminfo file \"%s\"%s%s%s" -msgstr "viminfo¥Õ¥¡¥¤¥ë \"%s\"%s%s%s ¤òÆÉ¹þ¤ßÃæ " +msgstr "viminfo¥Õ¥¡¥¤¥ë \"%s\"%s%s%s ¤òÆÉ¹þ¤ßÃæ" #: ../ex_cmds.c:1460 msgid " info" @@ -1357,6 +1372,10 @@ msgstr "E158: ̵¸ú¤Ê¥Ð¥Ã¥Õ¥¡Ì¾¤Ç¤¹: %s" msgid "E157: Invalid sign ID: %<PRId64>" msgstr "E157: ̵¸ú¤Êsign¼±Ê̻ҤǤ¹: %<PRId64>" +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: Êѹ¹¤Ç¤¤Ê¤¤ sign ¤Ç¤¹: %s" + #: ../ex_cmds.c:6066 msgid " (not supported)" msgstr " (È󥵥ݡ¼¥È)" @@ -1379,6 +1398,13 @@ msgstr "¹Ô %<PRId64>: %s" msgid "cmd: %s" msgstr "¥³¥Þ¥ó¥É: %s" +msgid "frame is zero" +msgstr "¥Õ¥ì¡¼¥à¤¬ 0 ¤Ç¤¹" + +#, c-format +msgid "frame at highest level: %d" +msgstr "ºÇ¹â¥ì¥Ù¥ë¤Î¥Õ¥ì¡¼¥à: %d" + #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" @@ -1528,7 +1554,8 @@ msgstr "E197: ¸À¸ì¤ò \"%s\" ¤ËÀßÄê¤Ç¤¤Þ¤»¤ó" #. don't wait for return #: ../ex_docmd.c:387 msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." -msgstr "Ex¥â¡¼¥É¤ËÆþ¤ê¤Þ¤¹. ¥Î¡¼¥Þ¥ë¤ËÌá¤ë¤Ë¤Ï\"visual\"¤ÈÆþÎϤ·¤Æ¤¯¤À¤µ¤¤." +msgstr "" +"Ex¥â¡¼¥É¤ËÆþ¤ê¤Þ¤¹. ¥Î¡¼¥Þ¥ë¥â¡¼¥É¤ËÌá¤ë¤Ë¤Ï\"visual\"¤ÈÆþÎϤ·¤Æ¤¯¤À¤µ¤¤." #: ../ex_docmd.c:428 msgid "E501: At end-of-file" @@ -1553,7 +1580,7 @@ msgstr "´Ø¿ô¤ÎºÇ¸å¤Ç¤¹" #: ../ex_docmd.c:1628 msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: ¥æ¡¼¥¶ÄêµÁ¥³¥Þ¥ó¥É¤Î¤¢¤¤¤Þ¤¤¤Ê»ÈÍѤǤ¹" +msgstr "E464: ¥æ¡¼¥¶¡¼ÄêµÁ¥³¥Þ¥ó¥É¤Î¤¢¤¤¤Þ¤¤¤Ê»ÈÍѤǤ¹" #: ../ex_docmd.c:1638 msgid "E492: Not an editor command" @@ -1606,14 +1633,14 @@ msgstr "E174: ¥³¥Þ¥ó¥É¤¬´û¤Ë¤¢¤ê¤Þ¤¹: ºÆÄêµÁ¤¹¤ë¤Ë¤Ï ! ¤òÄɲ䷤Ƥ¯¤À¤µ¤¤" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" ̾Á° °ú¿ô ÈÏ°Ï Êä´° ÄêµÁ" +" ̾Á° °ú¿ô ¥¢¥É¥ì¥¹ Êä´° ÄêµÁ" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" -msgstr "¥æ¡¼¥¶ÄêµÁ¥³¥Þ¥ó¥É¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿" +msgstr "¥æ¡¼¥¶¡¼ÄêµÁ¥³¥Þ¥ó¥É¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¤Ç¤·¤¿" #: ../ex_docmd.c:4538 msgid "E175: No attribute specified" @@ -1633,7 +1660,10 @@ msgstr "E178: ¥«¥¦¥ó¥È¤Î¾ÊάÃͤ¬Ìµ¸ú¤Ç¤¹" #: ../ex_docmd.c:4625 msgid "E179: argument required for -complete" -msgstr "E179: -Êä´°¤Î¤¿¤á¤Î°ú¿ô¤¬É¬ÍפǤ¹" +msgstr "E179: -complete ¤Ë¤Ï°ú¿ô¤¬É¬ÍפǤ¹" + +msgid "E179: argument required for -addr" +msgstr "E179: -addr ¤Ë¤Ï°ú¿ô¤¬É¬ÍפǤ¹" #: ../ex_docmd.c:4635 #, c-format @@ -1650,12 +1680,16 @@ msgstr "E183: ¥æ¡¼¥¶ÄêµÁ¥³¥Þ¥ó¥É¤Ï±ÑÂçʸ»ú¤Ç»Ï¤Þ¤é¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó" #: ../ex_docmd.c:4696 msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E841: ͽÌó̾¤Ê¤Î¤Ç, ¥æ¡¼¥¶ÄêµÁ¥³¥Þ¥ó¥É¤ËÍøÍѤǤ¤Þ¤»¤ó" +msgstr "E841: ͽÌó̾¤Ê¤Î¤Ç, ¥æ¡¼¥¶¡¼ÄêµÁ¥³¥Þ¥ó¥É¤ËÍøÍѤǤ¤Þ¤»¤ó" #: ../ex_docmd.c:4751 #, c-format msgid "E184: No such user-defined command: %s" -msgstr "E184: ¤½¤Î¥æ¡¼¥¶ÄêµÁ¥³¥Þ¥ó¥É¤Ï¤¢¤ê¤Þ¤»¤ó: %s" +msgstr "E184: ¤½¤Î¥æ¡¼¥¶¡¼ÄêµÁ¥³¥Þ¥ó¥É¤Ï¤¢¤ê¤Þ¤»¤ó: %s" + +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: ̵¸ú¤Ê¥¢¥É¥ì¥¹¥¿¥¤¥×ÃͤǤ¹: %s" #: ../ex_docmd.c:5219 #, c-format @@ -2019,11 +2053,11 @@ msgstr "ÉÔÀµ¤Ê¥Õ¥¡¥¤¥ë̾" #: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578 msgid "is a directory" -msgstr " ¤Ï¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤¹" +msgstr "¤Ï¥Ç¥£¥ì¥¯¥È¥ê¤Ç¤¹" #: ../fileio.c:397 msgid "is not a file" -msgstr " ¤Ï¥Õ¥¡¥¤¥ë¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó" +msgstr "¤Ï¥Õ¥¡¥¤¥ë¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó" #: ../fileio.c:508 ../fileio.c:3522 msgid "[New File]" @@ -2039,7 +2073,7 @@ msgstr "[¥Õ¥¡¥¤¥ë²áÂç]" #: ../fileio.c:534 msgid "[Permission Denied]" -msgstr "[ǧ²Ä¤¬¤¢¤ê¤Þ¤»¤ó]" +msgstr "[¸¢¸Â¤¬¤¢¤ê¤Þ¤»¤ó]" #: ../fileio.c:653 msgid "E200: *ReadPre autocommands made the file unreadable" @@ -2210,7 +2244,7 @@ msgstr " ÊÑ´¹¥¨¥é¡¼" #: ../fileio.c:3509 #, c-format msgid " in line %<PRId64>;" -msgstr "¹Ô %<PRId64>;" +msgstr " ¹Ô %<PRId64>;" #: ../fileio.c:3519 msgid "[Device]" @@ -2766,9 +2800,8 @@ msgid "E37: No write since last change (add ! to override)" msgstr "E37: ºÇ¸å¤ÎÊѹ¹¤¬Êݸ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó (! ¤òÄɲäÇÊѹ¹¤òÇË´þ)" #: ../globals.h:1055 -#, fuzzy msgid "E37: No write since last change" -msgstr "[ºÇ¸å¤ÎÊѹ¹¤¬Êݸ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó]\n" +msgstr "E37: ºÇ¸å¤ÎÊѹ¹¤¬Êݸ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó" #: ../globals.h:1056 msgid "E38: Null argument" @@ -2810,7 +2843,7 @@ msgstr "E42: ¥¨¥é¡¼¤Ï¤¢¤ê¤Þ¤»¤ó" #: ../globals.h:1067 msgid "E776: No location list" -msgstr "E776: ¾ì½ê¥ê¥¹¥È¤Ï¤¢¤ê¤Þ¤»¤ó" +msgstr "E776: ¥í¥±¡¼¥·¥ç¥ó¥ê¥¹¥È¤Ï¤¢¤ê¤Þ¤»¤ó" #: ../globals.h:1068 msgid "E43: Damaged match string" @@ -3831,7 +3864,7 @@ msgid "" "\n" msgstr "" "\n" -"¤½¤ì¤«¤é.swp¥Õ¥¡¥¤¥ë¤òºï½ü¤·¤Æ¤¯¤À¤µ¤¤\n" +"¸µ¤Î.swp¥Õ¥¡¥¤¥ë¤Ïºï½ü¤·¤Æ¤â¹½¤¤¤Þ¤»¤ó\n" "\n" #. use msg() to start the scrolling properly @@ -3845,7 +3878,7 @@ msgstr " ¸½ºß¤Î¥Ç¥£¥ì¥¯¥È¥ê:\n" #: ../memline.c:1448 msgid " Using specified name:\n" -msgstr " ¤¢¤ë̾Á°¤ò»ÈÍÑÃæ:\n" +msgstr " °Ê²¼¤Î̾Á°¤ò»ÈÍÑÃæ:\n" #: ../memline.c:1450 msgid " In directory " @@ -3901,7 +3934,7 @@ msgid "" " user name: " msgstr "" "\n" -" ¥æ¡¼¥¶Ì¾: " +" ¥æ¡¼¥¶¡¼Ì¾: " #: ../memline.c:1568 msgid " host name: " @@ -4050,12 +4083,12 @@ msgid "" msgstr "" "\n" "(1) ÊÌ¤Î¥×¥í¥°¥é¥à¤¬Æ±¤¸¥Õ¥¡¥¤¥ë¤òÊÔ½¸¤·¤Æ¤¤¤ë¤«¤â¤·¤ì¤Þ¤»¤ó.\n" -" ¤³¤Î¾ì¹ç¤Ë¤Ï, Êѹ¹¤ò¤·¤¿ºÝ¤ËºÇ½ªÅª¤Ë, Ʊ¤¸¥Õ¥¡¥¤¥ë¤Î°Û¤Ê¤ë\n" -" 2¤Ä¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤¬¤Ç¤¤Æ¤·¤Þ¤¦¤³¤È¤ËÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤." +" ¤³¤Î¾ì¹ç¤Ë¤Ï, Êѹ¹¤ò¤·¤Æ¤·¤Þ¤¦¤È1¤Ä¤Î¥Õ¥¡¥¤¥ë¤ËÂФ·¤Æ°Û¤Ê¤ë2¤Ä¤Î\n" +" ¥¤¥ó¥¹¥¿¥ó¥¹¤¬¤Ç¤¤Æ¤·¤Þ¤¦¤Î¤Ç, ¤½¤¦¤·¤Ê¤¤¤è¤¦¤Ëµ¤¤ò¤Ä¤±¤Æ¤¯¤À¤µ¤¤." #: ../memline.c:3245 msgid " Quit, or continue with caution.\n" -msgstr " ½ªÎ»¤¹¤ë¤«, Ãí°Õ¤·¤Ê¤¬¤é³¤±¤Æ¤¯¤À¤µ¤¤.\n" +msgstr " ½ªÎ»¤¹¤ë¤«, Ãí°Õ¤·¤Ê¤¬¤é³¤±¤Æ¤¯¤À¤µ¤¤.\n" #: ../memline.c:3246 msgid "(2) An edit session for this file crashed.\n" @@ -4479,6 +4512,11 @@ msgstr "" msgid "E574: Unknown register type %d" msgstr "E574: ̤ÃΤΥ쥸¥¹¥¿·¿ %d ¤Ç¤¹" +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "E883: ¸¡º÷¥Ñ¥¿¡¼¥ó¤È¼°¥ì¥¸¥¹¥¿¤Ë¤Ï2¹Ô°Ê¾å¤ò´Þ¤á¤é¤ì¤Þ¤»¤ó" + #: ../ops.c:5089 #, c-format msgid "%<PRId64> Cols; " @@ -4563,6 +4601,10 @@ msgstr "E522: termcap Æâ¤Ë¸«¤Ä¤«¤ê¤Þ¤»¤ó" msgid "E539: Illegal character <%s>" msgstr "E539: ÉÔÀµ¤Êʸ»ú¤Ç¤¹ <%s>" +#, c-format +msgid "For option %s" +msgstr "¥ª¥×¥·¥ç¥ó: %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: 'term' ¤Ë¤Ï¶õʸ»úÎó¤òÀßÄê¤Ç¤¤Þ¤»¤ó" @@ -4740,6 +4782,14 @@ msgstr "" "\n" "¥»¥¥å¥ê¥Æ¥£¥³¥ó¥Æ¥¥¹¥È¤òÀßÄê¤Ç¤¤Þ¤»¤ó " +#, c-format +msgid "Could not set security context %s for %s" +msgstr "¥»¥¥å¥ê¥Æ¥£¥³¥ó¥Æ¥¥¹¥È %s ¤ò %s ¤ËÀßÄê¤Ç¤¤Þ¤»¤ó" + +#, c-format +msgid "Could not get security context %s for %s. Removing it!" +msgstr "¥»¥¥å¥ê¥Æ¥£¥³¥ó¥Æ¥¥¹¥È %s ¤ò %s ¤«¤é¼èÆÀ¤Ç¤¤Þ¤»¤ó. ºï½ü¤·¤Þ¤¹!" + #: ../os_unix.c:1558 ../os_unix.c:1647 #, c-format msgid "dlerror = \"%s\"" @@ -4960,6 +5010,10 @@ msgstr "E554: %s{...} Æâ¤Ëʸˡ¥¨¥é¡¼¤¬¤¢¤ê¤Þ¤¹" msgid "External submatches:\n" msgstr "³°Éô¤ÎÉôʬ³ºÅö:\n" +#, c-format +msgid "E888: (NFA regexp) cannot repeat %s" +msgstr "E888: (NFA Àµµ¬É½¸½) ·«¤êÊÖ¤»¤Þ¤»¤ó %s" + #: ../regexp.c:7022 msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " @@ -4968,6 +5022,9 @@ msgstr "" "E864: \\%#= ¤Ë¤Ï 0, 1 ¤â¤·¤¯¤Ï 2 ¤Î¤ß¤¬Â³¤±¤é¤ì¤Þ¤¹¡£Àµµ¬É½¸½¥¨¥ó¥¸¥ó¤Ï¼«Æ°Áª" "Âò¤µ¤ì¤Þ¤¹¡£" +msgid "Switching to backtracking RE engine for pattern: " +msgstr "¼¡¤Î¥Ñ¥¿¡¼¥ó¤Ë¥Ð¥Ã¥¯¥È¥é¥Ã¥¥ó¥° RE ¥¨¥ó¥¸¥ó¤òŬÍѤ·¤Þ¤¹: " + #: ../regexp_nfa.c:239 msgid "E865: (NFA) Regexp end encountered prematurely" msgstr "E865: (NFA) ´üÂÔ¤è¤êÁ᤯Àµµ¬É½¸½¤Î½ªÃ¼¤ËÅþ㤷¤Þ¤·¤¿" @@ -4980,7 +5037,7 @@ msgstr "E866: (NFA Àµµ¬É½¸½) °ÌÃÖ¤¬¸í¤Ã¤Æ¤¤¤Þ¤¹: %c" #: ../regexp_nfa.c:242 #, c-format msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" +msgstr "E877: (NFA Àµµ¬É½¸½) ̵¸ú¤Êʸ»ú¥¯¥é¥¹: %<PRId64>" #: ../regexp_nfa.c:1261 #, c-format @@ -5590,14 +5647,14 @@ msgid "E765: 'spellfile' does not have %<PRId64> entries" msgstr "E765: 'spellfile' ¤Ë¤Ï %<PRId64> ¸Ä¤Î¥¨¥ó¥È¥ê¤Ï¤¢¤ê¤Þ¤»¤ó" #: ../spell.c:8074 -#, fuzzy, c-format +#, c-format msgid "Word '%.*s' removed from %s" -msgstr "%s ¤«¤éñ¸ì¤¬ºï½ü¤µ¤ì¤Þ¤·¤¿" +msgstr "ñ¸ì '%.*s' ¤¬ %s ¤«¤éºï½ü¤µ¤ì¤Þ¤·¤¿" #: ../spell.c:8117 -#, fuzzy, c-format +#, c-format msgid "Word '%.*s' added to %s" -msgstr "%s ¤Ëñ¸ì¤¬Äɲ䵤ì¤Þ¤·¤¿" +msgstr "ñ¸ì '%.*s' ¤¬ %s ¤ØÄɲ䵤ì¤Þ¤·¤¿" #: ../spell.c:8381 msgid "E763: Word characters differ between spell files" @@ -5673,6 +5730,9 @@ msgstr "¤³¤Î¥Ð¥Ã¥Õ¥¡¤ËÄêµÁ¤µ¤ì¤¿¹½Ê¸Í×ÁǤϤ¢¤ê¤Þ¤»¤ó" msgid "E390: Illegal argument: %s" msgstr "E390: ÉÔÀµ¤Ê°ú¿ô¤Ç¤¹: %s" +msgid "syntax iskeyword " +msgstr "¥·¥ó¥¿¥Ã¥¯¥¹ÍÑ iskeyword " + #: ../syntax.c:3299 #, c-format msgid "E391: No such syntax cluster: %s" @@ -5769,6 +5829,10 @@ msgstr "E847: ¹½Ê¸¤Î¼è¤ê¹þ¤ß(include)¤¬Â¿²á¤®¤Þ¤¹" msgid "E789: Missing ']': %s" msgstr "E789: ']' ¤¬¤¢¤ê¤Þ¤»¤ó: %s" +#, c-format +msgid "E890: trailing char after ']': %s]%s" +msgstr "E890: ']' ¤Î¸å¤í¤Ë;ʬ¤Êʸ»ú¤¬¤¢¤ê¤Þ¤¹: %s]%s" + #: ../syntax.c:4531 #, c-format msgid "E398: Missing '=': %s" @@ -5874,7 +5938,7 @@ msgstr "E415: ͽ´ü¤»¤ÌÅù¹æ¤Ç¤¹: %s" #: ../syntax.c:6395 #, c-format msgid "E416: missing equal sign: %s" -msgstr "E416: Åù¹æ¤¬¤¬¤¢¤ê¤Þ¤»¤ó: %s" +msgstr "E416: Åù¹æ¤¬¤¢¤ê¤Þ¤»¤ó: %s" #: ../syntax.c:6418 #, c-format @@ -6078,9 +6142,8 @@ msgstr "Vim: ÆþÎϤòÆÉ¹þ¤ßÃæ¤Î¥¨¥é¡¼¤Ë¤è¤ê½ªÎ»¤·¤Þ¤¹...\n" #. This happens when the FileChangedRO autocommand changes the #. * file in a way it becomes shorter. #: ../undo.c:379 -#, fuzzy msgid "E881: Line count changed unexpectedly" -msgstr "E834: ͽ´ü¤»¤º¹Ô¥«¥¦¥ó¥È¤¬ÊѤï¤ê¤Þ¤·¤¿" +msgstr "E881: ͽ´ü¤»¤º¹Ô¥«¥¦¥ó¥È¤¬ÊѤï¤ê¤Þ¤·¤¿" #: ../undo.c:627 #, c-format @@ -6287,23 +6350,23 @@ msgstr " ¥·¥¹¥Æ¥à vimrc: \"" #: ../version.c:672 msgid " user vimrc file: \"" -msgstr " ¥æ¡¼¥¶ vimrc: \"" +msgstr " ¥æ¡¼¥¶¡¼ vimrc: \"" #: ../version.c:677 msgid " 2nd user vimrc file: \"" -msgstr " Âè2¥æ¡¼¥¶ vimrc: \"" +msgstr " Âè2¥æ¡¼¥¶¡¼ vimrc: \"" #: ../version.c:682 msgid " 3rd user vimrc file: \"" -msgstr " Âè3¥æ¡¼¥¶ vimrc: \"" +msgstr " Âè3¥æ¡¼¥¶¡¼ vimrc: \"" #: ../version.c:687 msgid " user exrc file: \"" -msgstr " ¥æ¡¼¥¶ exrc: \"" +msgstr " ¥æ¡¼¥¶¡¼ exrc: \"" #: ../version.c:692 msgid " 2nd user exrc file: \"" -msgstr " Âè2¥æ¡¼¥¶ exrc: \"" +msgstr " Âè2¥æ¡¼¥¶¡¼ exrc: \"" #: ../version.c:699 msgid " fall-back for $VIM: \"" @@ -6379,7 +6442,7 @@ msgstr "Vim¤Î³«È¯¤ò±þ±ç¤·¤Æ¤¯¤À¤µ¤¤!" #: ../version.c:828 msgid "Become a registered Vim user!" -msgstr "Vim¤ÎÅÐÏ¿¥æ¡¼¥¶¤Ë¤Ê¤Ã¤Æ¤¯¤À¤µ¤¤!" +msgstr "Vim¤ÎÅÐÏ¿¥æ¡¼¥¶¡¼¤Ë¤Ê¤Ã¤Æ¤¯¤À¤µ¤¤!" #: ../version.c:831 msgid "type :help sponsor<Enter> for information " @@ -6391,7 +6454,7 @@ msgstr "¾ÜºÙ¤Ê¾ðÊó¤Ï :help register<Enter> " #: ../version.c:834 msgid "menu Help->Sponsor/Register for information " -msgstr "¾ÜºÙ¤Ï¥á¥Ë¥å¡¼¤Î ¥Ø¥ë¥×¢ª¥¹¥Ý¥ó¥µ¡¼/ÅÐÏ¿ ¤ò»²¾È¤·¤Æ²¼¤µ¤¤ " +msgstr "¾ÜºÙ¤Ï¥á¥Ë¥å¡¼¤Î ¥Ø¥ë¥×->¥¹¥Ý¥ó¥µ¡¼/ÅÐÏ¿ ¤ò»²¾È¤·¤Æ²¼¤µ¤¤" #: ../window.c:119 msgid "Already only one window" @@ -6429,6 +6492,9 @@ msgstr "E445: ¾¤Î¥¦¥£¥ó¥É¥¦¤Ë¤ÏÊѹ¹¤¬¤¢¤ê¤Þ¤¹" msgid "E446: No file name under cursor" msgstr "E446: ¥«¡¼¥½¥ë¤Î²¼¤Ë¥Õ¥¡¥¤¥ë̾¤¬¤¢¤ê¤Þ¤»¤ó" +msgid "List or number required" +msgstr "¥ê¥¹¥È¤«¿ôÃͤ¬É¬ÍפǤ¹" + #~ msgid "E831: bf_key_init() called with empty password" #~ msgstr "E831: bf_key_init() ¤¬¶õ¥Ñ¥¹¥ï¡¼¥É¤Ç¸Æ¤Ó½Ð¤µ¤ì¤Þ¤·¤¿" diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po index 6bdfcb426f..0326f33bb5 100644 --- a/src/nvim/po/ja.po +++ b/src/nvim/po/ja.po @@ -1,11 +1,11 @@ -# Japanese translation for Vim vim:set foldmethod=marker: +# Japanese translation for Vim # # Do ":help uganda" in Vim to read copying and usage conditions. # Do ":help credits" in Vim to see a list of people who contributed. # -# Last Change: 2013 Jul 06 +# Copyright (C) 2001-2016 MURAOKA Taro <koron.kaoriya@gmail.com>, +# vim-jp (http://vim-jp.org/) # -# Copyright (C) 2001-13 MURAOKA Taro <koron.kaoriya@gmail.com> # THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. # # Original translations. @@ -14,10 +14,10 @@ msgid "" msgstr "" "Project-Id-Version: Vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-07-06 15:00+0900\n" +"POT-Creation-Date: 2016-02-01 09:02+0900\n" +"PO-Revision-Date: 2013-06-02-01 09:08+09n" "Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n" -"Language-Team: MURAOKA Taro <koron.kaoriya@gmail.com>\n" +"Language-Team: vim-jp (https://github.com/vim-jp/lang-ja)\n" "Language: Japanese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" @@ -34,7 +34,7 @@ msgstr "内部エラー: 未知ã®ã‚ªãƒ—ション型ã§ã™" #: ../buffer.c:92 msgid "[Location List]" -msgstr "[å ´æ‰€ãƒªã‚¹ãƒˆ]" +msgstr "[ãƒã‚±ãƒ¼ã‚·ãƒ§ãƒ³ãƒªã‚¹ãƒˆ]" #: ../buffer.c:93 msgid "[Quickfix List]" @@ -277,7 +277,7 @@ msgstr "E810: 一時ファイルã®èªè¾¼ã‚‚ã—ãã¯æ›¸è¾¼ãŒã§ãã¾ã›ã‚“" #: ../diff.c:755 msgid "E97: Cannot create diffs" -msgstr "E97: 差分を作æˆã§ãã¾ã›ã‚“ " +msgstr "E97: 差分を作æˆã§ãã¾ã›ã‚“" #: ../diff.c:966 msgid "E816: Cannot read patch output" @@ -293,7 +293,7 @@ msgstr "E99: ç¾åœ¨ã®ãƒãƒƒãƒ•ã‚¡ã¯å·®åˆ†ãƒ¢ãƒ¼ãƒ‰ã§ã¯ã‚りã¾ã›ã‚“" #: ../diff.c:2100 msgid "E793: No other buffer in diff mode is modifiable" -msgstr "E793: 差分モードã§ã‚ã‚‹ä»–ã®ãƒãƒƒãƒ•ã‚¡ã¯å¤‰æ›´å¯èƒ½ã§ã™" +msgstr "E793: 差分モードã§ã‚ã‚‹ä»–ã®ãƒãƒƒãƒ•ã‚¡ã¯å¤‰æ›´ã§ãã¾ã›ã‚“" #: ../diff.c:2102 msgid "E100: No other buffer in diff mode" @@ -349,7 +349,7 @@ msgstr " 行(全体)補完 (^L^N^P)" #: ../edit.c:86 msgid " File name completion (^F^N^P)" -msgstr "ファイルå補完 (^F^N^P)" +msgstr " ファイルå補完 (^F^N^P)" #: ../edit.c:87 msgid " Tag completion (^]^N^P)" @@ -377,7 +377,7 @@ msgstr " コマンドライン補完 (^V^N^P)" #: ../edit.c:94 msgid " User defined completion (^U^N^P)" -msgstr " ユーザ定義補完 (^U^N^P)" +msgstr " ユーザー定義補完 (^U^N^P)" #: ../edit.c:95 msgid " Omni completion (^O^N^P)" @@ -679,6 +679,10 @@ msgstr "E696: リスト型ã«ã‚«ãƒ³ãƒžãŒã‚りã¾ã›ã‚“: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: ãƒªã‚¹ãƒˆåž‹ã®æœ€å¾Œã« ']' ãŒã‚りã¾ã›ã‚“: %s" +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "" +"ã‚¬ãƒ¼ãƒ™ãƒƒã‚¸ã‚³ãƒ¬ã‚¯ã‚·ãƒ§ãƒ³ã‚’ä¸æ¢ã—ã¾ã—ãŸ! å‚照を作æˆã™ã‚‹ã®ã«ãƒ¡ãƒ¢ãƒªãŒä¸è¶³ã—ã¾ã—ãŸ" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -721,7 +725,7 @@ msgstr "E117: 未知ã®é–¢æ•°ã§ã™: %s" #: ../eval.c:7383 #, c-format msgid "E119: Not enough arguments for function: %s" -msgstr "E119: 関数ã®å¼•æ•°ãŒå°‘ãªéŽãŽã¾ã™: %s" +msgstr "E119: 関数ã®å¼•æ•°ãŒè¶³ã‚Šã¾ã›ã‚“: %s" #: ../eval.c:7387 #, c-format @@ -826,18 +830,16 @@ msgid "sort() argument" msgstr "sort() ã®å¼•æ•°" #: ../eval.c:13721 -#, fuzzy msgid "uniq() argument" -msgstr "add() ã®å¼•æ•°" +msgstr "uniq() ã®å¼•æ•°" #: ../eval.c:13776 msgid "E702: Sort compare function failed" msgstr "E702: ã‚½ãƒ¼ãƒˆã®æ¯”較関数ãŒå¤±æ•—ã—ã¾ã—ãŸ" #: ../eval.c:13806 -#, fuzzy msgid "E882: Uniq compare function failed" -msgstr "E702: ã‚½ãƒ¼ãƒˆã®æ¯”較関数ãŒå¤±æ•—ã—ã¾ã—ãŸ" +msgstr "E882: Uniq ã®æ¯”較関数ãŒå¤±æ•—ã—ã¾ã—ãŸ" #: ../eval.c:14085 msgid "(Invalid)" @@ -863,6 +865,18 @@ msgstr "E745: リスト型を数値ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™" msgid "E728: Using a Dictionary as a Number" msgstr "E728: 辞書型を数値ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™" +msgid "E891: Using a Funcref as a Float" +msgstr "E891: 関数å‚ç…§åž‹ã‚’æµ®å‹•å°æ•°ç‚¹æ•°ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™ã€‚" + +msgid "E892: Using a String as a Float" +msgstr "E892: æ–‡å—åˆ—ã‚’æµ®å‹•å°æ•°ç‚¹æ•°ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™" + +msgid "E893: Using a List as a Float" +msgstr "E893: ãƒªã‚¹ãƒˆåž‹ã‚’æµ®å‹•å°æ•°ç‚¹æ•°ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: è¾žæ›¸åž‹ã‚’æµ®å‹•å°æ•°ç‚¹æ•°ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™" + #: ../eval.c:16259 msgid "E729: using Funcref as a String" msgstr "E729: 関数å‚照型を文å—列ã¨ã—ã¦æ‰±ã£ã¦ã„ã¾ã™" @@ -961,14 +975,14 @@ msgid "E129: Function name required" msgstr "E129: 関数åãŒè¦æ±‚ã•れã¾ã™" #: ../eval.c:17824 -#, fuzzy, c-format +#, c-format msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: 関数åã¯å¤§æ–‡å—ã§å§‹ã¾ã‚‹ã‹ã‚³ãƒãƒ³ã‚’å«ã¾ãªã‘れã°ãªã‚Šã¾ã›ã‚“: %s" +msgstr "E128: 関数åã¯å¤§æ–‡å—ã‹ \"s:\" ã§å§‹ã¾ã‚‰ãªã‘れã°ãªã‚Šã¾ã›ã‚“: %s" #: ../eval.c:17833 -#, fuzzy, c-format +#, c-format msgid "E884: Function name cannot contain a colon: %s" -msgstr "E128: 関数åã¯å¤§æ–‡å—ã§å§‹ã¾ã‚‹ã‹ã‚³ãƒãƒ³ã‚’å«ã¾ãªã‘れã°ãªã‚Šã¾ã›ã‚“: %s" +msgstr "E884: 関数åã«ã¯ã‚³ãƒãƒ³ã¯å«ã‚られã¾ã›ã‚“: %s" #: ../eval.c:18336 #, c-format @@ -1081,7 +1095,7 @@ msgstr "E136: viminfo: エラーãŒå¤šéŽãŽã‚‹ã®ã§, 以é™ã¯ã‚¹ã‚ップã—ã #: ../ex_cmds.c:1458 #, c-format msgid "Reading viminfo file \"%s\"%s%s%s" -msgstr "viminfoファイル \"%s\"%s%s%s ã‚’èªè¾¼ã¿ä¸ " +msgstr "viminfoファイル \"%s\"%s%s%s ã‚’èªè¾¼ã¿ä¸" #: ../ex_cmds.c:1460 msgid " info" @@ -1357,6 +1371,10 @@ msgstr "E158: 無効ãªãƒãƒƒãƒ•ã‚¡åã§ã™: %s" msgid "E157: Invalid sign ID: %<PRId64>" msgstr "E157: 無効ãªsignè˜åˆ¥åã§ã™: %<PRId64>" +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: 変更ã§ããªã„ sign ã§ã™: %s" + #: ../ex_cmds.c:6066 msgid " (not supported)" msgstr " (éžã‚µãƒãƒ¼ãƒˆ)" @@ -1379,6 +1397,13 @@ msgstr "行 %<PRId64>: %s" msgid "cmd: %s" msgstr "コマンド: %s" +msgid "frame is zero" +msgstr "フレーム㌠0 ã§ã™" + +#, c-format +msgid "frame at highest level: %d" +msgstr "最高レベルã®ãƒ•レーム: %d" + #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" @@ -1528,7 +1553,8 @@ msgstr "E197: 言語を \"%s\" ã«è¨å®šã§ãã¾ã›ã‚“" #. don't wait for return #: ../ex_docmd.c:387 msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." -msgstr "Exモードã«å…¥ã‚Šã¾ã™. ãƒŽãƒ¼ãƒžãƒ«ã«æˆ»ã‚‹ã«ã¯\"visual\"ã¨å…¥åŠ›ã—ã¦ãã ã•ã„." +msgstr "" +"Exモードã«å…¥ã‚Šã¾ã™. ãƒŽãƒ¼ãƒžãƒ«ãƒ¢ãƒ¼ãƒ‰ã«æˆ»ã‚‹ã«ã¯\"visual\"ã¨å…¥åŠ›ã—ã¦ãã ã•ã„." #: ../ex_docmd.c:428 msgid "E501: At end-of-file" @@ -1553,7 +1579,7 @@ msgstr "é–¢æ•°ã®æœ€å¾Œã§ã™" #: ../ex_docmd.c:1628 msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: ユーザ定義コマンドã®ã‚ã„ã¾ã„ãªä½¿ç”¨ã§ã™" +msgstr "E464: ユーザー定義コマンドã®ã‚ã„ã¾ã„ãªä½¿ç”¨ã§ã™" #: ../ex_docmd.c:1638 msgid "E492: Not an editor command" @@ -1606,14 +1632,14 @@ msgstr "E174: ã‚³ãƒžãƒ³ãƒ‰ãŒæ—¢ã«ã‚りã¾ã™: å†å®šç¾©ã™ã‚‹ã«ã¯ ! を追å #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" åå‰ å¼•æ•° 範囲 補完 定義" +" åå‰ å¼•æ•° アドレス 補完 定義" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" -msgstr "ユーザ定義コマンドãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ" +msgstr "ユーザー定義コマンドãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ" #: ../ex_docmd.c:4538 msgid "E175: No attribute specified" @@ -1633,7 +1659,10 @@ msgstr "E178: カウントã®çœç•¥å€¤ãŒç„¡åйã§ã™" #: ../ex_docmd.c:4625 msgid "E179: argument required for -complete" -msgstr "E179: -補完ã®ãŸã‚ã®å¼•æ•°ãŒå¿…è¦ã§ã™" +msgstr "E179: -complete ã«ã¯å¼•æ•°ãŒå¿…è¦ã§ã™" + +msgid "E179: argument required for -addr" +msgstr "E179: -addr ã«ã¯å¼•æ•°ãŒå¿…è¦ã§ã™" #: ../ex_docmd.c:4635 #, c-format @@ -1646,16 +1675,20 @@ msgstr "E182: 無効ãªã‚³ãƒžãƒ³ãƒ‰åã§ã™" #: ../ex_docmd.c:4691 msgid "E183: User defined commands must start with an uppercase letter" -msgstr "E183: ユーザ定義コマンドã¯è‹±å¤§æ–‡å—ã§å§‹ã¾ã‚‰ãªã‘れã°ãªã‚Šã¾ã›ã‚“" +msgstr "E183: ユーザー定義コマンドã¯è‹±å¤§æ–‡å—ã§å§‹ã¾ã‚‰ãªã‘れã°ãªã‚Šã¾ã›ã‚“" #: ../ex_docmd.c:4696 msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E841: 予約åãªã®ã§, ユーザ定義コマンドã«åˆ©ç”¨ã§ãã¾ã›ã‚“" +msgstr "E841: 予約åãªã®ã§, ユーザー定義コマンドã«åˆ©ç”¨ã§ãã¾ã›ã‚“" #: ../ex_docmd.c:4751 #, c-format msgid "E184: No such user-defined command: %s" -msgstr "E184: ãã®ãƒ¦ãƒ¼ã‚¶å®šç¾©ã‚³ãƒžãƒ³ãƒ‰ã¯ã‚りã¾ã›ã‚“: %s" +msgstr "E184: ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å®šç¾©ã‚³ãƒžãƒ³ãƒ‰ã¯ã‚りã¾ã›ã‚“: %s" + +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: 無効ãªã‚¢ãƒ‰ãƒ¬ã‚¹ã‚¿ã‚¤ãƒ—値ã§ã™: %s" #: ../ex_docmd.c:5219 #, c-format @@ -2019,11 +2052,11 @@ msgstr "䏿£ãªãƒ•ァイルå" #: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578 msgid "is a directory" -msgstr " ã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã§ã™" +msgstr "ã¯ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã§ã™" #: ../fileio.c:397 msgid "is not a file" -msgstr " ã¯ãƒ•ァイルã§ã¯ã‚りã¾ã›ã‚“" +msgstr "ã¯ãƒ•ァイルã§ã¯ã‚りã¾ã›ã‚“" #: ../fileio.c:508 ../fileio.c:3522 msgid "[New File]" @@ -2039,7 +2072,7 @@ msgstr "[ファイルéŽå¤§]" #: ../fileio.c:534 msgid "[Permission Denied]" -msgstr "[èªå¯ãŒã‚りã¾ã›ã‚“]" +msgstr "[権é™ãŒã‚りã¾ã›ã‚“]" #: ../fileio.c:653 msgid "E200: *ReadPre autocommands made the file unreadable" @@ -2210,7 +2243,7 @@ msgstr " 変æ›ã‚¨ãƒ©ãƒ¼" #: ../fileio.c:3509 #, c-format msgid " in line %<PRId64>;" -msgstr "行 %<PRId64>;" +msgstr " 行 %<PRId64>;" #: ../fileio.c:3519 msgid "[Device]" @@ -2766,9 +2799,8 @@ msgid "E37: No write since last change (add ! to override)" msgstr "E37: 最後ã®å¤‰æ›´ãŒä¿å˜ã•れã¦ã„ã¾ã›ã‚“ (! ã‚’è¿½åŠ ã§å¤‰æ›´ã‚’ç ´æ£„)" #: ../globals.h:1055 -#, fuzzy msgid "E37: No write since last change" -msgstr "[最後ã®å¤‰æ›´ãŒä¿å˜ã•れã¦ã„ã¾ã›ã‚“]\n" +msgstr "E37: 最後ã®å¤‰æ›´ãŒä¿å˜ã•れã¦ã„ã¾ã›ã‚“" #: ../globals.h:1056 msgid "E38: Null argument" @@ -2810,7 +2842,7 @@ msgstr "E42: エラーã¯ã‚りã¾ã›ã‚“" #: ../globals.h:1067 msgid "E776: No location list" -msgstr "E776: å ´æ‰€ãƒªã‚¹ãƒˆã¯ã‚りã¾ã›ã‚“" +msgstr "E776: ãƒã‚±ãƒ¼ã‚·ãƒ§ãƒ³ãƒªã‚¹ãƒˆã¯ã‚りã¾ã›ã‚“" #: ../globals.h:1068 msgid "E43: Damaged match string" @@ -3831,7 +3863,7 @@ msgid "" "\n" msgstr "" "\n" -"ãれã‹ã‚‰.swpファイルを削除ã—ã¦ãã ã•ã„\n" +"å…ƒã®.swpファイルã¯å‰Šé™¤ã—ã¦ã‚‚æ§‹ã„ã¾ã›ã‚“\n" "\n" #. use msg() to start the scrolling properly @@ -3845,7 +3877,7 @@ msgstr " ç¾åœ¨ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª:\n" #: ../memline.c:1448 msgid " Using specified name:\n" -msgstr " ã‚ã‚‹åå‰ã‚’使用ä¸:\n" +msgstr " 以下ã®åå‰ã‚’使用ä¸:\n" #: ../memline.c:1450 msgid " In directory " @@ -3901,7 +3933,7 @@ msgid "" " user name: " msgstr "" "\n" -" ユーザå: " +" ユーザーå: " #: ../memline.c:1568 msgid " host name: " @@ -4050,12 +4082,12 @@ msgid "" msgstr "" "\n" "(1) 別ã®ãƒ—ãƒã‚°ãƒ©ãƒ ãŒåŒã˜ãƒ•ァイルを編集ã—ã¦ã„ã‚‹ã‹ã‚‚ã—れã¾ã›ã‚“.\n" -" ã“ã®å ´åˆã«ã¯, 変更をã—ãŸéš›ã«æœ€çµ‚çš„ã«, åŒã˜ãƒ•ァイルã®ç•°ãªã‚‹\n" -" 2ã¤ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒã§ãã¦ã—ã¾ã†ã“ã¨ã«æ³¨æ„ã—ã¦ãã ã•ã„." +" ã“ã®å ´åˆã«ã¯, 変更をã—ã¦ã—ã¾ã†ã¨1ã¤ã®ãƒ•ァイルã«å¯¾ã—ã¦ç•°ãªã‚‹2ã¤ã®\n" +" インスタンスãŒã§ãã¦ã—ã¾ã†ã®ã§, ãã†ã—ãªã„よã†ã«æ°—ã‚’ã¤ã‘ã¦ãã ã•ã„." #: ../memline.c:3245 msgid " Quit, or continue with caution.\n" -msgstr " 終了ã™ã‚‹ã‹, 注æ„ã—ãªãŒã‚‰ç¶šã‘ã¦ãã ã•ã„.\n" +msgstr " 終了ã™ã‚‹ã‹, 注æ„ã—ãªãŒã‚‰ç¶šã‘ã¦ãã ã•ã„.\n" #: ../memline.c:3246 msgid "(2) An edit session for this file crashed.\n" @@ -4479,6 +4511,11 @@ msgstr "" msgid "E574: Unknown register type %d" msgstr "E574: 未知ã®ãƒ¬ã‚¸ã‚¹ã‚¿åž‹ %d ã§ã™" +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "E883: 検索パターンã¨å¼ãƒ¬ã‚¸ã‚¹ã‚¿ã«ã¯2行以上をå«ã‚られã¾ã›ã‚“" + #: ../ops.c:5089 #, c-format msgid "%<PRId64> Cols; " @@ -4563,6 +4600,10 @@ msgstr "E522: termcap 内ã«è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“" msgid "E539: Illegal character <%s>" msgstr "E539: 䏿£ãªæ–‡å—ã§ã™ <%s>" +#, c-format +msgid "For option %s" +msgstr "オプション: %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: 'term' ã«ã¯ç©ºæ–‡å—列をè¨å®šã§ãã¾ã›ã‚“" @@ -4740,6 +4781,14 @@ msgstr "" "\n" "ã‚»ã‚ュリティコンテã‚ストをè¨å®šã§ãã¾ã›ã‚“ " +#, c-format +msgid "Could not set security context %s for %s" +msgstr "ã‚»ã‚ュリティコンテã‚スト %s ã‚’ %s ã«è¨å®šã§ãã¾ã›ã‚“" + +#, c-format +msgid "Could not get security context %s for %s. Removing it!" +msgstr "ã‚»ã‚ュリティコンテã‚スト %s ã‚’ %s ã‹ã‚‰å–å¾—ã§ãã¾ã›ã‚“. 削除ã—ã¾ã™!" + #: ../os_unix.c:1558 ../os_unix.c:1647 #, c-format msgid "dlerror = \"%s\"" @@ -4960,6 +5009,10 @@ msgstr "E554: %s{...} å†…ã«æ–‡æ³•エラーãŒã‚りã¾ã™" msgid "External submatches:\n" msgstr "外部ã®éƒ¨åˆ†è©²å½“:\n" +#, c-format +msgid "E888: (NFA regexp) cannot repeat %s" +msgstr "E888: (NFA æ£è¦è¡¨ç¾) 繰り返ã›ã¾ã›ã‚“ %s" + #: ../regexp.c:7022 msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " @@ -4968,6 +5021,9 @@ msgstr "" "E864: \\%#= ã«ã¯ 0, 1 ã‚‚ã—ã㯠2 ã®ã¿ãŒç¶šã‘られã¾ã™ã€‚æ£è¦è¡¨ç¾ã‚¨ãƒ³ã‚¸ãƒ³ã¯è‡ªå‹•é¸" "択ã•れã¾ã™ã€‚" +msgid "Switching to backtracking RE engine for pattern: " +msgstr "次ã®ãƒ‘ターンã«ãƒãƒƒã‚¯ãƒˆãƒ©ãƒƒã‚ング RE エンジンをé©ç”¨ã—ã¾ã™: " + #: ../regexp_nfa.c:239 msgid "E865: (NFA) Regexp end encountered prematurely" msgstr "E865: (NFA) æœŸå¾…ã‚ˆã‚Šæ—©ãæ£è¦è¡¨ç¾ã®çµ‚端ã«åˆ°é”ã—ã¾ã—ãŸ" @@ -4980,7 +5036,7 @@ msgstr "E866: (NFA æ£è¦è¡¨ç¾) ä½ç½®ãŒèª¤ã£ã¦ã„ã¾ã™: %c" #: ../regexp_nfa.c:242 #, c-format msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" +msgstr "E877: (NFA æ£è¦è¡¨ç¾) ç„¡åŠ¹ãªæ–‡å—クラス: %<PRId64>" #: ../regexp_nfa.c:1261 #, c-format @@ -5590,14 +5646,14 @@ msgid "E765: 'spellfile' does not have %<PRId64> entries" msgstr "E765: 'spellfile' ã«ã¯ %<PRId64> 個ã®ã‚¨ãƒ³ãƒˆãƒªã¯ã‚りã¾ã›ã‚“" #: ../spell.c:8074 -#, fuzzy, c-format +#, c-format msgid "Word '%.*s' removed from %s" -msgstr "%s ã‹ã‚‰å˜èªžãŒå‰Šé™¤ã•れã¾ã—ãŸ" +msgstr "å˜èªž '%.*s' ㌠%s ã‹ã‚‰å‰Šé™¤ã•れã¾ã—ãŸ" #: ../spell.c:8117 -#, fuzzy, c-format +#, c-format msgid "Word '%.*s' added to %s" -msgstr "%s ã«å˜èªžãŒè¿½åŠ ã•れã¾ã—ãŸ" +msgstr "å˜èªž '%.*s' ㌠%s ã¸è¿½åŠ ã•れã¾ã—ãŸ" #: ../spell.c:8381 msgid "E763: Word characters differ between spell files" @@ -5673,6 +5729,9 @@ msgstr "ã“ã®ãƒãƒƒãƒ•ã‚¡ã«å®šç¾©ã•ã‚ŒãŸæ§‹æ–‡è¦ç´ ã¯ã‚りã¾ã›ã‚“" msgid "E390: Illegal argument: %s" msgstr "E390: 䏿£ãªå¼•æ•°ã§ã™: %s" +msgid "syntax iskeyword " +msgstr "シンタックス用 iskeyword " + #: ../syntax.c:3299 #, c-format msgid "E391: No such syntax cluster: %s" @@ -5769,6 +5828,10 @@ msgstr "E847: æ§‹æ–‡ã®å–り込ã¿(include)ãŒå¤šéŽãŽã¾ã™" msgid "E789: Missing ']': %s" msgstr "E789: ']' ãŒã‚りã¾ã›ã‚“: %s" +#, c-format +msgid "E890: trailing char after ']': %s]%s" +msgstr "E890: ']' ã®å¾Œã‚ã«ä½™åˆ†ãªæ–‡å—ãŒã‚りã¾ã™: %s]%s" + #: ../syntax.c:4531 #, c-format msgid "E398: Missing '=': %s" @@ -5874,7 +5937,7 @@ msgstr "E415: 予期ã›ã¬ç‰å·ã§ã™: %s" #: ../syntax.c:6395 #, c-format msgid "E416: missing equal sign: %s" -msgstr "E416: ç‰å·ãŒãŒã‚りã¾ã›ã‚“: %s" +msgstr "E416: ç‰å·ãŒã‚りã¾ã›ã‚“: %s" #: ../syntax.c:6418 #, c-format @@ -6078,9 +6141,8 @@ msgstr "Vim: 入力をèªè¾¼ã¿ä¸ã®ã‚¨ãƒ©ãƒ¼ã«ã‚ˆã‚Šçµ‚了ã—ã¾ã™...\n" #. This happens when the FileChangedRO autocommand changes the #. * file in a way it becomes shorter. #: ../undo.c:379 -#, fuzzy msgid "E881: Line count changed unexpectedly" -msgstr "E834: 予期ã›ãšè¡Œã‚«ã‚¦ãƒ³ãƒˆãŒå¤‰ã‚りã¾ã—ãŸ" +msgstr "E881: 予期ã›ãšè¡Œã‚«ã‚¦ãƒ³ãƒˆãŒå¤‰ã‚りã¾ã—ãŸ" #: ../undo.c:627 #, c-format @@ -6287,23 +6349,23 @@ msgstr " システムvimrc: \"" #: ../version.c:672 msgid " user vimrc file: \"" -msgstr " ユーザ vimrc: \"" +msgstr " ユーザー vimrc: \"" #: ../version.c:677 msgid " 2nd user vimrc file: \"" -msgstr " 第2ユーザ vimrc: \"" +msgstr " 第2ユーザー vimrc: \"" #: ../version.c:682 msgid " 3rd user vimrc file: \"" -msgstr " 第3ユーザ vimrc: \"" +msgstr " 第3ユーザー vimrc: \"" #: ../version.c:687 msgid " user exrc file: \"" -msgstr " ユーザ exrc: \"" +msgstr " ユーザー exrc: \"" #: ../version.c:692 msgid " 2nd user exrc file: \"" -msgstr " 第2ユーザ exrc: \"" +msgstr " 第2ユーザー exrc: \"" #: ../version.c:699 msgid " fall-back for $VIM: \"" @@ -6379,7 +6441,7 @@ msgstr "Vimã®é–‹ç™ºã‚’応æ´ã—ã¦ãã ã•ã„!" #: ../version.c:828 msgid "Become a registered Vim user!" -msgstr "Vimã®ç™»éŒ²ãƒ¦ãƒ¼ã‚¶ã«ãªã£ã¦ãã ã•ã„!" +msgstr "Vimã®ç™»éŒ²ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãªã£ã¦ãã ã•ã„!" #: ../version.c:831 msgid "type :help sponsor<Enter> for information " @@ -6391,7 +6453,7 @@ msgstr "è©³ç´°ãªæƒ…å ±ã¯ :help register<Enter> " #: ../version.c:834 msgid "menu Help->Sponsor/Register for information " -msgstr "詳細ã¯ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã® ヘルプ→スãƒãƒ³ã‚µãƒ¼/登録 ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„ " +msgstr "詳細ã¯ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã® ヘルプ->スãƒãƒ³ã‚µãƒ¼/登録 ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„" #: ../window.c:119 msgid "Already only one window" @@ -6429,6 +6491,9 @@ msgstr "E445: ä»–ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã«ã¯å¤‰æ›´ãŒã‚りã¾ã™" msgid "E446: No file name under cursor" msgstr "E446: カーソルã®ä¸‹ã«ãƒ•ァイルåãŒã‚りã¾ã›ã‚“" +msgid "List or number required" +msgstr "ãƒªã‚¹ãƒˆã‹æ•°å€¤ãŒå¿…è¦ã§ã™" + #~ msgid "E831: bf_key_init() called with empty password" #~ msgstr "E831: bf_key_init() ãŒç©ºãƒ‘スワードã§å‘¼ã³å‡ºã•れã¾ã—ãŸ" diff --git a/src/nvim/po/ja.sjis.po b/src/nvim/po/ja.sjis.po index 7dac89e172..16a5d2ce36 100644 --- a/src/nvim/po/ja.sjis.po +++ b/src/nvim/po/ja.sjis.po @@ -1,11 +1,11 @@ -# Japanese translation for Vim vim:set foldmethod=marker: +# Japanese translation for Vim # # Do ":help uganda" in Vim to read copying and usage conditions. # Do ":help credits" in Vim to see a list of people who contributed. # -# Last Change: 2013 Jul 06 +# Copyright (C) 2001-2016 MURAOKA Taro <koron.kaoriya@gmail.com>, +# vim-jp (http://vim-jp.org/) # -# Copyright (C) 2001-13 MURAOKA Taro <koron.kaoriya@gmail.com> # THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. # # Original translations. @@ -14,10 +14,10 @@ msgid "" msgstr "" "Project-Id-Version: Vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" -"PO-Revision-Date: 2013-07-06 15:00+0900\n" +"POT-Creation-Date: 2016-02-01 09:02+0900\n" +"PO-Revision-Date: 2016-02-01 09:08+0900\n" "Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n" -"Language-Team: MURAOKA Taro <koron.kaoriya@gmail.com>\n" +"Language-Team: vim-jpj (https://github.com/vim-jp/lang-ja)\n" "Language: Japanese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=cp932\n" @@ -34,7 +34,7 @@ msgstr "“à•”ƒGƒ‰[: –¢’m‚̃IƒvƒVƒ‡ƒ“Œ^‚Å‚·" #: ../buffer.c:92 msgid "[Location List]" -msgstr "[êŠƒŠƒXƒg]" +msgstr "[ƒƒP[ƒVƒ‡ƒ“ƒŠƒXƒg]" #: ../buffer.c:93 msgid "[Quickfix List]" @@ -277,7 +277,7 @@ msgstr "E810: ˆêŽžƒtƒ@ƒCƒ‹‚̓Ǟ‚à‚µ‚‚Í‘ž‚ª‚Å‚«‚Ü‚¹‚ñ" #: ../diff.c:755 msgid "E97: Cannot create diffs" -msgstr "E97: ·•ª‚ð쬂ł«‚Ü‚¹‚ñ " +msgstr "E97: ·•ª‚ð쬂ł«‚Ü‚¹‚ñ" #: ../diff.c:966 msgid "E816: Cannot read patch output" @@ -293,7 +293,7 @@ msgstr "E99: Œ»Ý‚̃oƒbƒtƒ@‚Í·•ªƒ‚[ƒh‚ł͂ ‚è‚Ü‚¹‚ñ" #: ../diff.c:2100 msgid "E793: No other buffer in diff mode is modifiable" -msgstr "E793: ·•ªƒ‚[ƒh‚Å‚ ‚鑼‚̃oƒbƒtƒ@‚Í•ÏX‰Â”\\‚Å‚·" +msgstr "E793: ·•ªƒ‚[ƒh‚Å‚ ‚鑼‚̃oƒbƒtƒ@‚Í•ÏX‚Å‚«‚Ü‚¹‚ñ" #: ../diff.c:2102 msgid "E100: No other buffer in diff mode" @@ -349,7 +349,7 @@ msgstr " s(‘S‘Ì)•⊮ (^L^N^P)" #: ../edit.c:86 msgid " File name completion (^F^N^P)" -msgstr "ƒtƒ@ƒCƒ‹–¼•⊮ (^F^N^P)" +msgstr " ƒtƒ@ƒCƒ‹–¼•⊮ (^F^N^P)" #: ../edit.c:87 msgid " Tag completion (^]^N^P)" @@ -377,7 +377,7 @@ msgstr " ƒRƒ}ƒ“ƒhƒ‰ƒCƒ“•⊮ (^V^N^P)" #: ../edit.c:94 msgid " User defined completion (^U^N^P)" -msgstr " ƒ†[ƒU’è‹`•⊮ (^U^N^P)" +msgstr " ƒ†[ƒU[’è‹`•⊮ (^U^N^P)" #: ../edit.c:95 msgid " Omni completion (^O^N^P)" @@ -679,6 +679,10 @@ msgstr "E696: ƒŠƒXƒgŒ^‚ɃJƒ“ƒ}‚ª‚ ‚è‚Ü‚¹‚ñ: %s" msgid "E697: Missing end of List ']': %s" msgstr "E697: ƒŠƒXƒgŒ^‚ÌÅŒã‚É ']' ‚ª‚ ‚è‚Ü‚¹‚ñ: %s" +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "" +"ƒK[ƒxƒbƒWƒRƒŒƒNƒVƒ‡ƒ“‚𒆎~‚µ‚Ü‚µ‚½! ŽQÆ‚ð쬂·‚é‚̂Ƀƒ‚ƒŠ‚ª•s‘«‚µ‚Ü‚µ‚½" + #: ../eval.c:6475 #, c-format msgid "E720: Missing colon in Dictionary: %s" @@ -721,7 +725,7 @@ msgstr "E117: –¢’m‚ÌŠÖ”‚Å‚·: %s" #: ../eval.c:7383 #, c-format msgid "E119: Not enough arguments for function: %s" -msgstr "E119: ŠÖ”‚̈ø”‚ª‚ȉ߂¬‚Ü‚·: %s" +msgstr "E119: ŠÖ”‚̈ø”‚ª‘«‚è‚Ü‚¹‚ñ: %s" #: ../eval.c:7387 #, c-format @@ -826,18 +830,16 @@ msgid "sort() argument" msgstr "sort() ‚̈ø”" #: ../eval.c:13721 -#, fuzzy msgid "uniq() argument" -msgstr "add() ‚̈ø”" +msgstr "uniq() ‚̈ø”" #: ../eval.c:13776 msgid "E702: Sort compare function failed" msgstr "E702: ƒ\\[ƒg‚Ì”äŠrŠÖ”‚ªŽ¸”s‚µ‚Ü‚µ‚½" #: ../eval.c:13806 -#, fuzzy msgid "E882: Uniq compare function failed" -msgstr "E702: ƒ\\[ƒg‚Ì”äŠrŠÖ”‚ªŽ¸”s‚µ‚Ü‚µ‚½" +msgstr "E882: Uniq ‚Ì”äŠrŠÖ”‚ªŽ¸”s‚µ‚Ü‚µ‚½" #: ../eval.c:14085 msgid "(Invalid)" @@ -863,6 +865,18 @@ msgstr "E745: ƒŠƒXƒgŒ^‚ð”’l‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·" msgid "E728: Using a Dictionary as a Number" msgstr "E728: Ž«‘Œ^‚ð”’l‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·" +msgid "E891: Using a Funcref as a Float" +msgstr "E891: ŠÖ”ŽQÆŒ^‚ð•‚“®¬”“_”‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·B" + +msgid "E892: Using a String as a Float" +msgstr "E892: •¶Žš—ñ‚ð•‚“®¬”“_”‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·" + +msgid "E893: Using a List as a Float" +msgstr "E893: ƒŠƒXƒgŒ^‚ð•‚“®¬”“_”‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: Ž«‘Œ^‚ð•‚“®¬”“_”‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·" + #: ../eval.c:16259 msgid "E729: using Funcref as a String" msgstr "E729: ŠÖ”ŽQÆŒ^‚ð•¶Žš—ñ‚Æ‚µ‚Ĉµ‚Á‚Ä‚¢‚Ü‚·" @@ -961,14 +975,14 @@ msgid "E129: Function name required" msgstr "E129: ŠÖ”–¼‚ª—v‹‚³‚ê‚Ü‚·" #: ../eval.c:17824 -#, fuzzy, c-format +#, c-format msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: ŠÖ”–¼‚Í‘å•¶Žš‚ÅŽn‚܂邩ƒRƒƒ“‚ðŠÜ‚܂Ȃ¯‚ê‚΂Ȃè‚Ü‚¹‚ñ: %s" +msgstr "E128: ŠÖ”–¼‚Í‘å•¶Žš‚© \"s:\" ‚ÅŽn‚Ü‚ç‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ: %s" #: ../eval.c:17833 -#, fuzzy, c-format +#, c-format msgid "E884: Function name cannot contain a colon: %s" -msgstr "E128: ŠÖ”–¼‚Í‘å•¶Žš‚ÅŽn‚܂邩ƒRƒƒ“‚ðŠÜ‚܂Ȃ¯‚ê‚΂Ȃè‚Ü‚¹‚ñ: %s" +msgstr "E884: ŠÖ”–¼‚ɂ̓Rƒƒ“‚͊܂߂ç‚ê‚Ü‚¹‚ñ: %s" #: ../eval.c:18336 #, c-format @@ -1081,7 +1095,7 @@ msgstr "E136: viminfo: ƒGƒ‰[‚ª‘½‰ß‚¬‚é‚Ì‚Å, ˆÈ~‚̓XƒLƒbƒv‚µ‚Ü‚·" #: ../ex_cmds.c:1458 #, c-format msgid "Reading viminfo file \"%s\"%s%s%s" -msgstr "viminfoƒtƒ@ƒCƒ‹ \"%s\"%s%s%s ‚ð“Çž‚Ý’† " +msgstr "viminfoƒtƒ@ƒCƒ‹ \"%s\"%s%s%s ‚ð“Çž‚Ý’†" #: ../ex_cmds.c:1460 msgid " info" @@ -1357,6 +1371,10 @@ msgstr "E158: –³Œø‚ȃoƒbƒtƒ@–¼‚Å‚·: %s" msgid "E157: Invalid sign ID: %<PRId64>" msgstr "E157: –³Œø‚Èsignޝ•ÊŽq‚Å‚·: %<PRId64>" +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: •ÏX‚Å‚«‚È‚¢ sign ‚Å‚·: %s" + #: ../ex_cmds.c:6066 msgid " (not supported)" msgstr " (”ñƒTƒ|[ƒg)" @@ -1379,6 +1397,13 @@ msgstr "s %<PRId64>: %s" msgid "cmd: %s" msgstr "ƒRƒ}ƒ“ƒh: %s" +msgid "frame is zero" +msgstr "ƒtƒŒ[ƒ€‚ª 0 ‚Å‚·" + +#, c-format +msgid "frame at highest level: %d" +msgstr "Å‚ƒŒƒxƒ‹‚̃tƒŒ[ƒ€: %d" + #: ../ex_cmds2.c:322 #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" @@ -1528,7 +1553,8 @@ msgstr "E197: Œ¾Œê‚ð \"%s\" ‚ÉÝ’è‚Å‚«‚Ü‚¹‚ñ" #. don't wait for return #: ../ex_docmd.c:387 msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." -msgstr "Exƒ‚[ƒh‚É“ü‚è‚Ü‚·. ƒm[ƒ}ƒ‹‚É–ß‚é‚É‚Í\"visual\"‚Æ“ü—Í‚µ‚Ä‚‚¾‚³‚¢." +msgstr "" +"Exƒ‚[ƒh‚É“ü‚è‚Ü‚·. ƒm[ƒ}ƒ‹ƒ‚[ƒh‚É–ß‚é‚É‚Í\"visual\"‚Æ“ü—Í‚µ‚Ä‚‚¾‚³‚¢." #: ../ex_docmd.c:428 msgid "E501: At end-of-file" @@ -1553,7 +1579,7 @@ msgstr "ŠÖ”‚ÌÅŒã‚Å‚·" #: ../ex_docmd.c:1628 msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: ƒ†[ƒU’è‹`ƒRƒ}ƒ“ƒh‚Ì‚ ‚¢‚Ü‚¢‚ÈŽg—p‚Å‚·" +msgstr "E464: ƒ†[ƒU[’è‹`ƒRƒ}ƒ“ƒh‚Ì‚ ‚¢‚Ü‚¢‚ÈŽg—p‚Å‚·" #: ../ex_docmd.c:1638 msgid "E492: Not an editor command" @@ -1606,14 +1632,14 @@ msgstr "E174: ƒRƒ}ƒ“ƒh‚ªŠù‚É‚ ‚è‚Ü‚·: Ä’è‹`‚·‚é‚É‚Í ! ‚ð’ljÁ‚µ‚Ä‚‚¾‚³‚¢" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" –¼‘O ˆø” ”ÍˆÍ •⊮ ’è‹`" +" –¼‘O ˆø” ƒAƒhƒŒƒX •⊮ ’è‹`" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" -msgstr "ƒ†[ƒU’è‹`ƒRƒ}ƒ“ƒh‚ªŒ©‚‚©‚è‚Ü‚¹‚ñ‚Å‚µ‚½" +msgstr "ƒ†[ƒU[’è‹`ƒRƒ}ƒ“ƒh‚ªŒ©‚‚©‚è‚Ü‚¹‚ñ‚Å‚µ‚½" #: ../ex_docmd.c:4538 msgid "E175: No attribute specified" @@ -1633,7 +1659,10 @@ msgstr "E178: ƒJƒEƒ“ƒg‚ÌÈ—ª’l‚ª–³Œø‚Å‚·" #: ../ex_docmd.c:4625 msgid "E179: argument required for -complete" -msgstr "E179: -•⊮‚Ì‚½‚߂̈ø”‚ª•K—v‚Å‚·" +msgstr "E179: -complete ‚ɂ͈ø”‚ª•K—v‚Å‚·" + +msgid "E179: argument required for -addr" +msgstr "E179: -addr ‚ɂ͈ø”‚ª•K—v‚Å‚·" #: ../ex_docmd.c:4635 #, c-format @@ -1646,16 +1675,20 @@ msgstr "E182: –³Œø‚ȃRƒ}ƒ“ƒh–¼‚Å‚·" #: ../ex_docmd.c:4691 msgid "E183: User defined commands must start with an uppercase letter" -msgstr "E183: ƒ†[ƒU’è‹`ƒRƒ}ƒ“ƒh‚͉p‘å•¶Žš‚ÅŽn‚Ü‚ç‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ" +msgstr "E183: ƒ†[ƒU[’è‹`ƒRƒ}ƒ“ƒh‚͉p‘å•¶Žš‚ÅŽn‚Ü‚ç‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ" #: ../ex_docmd.c:4696 msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E841: —\\–ñ–¼‚Ȃ̂Å, ƒ†[ƒU’è‹`ƒRƒ}ƒ“ƒh‚É—˜—p‚Å‚«‚Ü‚¹‚ñ" +msgstr "E841: —\\–ñ–¼‚Ȃ̂Å, ƒ†[ƒU[’è‹`ƒRƒ}ƒ“ƒh‚É—˜—p‚Å‚«‚Ü‚¹‚ñ" #: ../ex_docmd.c:4751 #, c-format msgid "E184: No such user-defined command: %s" -msgstr "E184: ‚»‚̃†[ƒU’è‹`ƒRƒ}ƒ“ƒh‚Í‚ ‚è‚Ü‚¹‚ñ: %s" +msgstr "E184: ‚»‚̃†[ƒU[’è‹`ƒRƒ}ƒ“ƒh‚Í‚ ‚è‚Ü‚¹‚ñ: %s" + +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: –³Œø‚ȃAƒhƒŒƒXƒ^ƒCƒv’l‚Å‚·: %s" #: ../ex_docmd.c:5219 #, c-format @@ -2019,11 +2052,11 @@ msgstr "•s³‚ȃtƒ@ƒCƒ‹–¼" #: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578 msgid "is a directory" -msgstr " ‚̓fƒBƒŒƒNƒgƒŠ‚Å‚·" +msgstr "‚̓fƒBƒŒƒNƒgƒŠ‚Å‚·" #: ../fileio.c:397 msgid "is not a file" -msgstr " ‚̓tƒ@ƒCƒ‹‚ł͂ ‚è‚Ü‚¹‚ñ" +msgstr "‚̓tƒ@ƒCƒ‹‚ł͂ ‚è‚Ü‚¹‚ñ" #: ../fileio.c:508 ../fileio.c:3522 msgid "[New File]" @@ -2039,7 +2072,7 @@ msgstr "[ƒtƒ@ƒCƒ‹‰ß‘å]" #: ../fileio.c:534 msgid "[Permission Denied]" -msgstr "[”F‰Â‚ª‚ ‚è‚Ü‚¹‚ñ]" +msgstr "[Œ ŒÀ‚ª‚ ‚è‚Ü‚¹‚ñ]" #: ../fileio.c:653 msgid "E200: *ReadPre autocommands made the file unreadable" @@ -2210,7 +2243,7 @@ msgstr " •ÏŠ·ƒGƒ‰[" #: ../fileio.c:3509 #, c-format msgid " in line %<PRId64>;" -msgstr "s %<PRId64>;" +msgstr " s %<PRId64>;" #: ../fileio.c:3519 msgid "[Device]" @@ -2766,9 +2799,8 @@ msgid "E37: No write since last change (add ! to override)" msgstr "E37: ÅŒã‚Ì•ÏX‚ª•Û‘¶‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ (! ‚ð’ljÁ‚Å•ÏX‚ð”jŠü)" #: ../globals.h:1055 -#, fuzzy msgid "E37: No write since last change" -msgstr "[ÅŒã‚Ì•ÏX‚ª•Û‘¶‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ]\n" +msgstr "E37: ÅŒã‚Ì•ÏX‚ª•Û‘¶‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ" #: ../globals.h:1056 msgid "E38: Null argument" @@ -2810,7 +2842,7 @@ msgstr "E42: ƒGƒ‰[‚Í‚ ‚è‚Ü‚¹‚ñ" #: ../globals.h:1067 msgid "E776: No location list" -msgstr "E776: êŠƒŠƒXƒg‚Í‚ ‚è‚Ü‚¹‚ñ" +msgstr "E776: ƒƒP[ƒVƒ‡ƒ“ƒŠƒXƒg‚Í‚ ‚è‚Ü‚¹‚ñ" #: ../globals.h:1068 msgid "E43: Damaged match string" @@ -2931,6 +2963,10 @@ msgstr "E363: ƒpƒ^[ƒ“‚ª 'maxmempattern' ˆÈã‚̃ƒ‚ƒŠ‚ðŽg—p‚µ‚Ü‚·" msgid "E749: empty buffer" msgstr "E749: ƒoƒbƒtƒ@‚ª‹ó‚Å‚·" +#, c-format +msgid "E86: Buffer %ld does not exist" +msgstr "E86: ƒoƒbƒtƒ@ %ld ‚Í‚ ‚è‚Ü‚¹‚ñ" + #: ../globals.h:1108 msgid "E682: Invalid search pattern or delimiter" msgstr "E682: ŒŸõƒpƒ^[ƒ“‚©‹æØ‚è‹L†‚ª•s³‚Å‚·" @@ -3831,7 +3867,7 @@ msgid "" "\n" msgstr "" "\n" -"‚»‚ê‚©‚ç.swpƒtƒ@ƒCƒ‹‚ð휂µ‚Ä‚‚¾‚³‚¢\n" +"Œ³‚Ì.swpƒtƒ@ƒCƒ‹‚Í휂µ‚Ä‚à\\‚¢‚Ü‚¹‚ñ\n" "\n" #. use msg() to start the scrolling properly @@ -3845,7 +3881,7 @@ msgstr " Œ»Ý‚̃fƒBƒŒƒNƒgƒŠ:\n" #: ../memline.c:1448 msgid " Using specified name:\n" -msgstr " ‚ ‚é–¼‘O‚ðŽg—p’†:\n" +msgstr " ˆÈ‰º‚Ì–¼‘O‚ðŽg—p’†:\n" #: ../memline.c:1450 msgid " In directory " @@ -3901,7 +3937,7 @@ msgid "" " user name: " msgstr "" "\n" -" ƒ†[ƒU–¼: " +" ƒ†[ƒU[–¼: " #: ../memline.c:1568 msgid " host name: " @@ -4050,12 +4086,12 @@ msgid "" msgstr "" "\n" "(1) •ʂ̃vƒƒOƒ‰ƒ€‚ª“¯‚¶ƒtƒ@ƒCƒ‹‚ð•ÒW‚µ‚Ä‚¢‚é‚©‚à‚µ‚ê‚Ü‚¹‚ñ.\n" -" ‚±‚ÌꇂɂÍ, •ÏX‚ð‚µ‚½Û‚ÉÅI“I‚É, “¯‚¶ƒtƒ@ƒCƒ‹‚̈قȂé\n" -" 2‚‚̃Cƒ“ƒXƒ^ƒ“ƒX‚ª‚Å‚«‚Ä‚µ‚Ü‚¤‚±‚ƂɒˆÓ‚µ‚Ä‚‚¾‚³‚¢." +" ‚±‚ÌꇂɂÍ, •ÏX‚ð‚µ‚Ä‚µ‚Ü‚¤‚Æ1‚‚̃tƒ@ƒCƒ‹‚ɑ΂µ‚ĈقȂé2‚‚Ì\n" +" ƒCƒ“ƒXƒ^ƒ“ƒX‚ª‚Å‚«‚Ä‚µ‚Ü‚¤‚Ì‚Å, ‚»‚¤‚µ‚È‚¢‚悤‚É‹C‚ð‚‚¯‚Ä‚‚¾‚³‚¢." #: ../memline.c:3245 msgid " Quit, or continue with caution.\n" -msgstr " I—¹‚·‚é‚©, ’ˆÓ‚µ‚È‚ª‚瑱‚¯‚Ä‚‚¾‚³‚¢.\n" +msgstr " I—¹‚·‚é‚©, ’ˆÓ‚µ‚È‚ª‚瑱‚¯‚Ä‚‚¾‚³‚¢.\n" #: ../memline.c:3246 msgid "(2) An edit session for this file crashed.\n" @@ -4479,6 +4515,11 @@ msgstr "" msgid "E574: Unknown register type %d" msgstr "E574: –¢’m‚̃ŒƒWƒXƒ^Œ^ %d ‚Å‚·" +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "E883: ŒŸõƒpƒ^[ƒ“‚ÆŽ®ƒŒƒWƒXƒ^‚É‚Í2sˆÈã‚ðŠÜ‚ß‚ç‚ê‚Ü‚¹‚ñ" + #: ../ops.c:5089 #, c-format msgid "%<PRId64> Cols; " @@ -4563,6 +4604,10 @@ msgstr "E522: termcap “à‚ÉŒ©‚‚©‚è‚Ü‚¹‚ñ" msgid "E539: Illegal character <%s>" msgstr "E539: •s³‚È•¶Žš‚Å‚· <%s>" +#, c-format +msgid "For option %s" +msgstr "ƒIƒvƒVƒ‡ƒ“: %s" + #: ../option.c:3862 msgid "E529: Cannot set 'term' to empty string" msgstr "E529: 'term' ‚ɂ͋󕶎š—ñ‚ðÝ’è‚Å‚«‚Ü‚¹‚ñ" @@ -4740,6 +4785,14 @@ msgstr "" "\n" "ƒZƒLƒ…ƒŠƒeƒBƒRƒ“ƒeƒLƒXƒg‚ðÝ’è‚Å‚«‚Ü‚¹‚ñ " +#, c-format +msgid "Could not set security context %s for %s" +msgstr "ƒZƒLƒ…ƒŠƒeƒBƒRƒ“ƒeƒLƒXƒg %s ‚ð %s ‚ÉÝ’è‚Å‚«‚Ü‚¹‚ñ" + +#, c-format +msgid "Could not get security context %s for %s. Removing it!" +msgstr "ƒZƒLƒ…ƒŠƒeƒBƒRƒ“ƒeƒLƒXƒg %s ‚ð %s ‚©‚çŽæ“¾‚Å‚«‚Ü‚¹‚ñ. 휂µ‚Ü‚·!" + #: ../os_unix.c:1558 ../os_unix.c:1647 #, c-format msgid "dlerror = \"%s\"" @@ -4960,6 +5013,10 @@ msgstr "E554: %s{...} “à‚É•¶–@ƒGƒ‰[‚ª‚ ‚è‚Ü‚·" msgid "External submatches:\n" msgstr "ŠO•”‚Ì•”•ªŠY“–:\n" +#, c-format +msgid "E888: (NFA regexp) cannot repeat %s" +msgstr "E888: (NFA ³‹K•\\Œ») ŒJ‚è•Ô‚¹‚Ü‚¹‚ñ %s" + #: ../regexp.c:7022 msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " @@ -4968,7 +5025,9 @@ msgstr "" "E864: \\%#= ‚É‚Í 0, 1 ‚à‚µ‚‚Í 2 ‚݂̂ª‘±‚¯‚ç‚ê‚Ü‚·B³‹K•\\Œ»ƒGƒ“ƒWƒ“‚ÍŽ©“®‘I" "‘ð‚³‚ê‚Ü‚·B" -#: ../regexp_nfa.c:239 +msgid "Switching to backtracking RE engine for pattern: " +msgstr "ŽŸ‚̃pƒ^[ƒ“‚ɃoƒbƒNƒgƒ‰ƒbƒLƒ“ƒO RE ƒGƒ“ƒWƒ“‚ð“K—p‚µ‚Ü‚·: " + msgid "E865: (NFA) Regexp end encountered prematurely" msgstr "E865: (NFA) Šú‘Ò‚æ‚è‘‚³‹K•\\Œ»‚ÌI’[‚É“ž’B‚µ‚Ü‚µ‚½" @@ -4980,7 +5039,7 @@ msgstr "E866: (NFA ³‹K•\\Œ») ˆÊ’u‚ªŒë‚Á‚Ä‚¢‚Ü‚·: %c" #: ../regexp_nfa.c:242 #, c-format msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" +msgstr "E877: (NFA ³‹K•\\Œ») –³Œø‚È•¶ŽšƒNƒ‰ƒX: %<PRId64>" #: ../regexp_nfa.c:1261 #, c-format @@ -5590,14 +5649,14 @@ msgid "E765: 'spellfile' does not have %<PRId64> entries" msgstr "E765: 'spellfile' ‚É‚Í %<PRId64> ŒÂ‚̃Gƒ“ƒgƒŠ‚Í‚ ‚è‚Ü‚¹‚ñ" #: ../spell.c:8074 -#, fuzzy, c-format +#, c-format msgid "Word '%.*s' removed from %s" -msgstr "%s ‚©‚ç’PŒê‚ªíœ‚³‚ê‚Ü‚µ‚½" +msgstr "’PŒê '%.*s' ‚ª %s ‚©‚ç휂³‚ê‚Ü‚µ‚½" #: ../spell.c:8117 -#, fuzzy, c-format +#, c-format msgid "Word '%.*s' added to %s" -msgstr "%s ‚É’PŒê‚ª’ljÁ‚³‚ê‚Ü‚µ‚½" +msgstr "’PŒê '%.*s' ‚ª %s ‚֒ljÁ‚³‚ê‚Ü‚µ‚½" #: ../spell.c:8381 msgid "E763: Word characters differ between spell files" @@ -5673,6 +5732,9 @@ msgstr "‚±‚̃oƒbƒtƒ@‚É’è‹`‚³‚ꂽ\\•¶—v‘f‚Í‚ ‚è‚Ü‚¹‚ñ" msgid "E390: Illegal argument: %s" msgstr "E390: •s³‚Ȉø”‚Å‚·: %s" +msgid "syntax iskeyword " +msgstr "ƒVƒ“ƒ^ƒbƒNƒX—p iskeyword " + #: ../syntax.c:3299 #, c-format msgid "E391: No such syntax cluster: %s" @@ -5769,6 +5831,10 @@ msgstr "E847: \\•¶‚ÌŽæ‚èž‚Ý(include)‚ª‘½‰ß‚¬‚Ü‚·" msgid "E789: Missing ']': %s" msgstr "E789: ']' ‚ª‚ ‚è‚Ü‚¹‚ñ: %s" +#, c-format +msgid "E890: trailing char after ']': %s]%s" +msgstr "E890: ']' ‚ÌŒã‚ë‚É—]•ª‚È•¶Žš‚ª‚ ‚è‚Ü‚·: %s]%s" + #: ../syntax.c:4531 #, c-format msgid "E398: Missing '=': %s" @@ -5874,7 +5940,7 @@ msgstr "E415: —\\Šú‚¹‚Ê“™†‚Å‚·: %s" #: ../syntax.c:6395 #, c-format msgid "E416: missing equal sign: %s" -msgstr "E416: “™†‚ª‚ª‚ ‚è‚Ü‚¹‚ñ: %s" +msgstr "E416: “™†‚ª‚ ‚è‚Ü‚¹‚ñ: %s" #: ../syntax.c:6418 #, c-format @@ -6078,9 +6144,8 @@ msgstr "Vim: “ü—Í‚ð“Çž‚Ý’†‚̃Gƒ‰[‚É‚æ‚èI—¹‚µ‚Ü‚·...\n" #. This happens when the FileChangedRO autocommand changes the #. * file in a way it becomes shorter. #: ../undo.c:379 -#, fuzzy msgid "E881: Line count changed unexpectedly" -msgstr "E834: —\\Šú‚¹‚¸sƒJƒEƒ“ƒg‚ª•Ï‚í‚è‚Ü‚µ‚½" +msgstr "E881: —\\Šú‚¹‚¸sƒJƒEƒ“ƒg‚ª•Ï‚í‚è‚Ü‚µ‚½" #: ../undo.c:627 #, c-format @@ -6287,23 +6352,23 @@ msgstr " ƒVƒXƒeƒ€ vimrc: \"" #: ../version.c:672 msgid " user vimrc file: \"" -msgstr " ƒ†[ƒU vimrc: \"" +msgstr " ƒ†[ƒU[ vimrc: \"" #: ../version.c:677 msgid " 2nd user vimrc file: \"" -msgstr " ‘æ2ƒ†[ƒU vimrc: \"" +msgstr " ‘æ2ƒ†[ƒU[ vimrc: \"" #: ../version.c:682 msgid " 3rd user vimrc file: \"" -msgstr " ‘æ3ƒ†[ƒU vimrc: \"" +msgstr " ‘æ3ƒ†[ƒU[ vimrc: \"" #: ../version.c:687 msgid " user exrc file: \"" -msgstr " ƒ†[ƒU exrc: \"" +msgstr " ƒ†[ƒU[ exrc: \"" #: ../version.c:692 msgid " 2nd user exrc file: \"" -msgstr " ‘æ2ƒ†[ƒU exrc: \"" +msgstr " ‘æ2ƒ†[ƒU[ exrc: \"" #: ../version.c:699 msgid " fall-back for $VIM: \"" @@ -6379,7 +6444,7 @@ msgstr "Vim‚ÌŠJ”‚ð‰ž‰‡‚µ‚Ä‚‚¾‚³‚¢!" #: ../version.c:828 msgid "Become a registered Vim user!" -msgstr "Vim‚Ì“o˜^ƒ†[ƒU‚ɂȂÁ‚Ä‚‚¾‚³‚¢!" +msgstr "Vim‚Ì“o˜^ƒ†[ƒU[‚ɂȂÁ‚Ä‚‚¾‚³‚¢!" #: ../version.c:831 msgid "type :help sponsor<Enter> for information " @@ -6391,7 +6456,7 @@ msgstr "ÚׂÈî•ñ‚Í :help register<Enter> " #: ../version.c:834 msgid "menu Help->Sponsor/Register for information " -msgstr "Úׂ̓ƒjƒ…[‚Ì ƒwƒ‹ƒv¨ƒXƒ|ƒ“ƒT[/“o˜^ ‚ðŽQÆ‚µ‚ĉº‚³‚¢ " +msgstr "Úׂ̓ƒjƒ…[‚Ì ƒwƒ‹ƒv->ƒXƒ|ƒ“ƒT[/“o˜^ ‚ðŽQÆ‚µ‚ĉº‚³‚¢" #: ../window.c:119 msgid "Already only one window" @@ -6429,6 +6494,9 @@ msgstr "E445: ‘¼‚̃EƒBƒ“ƒhƒE‚ɂ͕ÏX‚ª‚ ‚è‚Ü‚·" msgid "E446: No file name under cursor" msgstr "E446: ƒJ[ƒ\\ƒ‹‚̉º‚Ƀtƒ@ƒCƒ‹–¼‚ª‚ ‚è‚Ü‚¹‚ñ" +msgid "List or number required" +msgstr "ƒŠƒXƒg‚©”’l‚ª•K—v‚Å‚·" + #~ msgid "E831: bf_key_init() called with empty password" #~ msgstr "E831: bf_key_init() ‚ª‹óƒpƒXƒ[ƒh‚ŌĂÑo‚³‚ê‚Ü‚µ‚½" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 151b9d3790..dfd795b0ba 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -571,8 +571,9 @@ restofline: *regmatch.endp[i] = c; if (vim_strchr((char_u *)"OPQ", idx) != NULL - && !os_file_exists(namebuf)) + && !os_path_exists(namebuf)) { continue; + } } if ((i = (int)fmt_ptr->addr[1]) > 0) { /* %n */ if (regmatch.startp[i] == NULL) @@ -706,11 +707,12 @@ restofline: } else if (vim_strchr((char_u *)"OPQ", idx) != NULL) { // global file names valid = false; - if (*namebuf == NUL || os_file_exists(namebuf)) { - if (*namebuf && idx == 'P') + if (*namebuf == NUL || os_path_exists(namebuf)) { + if (*namebuf && idx == 'P') { currfile = qf_push_dir(namebuf, &file_stack); - else if (idx == 'Q') + } else if (idx == 'Q') { currfile = qf_pop_dir(&file_stack); + } *namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); @@ -1080,7 +1082,7 @@ static int qf_get_fnum(char_u *directory, char_u *fname) * "leaving directory"-messages we might have missed a * directory change. */ - if (!os_file_exists(ptr)) { + if (!os_path_exists(ptr)) { xfree(ptr); directory = qf_guess_filepath(fname); if (directory) @@ -1232,8 +1234,9 @@ static char_u *qf_guess_filepath(char_u *filename) xfree(fullname); fullname = (char_u *)concat_fnames((char *)ds_ptr->dirname, (char *)filename, TRUE); - if (os_file_exists(fullname)) + if (os_path_exists(fullname)) { break; + } ds_ptr = ds_ptr->next; } @@ -1576,14 +1579,23 @@ win_found: * set b_p_ro flag). */ if (!can_abandon(curbuf, forceit)) { EMSG(_(e_nowrtmsg)); - ok = FALSE; - } else + ok = false; + } else { ok = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1, - ECMD_HIDE + ECMD_SET_HELP, - oldwin == curwin ? curwin : NULL); - } else - ok = buflist_getfile(qf_ptr->qf_fnum, - (linenr_T)1, GETF_SETMARK | GETF_SWITCH, forceit); + ECMD_HIDE + ECMD_SET_HELP, + oldwin == curwin ? curwin : NULL); + } + } else { + ok = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, + GETF_SETMARK | GETF_SWITCH, forceit); + if (qi != &ql_info && !win_valid(oldwin)) { + EMSG(_("E924: Current window was closed")); + ok = false; + qi = NULL; + qf_ptr = NULL; + opened_window = false; + } + } } if (ok == OK) { @@ -1663,21 +1675,22 @@ win_found: msg_scroll = (int)i; } } else { - if (opened_window) - win_close(curwin, TRUE); /* Close opened window */ - if (qf_ptr->qf_fnum != 0) { - /* - * Couldn't open file, so put index back where it was. This could - * happen if the file was readonly and we changed something. - */ + if (opened_window) { + win_close(curwin, true); // Close opened window + } + if (qf_ptr != NULL && qf_ptr->qf_fnum != 0) { + // Couldn't open file, so put index back where it was. This could + // happen if the file was readonly and we changed something. failed: qf_ptr = old_qf_ptr; qf_index = old_qf_index; } } theend: - qi->qf_lists[qi->qf_curlist].qf_ptr = qf_ptr; - qi->qf_lists[qi->qf_curlist].qf_index = qf_index; + if (qi != NULL) { + qi->qf_lists[qi->qf_curlist].qf_ptr = qf_ptr; + qi->qf_lists[qi->qf_curlist].qf_index = qf_index; + } if (p_swb != old_swb && opened_window) { /* Restore old 'switchbuf' value, but not when an autocommand or * modeline has changed the value. */ diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index b3805a3a28..a2cc432eca 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -15,7 +15,7 @@ RBuffer *rbuffer_new(size_t capacity) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET { if (!capacity) { - capacity = 0xffff; + capacity = 0x10000; } RBuffer *rv = xmalloc(sizeof(RBuffer) + capacity); @@ -153,7 +153,7 @@ void rbuffer_consumed(RBuffer *buf, size_t count) // Higher level functions for copying from/to RBuffer instances and data // pointers -size_t rbuffer_write(RBuffer *buf, char *src, size_t src_size) +size_t rbuffer_write(RBuffer *buf, const char *src, size_t src_size) FUNC_ATTR_NONNULL_ALL { size_t size = src_size; diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h index 35fb16508e..454972c69d 100644 --- a/src/nvim/rbuffer.h +++ b/src/nvim/rbuffer.h @@ -36,30 +36,36 @@ // // Note that the rbuffer_{produced,consumed} calls are necessary or these macros // create infinite loops -#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ - for (size_t rcnt = 0, _r = 1; _r; _r = 0) \ - for (char *rptr = rbuffer_read_ptr(buf, &rcnt); \ - buf->size; \ - rptr = rbuffer_read_ptr(buf, &rcnt)) +#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ + for (size_t rcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \ + for ( /* NOLINT(readability/braces) */ \ + char *rptr = rbuffer_read_ptr(buf, &rcnt); \ + buf->size; \ + rptr = rbuffer_read_ptr(buf, &rcnt)) -#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ - for (size_t wcnt = 0, _r = 1; _r; _r = 0) \ - for (char *wptr = rbuffer_write_ptr(buf, &wcnt); \ - rbuffer_space(buf); \ - wptr = rbuffer_write_ptr(buf, &wcnt)) +#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ + for (size_t wcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \ + for ( /* NOLINT(readability/braces) */ \ + char *wptr = rbuffer_write_ptr(buf, &wcnt); \ + rbuffer_space(buf); \ + wptr = rbuffer_write_ptr(buf, &wcnt)) // Iteration -#define RBUFFER_EACH(buf, c, i) \ - for (size_t i = 0; i < buf->size; i = buf->size) \ - for (char c = 0; \ - i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ +#define RBUFFER_EACH(buf, c, i) \ + for (size_t i = 0; /* NOLINT(readability/braces) */ \ + i < buf->size; \ + i = buf->size) \ + for (char c = 0; /* NOLINT(readability/braces) */ \ + i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ i++) -#define RBUFFER_EACH_REVERSE(buf, c, i) \ - for (size_t i = buf->size; i != SIZE_MAX; i = SIZE_MAX) \ - for (char c = 0; \ - i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ +#define RBUFFER_EACH_REVERSE(buf, c, i) \ + for (size_t i = buf->size; /* NOLINT(readability/braces) */ \ + i != SIZE_MAX; \ + i = SIZE_MAX) \ + for (char c = 0; /* NOLINT(readability/braces) */ \ + i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ ) typedef struct rbuffer RBuffer; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 7e53b2ccd1..f97dce9e0d 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -357,13 +357,14 @@ static int nfa_ll_index = 0; # include "regexp_nfa.c.generated.h" #endif -/* helper functions used when doing re2post() ... regatom() parsing */ -#define EMIT(c) do { \ - if (post_ptr >= post_end) { \ - realloc_post_list(); \ - } \ - *post_ptr++ = c; \ -} while (0) +// Helper functions used when doing re2post() ... regatom() parsing +#define EMIT(c) \ + do { \ + if (post_ptr >= post_end) { \ + realloc_post_list(); \ + } \ + *post_ptr++ = c; \ + } while (0) /* * Initialize internal variables before NFA compilation. @@ -2892,12 +2893,11 @@ static nfa_state_T *post2nfa(int *postfix, int *end, int nfa_calc_size) return NULL; #define PUSH(s) st_push((s), &stackp, stack_end) -#define POP() st_pop(&stackp, stack); \ - if (stackp < stack) \ - { \ - st_error(postfix, end, p); \ - xfree(stack); \ - return NULL; \ +#define POP() st_pop(&stackp, stack); \ + if (stackp < stack) { \ + st_error(postfix, end, p); \ + xfree(stack); \ + return NULL; \ } if (nfa_calc_size == FALSE) { @@ -4904,10 +4904,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, } else addstate(thislist, start, m, NULL, 0); -#define ADD_STATE_IF_MATCH(state) \ - if (result) { \ - add_state = state->out; \ - add_off = clen; \ +#define ADD_STATE_IF_MATCH(state) \ + if (result) { \ + add_state = state->out; \ + add_off = clen; \ } /* diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 34eef83164..d67142822f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3416,12 +3416,10 @@ win_line ( /* * Handling of non-printable characters. */ - if (!(chartab[c & 0xff] & CT_PRINT_CHAR)) { - /* - * when getting a character from the file, we may have to - * turn it into something else on the way to putting it - * into "ScreenLines". - */ + if (!vim_isprintc(c)) { + // when getting a character from the file, we may have to + // turn it into something else on the way to putting it + // into "ScreenLines". if (c == TAB && (!wp->w_p_list || lcs_tab1)) { int tab_len = 0; long vcol_adjusted = vcol; // removed showbreak length diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 51c8597d53..b5921eb810 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -5,7 +5,6 @@ #include <stdint.h> #include <inttypes.h> #include <errno.h> -#include <fcntl.h> #include <assert.h> #include <msgpack.h> @@ -36,6 +35,7 @@ #include "nvim/version.h" #include "nvim/path.h" #include "nvim/fileio.h" +#include "nvim/os/fileio.h" #include "nvim/strings.h" #include "nvim/quickfix.h" #include "nvim/eval/encode.h" @@ -409,7 +409,7 @@ typedef struct sd_read_def { ShaDaFileSkipper skip; ///< Function used to skip some bytes. void *cookie; ///< Data describing object read from. bool eof; ///< True if reader reached end of file. - char *error; ///< Error message in case of error. + const char *error; ///< Error message in case of error. uintmax_t fpos; ///< Current position (amount of bytes read since ///< reader structure initialization). May overflow. vimconv_T sd_conv; ///< Structure used for converting encodings of some @@ -433,7 +433,7 @@ typedef struct sd_write_def { ShaDaFileWriter write; ///< Writer function. ShaDaWriteCloser close; ///< Close function. void *cookie; ///< Data describing object written to. - char *error; ///< Error message in case of error. + const char *error; ///< Error message in case of error. vimconv_T sd_conv; ///< Structure used for converting encodings of some ///< items. } ShaDaWriteDef; @@ -666,38 +666,14 @@ static ptrdiff_t read_file(ShaDaReadDef *const sd_reader, void *const dest, const size_t size) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - size_t read_bytes = 0; - bool did_try_to_free = false; - const int fd = (int)(intptr_t) sd_reader->cookie; - while (read_bytes != size) { - const ptrdiff_t cur_read_bytes = read(fd, ((char *) dest) + read_bytes, - size - read_bytes); - if (cur_read_bytes > 0) { - read_bytes += (size_t) cur_read_bytes; - sd_reader->fpos += (uintmax_t) cur_read_bytes; - assert(read_bytes <= size); - } - if (cur_read_bytes < 0) { - if (errno == EINTR || errno == EAGAIN) { - errno = 0; - continue; - } else if (errno == ENOMEM && !did_try_to_free) { - try_to_free_memory(); - did_try_to_free = true; - errno = 0; - continue; - } else { - sd_reader->error = strerror(errno); - errno = 0; - return -1; - } - } - if (cur_read_bytes == 0) { - sd_reader->eof = true; - break; - } + const ptrdiff_t ret = file_read(sd_reader->cookie, dest, size); + sd_reader->eof = file_eof(sd_reader->cookie); + if (ret < 0) { + sd_reader->error = os_strerror((int)ret); + return -1; } - return (ptrdiff_t) read_bytes; + sd_reader->fpos += (size_t)ret; + return ret; } /// Read one character @@ -720,50 +696,26 @@ static ptrdiff_t write_file(ShaDaWriteDef *const sd_writer, const size_t size) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - size_t written_bytes = 0; - const int fd = (int)(intptr_t) sd_writer->cookie; - while (written_bytes != size) { - const ptrdiff_t cur_written_bytes = write(fd, (char *) dest + written_bytes, - size - written_bytes); - if (cur_written_bytes > 0) { - written_bytes += (size_t) cur_written_bytes; - } - if (cur_written_bytes < 0) { - if (errno == EINTR || errno == EAGAIN) { - errno = 0; - continue; - } else { - sd_writer->error = strerror(errno); - errno = 0; - return -1; - } - } - if (cur_written_bytes == 0) { - sd_writer->error = "Zero bytes written."; - return -1; - } + const ptrdiff_t ret = file_write(sd_writer->cookie, dest, size); + if (ret < 0) { + sd_writer->error = os_strerror((int)ret); + return -1; } - return (ptrdiff_t) written_bytes; + return ret; } /// Wrapper for closing file descriptors opened for reading static void close_sd_reader(ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ALL { - close_file((int)(intptr_t) sd_reader->cookie); + close_file(sd_reader->cookie); } /// Wrapper for closing file descriptors opened for writing static void close_sd_writer(ShaDaWriteDef *const sd_writer) FUNC_ATTR_NONNULL_ALL { - const int fd = (int)(intptr_t) sd_writer->cookie; - if (os_fsync(fd) < 0) { - emsgf(_(SERR "System error while synchronizing ShaDa file: %s"), - os_strerror(errno)); - errno = 0; - } - close_file(fd); + close_file(sd_writer->cookie); } /// Wrapper for read that reads to IObuff and ignores bytes read @@ -779,19 +731,20 @@ static int sd_reader_skip_read(ShaDaReadDef *const sd_reader, const size_t offset) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - size_t read_bytes = 0; - do { - ptrdiff_t new_read_bytes = sd_reader->read( - sd_reader, IObuff, (size_t) (offset - read_bytes > IOSIZE - ? IOSIZE - : offset - read_bytes)); - if (new_read_bytes == -1) { - return FAIL; + const ptrdiff_t skip_bytes = file_skip(sd_reader->cookie, offset); + if (skip_bytes < 0) { + sd_reader->error = os_strerror((int)skip_bytes); + return FAIL; + } else if (skip_bytes != (ptrdiff_t)offset) { + assert(skip_bytes < (ptrdiff_t)offset); + sd_reader->eof = file_eof(sd_reader->cookie); + if (!sd_reader->eof) { + sd_reader->error = _("too few bytes read"); } - read_bytes += (size_t) new_read_bytes; - } while (read_bytes < offset && !sd_reader->eof); - - return (read_bytes == offset ? OK : FAIL); + return FAIL; + } + sd_reader->fpos += (size_t)skip_bytes; + return OK; } /// Wrapper for read that can be used when lseek cannot be used @@ -824,37 +777,6 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, return kSDReadStatusSuccess; } -/// Wrapper for opening file descriptors -/// -/// All arguments are passed to os_open(). -/// -/// @return file descriptor or libuv error on failure. -static int open_file(const char *const fname, const int flags, const int mode) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - bool did_try_to_free = false; - int fd; -open_file_start: - fd = os_open(fname, flags, mode); - - if (fd < 0) { - if (fd == UV_ENOENT) { - return fd; - } - if (fd == UV_ENOMEM && !did_try_to_free) { - try_to_free_memory(); - did_try_to_free = true; - goto open_file_start; - } - if (fd != UV_EEXIST) { - emsgf(_(SERR "System error while opening ShaDa file %s: %s"), - fname, os_strerror(fd)); - } - return fd; - } - return fd; -} - /// Open ShaDa file for reading /// /// @param[in] fname File name to open. @@ -865,11 +787,7 @@ static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd_reader) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - const intptr_t fd = (intptr_t) open_file(fname, O_RDONLY, 0); - - if (fd < 0) { - return (int) fd; - } + int error; *sd_reader = (ShaDaReadDef) { .read = &read_file, @@ -878,8 +796,11 @@ static int open_shada_file_for_reading(const char *const fname, .error = NULL, .eof = false, .fpos = 0, - .cookie = (void *) fd, + .cookie = file_open_new(&error, fname, kFileReadOnly, 0), }; + if (sd_reader->cookie == NULL) { + return error; + } convert_setup(&sd_reader->sd_conv, "utf-8", p_enc); @@ -887,18 +808,12 @@ static int open_shada_file_for_reading(const char *const fname, } /// Wrapper for closing file descriptors -static void close_file(int fd) +static void close_file(void *cookie) { -close_file_start: - if (close(fd) == -1) { - if (errno == EINTR) { - errno = 0; - goto close_file_start; - } else { - emsgf(_(SERR "System error while closing ShaDa file: %s"), - strerror(errno)); - errno = 0; - } + const int error = file_free(cookie); + if (error != 0) { + emsgf(_(SERR "System error while closing ShaDa file: %s"), + os_strerror(error)); } } @@ -978,7 +893,7 @@ static int shada_read_file(const char *const file, const int flags) } if (of_ret != 0) { - if (of_ret == UV_ENOENT && (flags & kShaDaMissingError)) { + if (of_ret != UV_ENOENT || (flags & kShaDaMissingError)) { emsgf(_(SERR "System error while opening ShaDa file %s for reading: %s"), fname, os_strerror(of_ret)); } @@ -1421,8 +1336,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } } if (!op_register_set(cur_entry.data.reg.name, (yankreg_T) { - .y_array = (char_u **) cur_entry.data.reg.contents, - .y_size = (linenr_T) cur_entry.data.reg.contents_size, + .y_array = (char_u **)cur_entry.data.reg.contents, + .y_size = cur_entry.data.reg.contents_size, .y_type = cur_entry.data.reg.type, .y_width = (colnr_T) cur_entry.data.reg.width, .timestamp = cur_entry.timestamp, @@ -2745,7 +2660,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (name == NUL) { break; } - if (limit_reg_lines && reg.y_size > max_reg_lines) { + if (limit_reg_lines && reg.y_size > (size_t)max_reg_lines) { continue; } wms->registers[op_reg_index(name)] = (PossiblyFreedShadaEntry) { @@ -2968,17 +2883,23 @@ int shada_write_file(const char *const file, bool nomerge) char *const fname = shada_filename(file); char *tempname = NULL; - ShaDaWriteDef sd_writer = (ShaDaWriteDef) { + ShaDaWriteDef sd_writer = { .write = &write_file, .close = &close_sd_writer, .error = NULL, }; - ShaDaReadDef sd_reader; - - intptr_t fd; + ShaDaReadDef sd_reader = { .close = NULL }; if (!nomerge) { - if (open_shada_file_for_reading(fname, &sd_reader) != 0) { + int error; + if ((error = open_shada_file_for_reading(fname, &sd_reader)) != 0) { + if (error != UV_ENOENT) { + emsgf(_(SERR "System error while opening ShaDa file %s for reading " + "to merge before writing it: %s"), + fname, os_strerror(error)); + // Try writing the file even if opening it emerged any issues besides + // file not existing: maybe writing will succeed nevertheless. + } nomerge = true; goto shada_write_file_nomerge; } @@ -2996,15 +2917,11 @@ int shada_write_file(const char *const file, bool nomerge) // 2: Make sure that user can always read and write the result. // 3: If somebody happened to delete the file after it was opened for // reading use u=rw permissions. -shada_write_file_open: - fd = (intptr_t) open_file(tempname, O_CREAT|O_WRONLY|O_NOFOLLOW|O_EXCL, - perm); - if (fd < 0) { - if (fd == UV_EEXIST -#ifdef ELOOP - || fd == UV_ELOOP -#endif - ) { +shada_write_file_open: {} + sd_writer.cookie = file_open_new( + &error, tempname, kFileCreateOnly|kFileNoSymlink, perm); + if (sd_writer.cookie == NULL) { + if (error == UV_EEXIST || error == UV_ELOOP) { // File already exists, try another name char *const wp = tempname + strlen(tempname) - 1; if (*wp == 'z') { @@ -3014,11 +2931,16 @@ shada_write_file_open: fname); xfree(fname); xfree(tempname); + assert(sd_reader.close != NULL); + sd_reader.close(&sd_reader); return FAIL; } else { (*wp)++; goto shada_write_file_open; } + } else { + emsgf(_(SERR "System error while opening temporary ShaDa file %s " + "for writing: %s"), tempname, os_strerror(error)); } } } @@ -3042,23 +2964,29 @@ shada_write_file_nomerge: {} } *tail = tail_save; } - fd = (intptr_t) open_file(fname, O_CREAT|O_WRONLY|O_TRUNC, - 0600); - } - - if (p_verbose > 0) { - verbose_enter(); - smsg(_("Writing ShaDa file \"%s\""), fname); - verbose_leave(); + int error; + sd_writer.cookie = file_open_new(&error, fname, kFileCreate|kFileTruncate, + 0600); + if (sd_writer.cookie == NULL) { + emsgf(_(SERR "System error while opening ShaDa file %s for writing: %s"), + fname, os_strerror(error)); + } } - if (fd < 0) { + if (sd_writer.cookie == NULL) { xfree(fname); xfree(tempname); + if (sd_reader.close != NULL) { + sd_reader.close(&sd_reader); + } return FAIL; } - sd_writer.cookie = (void *) fd; + if (p_verbose > 0) { + verbose_enter(); + smsg(_("Writing ShaDa file \"%s\""), fname); + verbose_leave(); + } convert_setup(&sd_writer.sd_conv, p_enc, "utf-8"); @@ -3066,15 +2994,11 @@ shada_write_file_nomerge: {} ? NULL : &sd_reader)); assert(sw_ret != kSDWriteIgnError); -#ifndef UNIX - sd_writer.close(&sd_writer); -#endif if (!nomerge) { sd_reader.close(&sd_reader); bool did_remove = false; if (sw_ret == kSDWriteSuccessfull) { #ifdef UNIX - bool closed = false; // For Unix we check the owner of the file. It's not very nice to // overwrite a user’s viminfo file after a "su root", with a // viminfo file that the user can't read. @@ -3083,16 +3007,15 @@ shada_write_file_nomerge: {} if (getuid() == ROOT_UID) { if (old_info.stat.st_uid != ROOT_UID || old_info.stat.st_gid != getgid()) { - const uv_uid_t old_uid = (uv_uid_t) old_info.stat.st_uid; - const uv_gid_t old_gid = (uv_gid_t) old_info.stat.st_gid; - const int fchown_ret = os_fchown((int) fd, old_uid, old_gid); - sd_writer.close(&sd_writer); + const uv_uid_t old_uid = (uv_uid_t)old_info.stat.st_uid; + const uv_gid_t old_gid = (uv_gid_t)old_info.stat.st_gid; + const int fchown_ret = os_fchown(file_fd(sd_writer.cookie), + old_uid, old_gid); if (fchown_ret != 0) { EMSG3(_(RNERR "Failed setting uid and gid for file %s: %s"), tempname, os_strerror(fchown_ret)); goto shada_write_file_did_not_remove; } - closed = true; } } else if (!(old_info.stat.st_uid == getuid() ? (old_info.stat.st_mode & 0200) @@ -3100,13 +3023,9 @@ shada_write_file_nomerge: {} ? (old_info.stat.st_mode & 0020) : (old_info.stat.st_mode & 0002)))) { EMSG2(_("E137: ShaDa file is not writable: %s"), fname); - sd_writer.close(&sd_writer); goto shada_write_file_did_not_remove; } } - if (!closed) { - sd_writer.close(&sd_writer); - } #endif if (vim_rename(tempname, fname) == -1) { EMSG3(_(RNERR "Can't rename ShaDa file from %s to %s!"), @@ -3133,6 +3052,7 @@ shada_write_file_did_not_remove: } xfree(tempname); } + sd_writer.close(&sd_writer); xfree(fname); return OK; @@ -3262,20 +3182,20 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { const ptrdiff_t read_bytes = sd_reader->read(sd_reader, buffer, length); - (void) read_bytes; - if (sd_reader->error != NULL) { - emsgf(_(SERR "System error while reading ShaDa file: %s"), - sd_reader->error); - return kSDReadStatusReadError; - } else if (sd_reader->eof) { - emsgf(_(RCERR "Error while reading ShaDa file: " - "last entry specified that it occupies %" PRIu64 " bytes, " - "but file ended earlier"), - (uint64_t) length); - return kSDReadStatusNotShaDa; + if (read_bytes != (ptrdiff_t)length) { + if (sd_reader->error != NULL) { + emsgf(_(SERR "System error while reading ShaDa file: %s"), + sd_reader->error); + return kSDReadStatusReadError; + } else { + emsgf(_(RCERR "Error while reading ShaDa file: " + "last entry specified that it occupies %" PRIu64 " bytes, " + "but file ended earlier"), + (uint64_t)length); + return kSDReadStatusNotShaDa; + } } - assert(read_bytes >= 0 && (size_t) read_bytes == length); return kSDReadStatusSuccess; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 84906a3548..610fb660e7 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2317,16 +2317,14 @@ static void spell_load_lang(char_u *lang) for (round = 1; round <= 2; ++round) { // Find the first spell file for "lang" in 'runtimepath' and load it. vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, - "spell/%s.%s.spl", - lang, spell_enc()); - r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &sl); + "spell/%s.%s.spl", lang, spell_enc()); + r = do_in_runtimepath(fname_enc, 0, spell_load_cb, &sl); if (r == FAIL && *sl.sl_lang != NUL) { // Try loading the ASCII version. vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, - "spell/%s.ascii.spl", - lang); - r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &sl); + "spell/%s.ascii.spl", lang); + r = do_in_runtimepath(fname_enc, 0, spell_load_cb, &sl); if (r == FAIL && *sl.sl_lang != NUL && round == 1 && apply_autocmds(EVENT_SPELLFILEMISSING, lang, @@ -2354,7 +2352,7 @@ static void spell_load_lang(char_u *lang) } else if (sl.sl_slang != NULL) { // At least one file was loaded, now load ALL the additions. STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl"); - do_in_runtimepath(fname_enc, TRUE, spell_load_cb, &sl); + do_in_runtimepath(fname_enc, DIP_ALL, spell_load_cb, &sl); } } @@ -7630,7 +7628,7 @@ mkspell ( else { // Check for overwriting before doing things that may take a lot of // time. - if (!over_write && os_file_exists(wfname)) { + if (!over_write && os_path_exists(wfname)) { EMSG(_(e_exists)); goto theend; } @@ -7686,7 +7684,7 @@ mkspell ( spin.si_region = 1 << i; vim_snprintf((char *)fname, MAXPATHL, "%s.aff", innames[i]); - if (os_file_exists(fname)) { + if (os_path_exists(fname)) { // Read the .aff file. Will init "spin->si_conv" based on the // "SET" line. afile[i] = spell_read_aff(&spin, fname); @@ -9319,7 +9317,56 @@ static void suggest_try_special(suginfo_T *su) } } +// Measure how much time is spent in each state. +// Output is dumped in "suggestprof". + +#ifdef SUGGEST_PROFILE +proftime_T current; +proftime_T total; +proftime_T times[STATE_FINAL + 1]; +long counts[STATE_FINAL + 1]; + + static void +prof_init(void) +{ + for (int i = 0; i <= STATE_FINAL; i++) { + profile_zero(×[i]); + counts[i] = 0; + } + profile_start(¤t); + profile_start(&total); +} + +// call before changing state + static void +prof_store(state_T state) +{ + profile_end(¤t); + profile_add(×[state], ¤t); + counts[state]++; + profile_start(¤t); +} +# define PROF_STORE(state) prof_store(state); + + static void +prof_report(char *name) +{ + FILE *fd = fopen("suggestprof", "a"); + + profile_end(&total); + fprintf(fd, "-----------------------\n"); + fprintf(fd, "%s: %s\n", name, profile_msg(&total)); + for (int i = 0; i <= STATE_FINAL; i++) { + fprintf(fd, "%d: %s ("%" PRId64)\n", i, profile_msg(×[i]), counts[i]); + } + fclose(fd); +} +#else +# define PROF_STORE(state) +#endif + // Try finding suggestions by adding/removing/swapping letters. + static void suggest_try_change(suginfo_T *su) { char_u fword[MAXWLEN]; // copy of the bad word, case-folded @@ -9344,7 +9391,14 @@ static void suggest_try_change(suginfo_T *su) continue; // Try it for this language. Will add possible suggestions. + // +#ifdef SUGGEST_PROFILE + prof_init(); +#endif suggest_trie_walk(su, lp, fword, false); +#ifdef SUGGEST_PROFILE + prof_report("try_change"); +#endif } } @@ -9478,6 +9532,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Always past NUL bytes now. n = (int)sp->ts_state; + PROF_STORE(sp->ts_state) sp->ts_state = STATE_ENDNUL; sp->ts_save_badflags = su->su_badflags; @@ -9517,6 +9572,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_curi > len || byts[arridx] != 0) { // Past bytes in node and/or past NUL bytes. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_ENDNUL; sp->ts_save_badflags = su->su_badflags; break; @@ -9870,6 +9926,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so #endif // Save things to be restored at STATE_SPLITUNDO. sp->ts_save_badflags = su->su_badflags; + PROF_STORE(sp->ts_state) sp->ts_state = STATE_SPLITUNDO; ++depth; @@ -9936,6 +9993,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so byts = pbyts; idxs = pidxs; sp->ts_prefixdepth = PFD_PREFIXTREE; + PROF_STORE(sp->ts_state) sp->ts_state = STATE_NOPREFIX; } } @@ -9948,6 +10006,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so su->su_badflags = sp->ts_save_badflags; // Continue looking for NUL bytes. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_START; // In case we went into the prefix tree. @@ -9962,9 +10021,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so && sp->ts_tcharlen == 0 ) { // The badword ends, can't use STATE_PLAIN. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_DEL; break; } + PROF_STORE(sp->ts_state) sp->ts_state = STATE_PLAIN; // FALLTHROUGH @@ -9975,10 +10036,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_curi > byts[arridx]) { // Done all bytes at this node, do next state. When still at // already changed bytes skip the other tricks. - if (sp->ts_fidx >= sp->ts_fidxtry) + PROF_STORE(sp->ts_state) + if (sp->ts_fidx >= sp->ts_fidxtry) { sp->ts_state = STATE_DEL; - else + } else { sp->ts_state = STATE_FINAL; + } } else { arridx += sp->ts_curi++; c = byts[arridx]; @@ -10110,10 +10173,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // When past the first byte of a multi-byte char don't try // delete/insert/swap a character. if (has_mbyte && sp->ts_tcharlen > 0) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_FINAL; break; } // Try skipping one character in the bad word (delete it). + PROF_STORE(sp->ts_state) sp->ts_state = STATE_INS_PREP; sp->ts_curi = 1; if (soundfold && sp->ts_fidx == 0 && fword[sp->ts_fidx] == '*') @@ -10161,6 +10226,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_flags & TSF_DIDDEL) { // If we just deleted a byte then inserting won't make sense, // a substitute is always cheaper. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_SWAP; break; } @@ -10170,11 +10236,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so for (;; ) { if (sp->ts_curi > byts[n]) { // Only NUL bytes at this node, go to next state. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_SWAP; break; } if (byts[n + sp->ts_curi] != NUL) { // Found a byte to insert. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_INS; break; } @@ -10190,6 +10258,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so n = sp->ts_arridx; if (sp->ts_curi > byts[n]) { // Done all bytes at this node, go to next state. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_SWAP; break; } @@ -10250,6 +10319,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so c = *p; if (c == NUL) { // End of word, can't swap or replace. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_FINAL; break; } @@ -10257,6 +10327,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Don't swap if the first character is not a word character. // SWAP3 etc. also don't make sense then. if (!soundfold && !spell_iswordp(p, curwin)) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; break; } @@ -10281,6 +10352,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // When the second character is NUL we can't swap. if (c2 == NUL) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; break; } @@ -10288,6 +10360,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // When characters are identical, swap won't do anything. // Also get here if the second char is not a word character. if (c == c2) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_SWAP3; break; } @@ -10298,6 +10371,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_twordlen, tword, fword + sp->ts_fidx, c, c2); #endif + PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNSWAP; ++depth; if (has_mbyte) { @@ -10312,6 +10386,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so } } else // If this swap doesn't work then SWAP3 won't either. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; break; @@ -10359,6 +10434,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Also get here when the third character is not a word character. // Second character may any char: "a.b" -> "b.a" if (c == c3 || c3 == NUL) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; break; } @@ -10369,6 +10445,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_twordlen, tword, fword + sp->ts_fidx, c, c3); #endif + PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNSWAP3; ++depth; if (has_mbyte) { @@ -10382,8 +10459,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so p[2] = c; stack[depth].ts_fidxtry = sp->ts_fidx + 3; } - } else + } else { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; + } break; case STATE_UNSWAP3: @@ -10409,6 +10488,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (!soundfold && !spell_iswordp(p, curwin)) { // Middle char is not a word char, skip the rotate. First and // third char were already checked at swap and swap3. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; break; } @@ -10423,6 +10503,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_twordlen, tword, fword + sp->ts_fidx, p[0], p[1], p[2]); #endif + PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNROT3L; ++depth; p = fword + sp->ts_fidx; @@ -10441,8 +10522,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so p[2] = c; stack[depth].ts_fidxtry = sp->ts_fidx + 3; } - } else + } else { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; + } break; case STATE_UNROT3L: @@ -10472,6 +10555,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_twordlen, tword, fword + sp->ts_fidx, p[0], p[1], p[2]); #endif + PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNROT3R; ++depth; p = fword + sp->ts_fidx; @@ -10490,8 +10574,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so *p = c; stack[depth].ts_fidxtry = sp->ts_fidx + 3; } - } else + } else { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_INI; + } break; case STATE_UNROT3R: @@ -10521,6 +10607,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if ((lp->lp_replang == NULL && !soundfold) || sp->ts_score + SCORE_REP >= su->su_maxscore || sp->ts_fidx < sp->ts_fidxtry) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_FINAL; break; } @@ -10533,10 +10620,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]]; if (sp->ts_curi < 0) { + PROF_STORE(sp->ts_state) sp->ts_state = STATE_FINAL; break; } + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP; // FALLTHROUGH @@ -10566,6 +10655,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so ftp->ft_from, ftp->ft_to); #endif // Need to undo this afterwards. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP_UNDO; // Change the "from" to the "to" string. @@ -10585,6 +10675,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) // No (more) matches. + PROF_STORE(sp->ts_state) sp->ts_state = STATE_FINAL; break; @@ -10604,6 +10695,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so repextra -= tl - fl; } memmove(p, ftp->ft_from, fl); + PROF_STORE(sp->ts_state) sp->ts_state = STATE_REP; break; @@ -11014,7 +11106,13 @@ static void suggest_try_soundalike(suginfo_T *su) // try all kinds of inserts/deletes/swaps/etc. // TODO: also soundfold the next words, so that we can try joining // and splitting +#ifdef SUGGEST_PROFILE + prof_init(); +#endif suggest_trie_walk(su, lp, salword, true); +#ifdef SUGGEST_PROFILE + prof_report("soundalike"); +#endif } } } @@ -13364,3 +13462,4 @@ int expand_spelling(linenr_T lnum, char_u *pat, char_u ***matchp) return ga.ga_len; } + diff --git a/src/nvim/state.c b/src/nvim/state.c index b2f3f0bebe..30133e2201 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -4,6 +4,7 @@ #include "nvim/state.h" #include "nvim/vim.h" +#include "nvim/main.h" #include "nvim/getchar.h" #include "nvim/ui.h" #include "nvim/os/input.h" @@ -32,7 +33,7 @@ getkey: // processing. Characters can come from mappings, scripts and other // sources, so this scenario is very common. key = safe_vgetc(); - } else if (!queue_empty(loop.events)) { + } else if (!queue_empty(main_loop.events)) { // Event was made available after the last queue_process_events call key = K_EVENT; } else { @@ -45,7 +46,7 @@ getkey: // directly. (void)os_inchar(NULL, 0, -1, 0); input_disable_events(); - key = !queue_empty(loop.events) ? K_EVENT : safe_vgetc(); + key = !queue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); } if (key == K_EVENT) { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 1f9dbd8228..27855184df 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -812,19 +812,39 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) validate_current_state(); } +static void save_chartab(char_u *chartab) +{ + if (syn_block->b_syn_isk != empty_option) { + memmove(chartab, syn_buf->b_chartab, (size_t)32); + memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab, (size_t)32); + } +} + +static void restore_chartab(char_u *chartab) +{ + if (syn_win->w_s->b_syn_isk != empty_option) { + memmove(syn_buf->b_chartab, chartab, (size_t)32); + } +} + /* * Return TRUE if the line-continuation pattern matches in line "lnum". */ static int syn_match_linecont(linenr_T lnum) { - regmmatch_T regmatch; - if (syn_block->b_syn_linecont_prog != NULL) { + regmmatch_T regmatch; + // chartab array for syn iskeyword + char_u buf_chartab[32]; + save_chartab(buf_chartab); + regmatch.rmm_ic = syn_block->b_syn_linecont_ic; regmatch.regprog = syn_block->b_syn_linecont_prog; int r = syn_regexec(®match, lnum, (colnr_T)0, IF_SYN_TIME(&syn_block->b_syn_linecont_time)); syn_block->b_syn_linecont_prog = regmatch.regprog; + + restore_chartab(buf_chartab); return r; } return FALSE; @@ -1617,8 +1637,9 @@ syn_current_attr ( lpos_T pos; int lc_col; reg_extmatch_T *cur_extmatch = NULL; - char_u *line; /* current line. NOTE: becomes invalid after - looking for a pattern match! */ + char_u buf_chartab[32]; // chartab array for syn iskeyword + char_u *line; // current line. NOTE: becomes invalid after + // looking for a pattern match! /* variables for zero-width matches that have a "nextgroup" argument */ int keep_next_list; @@ -1668,6 +1689,9 @@ syn_current_attr ( * avoid matching the same item in the same position twice. */ ga_init(&zero_width_next_ga, (int)sizeof(int), 10); + // use syntax iskeyword option + save_chartab(buf_chartab); + /* * Repeat matching keywords and patterns, to find contained items at the * same column. This stops when there are no extra matches at the current @@ -1992,6 +2016,8 @@ syn_current_attr ( } while (found_match); + restore_chartab(buf_chartab); + /* * Use attributes from the current state, if within its highlighting. * If not, use attributes from the current-but-one state, etc. @@ -2522,7 +2548,8 @@ find_endpos ( regmmatch_T best_regmatch; /* startpos/endpos of best match */ lpos_T pos; char_u *line; - int had_match = FALSE; + int had_match = false; + char_u buf_chartab[32]; // chartab array for syn option iskeyword /* just in case we are invoked for a keyword */ if (idx < 0) @@ -2562,9 +2589,13 @@ find_endpos ( unref_extmatch(re_extmatch_in); re_extmatch_in = ref_extmatch(start_ext); - matchcol = startpos->col; /* start looking for a match at sstart */ - start_idx = idx; /* remember the first END pattern. */ - best_regmatch.startpos[0].col = 0; /* avoid compiler warning */ + matchcol = startpos->col; // start looking for a match at sstart + start_idx = idx; // remember the first END pattern. + best_regmatch.startpos[0].col = 0; // avoid compiler warning + + // use syntax iskeyword option + save_chartab(buf_chartab); + for (;; ) { /* * Find end pattern that matches first after "matchcol". @@ -2707,6 +2738,8 @@ find_endpos ( if (!had_match) m_endpos->lnum = 0; + restore_chartab(buf_chartab); + /* Remove external matches. */ unref_extmatch(re_extmatch_in); re_extmatch_in = NULL; @@ -3027,6 +3060,46 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) redraw_win_later(curwin, NOT_VALID); } +/// Handle ":syntax iskeyword" command. +static void syn_cmd_iskeyword(exarg_T *eap, int syncing) +{ + char_u *arg = eap->arg; + char_u save_chartab[32]; + char_u *save_isk; + + if (eap->skip) { + return; + } + + arg = skipwhite(arg); + if (*arg == NUL) { + MSG_PUTS("\n"); + MSG_PUTS(_("syntax iskeyword ")); + if (curwin->w_s->b_syn_isk != empty_option) { + msg_outtrans(curwin->w_s->b_syn_isk); + } else { + msg_outtrans((char_u *)"not set"); + } + } else { + if (STRNICMP(arg, "clear", 5) == 0) { + memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32); + clear_string_option(&curwin->w_s->b_syn_isk); + } else { + memmove(save_chartab, curbuf->b_chartab, (size_t)32); + save_isk = curbuf->b_p_isk; + curbuf->b_p_isk = vim_strsave(arg); + + buf_init_chartab(curbuf, false); + memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32); + memmove(curbuf->b_chartab, save_chartab, (size_t)32); + clear_string_option(&curwin->w_s->b_syn_isk); + curwin->w_s->b_syn_isk = curbuf->b_p_isk; + curbuf->b_p_isk = save_isk; + } + } + redraw_win_later(curwin, NOT_VALID); +} + /* * Clear all syntax info for one buffer. */ @@ -3065,6 +3138,7 @@ void syntax_clear(synblock_T *block) xfree(block->b_syn_linecont_pat); block->b_syn_linecont_pat = NULL; block->b_syn_folditems = 0; + clear_string_option(&block->b_syn_isk); /* free the stored states */ syn_stack_free_all(block); @@ -3107,6 +3181,7 @@ static void syntax_sync_clear(void) curwin->w_s->b_syn_linecont_prog = NULL; xfree(curwin->w_s->b_syn_linecont_pat); curwin->w_s->b_syn_linecont_pat = NULL; + clear_string_option(&curwin->w_s->b_syn_isk); syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ } @@ -3266,6 +3341,7 @@ static void syn_cmd_enable(exarg_T *eap, int syncing) /* * Handle ":syntax reset" command. + * It actually resets highlighting, not syntax. */ static void syn_cmd_reset(exarg_T *eap, int syncing) { @@ -4131,9 +4207,10 @@ static void syn_cmd_include(exarg_T *eap, int syncing) current_syn_inc_tag = ++running_syn_inc_tag; prev_toplvl_grp = curwin->w_s->b_syn_topgrp; curwin->w_s->b_syn_topgrp = sgl_id; - if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL - : source_runtime(eap->arg, TRUE) == FAIL) + if (source ? do_source(eap->arg, false, DOSO_NONE) == FAIL + : source_runtime(eap->arg, DIP_ALL) == FAIL) { EMSG2(_(e_notopen), eap->arg); + } curwin->w_s->b_syn_topgrp = prev_toplvl_grp; current_syn_inc_tag = prev_syn_inc_tag; } @@ -5363,24 +5440,25 @@ struct subcommand { static struct subcommand subcommands[] = { - {"case", syn_cmd_case}, - {"clear", syn_cmd_clear}, - {"cluster", syn_cmd_cluster}, - {"conceal", syn_cmd_conceal}, - {"enable", syn_cmd_enable}, - {"include", syn_cmd_include}, - {"keyword", syn_cmd_keyword}, - {"list", syn_cmd_list}, - {"manual", syn_cmd_manual}, - {"match", syn_cmd_match}, - {"on", syn_cmd_on}, - {"off", syn_cmd_off}, - {"region", syn_cmd_region}, - {"reset", syn_cmd_reset}, - {"spell", syn_cmd_spell}, - {"sync", syn_cmd_sync}, - {"", syn_cmd_list}, - {NULL, NULL} + { "case", syn_cmd_case }, + { "clear", syn_cmd_clear }, + { "cluster", syn_cmd_cluster }, + { "conceal", syn_cmd_conceal }, + { "enable", syn_cmd_enable }, + { "include", syn_cmd_include }, + { "iskeyword", syn_cmd_iskeyword }, + { "keyword", syn_cmd_keyword }, + { "list", syn_cmd_list }, + { "manual", syn_cmd_manual }, + { "match", syn_cmd_match }, + { "on", syn_cmd_on }, + { "off", syn_cmd_off }, + { "region", syn_cmd_region }, + { "reset", syn_cmd_reset }, + { "spell", syn_cmd_spell }, + { "sync", syn_cmd_sync }, + { "", syn_cmd_list }, + { NULL, NULL } }; /* @@ -5434,6 +5512,7 @@ void ex_ownsyntax(exarg_T *eap) clear_string_option(&curwin->w_s->b_p_spc); clear_string_option(&curwin->w_s->b_p_spf); clear_string_option(&curwin->w_s->b_p_spl); + clear_string_option(&curwin->w_s->b_syn_isk); } /* save value of b:current_syntax */ @@ -5945,12 +6024,12 @@ init_highlight ( if (get_var_value((char_u *)"g:syntax_on") != NULL) { static int recursive = 0; - if (recursive >= 5) + if (recursive >= 5) { EMSG(_("E679: recursive loop loading syncolor.vim")); - else { - ++recursive; - (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE); - --recursive; + } else { + recursive++; + (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL); + recursive--; } } } @@ -5963,22 +6042,24 @@ int load_colors(char_u *name) { char_u *buf; int retval = FAIL; - static int recursive = FALSE; + static int recursive = false; - /* When being called recursively, this is probably because setting - * 'background' caused the highlighting to be reloaded. This means it is - * working, thus we should return OK. */ - if (recursive) + // When being called recursively, this is probably because setting + // 'background' caused the highlighting to be reloaded. This means it is + // working, thus we should return OK. + if (recursive) { return OK; + } - recursive = TRUE; - buf = xmalloc(STRLEN(name) + 12); - sprintf((char *)buf, "colors/%s.vim", name); - retval = source_runtime(buf, FALSE); + recursive = true; + size_t buflen = STRLEN(name) + 12; + buf = xmalloc(buflen); + snprintf((char *)buf, buflen, "colors/%s.vim", name); + retval = source_runtime(buf, DIP_START + DIP_OPT); xfree(buf); apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf); - recursive = FALSE; + recursive = false; ui_refresh(); return retval; @@ -6874,8 +6955,23 @@ highlight_color ( else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) return NULL; if (modec == 'g') { - if (fg) + if (what[2] == '#' && ui_rgb_attached()) { + if (fg) { + n = HL_TABLE()[id - 1].sg_rgb_fg; + } else if (sp) { + n = HL_TABLE()[id - 1].sg_rgb_sp; + } else { + n = HL_TABLE()[id - 1].sg_rgb_bg; + } + if (n < 0 || n > 0xffffff) { + return NULL; + } + snprintf((char *)name, sizeof(name), "#%06x", n); + return name; + } + if (fg) { return HL_TABLE()[id - 1].sg_rgb_fg_name; + } if (sp) { return HL_TABLE()[id - 1].sg_rgb_sp_name; } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 7885d467d8..dfecfb776d 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2023,9 +2023,8 @@ get_tagfname ( if (first) { ga_clear_strings(&tag_fnames); ga_init(&tag_fnames, (int)sizeof(char_u *), 10); - do_in_runtimepath((char_u *) - "doc/tags doc/tags-??" - , TRUE, found_tagfile_cb, NULL); + do_in_runtimepath((char_u *)"doc/tags doc/tags-??", DIP_ALL, + found_tagfile_cb, NULL); } if (tnp->tn_hf_idx >= tag_fnames.ga_len) { @@ -2353,7 +2352,7 @@ jumpto_tag ( * file. Also accept a file name for which there is a matching BufReadCmd * autocommand event (e.g., http://sys/file). */ - if (!os_file_exists(fname) + if (!os_path_exists(fname) && !has_autocmd(EVENT_BUFREADCMD, fname, NULL) ) { retval = NOTAGFILE; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 104cc47cda..6f50c03be9 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -63,6 +63,7 @@ #include "nvim/map.h" #include "nvim/misc1.h" #include "nvim/move.h" +#include "nvim/main.h" #include "nvim/state.h" #include "nvim/ex_docmd.h" #include "nvim/ex_cmds.h" @@ -163,9 +164,9 @@ static VTermColor default_vt_bg_rgb; void terminal_init(void) { invalidated_terminals = pmap_new(ptr_t)(); - time_watcher_init(&loop, &refresh_timer, NULL); + 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(loop.events); + refresh_timer.events = queue_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) @@ -240,6 +241,7 @@ Terminal *terminal_open(TerminalOptions opts) set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); + buf_set_term_title(curbuf, (char *)curbuf->b_ffname); RESET_BINDING(curwin); // Apply TermOpen autocmds so the user can configure the terminal apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf); @@ -347,15 +349,6 @@ void terminal_resize(Terminal *term, uint16_t width, uint16_t height) height = (uint16_t)curheight; } - // The new width/height are the minimum for all windows that display the - // terminal in the current tab. - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (!wp->w_closing && wp->w_buffer->terminal == term) { - width = (uint16_t)MIN(width, (uint16_t)(wp->w_width - win_col_off(wp))); - height = (uint16_t)MIN(height, (uint16_t)wp->w_height); - } - } - if (curheight == height && curwidth == width) { return; } @@ -452,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(loop.events); + queue_process_events(main_loop.events); s->term->refcount--; if (s->term->buf_handle == 0) { s->close = true; @@ -626,6 +619,17 @@ static int term_movecursor(VTermPos new, VTermPos old, int visible, return 1; } +static void buf_set_term_title(buf_T *buf, char *title) + FUNC_ATTR_NONNULL_ALL +{ + Error err; + api_free_object(dict_set_value(buf->b_vars, + cstr_as_string("term_title"), + STRING_OBJ(cstr_as_string(title)), + false, + &err)); +} + static int term_settermprop(VTermProp prop, VTermValue *val, void *data) { Terminal *term = data; @@ -641,12 +645,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data) case VTERM_PROP_TITLE: { buf_T *buf = handle_get_buffer(term->buf_handle); - Error err; - api_free_object(dict_set_value(buf->b_vars, - cstr_as_string("term_title"), - STRING_OBJ(cstr_as_string(val->string)), - false, - &err)); + buf_set_term_title(buf, val->string); break; } @@ -1158,15 +1157,15 @@ static bool is_focused(Terminal *term) return State & TERM_FOCUS && curbuf->terminal == term; } -#define GET_CONFIG_VALUE(k, o) \ - do { \ - Error err; \ - /* Only called from terminal_open where curbuf->terminal is the */ \ - /* context */ \ - o = dict_get_value(curbuf->b_vars, cstr_as_string(k), &err); \ - if (o.type == kObjectTypeNil) { \ - o = dict_get_value(&globvardict, cstr_as_string(k), &err); \ - } \ +#define GET_CONFIG_VALUE(k, o) \ + do { \ + Error err; \ + /* Only called from terminal_open where curbuf->terminal is the */ \ + /* context */ \ + o = dict_get_value(curbuf->b_vars, cstr_as_string(k), &err); \ + if (o.type == kObjectTypeNil) { \ + o = dict_get_value(&globvardict, cstr_as_string(k), &err); \ + } \ } while (0) static char *get_config_string(char *key) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 867cab9fbf..4b0b5e8d26 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -9,7 +9,6 @@ SCRIPTSOURCE := ../../../runtime SCRIPTS := \ test8.out \ - test10.out \ test12.out \ test13.out \ test14.out \ @@ -17,7 +16,6 @@ SCRIPTS := \ test24.out \ test30.out \ test32.out \ - test34.out \ test37.out \ test40.out \ test42.out \ @@ -26,7 +24,6 @@ SCRIPTS := \ test49.out \ test52.out \ test53.out \ - test55.out \ test64.out \ test69.out \ test73.out \ @@ -37,9 +34,13 @@ SCRIPTS := \ # Keep test_alot*.res as the last one, sort the others. NEW_TESTS = \ test_cursor_func.res \ + test_hardcopy.res \ test_help_tagjump.res \ + test_langmap.res \ test_menu.res \ + test_syntax.res \ test_timers.res \ + test_unlet.res \ test_viml.res \ test_alot.res @@ -59,7 +60,7 @@ ifdef USE_VALGRIND TOOL := valgrind -q \ -q \ $(VALGRIND_TOOL) \ - --suppressions=../../../.valgrind.supp \ + --suppressions=../../.valgrind.supp \ --error-exitcode=123 \ --log-file=valgrind.\%p.$* \ $(VGDB) \ diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 2712fb9371..1b1f5d7688 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -39,6 +39,9 @@ set nomore " Output all messages in English. lang mess C +" Always use forward slashes. +set shellslash + " Source the test script. First grab the file name, in case the script " navigates away. let testname = expand('%') @@ -61,12 +64,13 @@ endif " Locate Test_ functions and execute them. set nomore redir @q -function /^Test_ +silent function /^Test_ redir END let tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) " Execute the tests in alphabetical order. for test in sort(tests) + echo 'Executing ' . test if exists("*SetUp") call SetUp() endif diff --git a/src/nvim/testdir/test10.in b/src/nvim/testdir/test10.in deleted file mode 100644 index 2178cf41ce..0000000000 --- a/src/nvim/testdir/test10.in +++ /dev/null @@ -1,110 +0,0 @@ -Test for 'errorformat'. This will fail if the quickfix feature was disabled. - -STARTTEST -:7/start of errorfile/,/end of errorfile/w! Xerrorfile1 -:7/start of errorfile/,/end of errorfile/-1w! Xerrorfile2 -:/start of testfile/,/end of testfile/w! Xtestfile -:set efm+==%f=\\,\ line\ %l%*\\D%v%*[^\ ]\ %m -:set efm^=%AError\ in\ \"%f\"\ at\ line\ %l:,%Z%p^,%C%m -:cf Xerrorfile2 -:clast -:copen -:let a=w:quickfix_title -:wincmd p -lgR=a
-:cf Xerrorfile1 -grA -:cn -gRLINE 6, COL 19 -:cn -gRNO COLUMN SPECIFIED -:cn -gRAGAIN NO COLUMN -:cn -gRCOL 1 -:cn -gRCOL 2 -:cn -gRCOL 10 -:cn -gRVCOL 10 -:cn -grI -:cn -gR. SPACE POINTER -:cn -gR. DOT POINTER -:cn -gR. DASH POINTER -:cn -gR. TAB-SPACE POINTER -:clast -:cprev -:cprev -:wincmd w -:let a=w:quickfix_title -:wincmd p -lgR=a
-:w! test.out " Write contents of this file -:qa! -ENDTEST - -start of errorfile -"Xtestfile", line 4.12: 1506-045 (S) Undeclared identifier fd_set. -"Xtestfile", line 6 col 19; this is an error -gcc -c -DHAVE_CONFIsing-prototypes -I/usr/X11R6/include version.c -Xtestfile:9: parse error before `asd' -make: *** [vim] Error 1 -in file "Xtestfile" linenr 10: there is an error - -2 returned -"Xtestfile", line 11 col 1; this is an error -"Xtestfile", line 12 col 2; this is another error -"Xtestfile", line 14:10; this is an error in column 10 -=Xtestfile=, line 15:10; this is another error, but in vcol 10 this time -"Xtestfile", linenr 16: yet another problem -Error in "Xtestfile" at line 17: -x should be a dot - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 17 - ^ -Error in "Xtestfile" at line 18: -x should be a dot - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 18 -.............^ -Error in "Xtestfile" at line 19: -x should be a dot - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 19 ---------------^ -Error in "Xtestfile" at line 20: -x should be a dot - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20 - ^ - -Does anyone know what is the problem and how to correction it? -"Xtestfile", line 21 col 9: What is the title of the quickfix window? -"Xtestfile", line 22 col 9: What is the title of the quickfix window? -end of errorfile - -start of testfile - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 2 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 3 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 4 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 5 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 6 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 7 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 8 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 9 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 10 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 11 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 12 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 13 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 14 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 15 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 16 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 17 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 18 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 19 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 21 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 22 -end of testfile diff --git a/src/nvim/testdir/test10.ok b/src/nvim/testdir/test10.ok deleted file mode 100644 index 76a02f40b4..0000000000 --- a/src/nvim/testdir/test10.ok +++ /dev/null @@ -1,23 +0,0 @@ -start of testfile - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 2 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 3 - xxxxxxxxxxAxxxxxxxxxxxxxxxxxxx line 4 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 5 - xxxxxxxxxxxxxxxxxLINE 6, COL 19 line 6 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 7 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 8 - NO COLUMN SPECIFIEDxxxxxxxxxxx line 9 - AGAIN NO COLUMNxxxxxxxxxxxxxxx line 10 -COL 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 11 - COL 2xxxxxxxxxxxxxxxxxxxxxxxxx line 12 - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 13 - xxxxxxxxCOL 10xxxxxxxxxxxxxxxx line 14 - xVCOL 10xxxxxxxxxxxxxxxxxxxxxx line 15 - Ixxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 16 - xxxx. SPACE POINTERxxxxxxxxxxx line 17 - xxxxx. DOT POINTERxxxxxxxxxxxx line 18 - xxxxxx. DASH POINTERxxxxxxxxxx line 19 - xxxxxxx. TAB-SPACE POINTERxxxx line 20 - xxxxxxxx:cf Xerrorfile1xxxxxxx line 21 - xxxxxxxx:cf Xerrorfile2xxxxxxx line 22 -end of testfile diff --git a/src/nvim/testdir/test10a.in b/src/nvim/testdir/test10a.in deleted file mode 100644 index 99a5a03db8..0000000000 --- a/src/nvim/testdir/test10a.in +++ /dev/null @@ -1,72 +0,0 @@ -Test for 'errorformat'. - -STARTTEST -:/start of errorfile/,/end of errorfile/w! Xerrorfile -:/start of testfile/,/end of testfile/w! Xtestfile -:cf Xerrorfile -rA -:cn -rB -:cn -rC -:cn -rD -:cn -rE -:w! test.out " Write contents of this file -:qa! -ENDTEST - -start of errorfile - - printf(" %d \n", (number/other)%10 ); -..................^ -%CC-E-NOSEMI, Missing ";". -at line number 4 in file SYS$DISK:XTESTFILE - - other=10000000; -.............^ -%CC-E-UNDECLARED, In this statement, "oszt" is not declared. -at line number 7 in file SYS$DISK:XTESTFILE - - for (i = 0; i<7 ; i++ ){ -..................^ -%CC-E-UNDECLARED, In this statement, "i" is not declared. -at line number 16 in file SYS$DISK:XTESTFILE - -some other error somewhere here. -...........................^ -%CC-W-WARRING, Sorry, but no expalnation for such an warring. -at line number 19 in file SYS$DISK:XTESTFILE - -and finally some other error exactly here. -.....................................^ -%CC-I-INFORMATIONAL, It should be some informational message. -at line number 20 in file SYS$DISK:XTESTFILE - -Does anyone know what is the problem and how to correct ?? :) -end of errorfile - -start of testfile -01234567890123456789012345678901234567 -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 7 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 8 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 10 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 11 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 12 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 13 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 14 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 15 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 16 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 17 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 18 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 19 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 20 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 21 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 22 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -end of testfile diff --git a/src/nvim/testdir/test10a.ok b/src/nvim/testdir/test10a.ok deleted file mode 100644 index 10e78c9239..0000000000 --- a/src/nvim/testdir/test10a.ok +++ /dev/null @@ -1,23 +0,0 @@ -start of testfile -01234567890123456789012345678901234567 -line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 4 xxxxxxxxxxAxxxxxxxxxxxxxxxxxxx -line 5 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 7 xxxxxBxxxxxxxxxxxxxxxxxxxxxxxx -line 8 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 9 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 10 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 11 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 12 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 13 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 14 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 15 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 16 xxxxxxxxxxCxxxxxxxxxxxxxxxxxxx -line 17 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 18 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 19 xxxxxxxxxxxxxxxxxxxDxxxxxxxxxx -line 20 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxE -line 21 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -line 22 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -end of testfile diff --git a/src/nvim/testdir/test34.in b/src/nvim/testdir/test34.in deleted file mode 100644 index 4cb7e9494a..0000000000 --- a/src/nvim/testdir/test34.in +++ /dev/null @@ -1,86 +0,0 @@ -Test for user functions. -Also test an <expr> mapping calling a function. -Also test that a builtin function cannot be replaced. -Also test for regression when calling arbitrary expression. - -STARTTEST -:function Table(title, ...) -: let ret = a:title -: let idx = 1 -: while idx <= a:0 -: exe "let ret = ret . a:" . idx -: let idx = idx + 1 -: endwhile -: return ret -:endfunction -:function Compute(n1, n2, divname) -: if a:n2 == 0 -: return "fail" -: endif -: exe "let g:" . a:divname . " = ". a:n1 / a:n2 -: return "ok" -:endfunction -:func Expr1() -: normal! v -: return "111" -:endfunc -:func Expr2() -: call search('XX', 'b') -: return "222" -:endfunc -:func ListItem() -: let g:counter += 1 -: return g:counter . '. ' -:endfunc -:func ListReset() -: let g:counter = 0 -: return '' -:endfunc -:func FuncWithRef(a) -: unlet g:FuncRef -: return a:a -:endfunc -:let g:FuncRef=function("FuncWithRef") -:let counter = 0 -:inoremap <expr> ( ListItem() -:inoremap <expr> [ ListReset() -:imap <expr> + Expr1() -:imap <expr> * Expr2() -:let retval = "nop" -/^here -C=Table("xxx", 4, "asdf") - =Compute(45, 0, "retval") - =retval - =Compute(45, 5, "retval") - =retval - =g:FuncRef(333) - -XX+-XX ----*--- -(one -(two -[(one again:call append(line('$'), max([1, 2, 3])) -:call extend(g:, {'max': function('min')}) -:call append(line('$'), max([1, 2, 3])) -:try -: " Regression: the first line below used to throw ?E110: Missing ')'? -: " Second is here just to prove that this line is correct when not skipping -: " rhs of &&. -: $put =(0&&(function('tr'))(1, 2, 3)) -: $put =(1&&(function('tr'))(1, 2, 3)) -:catch -: $put ='!!! Unexpected exception:' -: $put =v:exception -:endtry -:$-9,$w! test.out -:delfunc Table -:delfunc Compute -:delfunc Expr1 -:delfunc Expr2 -:delfunc ListItem -:delfunc ListReset -:unlet retval counter -:q! -ENDTEST - -here diff --git a/src/nvim/testdir/test34.ok b/src/nvim/testdir/test34.ok deleted file mode 100644 index 97995de80e..0000000000 --- a/src/nvim/testdir/test34.ok +++ /dev/null @@ -1,10 +0,0 @@ -xxx4asdf fail nop ok 9 333 -XX111-XX ----222--- -1. one -2. two -1. one again -3 -3 -0 -1 diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in deleted file mode 100644 index 9a55eac6f6..0000000000 --- a/src/nvim/testdir/test55.in +++ /dev/null @@ -1,600 +0,0 @@ -Tests for List and Dictionary types. vim: set ft=vim : - -STARTTEST -:fun Test(...) -:lang C -:" Creating List directly with different types -:let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] -:$put =string(l) -:$put =string(l[-1]) -:$put =string(l[-4]) -:try -: $put =string(l[-5]) -:catch -: $put =v:exception[:14] -:endtry -:" List slices -:$put =string(l[:]) -:$put =string(l[1:]) -:$put =string(l[:-2]) -:$put =string(l[0:8]) -:$put =string(l[8:-1]) -:" -:" List identity -:let ll = l -:let lx = copy(l) -:try -: $put =(l == ll) . (l isnot ll) . (l is ll) . (l == lx) . (l is lx) . (l isnot lx) -:catch -: $put =v:exception -:endtry -:" -:" Creating Dictionary directly with different types -:let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},} -:$put =string(d) . d.1 -:$put =string(sort(keys(d))) -:$put =string (values(d)) -:for [key, val] in items(d) -: $put =key . ':' . string(val) -: unlet key val -:endfor -:call extend (d, {3:33, 1:99}) -:call extend(d, {'b':'bbb', 'c':'ccc'}, "keep") -:try -: call extend(d, {3:333,4:444}, "error") -:catch -: $put =v:exception[:15] . v:exception[-1:-1] -:endtry -:$put =string(d) -:call filter(d, 'v:key =~ ''[ac391]''') -:$put =string(d) -:" -:" Dictionary identity -:let dd = d -:let dx = copy(d) -:try -: $put =(d == dd) . (d isnot dd) . (d is dd) . (d == dx) . (d is dx) . (d isnot dx) -:catch -: $put =v:exception -:endtry -:" -:" Changing var type should fail -:try -: let d = [] -:catch -: $put =v:exception[:14] . v:exception[-1:-1] -:endtry -:try -: let l = {} -:catch -: $put =v:exception[:14] . v:exception[-1:-1] -:endtry -:" -:" removing items with :unlet -:unlet l[2] -:$put =string(l) -:let l = range(8) -:try -:unlet l[:3] -:unlet l[1:] -:catch -:$put =v:exception -:endtry -:$put =string(l) -:" -:unlet d.c -:unlet d[-1] -:$put =string(d) -:" -:" removing items out of range: silently skip items that don't exist -let l = [0, 1, 2, 3] -:unlet l[2:1] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[2:2] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[2:3] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[2:4] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[2:5] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[-1:2] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[-2:2] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[-3:2] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[-4:2] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[-5:2] -:$put =string(l) -let l = [0, 1, 2, 3] -:unlet l[-6:2] -:$put =string(l) -:" -:" assignment to a list -:let l = [0, 1, 2, 3] -:let [va, vb] = l[2:3] -:$put =va -:$put =vb -:try -: let [va, vb] = l -:catch -: $put =v:exception[:14] -:endtry -:try -: let [va, vb] = l[1:1] -:catch -: $put =v:exception[:14] -:endtry -:" -:" manipulating a big Dictionary (hashtable.c has a border of 1000 entries) -:let d = {} -:for i in range(1500) -: let d[i] = 3000 - i -:endfor -:$put =d[0] . ' ' . d[100] . ' ' . d[999] . ' ' . d[1400] . ' ' . d[1499] -:try -: let n = d[1500] -:catch -: $put =substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '') -:endtry -:" lookup each items -:for i in range(1500) -: if d[i] != 3000 - i -: $put =d[i] -: endif -:endfor -: let i += 1 -:" delete even items -:while i >= 2 -: let i -= 2 -: unlet d[i] -:endwhile -:$put =get(d, 1500 - 100, 'NONE') . ' ' . d[1] -:" delete odd items, checking value, one intentionally wrong -:let d[33] = 999 -:let i = 1 -:while i < 1500 -: if d[i] != 3000 - i -: $put =i . '=' . d[i] -: else -: unlet d[i] -: endif -: let i += 2 -:endwhile -:$put =string(d) " must be almost empty now -:unlet d -:" -:" Dictionary function -:let dict = {} -:func dict.func(a) dict -: $put =a:a . len(self.data) -:endfunc -:let dict.data = [1,2,3] -:call dict.func("len: ") -:let x = dict.func("again: ") -:try -: let Fn = dict.func -: call Fn('xxx') -:catch -: $put =v:exception[:15] -:endtry -:" -:" Function in script-local List or Dict -:let g:dict = {} -:function g:dict.func() dict -: $put ='g:dict.func'.self.foo[1].self.foo[0]('asdf') -:endfunc -:let g:dict.foo = ['-', 2, 3] -:call insert(g:dict.foo, function('strlen')) -:call g:dict.func() -:" -:" Nasty: remove func from Dict that's being called (works) -:let d = {1:1} -:func d.func(a) -: return "a:". a:a -:endfunc -:$put =d.func(string(remove(d, 'func'))) -:" -:" Nasty: deepcopy() dict that refers to itself (fails when noref used) -:let d = {1:1, 2:2} -:let l = [4, d, 6] -:let d[3] = l -:let dc = deepcopy(d) -:try -: let dc = deepcopy(d, 1) -:catch -: $put =v:exception[:14] -:endtry -:let l2 = [0, l, l, 3] -:let l[1] = l2 -:let l3 = deepcopy(l2) -:$put ='same list: ' . (l3[1] is l3[2]) -:" -:" Locked variables -:for depth in range(5) -: $put ='depth is ' . depth -: for u in range(3) -: unlet l -: let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] -: exe "lockvar " . depth . " l" -: if u == 1 -: exe "unlockvar l" -: elseif u == 2 -: exe "unlockvar " . depth . " l" -: endif -: let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") -: $put =ps -: let ps = '' -: try -: let l[1][1][0] = 99 -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: let l[1][1] = [99] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: let l[1] = [99] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: let l[2]['6'][7] = 99 -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: let l[2][6] = {99: 99} -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: let l[2] = {99: 99} -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: let l = [99] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: $put =ps -: endfor -:endfor -:" -:" Unletting locked variables -:$put ='Unletting:' -:for depth in range(5) -: $put ='depth is ' . depth -: for u in range(3) -: unlet l -: let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] -: exe "lockvar " . depth . " l" -: if u == 1 -: exe "unlockvar l" -: elseif u == 2 -: exe "unlockvar " . depth . " l" -: endif -: let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") -: $put =ps -: let ps = '' -: try -: unlet l[2]['6'][7] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: unlet l[2][6] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: unlet l[2] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: unlet l[1][1][0] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: unlet l[1][1] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: unlet l[1] -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: try -: unlet l -: let ps .= 'p' -: catch -: let ps .= 'F' -: endtry -: $put =ps -: endfor -:endfor -:" -:" Locked variables and :unlet or list / dict functions -:$put ='Locks and commands or functions:' -:" -:$put ='No :unlet after lock on dict:' -:unlet! d -:let d = {'a': 99, 'b': 100} -:lockvar 1 d -:try -: unlet d.a -: $put ='did :unlet' -:catch -: $put =v:exception[:16] -:endtry -:$put =string(d) -:" -:$put =':unlet after lock on dict item:' -:unlet! d -:let d = {'a': 99, 'b': 100} -:lockvar d.a -:try -: unlet d.a -: $put ='did :unlet' -:catch -: $put =v:exception[:16] -:endtry -:$put =string(d) -:" -:$put ='filter() after lock on dict item:' -:unlet! d -:let d = {'a': 99, 'b': 100} -:lockvar d.a -:try -: call filter(d, 'v:key != "a"') -: $put ='did filter()' -:catch -: $put =v:exception[:16] -:endtry -:$put =string(d) -:" -:$put ='map() after lock on dict:' -:unlet! d -:let d = {'a': 99, 'b': 100} -:lockvar 1 d -:try -: call map(d, 'v:val + 200') -: $put ='did map()' -:catch -: $put =v:exception[:16] -:endtry -:$put =string(d) -:" -:$put ='No extend() after lock on dict item:' -:unlet! d -:let d = {'a': 99, 'b': 100} -:lockvar d.a -:try -: $put =string(extend(d, {'a': 123})) -: $put ='did extend()' -:catch -: $put =v:exception[:14] -:endtry -:$put =string(d) -:" -:$put ='No remove() of write-protected scope-level variable:' -:fun! Tfunc(this_is_a_loooooooooong_parameter_name) -: try -: $put =string(remove(a:, 'this_is_a_loooooooooong_parameter_name')) -: $put ='did remove()' -: catch -: $put =v:exception[:14] -: endtry -:endfun -:call Tfunc('testval') -:" -:$put ='No extend() of write-protected scope-level variable:' -:fun! Tfunc(this_is_a_loooooooooong_parameter_name) -: try -: $put =string(extend(a:, {'this_is_a_loooooooooong_parameter_name': 1234})) -: $put ='did extend()' -: catch -: $put =v:exception[:14] -: endtry -:endfun -:call Tfunc('testval') -:" -:$put ='No :unlet of variable in locked scope:' -:let b:testvar = 123 -:lockvar 1 b: -:try -: unlet b:testvar -: $put ='b:testvar was :unlet: '. (!exists('b:testvar')) -:catch -: $put =v:exception[:16] -:endtry -:unlockvar 1 b: -:unlet! b:testvar -:" -:$put ='No :let += of locked list variable:' -:let l = ['a', 'b', 3] -:lockvar 1 l -:try -: let l += ['x'] -: $put ='did :let +=' -:catch -: $put =v:exception[:14] -:endtry -:$put =string(l) -:" -:unlet l -:let l = [1, 2, 3, 4] -:lockvar! l -:$put =string(l) -:unlockvar l[1] -:unlet l[0:1] -:$put =string(l) -:unlet l[1:2] -:$put =string(l) -:unlockvar l[1] -:let l[0:1] = [0, 1] -:$put =string(l) -:let l[1:2] = [0, 1] -:$put =string(l) -:unlet l -:" :lockvar/islocked() triggering script autoloading -:set rtp+=./sautest -:lockvar g:footest#x -:unlockvar g:footest#x -:$put ='locked g:footest#x:'.islocked('g:footest#x') -:$put ='exists g:footest#x:'.exists('g:footest#x') -:$put ='g:footest#x: '.g:footest#x -:" -:" a:000 function argument -:" first the tests that should fail -:try -: let a:000 = [1, 2] -:catch -: $put ='caught a:000' -:endtry -:try -: let a:000[0] = 9 -:catch -: $put ='caught a:000[0]' -:endtry -:try -: let a:000[2] = [9, 10] -:catch -: $put ='caught a:000[2]' -:endtry -:try -: let a:000[3] = {9: 10} -:catch -: $put ='caught a:000[3]' -:endtry -:" now the tests that should pass -:try -: let a:000[2][1] = 9 -: call extend(a:000[2], [5, 6]) -: let a:000[3][5] = 8 -: let a:000[3]['a'] = 12 -: $put =string(a:000) -:catch -: $put ='caught ' . v:exception -:endtry -:" -:" reverse(), sort(), uniq() -:let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] -:$put =string(uniq(copy(l))) -:$put =string(reverse(l)) -:$put =string(reverse(reverse(l))) -:$put =string(sort(l)) -:$put =string(reverse(sort(l))) -:$put =string(sort(reverse(sort(l)))) -:$put =string(uniq(sort(l))) -:let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] -:$put =string(sort(copy(l), 'n')) -:let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] -:$put =string(sort(copy(l), 1)) -:$put =string(sort(copy(l), 'i')) -:$put =string(sort(copy(l))) -:" -:" splitting a string to a List -:$put =string(split(' aa bb ')) -:$put =string(split(' aa bb ', '\W\+', 0)) -:$put =string(split(' aa bb ', '\W\+', 1)) -:$put =string(split(' aa bb ', '\W', 1)) -:$put =string(split(':aa::bb:', ':', 0)) -:$put =string(split(':aa::bb:', ':', 1)) -:$put =string(split('aa,,bb, cc,', ',\s*', 1)) -:$put =string(split('abc', '\zs')) -:$put =string(split('abc', '\zs', 1)) -:" -:" compare recursively linked list and dict -:let l = [1, 2, 3, 4] -:let d = {'1': 1, '2': l, '3': 3} -:let l[1] = d -:$put =(l == l) -:$put =(d == d) -:$put =(l != deepcopy(l)) -:$put =(d != deepcopy(d)) -:" -:" compare complex recursively linked list and dict -:let l = [] -:call add(l, l) -:let dict4 = {"l": l} -:call add(dict4.l, dict4) -:let lcopy = deepcopy(l) -:let dict4copy = deepcopy(dict4) -:$put =(l == lcopy) -:$put =(dict4 == dict4copy) -:" -:" Pass the same List to extend() -:let l = [1, 2, 3, 4, 5] -:call extend(l, l) -:$put =string(l) -:" -:" Pass the same Dict to extend() -:let d = { 'a': {'b': 'B'}} -:call extend(d, d) -:$put =string(d) -:" -:" Pass the same Dict to extend() with "error" -:try -: call extend(d, d, "error") -:catch -: $put =v:exception[:15] . v:exception[-1:-1] -:endtry -:$put =string(d) -:" -:" test for range assign -:let l = [0] -:let l[:] = [1, 2] -:$put =string(l) -:endfun -:" -:call Test(1, 2, [3, 4], {5: 6}) " This may take a while -:" -:delfunc Test -:unlet dict -:call garbagecollect(1) -:" -:" test for patch 7.3.637 -:let a = 'No error caught' -:try|foldopen|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry -o=a
:" -:lang C -:redir => a -:try|foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry -:redir END -o=a
:" -:" -:/^start:/,$wq! test.out -ENDTEST - -start: diff --git a/src/nvim/testdir/test55.ok b/src/nvim/testdir/test55.ok deleted file mode 100644 index 607a95ead9..0000000000 --- a/src/nvim/testdir/test55.ok +++ /dev/null @@ -1,199 +0,0 @@ -start: -[1, 'as''d', [1, 2, function('strlen')], {'a': 1}] -{'a': 1} -1 -Vim(put):E684: -[1, 'as''d', [1, 2, function('strlen')], {'a': 1}] -['as''d', [1, 2, function('strlen')], {'a': 1}] -[1, 'as''d', [1, 2, function('strlen')]] -[1, 'as''d', [1, 2, function('strlen')], {'a': 1}] -[] -101101 -{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}asd -['-1', '1', 'b'] -['asd', [1, 2, function('strlen')], {'a': 1}] -1:'asd' -b:[1, 2, function('strlen')] --1:{'a': 1} -Vim(call):E737: 3 -{'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}} -{'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}} -101101 -Vim(let):E706: d -Vim(let):E706: l -[1, 'as''d', {'a': 1}] -[4] -{'1': 99, '3': 33} -[0, 1, 2, 3] -[0, 1, 3] -[0, 1] -[0, 1] -[0, 1] -[0, 1, 2, 3] -[0, 1, 3] -[0, 3] -[3] -[3] -[3] -2 -3 -Vim(let):E687: -Vim(let):E688: -3000 2900 2001 1600 1501 -Vim(let):E716: 1500 -NONE 2999 -33=999 -{'33': 999} -len: 3 -again: 3 -Vim(call):E725: -g:dict.func-4 -a:function('3') -Vim(let):E698: -same list: 1 -depth is 0 -0000-000 -ppppppp -0000-000 -ppppppp -0000-000 -ppppppp -depth is 1 -1000-000 -ppppppF -0000-000 -ppppppp -0000-000 -ppppppp -depth is 2 -1100-100 -ppFppFF -0000-000 -ppppppp -0000-000 -ppppppp -depth is 3 -1110-110 -pFFpFFF -0010-010 -pFppFpp -0000-000 -ppppppp -depth is 4 -1111-111 -FFFFFFF -0011-011 -FFpFFpp -0000-000 -ppppppp -Unletting: -depth is 0 -0000-000 -ppppppp -0000-000 -ppppppp -0000-000 -ppppppp -depth is 1 -1000-000 -ppFppFp -0000-000 -ppppppp -0000-000 -ppppppp -depth is 2 -1100-100 -pFFpFFp -0000-000 -ppppppp -0000-000 -ppppppp -depth is 3 -1110-110 -FFFFFFp -0010-010 -FppFppp -0000-000 -ppppppp -depth is 4 -1111-111 -FFFFFFp -0011-011 -FppFppp -0000-000 -ppppppp -Locks and commands or functions: -No :unlet after lock on dict: -Vim(unlet):E741: -{'a': 99, 'b': 100} -:unlet after lock on dict item: -did :unlet -{'b': 100} -filter() after lock on dict item: -did filter() -{'b': 100} -map() after lock on dict: -did map() -{'a': 299, 'b': 300} -No extend() after lock on dict item: -Vim(put):E741: -{'a': 99, 'b': 100} -No remove() of write-protected scope-level variable: -Vim(put):E795: -No extend() of write-protected scope-level variable: -Vim(put):E742: -No :unlet of variable in locked scope: -Vim(unlet):E741: -No :let += of locked list variable: -Vim(let):E741: -['a', 'b', 3] -[1, 2, 3, 4] -[1, 2, 3, 4] -[1, 2, 3, 4] -[1, 2, 3, 4] -[1, 2, 3, 4] -locked g:footest#x:-1 -exists g:footest#x:0 -g:footest#x: 1 -caught a:000 -caught a:000[0] -caught a:000[2] -caught a:000[3] -[1, 2, [3, 9, 5, 6], {'a': 12, '5': 8}] -['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] -[1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'] -[1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'] -['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]] -[[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'] -['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]] -['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]] -[-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255] -['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}] -['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}] -['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}] -['aa', 'bb'] -['aa', 'bb'] -['', 'aa', 'bb', ''] -['', '', 'aa', '', 'bb', '', ''] -['aa', '', 'bb'] -['', 'aa', '', 'bb', ''] -['aa', '', 'bb', 'cc', ''] -['a', 'b', 'c'] -['', 'a', '', 'b', '', 'c', ''] -1 -1 -0 -0 -1 -1 -[1, 2, 3, 4, 5, 1, 2, 3, 4, 5] -{'a': {'b': 'B'}} -Vim(call):E737: a -{'a': {'b': 'B'}} -[1, 2] -Vim(foldopen):E490: - - -Error detected while processing : -E492: Not an editor command: foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry - diff --git a/src/nvim/testdir/test_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim new file mode 100644 index 0000000000..4629d17dd2 --- /dev/null +++ b/src/nvim/testdir/test_hardcopy.vim @@ -0,0 +1,58 @@ +" Test :hardcopy + +func Test_printoptions_parsing() + " Only test that this doesn't throw an error. + set printoptions=left:5in,right:10pt,top:8mm,bottom:2pc + set printoptions=left:2in,top:30pt,right:16mm,bottom:3pc + set printoptions=header:3,syntax:y,number:7,wrap:n + set printoptions=duplex:short,collate:n,jobsplit:y,portrait:n + set printoptions=paper:10x14 + set printoptions=paper:A3 + set printoptions=paper:A4 + set printoptions=paper:A5 + set printoptions=paper:B4 + set printoptions=paper:B5 + set printoptions=paper:executive + set printoptions=paper:folio + set printoptions=paper:ledger + set printoptions=paper:legal + set printoptions=paper:letter + set printoptions=paper:quarto + set printoptions=paper:statement + set printoptions=paper:tabloid + set printoptions=formfeed:y + set printoptions= + set printoptions& +endfunc + +func Test_printmbfont_parsing() + " Only test that this doesn't throw an error. + set printmbfont=r:WadaMin-Regular,b:WadaMin-Bold,i:WadaMin-Italic,o:WadaMin-Bold-Italic,c:yes,a:no + set printmbfont= + set printmbfont& +endfunc + +func Test_printheader_parsing() + " Only test that this doesn't throw an error. + set printheader=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P + set printheader=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P + set printheader=%<%f%=\ [%1*%M%*%n%R%H]\ %-19(%3l,%02c%03V%)%O'%02b' + set printheader=...%r%{VarExists('b:gzflag','\ [GZ]')}%h... + set printheader= + set printheader& +endfunc + +" Test that :hardcopy produces a non-empty file. +" We don't check much of the contents. +func Test_with_syntax() + if has('postscript') + set printoptions=syntax:y + syn on + hardcopy > Xhardcopy + let lines = readfile('Xhardcopy') + call assert_true(len(lines) > 20) + call assert_true(lines[0] =~ 'PS-Adobe') + call delete('Xhardcopy') + set printoptions& + endif +endfunc diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim new file mode 100644 index 0000000000..066c3bf2bd --- /dev/null +++ b/src/nvim/testdir/test_langmap.vim @@ -0,0 +1,24 @@ +" tests for 'langmap' + +func Test_langmap() + new + set langmap=}l,^x,%v + + call setline(1, ['abc']) + call feedkeys('gg0}^', 'tx') + call assert_equal('ac', getline(1)) + + " in Replace mode + " need silent! to avoid a delay when entering Insert mode + call setline(1, ['abcde']) + silent! call feedkeys("gg0lR%{z\<Esc>00", 'tx') + call assert_equal('a%{ze', getline(1)) + + " in Select mode + " need silent! to avoid a delay when entering Insert mode + call setline(1, ['abcde']) + silent! call feedkeys("gg0}%}\<C-G>}^\<Esc>00", 'tx') + call assert_equal('a}^de', getline(1)) + + quit! +endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim new file mode 100644 index 0000000000..309c0f460b --- /dev/null +++ b/src/nvim/testdir/test_syntax.vim @@ -0,0 +1,63 @@ +" Test for syntax and syntax iskeyword option + +func GetSyntaxItem(pat) + let c = '' + let a = ['a', getreg('a'), getregtype('a')] + 0 + redraw! + call search(a:pat, 'W') + let synid = synID(line('.'), col('.'), 1) + while synid == synID(line('.'), col('.'), 1) + norm! v"ay + " stop at whitespace + if @a =~# '\s' + break + endif + let c .= @a + norm! l + endw + call call('setreg', a) + 0 + return c +endfunc + +func Test_syn_iskeyword() + new + call setline(1, [ + \ 'CREATE TABLE FOOBAR(', + \ ' DLTD_BY VARCHAR2(100)', + \ ');', + \ '']) + + syntax on + set ft=sql + syn match SYN /C\k\+\>/ + hi link SYN ErrorMsg + call assert_equal('DLTD_BY', GetSyntaxItem('DLTD')) + /\<D\k\+\>/:norm! ygn + call assert_equal('DLTD_BY', @0) + redir @c + syn iskeyword + redir END + call assert_equal("\nsyntax iskeyword not set", @c) + + syn iskeyword @,48-57,_,192-255 + redir @c + syn iskeyword + redir END + call assert_equal("\nsyntax iskeyword @,48-57,_,192-255", @c) + + setlocal isk-=_ + call assert_equal('DLTD_BY', GetSyntaxItem('DLTD')) + /\<D\k\+\>/:norm! ygn + let b2=@0 + call assert_equal('DLTD', @0) + + syn iskeyword clear + redir @c + syn iskeyword + redir END + call assert_equal("\nsyntax iskeyword not set", @c) + + quit! +endfunc diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim new file mode 100644 index 0000000000..f6705997a9 --- /dev/null +++ b/src/nvim/testdir/test_unlet.vim @@ -0,0 +1,26 @@ +" Tests for :unlet + +func Test_read_only() + try + " this caused a crash + unlet count + catch + call assert_true(v:exception =~ ':E795:') + endtry +endfunc + +func Test_existing() + let does_exist = 1 + call assert_true(exists('does_exist')) + unlet does_exist + call assert_false(exists('does_exist')) +endfunc + +func Test_not_existing() + unlet! does_not_exist + try + unlet does_not_exist + catch + call assert_true(v:exception =~ ':E108:') + endtry +endfunc diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 99eb230a88..be256f3ebc 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -4,6 +4,7 @@ #include "nvim/api/vim.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" +#include "nvim/main.h" #include "nvim/misc2.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -92,7 +93,7 @@ static void flush_input(TermInput *input, bool wait_until_empty) size_t drain_boundary = wait_until_empty ? 0 : 0xff; do { uv_mutex_lock(&input->key_buffer_mutex); - loop_schedule(&loop, event_create(1, wait_input_enqueue, 1, input)); + loop_schedule(&main_loop, event_create(1, wait_input_enqueue, 1, input)); input->waiting = true; while (input->waiting) { uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex); @@ -138,6 +139,9 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key) if (key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_ESCAPE) { len = (size_t)snprintf(buf, sizeof(buf), "<Esc>"); + } else if (key->type == TERMKEY_TYPE_KEYSYM + && key->code.sym == TERMKEY_SYM_SUSPEND) { + len = (size_t)snprintf(buf, sizeof(buf), "<C-Z>"); } else { len = termkey_strfkey(input->tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM); } @@ -153,7 +157,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) TermKeyMouseEvent ev; termkey_interpret_mouse(input->tk, key, &ev, &button, &row, &col); - if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG) { + if (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG + && ev != TERMKEY_MOUSE_RELEASE) { return; } @@ -190,6 +195,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) } } else if (ev == TERMKEY_MOUSE_DRAG) { len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag"); + } else if (ev == TERMKEY_MOUSE_RELEASE) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); } len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); @@ -336,7 +343,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, stream_close(&input->read_stream, NULL); queue_put(input->loop->fast_events, restart_reading, 1, input); } else { - loop_schedule(&loop, event_create(1, input_done_event, 0)); + loop_schedule(&main_loop, event_create(1, input_done_event, 0)); } return; } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 62bc81ba64..d220df508a 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -11,6 +11,7 @@ #include "nvim/vim.h" #include "nvim/ui.h" #include "nvim/map.h" +#include "nvim/main.h" #include "nvim/memory.h" #include "nvim/api/vim.h" #include "nvim/api/private/helpers.h" @@ -134,6 +135,8 @@ static void terminfo_start(UI *ui) data->ut = unibi_dummy(); } fix_terminfo(data); + // 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 unibi_out(ui, unibi_enter_ca_mode); unibi_out(ui, unibi_clear_screen); @@ -261,7 +264,7 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) UI *ui = data; update_size(ui); // run refresh_event in nvim main loop - loop_schedule(&loop, event_create(1, refresh_event, 0)); + loop_schedule(&main_loop, event_create(1, refresh_event, 0)); } static bool attrs_differ(HlAttrs a1, HlAttrs a2) @@ -681,7 +684,7 @@ static void invalidate(UI *ui, int top, int bot, int left, int right) intersects->right = MAX(right, intersects->right); } else { // Else just add a new entry; - kv_push(Rect, data->invalid_regions, ((Rect){top, bot, left, right})); + kv_push(data->invalid_regions, ((Rect) { top, bot, left, right })); } } @@ -785,6 +788,7 @@ static void fix_terminfo(TUIData *data) unibi_term *ut = data->ut; const char *term = os_getenv("TERM"); + const char *colorterm = os_getenv("COLORTERM"); if (!term) { goto end; } @@ -830,10 +834,10 @@ static void fix_terminfo(TUIData *data) #define XTERM_SETAB \ "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" - if (os_getenv("COLORTERM") != NULL - && (!strcmp(term, "xterm") || !strcmp(term, "screen"))) { - // probably every modern terminal that sets TERM=xterm supports 256 - // colors(eg: gnome-terminal). Also do it when TERM=screen. + if ((colorterm && strstr(colorterm, "256")) + || strstr(term, "256") + || strstr(term, "xterm")) { + // Assume TERM~=xterm or COLORTERM~=256 supports 256 colors. unibi_set_num(ut, unibi_max_colors, 256); unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF); unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB); diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index ad6d96a168..268362bf1b 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -23,16 +23,16 @@ struct ugrid { #define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 }) -#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ - do { \ - for (int row = top; row <= bot; ++row) { \ - UCell *row_cells = (grid)->cells[row]; \ - for (int col = left; col <= right; ++col) { \ - UCell *cell = row_cells + col; \ - (void)(cell); \ - code; \ - } \ - } \ +#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ + do { \ + for (int row = top; row <= bot; row++) { \ + UCell *row_cells = (grid)->cells[row]; \ + for (int col = left; col <= right; col++) { \ + UCell *cell = row_cells + col; \ + (void)(cell); \ + code; \ + } \ + } \ } while (0) #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/ui.c b/src/nvim/ui.c index ae38754c1e..d968cbc390 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -60,22 +60,22 @@ static int height, width; // See http://stackoverflow.com/a/11172679 for a better explanation of how it // works. #ifdef _MSC_VER - #define UI_CALL(funname, ...) \ - do { \ - flush_cursor_update(); \ - for (size_t i = 0; i < ui_count; i++) { \ - UI *ui = uis[i]; \ - UI_CALL_MORE(funname, __VA_ARGS__); \ - } \ +# define UI_CALL(funname, ...) \ + do { \ + flush_cursor_update(); \ + for (size_t i = 0; i < ui_count; i++) { \ + UI *ui = uis[i]; \ + UI_CALL_MORE(funname, __VA_ARGS__); \ + } \ } while (0) #else - #define UI_CALL(...) \ - do { \ - flush_cursor_update(); \ - for (size_t i = 0; i < ui_count; i++) { \ - UI *ui = uis[i]; \ - UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ - } \ +# define UI_CALL(...) \ + do { \ + flush_cursor_update(); \ + for (size_t i = 0; i < ui_count; i++) { \ + UI *ui = uis[i]; \ + UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ + } \ } while (0) #endif #define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore) diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index d17fa4a782..6290fb3d87 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -5,6 +5,7 @@ #include <stdio.h> #include <limits.h> +#include "nvim/main.h" #include "nvim/vim.h" #include "nvim/ui.h" #include "nvim/memory.h" @@ -18,9 +19,9 @@ #define UI(b) (((UIBridgeData *)b)->ui) // Call a function in the UI thread -#define UI_CALL(ui, name, argc, ...) \ - ((UIBridgeData *)ui)->scheduler( \ - event_create(1, ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) +#define UI_CALL(ui, name, argc, ...) \ + ((UIBridgeData *)ui)->scheduler( \ + event_create(1, ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) #define INT2PTR(i) ((void *)(uintptr_t)i) #define PTR2INT(p) ((int)(uintptr_t)p) @@ -100,7 +101,7 @@ static void ui_bridge_stop(UI *b) if (stopped) { break; } - loop_poll_events(&loop, 10); + loop_poll_events(&main_loop, 10); } uv_thread_join(&bridge->ui_thread); uv_mutex_destroy(&bridge->mutex); diff --git a/src/nvim/ui_bridge.h b/src/nvim/ui_bridge.h index 31b9a69216..561ddb6b24 100644 --- a/src/nvim/ui_bridge.h +++ b/src/nvim/ui_bridge.h @@ -28,13 +28,13 @@ struct ui_bridge_data { bool stopped; }; -#define CONTINUE(b) \ - do { \ - UIBridgeData *d = (UIBridgeData *)b; \ - uv_mutex_lock(&d->mutex); \ - d->ready = true; \ - uv_cond_signal(&d->cond); \ - uv_mutex_unlock(&d->mutex); \ +#define CONTINUE(b) \ + do { \ + UIBridgeData *d = (UIBridgeData *)b; \ + uv_mutex_lock(&d->mutex); \ + d->ready = true; \ + uv_cond_signal(&d->cond); \ + uv_mutex_unlock(&d->mutex); \ } while (0) diff --git a/src/nvim/undo.c b/src/nvim/undo.c index f16b4264d7..fc5d6acaa4 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -712,7 +712,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) // When reading check if the file exists. if (undo_file_name != NULL - && (!reading || os_file_exists((char_u *)undo_file_name))) { + && (!reading || os_path_exists((char_u *)undo_file_name))) { break; } xfree(undo_file_name); @@ -1094,7 +1094,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, /* If the undo file already exists, verify that it actually is an undo * file, and delete it. */ - if (os_file_exists((char_u *)file_name)) { + if (os_path_exists((char_u *)file_name)) { if (name == NULL || !forceit) { /* Check we can read it and it's an undo file. */ fd = os_open(file_name, O_RDONLY, 0); diff --git a/src/nvim/version.c b/src/nvim/version.c index c491021328..966e4f9b4f 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -75,6 +75,9 @@ static char *features[] = { // clang-format off static int included_patches[] = { + 1973, + 1960, + 1840, 1832, 1831, 1809, @@ -85,8 +88,12 @@ static int included_patches[] = { 1755, 1753, 1728, + 1716, + 1712, + 1695, 1654, 1652, + 1649, 1643, 1641, // 1624 NA @@ -95,11 +102,11 @@ static int included_patches[] = { // 1599 NA // 1598 NA // 1597 NA - // 1596, + 1596, // 1595 NA // 1594 NA // 1593 NA - // 1592, + 1592, // 1591, // 1590, // 1589, @@ -113,13 +120,13 @@ static int included_patches[] = { // 1581, // 1580, - // 1579, + // 1579 NA 1578, // 1577, 1576, // 1575 NA 1574, - // 1573, + // 1573 NA // 1572 NA 1571, 1570, @@ -132,17 +139,17 @@ static int included_patches[] = { // 1563, // 1562 NA // 1561 NA - // 1560, + // 1560 NA // 1559, // 1558, // 1557, - // 1556, - // 1555, - // 1554, - // 1553, - // 1552, - // 1551, - // 1550, + // 1556 NA + // 1555 NA + 1554, + 1553, + 1552, + 1551, + 1550, // 1549, // 1548, // 1547, @@ -164,7 +171,7 @@ static int included_patches[] = { // 1531 NA // 1530 NA // 1529 NA - // 1528, + 1528, // 1527 NA // 1526 NA // 1525 NA @@ -193,28 +200,28 @@ static int included_patches[] = { // 1502 NA // 1501 NA 1500, - // 1499, + 1499, // 1498 NA // 1497 NA // 1496 NA // 1495 NA // 1494, // 1493 NA - // 1492, + 1492, // 1491, // 1490 NA // 1489 NA // 1488 NA // 1487 NA - // 1486, + 1486, // 1485 NA // 1484 NA // 1483 NA // 1482 NA // 1481 NA - // 1480, - // 1479, - // 1478, + 1480, + 1479, + 1478, // 1477, // 1476 NA // 1475 NA @@ -224,11 +231,11 @@ static int included_patches[] = { // 1471 NA // 1470 NA // 1469 NA - // 1468, + 1468, // 1467 NA // 1466 NA // 1465 NA - // 1464, + 1464, // 1463 NA // 1462 NA // 1461 NA @@ -295,20 +302,20 @@ static int included_patches[] = { // 1400 NA // 1399 NA // 1398 NA - // 1397, - // 1396, + 1397, + 1396, // 1395 NA - // 1394, + 1394, // 1393 NA // 1392 NA // 1391 NA // 1390 NA // 1389 NA - // 1388, + 1388, // 1387 NA // 1386 NA // 1385 NA - // 1384, + 1384, // 1383 NA // 1382 NA // 1381 NA @@ -340,7 +347,7 @@ static int included_patches[] = { // 1355 NA // 1354 NA // 1353 NA - // 1352, + 1352, // 1351 NA // 1350 NA // 1349 NA @@ -416,7 +423,7 @@ static int included_patches[] = { // 1279 NA // 1278 NA // 1277 NA - // 1276, + 1276, // 1275 NA // 1274 NA // 1273, @@ -469,7 +476,7 @@ static int included_patches[] = { // 1226 NA // 1225 NA // 1224 NA - // 1223, + 1223, // 1222 NA // 1221 NA // 1220 NA @@ -531,7 +538,7 @@ static int included_patches[] = { 1164, 1163, // 1162 NA - // 1161, + 1161, 1160, // 1159 NA // 1158 NA @@ -539,41 +546,41 @@ static int included_patches[] = { // 1156 NA // 1155 NA // 1154 NA - // 1153, + 1153, // 1152 NA - // 1151, - // 1150, + 1151, + 1150, 1149, // 1148 NA - // 1147, + 1147, // 1146 NA // 1145 NA 1144, 1143, - // 1142, + 1142, 1141, - // 1140, + 1140, // 1139 NA // 1138 NA 1137, - // 1136, + 1136, // 1135 NA // 1134 NA // 1133 NA - // 1132, + 1132, // 1131 NA - // 1130, + // 1130 NA // 1129 NA // 1128 NA // 1127 NA - // 1126, + 1126, // 1125 NA // 1124 NA - // 1123, + 1123, // 1122 NA - // 1121, + 1121, 1120, - // 1119, + 1119, 1118, 1117, 1116, @@ -581,10 +588,10 @@ static int included_patches[] = { 1114, 1113, 1112, - // 1111, + 1111, 1110, // 1109 NA - // 1108, + 1108, 1107, // 1106 NA 1105, @@ -595,14 +602,14 @@ static int included_patches[] = { // 1100 NA // 1099 NA // 1098 NA - // 1097, + // 1097 NA 1096, // 1095 NA - // 1094, + 1094, 1093, 1092, 1091, - // 1090, + 1090, 1089, 1088, 1087, @@ -613,18 +620,18 @@ static int included_patches[] = { // 1082 NA 1081, // 1080 NA - // 1079, + // 1079 NA // 1078 NA // 1077 NA 1076, 1075, // 1074 NA, - // 1073, + // 1073 NA 1072, 1071, // 1070 NA // 1069 NA - // 1068, + 1068, // 1067 NA // 1066 NA 1065, @@ -634,14 +641,14 @@ static int included_patches[] = { 1061, // 1060 NA 1059, - // 1058, + // 1058 NA 1057, - // 1056, + 1056, 1055, 1054, 1053, 1052, - // 1051, + 1051, 1050, 1049, 1048, @@ -653,7 +660,7 @@ static int included_patches[] = { 1042, 1041, // 1040 NA - // 1039, + // 1039 NA // 1038 NA 1037, 1036, @@ -1903,21 +1910,15 @@ void intro_message(int colon) N_("by Bram Moolenaar et al."), N_("Vim is open source and freely distributable"), "", - N_("First time using a vi-like editor?"), - N_("Type :Tutor<Enter> to get started!"), + N_("Type \":Tutor\" or \":help nvim\" to get started!"), "", - N_("Already know your way around Vim?"), - N_("See :help nvim-intro for an introduction to Neovim."), + N_("Still have questions? https://neovim.io/community"), "", - N_("Still have questions?"), - N_("Reach out to the Neovim community at neovim.io/community."), + N_("type :q<Enter> to exit "), + N_("type :help<Enter> or <F1> for on-line help"), "", N_("Help poor children in Uganda!"), N_("type :help iccf<Enter> for information "), - "", - N_("type :q<Enter> to exit "), - N_("type :help<Enter> or <F1> for on-line help"), - N_("type :help nvim<Enter> for Neovim help "), }; // blanklines = screen height - # message lines @@ -2019,3 +2020,4 @@ void ex_intro(exarg_T *eap) intro_message(TRUE); wait_return(TRUE); } + diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 165a44a148..09e9e850a7 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -174,6 +174,7 @@ enum { EXPAND_USER, EXPAND_SYNTIME, EXPAND_USER_ADDR_TYPE, + EXPAND_PACKADD, }; @@ -307,4 +308,12 @@ enum { # define SET_NO_HLSEARCH(flag) no_hlsearch = (flag); set_vim_var_nr( \ VV_HLSEARCH, !no_hlsearch && p_hls) +// Used for flags in do_in_path() +#define DIP_ALL 0x01 // all matches, not just the first one +#define DIP_DIR 0x02 // find directories instead of files +#define DIP_ERR 0x04 // give an error message when none found +#define DIP_START 0x08 // also use "start" directory in 'packpath' +#define DIP_OPT 0x10 // also use "opt" directory in 'packpath' +#define DIP_NORTP 0x20 // do not use 'runtimepath' + #endif /* NVIM_VIM_H */ diff --git a/src/nvim/window.c b/src/nvim/window.c index bea55c465f..e267d493bf 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -251,11 +251,14 @@ newwindow: if (win_new_tabpage((int)Prenum, NULL) == OK && valid_tabpage(oldtab)) { newtab = curtab; - goto_tabpage_tp(oldtab, TRUE, TRUE); - if (curwin == wp) - win_close(curwin, FALSE); - if (valid_tabpage(newtab)) - goto_tabpage_tp(newtab, TRUE, TRUE); + goto_tabpage_tp(oldtab, true, true); + if (curwin == wp) { + win_close(curwin, false); + } + if (valid_tabpage(newtab)) { + goto_tabpage_tp(newtab, true, true); + apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, false, curbuf); + } } } break; @@ -3293,8 +3296,11 @@ void tabpage_move(int nr) tabpage_T *tp; tabpage_T *tp_dst; - if (first_tabpage->tp_next == NULL) + assert(curtab != NULL); + + if (first_tabpage->tp_next == NULL) { return; + } for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) { ++n; @@ -3613,6 +3619,10 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri /* Change directories when the 'acd' option is set. */ do_autochdir(); + + if (curbuf->terminal) { + terminal_resize(curbuf->terminal, curwin->w_width, curwin->w_height); + } } |