diff options
62 files changed, 3095 insertions, 3468 deletions
diff --git a/.travis.yml b/.travis.yml index af3a88ed52..d464d67bb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ matrix: - os: osx env: CI_TARGET=gcc compiler: gcc-4.9 + fast_finish: true before_install: # Pins the version of the java package installed on the Travis VMs # and avoids a lengthy upgrade process for them. @@ -163,48 +163,48 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] # 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', - 'runtime/arrays', - 'runtime/int', - 'runtime/invalid_increment', - 'runtime/memset', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/threadsafe_fn', - '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' - ] + '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', + 'runtime/arrays', + 'runtime/int', + 'runtime/invalid_increment', + 'runtime/memset', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/threadsafe_fn', + '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' +] # 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 @@ -233,7 +233,7 @@ _ALT_TOKEN_REPLACEMENT = { '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. @@ -278,457 +278,467 @@ _line_length = 80 # 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) + """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: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) + 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 ResetNolintSuppressions(): - "Resets the set of NOLINT suppressions to empty." - _error_suppressions.clear() + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. + """Returns true if the specified error category is suppressed on this line. - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. + 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())) - 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 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) + """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) + """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. + """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. - Returns: - Canonicalized path. """ - return header_path.replace('-', '_').lower() + # 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 CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. + def __init__(self): + dict.__init__(self) + self.ResetSection() - This function also updates the internal state to be ready to check - the next include. + def ResetSection(self): + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' - Args: - header_type: One of the _XXX_HEADER constants defined above. + def SetLastHeader(self, header_path): + self._last_header = header_path - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) + - replaces "-" with "_" so they both cmp the same. + - lowercase everything, just in case. - last_section = self._section + Args: + header_path: Path to be canonicalized. - 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 + Returns: + Canonicalized path. + """ + return header_path.replace('-', '_').lower() - if last_section != self._section: - self._last_header = '' + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. - return '' + 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. -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' - - 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 + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) - def SetFilters(self, filters): - """Sets the error-message filters. + last_section = self._section - These filters are applied when deciding whether to emit a given - error message. + 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 - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. + if last_section != self._section: + self._last_header = '' - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # 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) + 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' + + 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) _cpplint_state = _CppLintState() def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format + """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) + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level + """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) + """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) + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.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. + """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. + These filters are applied when deciding whether to emit a given + error message. Args: - function_name: The name of the function being tracked. + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. """ - 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. + _cpplint_state.SetFilters(filters) - 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)) +class _FunctionState(object): - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False + """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. - """ + """Provides utility functions for filenames. - def __init__(self, filename): - self._filename = filename + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') + def __init__(self, filename): + self._filename = filename - def RelativePath(self): - """FullName with <prefix>/src/nvim/ chopped off.""" - fullname = self.FullName() + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) + def RelativePath(self): + """FullName with <prefix>/src/nvim/ chopped off.""" + fullname = self.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(fullname): + project_dir = os.path.dirname(fullname) - 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:] + 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) - # Don't know what to do; header guard warnings may be wrong... - return fullname + 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:] - def Split(self): - """Splits the file into the directory, basename, and extension. + # Don't know what to do; header guard warnings may be wrong... + return fullname - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') + def Split(self): + """Splits the file into the directory, basename, and extension. - Returns: - A tuple of (directory, basename, 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) + 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 the final period.""" - return self.Split()[1] + 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 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 is not suppressed.""" + """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 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 - # 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 confidence < _cpplint_state.verbose_level: - return False + return True - 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. -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)) + 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. @@ -754,447 +764,459 @@ _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. + """Does line terminate so, that the next symbol is in string constant. - This function does not consider single-line nor multi-line comments. + This function does not consider single-line nor multi-line comments. - Args: - line: is a partial line of code starting from the 0..n. + 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. - """ + 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 + 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) + """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) + """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' + """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 + """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. + """Removes //-comments and single-line C-style /* */ comments. - Args: - line: A line of C++ source. + 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) + 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 3 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 without processing. - All these three members are of <type 'list'>, and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - self.lines_without_raw_strings = lines - 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)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """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. + """Holds 3 copies of all lines with different preprocessing applied to them. - Returns: - The line with collapsed strings. + 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 without processing. + All these three members are of <type 'list'>, and of the same length. """ - 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('', elided) - elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) - elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) - return elided + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + self.lines_without_raw_strings = lines + 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)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """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. + + 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('', elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) + return elided 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) + """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 [ or <, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' 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 '({[<': - return (line, clean_lines.NumLines(), -1) - if startchar == '(': endchar = ')' - if startchar == '[': endchar = ']' - if startchar == '{': endchar = '}' - if startchar == '<': endchar = '>' - - # 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 + """If input points to ( or { or [ or <, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' 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 '({[<': + return (line, clean_lines.NumLines(), -1) + if startchar == '(': + endchar = ')' + if startchar == '[': + endchar = ']' + if startchar == '{': + endchar = '}' + if startchar == '<': + endchar = '>' + + # Check first line (end_pos, num_open) = FindEndOfExpressionInLine( - line, 0, num_open, startchar, endchar) + line, pos, 0, 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) + 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) + """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 + """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, len(line) - 1, num_open, startchar, endchar) + line, pos, 0, 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) + 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. + """Returns the CPP variable that should be used as a header guard. - Args: - filename: The name of a C++ header file. + 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. + 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) + # 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() + 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) + """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. + """Logs an error for each line containing bad characters. - Two kinds of 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. + 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. + 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.') + 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. + """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. - """ + 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.') + # 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.') + """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.') threading_list = ( @@ -1220,35 +1242,36 @@ threading_list = ( ('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.') + """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.') # Matches invalid increment: *count++, which moves pointer instead of @@ -1258,1291 +1281,1318 @@ _RE_PATTERN_INVALID_INCREMENT = re.compile( 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 + """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 + """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 = [] + # The entire nesting stack up to #else + self.stack_before_else = [] - # Whether we have already seen #else or #elif - self.seen_else = False + # 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 = [] + """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) - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. +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. - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. + 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 """ - return (not self.stack) or self.stack[-1].seen_open_brace - def UpdatePreprocessor(self, line): - """Update preprocessor stack. + # Remove comments from the line, but leave in strings for now. + line = clean_lines.lines[linenum] - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif + if Search(r'printf\s*\(.*".*%[-+ ]?\d*q', line): + error(filename, linenum, 'runtime/printf_format', 3, + '"%q" in format strings is deprecated. Use "%" PRId64 instead.') - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. + if Search(r'printf\s*\(.*".*%\d+\$', line): + error(filename, linenum, 'runtime/printf_format', 2, + '%N$ formats are unconventional. Try rewriting to avoid them.') - - 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. + # Remove escaped backslashes before looking for undefined escapes. + line = line.replace('\\\\', '') - 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. + if Search(r'("|\').*\\(%|\[|\(|{)', line): + error(filename, linenum, 'build/printf_format', 3, + '%, [, (, and { are undefined character escapes. Unescape them.') - 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. - """ + # For the rest, work with both comments and strings removed. 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) + 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 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. + """ -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: - fncall = match.group(1) # look inside the parens for function calls - 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)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - 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 )') + # 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. + """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. + 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. + Args: + line: A line of a string. - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() + 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() - if Search(r'(;|})', start_line): # Declarations and trivial functions - 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. + """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') + """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() + """Find the corresponding > to close a template. - else: - # Scan the next line - linenum += 1 - if linenum >= len(clean_lines.elided): - break - line = clean_lines.elided[linenum] + 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() - # 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 + 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 + """Find the corresponding < that started a template. - else: - # Expecting opening parenthesis or opening bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator in ('(', '['): - nesting_stack.pop() + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_prefix: Part of the current line before the initial >. - else: - # Scan the previous line - linenum -= 1 - if linenum < 0: - break - line = clean_lines.elided[linenum] + 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() - # Exhausted all earlier lines and still no matching angle bracket. - return False + 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 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. - - 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 + """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. + + 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 { # - # } 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. - - # 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)) - # 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) + # + # 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. + + # 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)) + # 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 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) - - # 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[{...}] = ...; + 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 check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<]". + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha # - # 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 { - # }; } + # 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). # - # 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. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - 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) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') + # 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) + + # 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. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + 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) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. + """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. + 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. - """ + 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) + 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 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 }') - - # 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) - if endline[endpos:].find('{') == -1: # must be brace after if - 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 + """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 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 }') + + # 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(...) { + # {}; + # } # - # 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: + # 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. # - # 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. + # 6. Block following another block: + # while (true) {} + # {}; # - # 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 }") + # 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') + """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))) + """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) + """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) + """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"') @@ -2556,514 +2606,546 @@ _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. + """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 + 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) + """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 could not 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] + 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(?! +double)|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: + """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(?! +double)|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 - 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)) + 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)) + 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 - 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) - 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) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) + """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 + 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) + 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) + 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() - - if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) - - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(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) + """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() - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForBadCharacters(filename, lines, error) + ResetNolintSuppressions() + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(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) - 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') + """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: - 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') + 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') - sys.stderr.write('Done processing %s\n' % filename) + sys.stderr.write('Done processing %s\n' % filename) def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. + """Prints a brief usage string and exits, optionally with an error message. - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) def PrintCategories(): - """Prints a list of all the error-categories used by error messages. + """Prints a list of all the error-categories used by error messages. - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) + These are the categories used to filter messages via --filter. + """ + sys.stderr.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=']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - filters = '' - counting_style = '' - - 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.') - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - - return filenames + """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=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + 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.') + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames def main(): - filenames = ParseArguments(sys.argv[1:]) + filenames = ParseArguments(sys.argv[1:]) - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - _cpplint_state.PrintErrorCounts() + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() - sys.exit(_cpplint_state.error_count > 0) + sys.exit(_cpplint_state.error_count > 0) if __name__ == '__main__': - main() + main() + + +# Ignore "too complex" warnings when using pymode. +# pylama:ignore=C901 diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index 227569f84c..8f303bae2e 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -17,8 +17,10 @@ execute_process( COMMAND ${BUSTED_PRG} -v -o ${BUSTED_OUTPUT_TYPE} --lpath=${BUILD_DIR}/?.lua ${TEST_PATH} WORKING_DIRECTORY ${WORKING_DIR} + ERROR_VARIABLE err RESULT_VARIABLE res) if(NOT res EQUAL 0) + message(STATUS "Output to stderr:\n${err}") message(FATAL_ERROR "Running ${TEST_TYPE} tests failed with error: ${res}.") endif() diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index a46f686d7c..c620c96e61 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -7,6 +7,8 @@ check_type_size("int" SIZEOF_INT) check_type_size("long" SIZEOF_LONG) check_type_size("intmax_t" SIZEOF_INTMAX_T) check_type_size("off_t" SIZEOF_OFF_T) +check_type_size("size_t" SIZEOF_SIZE_T) +check_type_size("long long" SIZEOF_LONG_LONG) check_type_size("void *" SIZEOF_VOID_PTR) check_symbol_exists(_NSGetEnviron crt_externs.h HAVE__NSGETENVIRON) diff --git a/runtime/doc/Makefile b/runtime/doc/Makefile index 1f2affe829..2b338cc565 100644 --- a/runtime/doc/Makefile +++ b/runtime/doc/Makefile @@ -254,28 +254,22 @@ HTMLS = \ MANPAGES = \ manpages/vim.man \ - manpages/vimdiff.man \ manpages/vimtutor.man \ manpages/xxd.man \ manpages/de/vim-de.man \ manpages/fr/vim-fr.man \ - manpages/fr/vimdiff-fr.man \ manpages/fr/vimtutor-fr.man \ manpages/fr/xxd-fr.man \ manpages/pl/vim-pl.man \ - manpages/pl/vimdiff-pl.man \ manpages/pl/vimtutor-pl.man \ manpages/pl/xxd-pl.man \ manpages/it/vim-it.man \ - manpages/it/vimdiff-it.man \ manpages/it/vimtutor-it.man \ manpages/it/xxd-it.man \ manpages/ru/vim-ru.man \ - manpages/ru/vimdiff-ru.man \ manpages/ru/vimtutor-ru.man \ manpages/ru/xxd-ru.man \ manpages/ja/vim-ja.man \ - manpages/ja/vimdiff-ja.man \ manpages/ja/vimtutor-ja.man \ manpages/ja/xxd-ja.man diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index 0ee5878341..4aea7b4968 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -1,39 +1,31 @@ -*diff.txt* For Vim version 7.4. Last change: 2014 May 20 +*diff.txt* For Vim version 7.4. Last change: 2015 Jan 19 VIM REFERENCE MANUAL by Bram Moolenaar - *diff* *vimdiff* *gvimdiff* *diff-mode* -This file describes the |+diff| feature: Showing differences between two, -three or four versions of the same file. + *diff* *diff-mode* +This file describes |diff-mode|, which shows the differences between two, +three, or four versions of the same file. The basics are explained in section |08.7| of the user manual. -1. Starting diff mode |vimdiff| +1. Starting diff mode |diff-mode| 2. Viewing diffs |view-diffs| 3. Jumping to diffs |jumpto-diffs| 4. Copying diffs |copy-diffs| 5. Diff options |diff-options| -{not in Vi} - ============================================================================== 1. Starting diff mode -The easiest way to start editing in diff mode is with the "vimdiff" command. -This starts Vim as usual, and additionally sets up for viewing the differences -between the arguments. > - - vimdiff file1 file2 [file3 [file4]] - -This is equivalent to: > +To start editing in diff mode, run "nvim -d". This starts Nvim as usual, and +additionally sets up for viewing the differences between the arguments. > - vim -d file1 file2 [file3 [file4]] + nvim -d file1 file2 [file3 [file4]] -You may also use "gvimdiff" or "vim -d -g". The GUI is started then. -You may also use "viewdiff" or "gviewdiff". Vim starts in readonly mode then. -"r" may be prepended for restricted mode (see |-Z|). +In addition to the |-d| argument, |-Z| and |-R| may be used for restricted +mode and readonly mode respectively. The second and following arguments may also be a directory name. Vim will then append the file name of the first argument to the directory name to find @@ -45,11 +37,11 @@ Diffs are local to the current tab page |tab-page|. You can't see diffs with a window in another tab page. This does make it possible to have several diffs at the same time, each in their own tab page. -What happens is that Vim opens a window for each of the files. This is like -using the |-O| argument. This uses vertical splits. If you prefer horizontal -splits add the |-o| argument: > +What happens is that Nvim opens a window for each of the files. This is like +using the |-O| argument. This uses vertical splits, but if you prefer +horizontal splits use the |-o| argument instead: > - vimdiff -o file1 file2 [file3 [file4]] + nvim -d -o file1 file2 [file3 [file4]] If you always prefer horizontal splits include "horizontal" in 'diffopt'. @@ -88,17 +80,17 @@ While already in Vim you can start diff mode in three ways. *E98* :diffs[plit] {filename} *:diffs* *:diffsplit* Open a new window on the file {filename}. The options are set - as for "vimdiff" for the current and the newly opened window. + as for "nvim -d" for the current and the newly opened window. Also see 'diffexpr'. *:difft* *:diffthis* :difft[his] Make the current window part of the diff windows. This sets - the options like for "vimdiff". + the options as for "nvim -d". :diffp[atch] {patchfile} *E816* *:diffp* *:diffpatch* Use the current buffer, patch it with the diff found in - {patchfile} and open a buffer on the result. The options are - set as for "vimdiff". + {patchfile} and open a buffer on the result. This sets the + options as for "nvim -d". {patchfile} can be in any format that the "patch" program understands or 'patchexpr' can handle. Note that {patchfile} should only contain a diff for one file, @@ -172,7 +164,7 @@ possible to view the changes you have made to a buffer since the file was loaded. Since Vim doesn't allow having two buffers for the same file, you need another buffer. This command is useful: > command DiffOrig vert new | set bt=nofile | r ++edit # | 0d_ - \ | diffthis | wincmd p | diffthis + \ | diffthis | wincmd p | diffthis (this is in |vimrc_example.vim|). Use ":DiffOrig" to see the differences between the current buffer and the file it was loaded from. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d8dee5726b..456cd5dff6 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1552,8 +1552,8 @@ v:profiling Normally zero. Set to one after using ":profile start". *v:progname* *progname-variable* v:progname Contains the name (with path removed) with which Vim was - invoked. Allows you to do special initialisations for |view|, - |vimdiff|, etc., or any other name you might symlink to Vim. + invoked. Allows you to do special initialisations for |view|, + |rview|, or any other name you might symlink to Vim. Read-only. *v:progpath* *progpath-variable* @@ -6665,7 +6665,6 @@ cscope Compiled with |cscope| support. debug Compiled with "DEBUG" defined. dialog_con Compiled with console dialog support. dialog_gui Compiled with GUI dialog support. -diff Compiled with |vimdiff| and 'diff' support. digraphs Compiled with support for digraphs. dnd Compiled with support for the "~ register |quote_~|. dos16 16 bits DOS version of Vim. diff --git a/runtime/doc/manpages/de/vim-de.1 b/runtime/doc/manpages/de/vim-de.1 index 3b26550072..d958372012 100644 --- a/runtime/doc/manpages/de/vim-de.1 +++ b/runtime/doc/manpages/de/vim-de.1 @@ -156,7 +156,7 @@ sehr ähnlich zu Vi, selbst wenn eine VimRC\-Datei existiert. \-d Startet im diff\-Modus. Es sollten zwei, drei oder vier Dateinamen als Parameter übergeben werden. \fBVim\fP öffnet sie alle und zeigt die -Unterschiede an. Arbeitet wie vimdiff(1). +Unterschiede an. .TP \-d {Gerät} Öffnet das {Gerät}, um es als Terminal zu nutzen. Nur für AmigaOS. Beispiel: diff --git a/runtime/doc/manpages/fr/vim-fr.1 b/runtime/doc/manpages/fr/vim-fr.1 index 0559f5714b..2b2a50765f 100644 --- a/runtime/doc/manpages/fr/vim-fr.1 +++ b/runtime/doc/manpages/fr/vim-fr.1 @@ -192,7 +192,6 @@ Démarre en mode Diff. Deux, trois ou quatre noms de fichiers doivent être spécifiés. .B Vim ouvrira alors tous les fichiers et affichera leurs différences. -Fonctionne comme vimdiff(1). .TP \-d {périph} Ouvre {périph} pour l'utiliser comme terminal. diff --git a/runtime/doc/manpages/fr/vimdiff-fr.1 b/runtime/doc/manpages/fr/vimdiff-fr.1 deleted file mode 100644 index 3b44a3dd58..0000000000 --- a/runtime/doc/manpages/fr/vimdiff-fr.1 +++ /dev/null @@ -1,54 +0,0 @@ -.TH VIMDIFF 1 "30 mars 2001" -.SH NOM -vimdiff \- ouvre deux, trois ou quatre versions d'un fichier dans Vim et -affiche leurs différences -.SH SYNOPSIS -.br -.B vimdiff -[options] fichier1 fichier2 [fichier3 [fichier4]] -.PP -.B gvimdiff -.SH DESCRIPTION -.B Vimdiff -démarre -.B Vim -avec deux (ou trois ou quatre) fichiers. -Chaque fichier est ouvert dans sa propre fenêtre. -Les différences entre ces fichiers sont mises en surbrillance. -C'est un outil très pratique pour visualiser et reporter les -changements entre deux versions d'un même fichier. -.PP -Consulter vim(1) pour des informations sur l'éditeur Vim lui-même. -.PP -Si -.B gvimdiff -est invoqué, l'IHM graphique est démarrée quand elle est disponible. -.PP -L'option 'diff' est activée dans chacune des fenêtres, ce qui provoque la mise -en surbrillance des différences. -.br -Les options 'wrap' et 'scrollbind' sont activées pour donner un aspect -agréable au texte. -.br -L'option 'foldmethod' est fixée à "diff", ce qui replie les lignes consécutives -identiques. 'foldcolumn' est fixé à 2 pour trouver les replis rapidement et -les ouvrir ou les fermer facilement. -.SH ARGUMENTS -La fenêtre de Vim est partagée verticalement afin d'afficher les lignes -correspondantes les unes en face des autres, comme si l'argument "\-O" était -spécifié. Utilisez l'argument "\-o" pour obtenir un partage horizontal à la -place. -.PP -Pour connaître les autres arguments disponibles, consultez vim(1). -.SH VOIR AUSSI -vim(1) -.SH AUTEUR -La majeure partie de -.B Vim -a été écrite par Bram Moolenaar, avec l'aide de nombreux autres contributeurs. -Voir ":help credits" dans -.B Vim. -.SH TRADUCTION -Cette page de manuel a été traduite par David Blanchet -<david.blanchet@free.fr> 2005-03-12. -Mise à jour 2012-05-06, Dominique Pellé <dominique.pelle@gmail.com> diff --git a/runtime/doc/manpages/it/vim-it.1 b/runtime/doc/manpages/it/vim-it.1 index 4d17025dc3..36ece8fb0b 100644 --- a/runtime/doc/manpages/it/vim-it.1 +++ b/runtime/doc/manpages/it/vim-it.1 @@ -188,7 +188,6 @@ Inizia in Modo Diff [differenze]. Dovrebbero esserci come argomenti due o tre o quattro nomi di file. .B Vim aprirà tutti i file evidenziando le differenze fra gli stessi. -Funziona come vimdiff(1). .TP \-d {dispositivo} Apre {dispositivo} per usarlo come terminale. diff --git a/runtime/doc/manpages/it/vimdiff-it.1 b/runtime/doc/manpages/it/vimdiff-it.1 deleted file mode 100644 index 090892fa93..0000000000 --- a/runtime/doc/manpages/it/vimdiff-it.1 +++ /dev/null @@ -1,48 +0,0 @@ -.TH VIMDIFF 1 "30 marzo 2001" -.SH NOME -vimdiff \- modifica due, tre o quattro versioni di un file con Vim, -visualizzando le differenze -.SH SINTASSI -.br -.B vimdiff -[opzioni] file1 file2 [file3 [file4]] -.PP -.B gvimdiff -.SH DESCRIZIONE -.B Vimdiff -inizia -.B Vim -per due (o tre o quattro) file. -Ogni file ha una sua finestra. -Le differenze fra file sono evidenziate. -È una maniera elegante per controllare modifiche e portare modifiche -verso un'altra versione dello stesso file. -.PP -Vedere vim(1) per dettagli su Vim in generale. -.PP -Se iniziato con -.B gvimdiff -la GUI sarà utilizzata, se disponibile. -.PP -In ogni finestra l'opzione 'diff' è impostata, evidenziando così le -differenze. -.br -Le opzioni 'wrap' e 'scrollbind' sono impostate per migliorare la -visibilità del testo. -.br -L'opzione 'foldmethod' è impostata al valore "diff", che mette gruppi di -linee uguali fra i diversi file in una piegatura. 'foldcolumn' è impostato -a due per poter facilmente visualizzare le piegature, aprirle e chiuderle. -.SH OPZIONI -Lo schermo è diviso verticalmente, come se aveste usato l'opzione "\-O". -Per dividerlo orizzontalmente, usare l'opzione "\-o". -.PP -Per tutte le altre opzioni, vedere vim(1). -.SH VEDERE ANCHE -vim(1) -.SH AUTORE -Buona parte di -.B Vim -è stato scritto da Bram Moolenaar, con molto aiuto da altri. -Vedere ":help credits" in -.B Vim. diff --git a/runtime/doc/manpages/ja/vim-ja.1 b/runtime/doc/manpages/ja/vim-ja.1 index 714ad0ae78..9a9111a69f 100644 --- a/runtime/doc/manpages/ja/vim-ja.1 +++ b/runtime/doc/manpages/ja/vim-ja.1 @@ -164,7 +164,6 @@ Note: "+" と "\-c" は合わせて 10 個まで指定できます。 差分モードで起動します。 二つか三つの四つのファイルを引数に指定してください。 指定されたファイルが開かれ、それらのファイルの差分が表示されます。 -vimdiff(1) と同様の動作です。 .TP \-d {device} {device} を端末として開きます。 diff --git a/runtime/doc/manpages/ja/vimdiff-ja.1 b/runtime/doc/manpages/ja/vimdiff-ja.1 deleted file mode 100644 index 56fadfef6e..0000000000 --- a/runtime/doc/manpages/ja/vimdiff-ja.1 +++ /dev/null @@ -1,44 +0,0 @@ -.TH VIMDIFF 1 "2001 March 30" -.SH 名前 -vimdiff \- 二つか三つか四つのファイルを Vim で開いて、その差分を表示する -.SH 書式 -.br -.B vimdiff -[options] file1 file2 [file3 [file4]] -.PP -.B gvimdiff -.SH 説明 -.B Vimdiff -は、二つ (か三つか四つ) のファイルを -.B Vim -で開きます。 -ファイルは個別のウィンドウで開かれ、差分が強調表示されます。 -同じファイルの別のバージョン間で、変更を確認したり、変更を移動したりするのが -簡単になります。 -.PP -Vim についての詳細は vim(1) を参照してください。 -.PP -.B gvimdiff -という名前で起動された場合は GUI で起動します。 -.PP -差分を強調表示するために、 -それぞれのウィンドウの 'diff' オプションがオンに設定されます。 -.br -テキストを見やすくするために、オプションの 'wrap' と 'scrollbind' もオンに設 -定されます。 -.br - 'foldmethod' オプションは "diff" に設定され、変更されていない行は折り畳まれ -ます。 -折り畳みの確認と開閉が簡単にできるように、'foldcolumn' は 2 に設定されます。 -.SH オプション -行を並べて表示するために、"\-O" 引数を使ったときのように、ウィンドウは垂直分 -割されます。 -ウィンドウを水平分割したい場合は "\-o" 引数を使ってください。 -.PP -その他の引数については vim(1) を参照してください。 -.SH 関連項目 -vim(1) -.SH 著者 -.B Vim -のほとんどの機能は Bram Moolenaar が開発し、多くの人が協力しました。 -":help credits" を参照してください。 diff --git a/runtime/doc/manpages/pl/vim-pl.1 b/runtime/doc/manpages/pl/vim-pl.1 index 97d28f3a78..e92c3fd35f 100644 --- a/runtime/doc/manpages/pl/vim-pl.1 +++ b/runtime/doc/manpages/pl/vim-pl.1 @@ -186,7 +186,6 @@ Uruchom w trybie diff. Powinno się użyć dwóch, trzech lub czterech nazwy plików jako argumentów. .B Vim otworzy wszystkie te pliki i pokaże różnice między nimi. -Działa jak vimdiff(1). .TP \-d {urządzenie} Otwórz {urządzenie} by używać jako terminal. diff --git a/runtime/doc/manpages/pl/vimdiff-pl.1 b/runtime/doc/manpages/pl/vimdiff-pl.1 deleted file mode 100644 index d66fdc9c03..0000000000 --- a/runtime/doc/manpages/pl/vimdiff-pl.1 +++ /dev/null @@ -1,46 +0,0 @@ -.TH VIMDIFF 1 "2001 Mar 30" -.SH NAZWA -vimdiff \- edytuj dwie, trzy lub cztery wersje pliku w Vimie i zobacz różnice -.SH SYNOPSIS -.br -.B vimdiff -[opcje] plik1 plik2 [plik3 [plik4]] -.PP -.B gvimdiff -.SH OPIS -.B Vimdiff -uruchomi -.B Vima -z dwoma (trzema lub czterema plikami), każdy z nich we własnym oknie. -Różnice między plikami zostaną podświetlone. -Jest to dobry sposób by przeanalizować różnice i przenieść zmiany z jednej -wersji pliku do innej. -.PP -Zobacz vim(1) by poznać więcej szczegółów o samym Vimie. -.PP -Kiedy uruchomiony jako -.B gvimdiff -zostanie uruchomione GUI (jeśli dostępne). -.PP -W każdym oknie zostanie ustawiona opcja 'diff', która spowoduje -podświetlenie różnic. -.br -Opcje 'wrap' i 'scrollbind' zostaną ustawione by tekst się -wygodnie przeglądało. -.br -Opcja 'foldmethod' zostanie ustawiona na "diff", dzięki czemu -niezmienione linie zostaną zwinięte. 'foldcolumn' będzie równe 2 aby -łatwo wyszukiwać, otwierać i zamykać zwinięcia. -.SH OPCJE -Pionowy podział zostanie użyty do wyrównania linii, tak jakby użyto -opcji "\-O". Aby uzyskać poziomy podział użyj opcji "\-o". -.PP -Aby poznać inne opcje zobacz vim(1). -.SH ZOBACZ TAKŻE -vim(1) -.SH AUTOR -Większość -.B Vima -została napisana przez Brama Moolenaara przy pomocy wielu innych osób. -Zobacz ":help credits w -.B Vimie. diff --git a/runtime/doc/manpages/ru/vim-ru.1 b/runtime/doc/manpages/ru/vim-ru.1 index ef527213e3..c48fb6691a 100644 --- a/runtime/doc/manpages/ru/vim-ru.1 +++ b/runtime/doc/manpages/ru/vim-ru.1 @@ -173,7 +173,6 @@ rvim rview rgvim rgview Должно быть указано два или три имени файла. .B Vim откроет все файлы и покажет различия между ними -(как vimdiff(1)). .TP \-d {устройство} Открыть {устройство} для использования в качестве терминала (только на Amiga). diff --git a/runtime/doc/manpages/ru/vimdiff-ru.1 b/runtime/doc/manpages/ru/vimdiff-ru.1 deleted file mode 100644 index 2f17e9c2e5..0000000000 --- a/runtime/doc/manpages/ru/vimdiff-ru.1 +++ /dev/null @@ -1,50 +0,0 @@ -.TH VIMDIFF 1 "2001 March 30" -.SH ИМЯ -vimdiff \- позволяет редактировать две или три версии файла с помощью Vim -с отображением различий. -.SH КОМАНДНАЯ СТРОКА -.br -.B vimdiff -[ключи] файл1 файл2 [файл3] -.PP -.B gvimdiff -.SH ОПИСАНИЕ -Команда -.B vimdiff -загружает в -.B Vim -два (или три) файла. Каждый файл открывается в собственном окне. -Различия между файлами отображаются с помощью подсветки синтаксиса. -Эта команда позволяет наглядно отображать внесённые изменения и переносить -различающиеся фрагменты из одной версии файла в другую. -.PP -Подробнее о Vim см. на странице справочника vim(1). -.PP -При запуске с помощью команды -.B gvimdiff -редактор запускается в режиме графического интерфейса, если это возможно. -.PP -В каждом окне включается опция 'diff', что позволяет использовать подсветку -синтаксиса для отображения различий между файлами. -.br -Для улучшения работы с файлами в режиме отображения отличий также включаются -опции 'wrap' и 'scrollbind'. -.br -Значение опции 'foldmethod' устанавливается равным "diff", что позволяет -прятать в складку области текста, которые не отличаются в различных версиях файла. -Значение опции 'foldcolumn' устанавливается равным 2, чтобы было проще -находить, открывать и закрывать такие складки. -.SH КЛЮЧИ ЗАПУСКА -Для выравнивания строк между окнами используется вертикальное разделение окон, -как если бы использовался ключ "\-O". Для того, чтобы использовать разделение -окон по горизонтали, следует указать в командной строке ключ "\-o". -.PP -Остальные ключи командной строки рассматриваются на странице справочника vim(1). -.SH СМОТРИ ТАКЖЕ -vim(1) -.SH АВТОРЫ -Большая часть -.B Vim -создана Брамом Мооленааром (Bram Moolenaar), которому помогает огромное -количество людей. См. ":help credits" в -.B Vim. diff --git a/runtime/doc/manpages/vim.1 b/runtime/doc/manpages/vim.1 index 678170f6e1..c35fa78f0d 100644 --- a/runtime/doc/manpages/vim.1 +++ b/runtime/doc/manpages/vim.1 @@ -179,10 +179,7 @@ behave mostly like Vi, even though a .vimrc file exists. .TP \-d Start in diff mode. -There should be two, three or four file name arguments. -.B Vim -will open all the files and show differences between them. -Works like vimdiff(1). +There should be two, three, or four file name arguments. .TP \-D Debugging. Go to debugging mode when executing the first command from a diff --git a/runtime/doc/manpages/vimdiff.1 b/runtime/doc/manpages/vimdiff.1 deleted file mode 100644 index bed2b32889..0000000000 --- a/runtime/doc/manpages/vimdiff.1 +++ /dev/null @@ -1,46 +0,0 @@ -.TH VIMDIFF 1 "2001 March 30" -.SH NAME -vimdiff \- edit two, three or four versions of a file with Vim and show differences -.SH SYNOPSIS -.br -.B vimdiff -[options] file1 file2 [file3 [file4]] -.PP -.B gvimdiff -.SH DESCRIPTION -.B Vimdiff -starts -.B Vim -on two (or three or four) files. -Each file gets its own window. -The differences between the files are highlighted. -This is a nice way to inspect changes and to move changes from one version -to another version of the same file. -.PP -See vim(1) for details about Vim itself. -.PP -When started as -.B gvimdiff -the GUI will be started, if available. -.PP -In each window the 'diff' option will be set, which causes the differences -to be highlighted. -.br -The 'wrap' and 'scrollbind' options are set to make the text look good. -.br -The 'foldmethod' option is set to "diff", which puts ranges of lines without -changes in a fold. 'foldcolumn' is set to two to make it easy to spot the -folds and open or close them. -.SH OPTIONS -Vertical splits are used to align the lines, as if the "\-O" argument was used. -To use horizontal splits instead, use the "\-o" argument. -.PP -For all other arguments see vim(1). -.SH SEE ALSO -vim(1) -.SH AUTHOR -Most of -.B Vim -was made by Bram Moolenaar, with a lot of help from others. -See ":help credits" in -.B Vim. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 6c14d8e8fd..928a98066f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -45,8 +45,7 @@ achieve special effects. These options come in three forms: :se[t] inv{option} Toggle option: Invert value. {not in Vi} *:set-default* *:set-&* *:set-&vi* *:set-&vim* -:se[t] {option}& Reset option to its default value. May depend on the - current value of 'compatible'. {not in Vi} +:se[t] {option}& Reset option to its default value. {not in Vi} :se[t] {option}&vi Reset option to its Vi default value. {not in Vi} :se[t] {option}&vim Reset option to its Vim default value. {not in Vi} @@ -63,8 +62,7 @@ achieve special effects. These options come in three forms: (hex and octal are only available for machines which have the strtol() function). The old value can be inserted by typing 'wildchar' (by - default this is a <Tab> or CTRL-E if 'compatible' is - set). See |cmdline-completion|. + default this is a <Tab>). See |cmdline-completion|. White space between {option} and '=' is allowed and will be ignored. White space between '=' and {value} is not allowed. @@ -121,8 +119,6 @@ set all" or ":verbose set" without an argument. When the option was set by hand there is no "Last set" message. When the option was set while executing a function, user command or autocommand, the script in which it was defined is reported. -Note that an option may also have been set as a side effect of setting -'compatible'. A few special texts: Last set from modeline ~ Option was set in a |modeline|. @@ -587,10 +583,6 @@ an abbreviation if there is one. Both forms may be used. In this document when a boolean option is "set" that means that ":set option" is entered. When an option is "reset", ":set nooption" is used. -For some options there are two default values: The "Vim default", which is -used when 'compatible' is not set, and the "Vi default", which is used when -'compatible' is set. - Most options are the same in all windows and buffers. There are a few that are specific to how the text is presented in a window. These can be set to a different value in each window. For example the 'list' option can be set in @@ -654,7 +646,6 @@ A jump table for the options with a short description can be found at |Q_op|. avoid that users that accidentally type CTRL-_ instead of SHIFT-_ get into reverse Insert mode, and don't know how to get out. See 'revins'. - NOTE: This option is reset when 'compatible' is set. *'altkeymap'* *'akm'* *'noaltkeymap'* *'noakm'* 'altkeymap' 'akm' boolean (default off) @@ -901,7 +892,6 @@ A jump table for the options with a short description can be found at |Q_op|. 2 same as ":set backspace=indent,eol,start" See |:fixdel| if your <BS> or <Del> key does not do what you want. - NOTE: This option is set to "" when 'compatible' is set. *'backup'* *'bk'* *'nobackup'* *'nobk'* 'backup' 'bk' boolean (default off) @@ -917,7 +907,6 @@ A jump table for the options with a short description can be found at |Q_op|. When the 'backupskip' pattern matches, a backup is not made anyway. When 'patchmode' is set, the backup may be renamed to become the oldest version of a file. - NOTE: This option is reset when 'compatible' is set. *'backupcopy'* *'bkc'* 'backupcopy' 'bkc' string (Vi default for Unix: "yes", otherwise: "auto") @@ -1358,13 +1347,12 @@ A jump table for the options with a short description can be found at |Q_op|. (parts of 'cdpath' can be passed to the shell to expand file names). *'cedit'* -'cedit' string (Vi default: "", Vim default: CTRL-F) +'cedit' string (Vim default: CTRL-F, Vi default: "") global {not in Vi} {not available when compiled without the |+vertsplit| feature} The key used in Command-line Mode to open the command-line window. - The default is CTRL-F when 'compatible' is off. Only non-printable keys are allowed. The key can be specified as a single character, but it is difficult to type. The preferred way is to use the <> notation. Examples: > @@ -1432,7 +1420,6 @@ A jump table for the options with a short description can be found at |Q_op|. When you don't like the way 'cindent' works, try the 'smartindent' option or 'indentexpr'. This option is not used when 'paste' is set. - NOTE: This option is reset when 'compatible' is set. *'cinkeys'* *'cink'* 'cinkeys' 'cink' string (default "0{,0},0),:,0#,!^F,o,O,e") @@ -1633,94 +1620,10 @@ A jump table for the options with a short description can be found at |Q_op|. |fold-marker|. *'compatible'* *'cp'* *'nocompatible'* *'nocp'* -'compatible' 'cp' boolean (default on, off when a |vimrc| or |gvimrc| - file is found) - global - {not in Vi} - This option has the effect of making Vim either more Vi-compatible, or - make Vim behave in a more useful way. - This is a special kind of option, because when it's set or reset, - other options are also changed as a side effect. CAREFUL: Setting or - resetting this option can have a lot of unexpected effects: Mappings - are interpreted in another way, undo behaves differently, etc. If you - set this option in your vimrc file, you should probably put it at the - very start. - By default this option is on and the Vi defaults are used for the - options. This default was chosen for those people who want to use Vim - just like Vi, and don't even (want to) know about the 'compatible' - option. - When a |vimrc| or |gvimrc| file is found while Vim is starting up, - this option is switched off, and all options that have not been - modified will be set to the Vim defaults. Effectively, this means - that when a |vimrc| or |gvimrc| file exists, Vim will use the Vim - defaults, otherwise it will use the Vi defaults. (Note: This doesn't - happen for the system-wide vimrc or gvimrc file, nor for a file given - with the |-u| argument). Also see |compatible-default| and - |posix-compliance|. - You can also set this option with the "-C" argument, and reset it with - "-N". See |-C| and |-N|. - Switching this option off makes the Vim defaults be used for options - that have a different Vi and Vim default value. See the options - marked with a '+' below. Other options are not modified. - At the moment this option is set, several other options will be set - or reset to make Vim as Vi-compatible as possible. See the table - below. This can be used if you want to revert to Vi compatible - editing. - See also 'cpoptions'. - - option + set value effect ~ - - 'allowrevins' off no CTRL-_ command - 'backupcopy' Unix: "yes" backup file is a copy - others: "auto" copy or rename backup file - 'backspace' "" normal backspace - 'backup' off no backup file - 'cindent' off no C code indentation - 'cedit' + "" no key to open the |cmdwin| - 'cpoptions' + (all flags) Vi-compatible flags - 'cscopetag' off don't use cscope for ":tag" - 'cscopetagorder' 0 see |cscopetagorder| - 'cscopeverbose' off see |cscopeverbose| - 'digraph' off no digraphs - 'esckeys' + off no <Esc>-keys in Insert mode - 'expandtab' off tabs not expanded to spaces - 'fileformats' + "" no automatic file format detection, - "dos,unix" except for DOS and Windows - 'formatoptions' + "vt" Vi compatible formatting - 'gdefault' off no default 'g' flag for ":s" - 'history' + 0 no commandline history - 'hkmap' off no Hebrew keyboard mapping - 'hkmapp' off no phonetic Hebrew keyboard mapping - 'hlsearch' off no highlighting of search matches - 'incsearch' off no incremental searching - 'indentexpr' "" no indenting by expression - 'insertmode' off do not start in Insert mode - 'iskeyword' + "@,48-57,_" keywords contain alphanumeric - characters and '_' - 'joinspaces' on insert 2 spaces after period - 'modeline' + off no modelines - 'more' + off no pauses in listings - 'revins' off no reverse insert - 'ruler' off no ruler - 'scrolljump' 1 no jump scroll - 'scrolloff' 0 no scroll offset - 'shiftround' off indent not rounded to shiftwidth - 'shortmess' + "" no shortening of messages - 'showcmd' + off command characters not shown - 'showmode' + off current mode not shown - 'smartcase' off no automatic ignore case switch - 'smartindent' off no smart indentation - 'smarttab' off no smart tab size - 'softtabstop' 0 tabs are always 'tabstop' positions - 'startofline' on goto startofline with some commands - 'tagrelative' + off tag file names are not relative - 'textwidth' 0 no automatic line wrap - 'tildeop' off tilde is not an operator - 'ttimeout' off no terminal timeout - 'whichwrap' + "" left-right movements don't wrap - 'wildchar' + CTRL-E only when the current value is <Tab> - use CTRL-E for cmdline completion - 'writebackup' on or off depends on the |+writebackup| feature +'compatible' 'cp' Removed. {Nvim} + + Support for Vi-compatible mode has been removed. "set nocp" is + ignored, and "set cp" is an error. *'complete'* *'cpt'* *E535* 'complete' 'cpt' string (default: ".,w,b,u,t,i") @@ -1881,12 +1784,11 @@ A jump table for the options with a short description can be found at |Q_op|. existing line. 'expandtab' has no effect on these characters, a Tab remains a Tab. If the new indent is greater than on the existing line, the remaining space is filled in the normal manner. - NOTE: 'copyindent' is reset when 'compatible' is set. - Also see 'preserveindent'. + See 'preserveindent'. *'cpoptions'* *'cpo'* 'cpoptions' 'cpo' string (Vim default: "aABceFs", - Vi default: all flags) + Vi default: all flags) global {not in Vi} A sequence of single character flags. When a character is present @@ -1896,8 +1798,6 @@ A jump table for the options with a short description can be found at |Q_op|. Commas can be added for readability. To avoid problems with flags that are added in the future, use the "+=" and "-=" feature of ":set" |add-option-flags|. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. NOTE: This option is set to the POSIX default value at startup when the Vi default value would be used and the $VIM_POSIX environment variable exists |posix|. This means Vim tries to behave like the @@ -2242,7 +2142,6 @@ A jump table for the options with a short description can be found at |Q_op|. feature} {not in Vi} Use cscope for tag commands. See |cscope-options|. - NOTE: This option is reset when 'compatible' is set. *'cscopetagorder'* *'csto'* 'cscopetagorder' 'csto' number (default 0) @@ -2252,7 +2151,6 @@ A jump table for the options with a short description can be found at |Q_op|. {not in Vi} Determines the order in which ":cstag" performs a search. See |cscopetagorder|. - NOTE: This option is set to 0 when 'compatible' is set. *'cscopeverbose'* *'csverb'* *'nocscopeverbose'* *'nocsverb'* @@ -2262,7 +2160,6 @@ A jump table for the options with a short description can be found at |Q_op|. feature} {not in Vi} Give messages when adding a cscope database. See |cscopeverbose|. - NOTE: This option is reset when 'compatible' is set. *'cursorbind'* *'crb'* *'nocursorbind'* *'nocrb'* 'cursorbind' 'crb' boolean (default off) @@ -2382,17 +2279,13 @@ A jump table for the options with a short description can be found at |Q_op|. 'diff' boolean (default off) local to window {not in Vi} - {not available when compiled without the |+diff| - feature} Join the current window in the group of windows that shows differences - between files. See |vimdiff|. + between files. See |diff-mode|. *'dex'* *'diffexpr'* 'diffexpr' 'dex' string (default "") global {not in Vi} - {not available when compiled without the |+diff| - feature} Expression which is evaluated to obtain an ed-style diff file from two versions of a file. See |diff-diffexpr|. This option cannot be set from a |modeline| or in the |sandbox|, for @@ -2402,8 +2295,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'diffopt' 'dip' string (default "filler") global {not in Vi} - {not available when compiled without the |+diff| - feature} Option settings for diff mode. It can consist of the following items. All are optional. Items must be separated by a comma. @@ -2452,7 +2343,6 @@ A jump table for the options with a short description can be found at |Q_op|. feature} Enable the entering of digraphs in Insert mode with {char1} <BS> {char2}. See |digraphs|. - NOTE: This option is reset when 'compatible' is set. *'directory'* *'dir'* 'directory' 'dir' string (default for MS-DOS and Win32: ".,$TEMP,c:\tmp,c:\temp", @@ -2689,8 +2579,6 @@ A jump table for the options with a short description can be found at |Q_op|. try changing the values for 'timeoutlen' and 'ttimeoutlen'. Note that when 'esckeys' is off, you can still map anything, but the cursor keys won't work by default. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'eventignore'* *'ei'* 'eventignore' 'ei' string (default "") @@ -2712,7 +2600,6 @@ A jump table for the options with a short description can be found at |Q_op|. <Tab>. Spaces are used in indents with the '>' and '<' commands and when 'autoindent' is on. To insert a real tab when 'expandtab' is on, use CTRL-V<Tab>. See also |:retab| and |ins-expandtab|. - NOTE: This option is reset when 'compatible' is set. *'exrc'* *'ex'* *'noexrc'* *'noex'* 'exrc' 'ex' boolean (default off) @@ -2863,10 +2750,9 @@ A jump table for the options with a short description can be found at |Q_op|. *'fileformats'* *'ffs'* 'fileformats' 'ffs' string (default: - Vim+Vi MS-DOS and MS-Windows: "dos,unix", + Vim+Vi Win32: "dos,unix", Vim Unix: "unix,dos", Vim Mac: "mac,unix,dos", - Vi Cygwin: "unix,dos", Vi others: "") global {not in Vi} @@ -2915,8 +2801,6 @@ A jump table for the options with a short description can be found at |Q_op|. <CR> in front of it, Dos format is used, otherwise Unix format is used. Also see |file-formats|. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'fileignorecase'* *'fic'* *'nofileignorecase'* *'nofic'* 'fileignorecase' 'fic' boolean (default on for systems where case in file @@ -3202,8 +3086,6 @@ A jump table for the options with a short description can be found at |Q_op|. be inserted for readability. To avoid problems with flags that are added in the future, use the "+=" and "-=" feature of ":set" |add-option-flags|. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'formatlistpat'* *'flp'* 'formatlistpat' 'flp' string (default: "^\s*\d\+[\]:.)}\t ]\s*") @@ -3298,8 +3180,6 @@ A jump table for the options with a short description can be found at |Q_op|. :s///g subst. one subst. all :s///gg subst. all subst. one - NOTE: This option is reset when 'compatible' is set. - *'grepformat'* *'gfm'* 'grepformat' 'gfm' string (default "%f:%l:%m,%f:%l%m,%f %l%m") global @@ -3784,17 +3664,16 @@ A jump table for the options with a short description can be found at |Q_op|. *'highlight'* *'hl'* 'highlight' 'hl' string (default (as a single string): - "8:SpecialKey,@:NonText,d:Directory, - e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg, - M:ModeMsg,n:LineNr,N:CursorLineNr, - r:Question,s:StatusLine,S:StatusLineNC, - c:VertSplit, t:Title,v:Visual, - w:WarningMsg,W:WildMenu, - f:Folded,F:FoldColumn,A:DiffAdd, - C:DiffChange,D:DiffDelete,T:DiffText, - >:SignColumn,B:SpellBad,P:SpellCap, - R:SpellRare,L:SpellLocal,-:Conceal, - +:Pmenu,=:PmenuSel, + "8:SpecialKey,~:EndOfBuffer,@:NonText,i + d:Directory,e:ErrorMsg,i:IncSearch, + l:Search,m:MoreMsg,M:ModeMsg,n:LineNr, + N:CursorLineNr,r:Question,s:StatusLine, + S:StatusLineNC,c:VertSplit,t:Title, + v:Visual,w:WarningMsg,W:WildMenu,f:Folded, + F:FoldColumn,A:DiffAdd,C:DiffChange, + D:DiffDelete,T:DiffText,>:SignColumn, + B:SpellBad,P:SpellCap,R:SpellRare, + L:SpellLocal,-:Conceal,+:Pmenu,=:PmenuSel, x:PmenuSbar,X:PmenuThumb") global {not in Vi} @@ -3803,7 +3682,8 @@ A jump table for the options with a short description can be found at |Q_op|. first character in a pair gives the occasion, the second the mode to use for that occasion. The occasions are: |hl-SpecialKey| 8 Meta and special keys listed with ":map" - |hl-NonText| @ '~' and '@' at the end of the window and + |hl-EndOfBuffer| ~ lines after the last line in the buffer + |hl-NonText| @ '@' at the end of the window and characters from 'showbreak' |hl-Directory| d directories in CTRL-D listing and other special things in listings @@ -3887,7 +3767,6 @@ A jump table for the options with a short description can be found at |Q_op|. drawn may not continue in a newly drawn line. You can specify whether the highlight status is restored on startup with the 'h' flag in 'viminfo' |viminfo-h|. - NOTE: This option is reset when 'compatible' is set. *'history'* *'hi'* 'history' 'hi' number (Vim default: 50, Vi default: 0) @@ -3897,8 +3776,6 @@ A jump table for the options with a short description can be found at |Q_op|. is remembered. This option decides how many entries may be stored in each of these histories (see |cmdline-editing|). The maximum value is 10000. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'hkmap'* *'hk'* *'nohkmap'* *'nohk'* 'hkmap' 'hk' boolean (default off) @@ -3909,7 +3786,6 @@ A jump table for the options with a short description can be found at |Q_op|. When on, the keyboard is mapped for the Hebrew character set. Normally you would set 'allowrevins' and use CTRL-_ in insert mode to toggle this option. See |rileft.txt|. - NOTE: This option is reset when 'compatible' is set. *'hkmapp'* *'hkp'* *'nohkmapp'* *'nohkp'* 'hkmapp' 'hkp' boolean (default off) @@ -3920,7 +3796,6 @@ A jump table for the options with a short description can be found at |Q_op|. When on, phonetic keyboard mapping is used. 'hkmap' must also be on. This is useful if you have a non-Hebrew keyboard. See |rileft.txt|. - NOTE: This option is reset when 'compatible' is set. *'icon'* *'noicon'* 'icon' boolean (default off, on when title can be restored) @@ -4166,7 +4041,6 @@ A jump table for the options with a short description can be found at |Q_op|. converted to lowercase. CTRL-R CTRL-W can be used to add the word at the end of the current match, excluding the characters that were already typed. - NOTE: This option is reset when 'compatible' is set. *'indentexpr'* *'inde'* 'indentexpr' 'inde' string (default "") @@ -4197,7 +4071,6 @@ A jump table for the options with a short description can be found at |Q_op|. < Error messages will be suppressed, unless the 'debug' option contains "msg". See |indent-expression|. - NOTE: This option is made empty when 'compatible' is set. The expression will be evaluated in the |sandbox| when set from a modeline, see |sandbox-option|. @@ -4256,8 +4129,6 @@ A jump table for the options with a short description can be found at |Q_op|. mappings with 'insertmode' set or not set. When executing commands with |:normal| 'insertmode' is not used. - NOTE: This option is reset when 'compatible' is set. - *'isfname'* *'isf'* 'isfname' 'isf' string (default for MS-DOS and Win32: "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" @@ -4326,10 +4197,10 @@ A jump table for the options with a short description can be found at |Q_op|. expand "$HOME/.viminfo". Maybe you should change 'iskeyword' instead. *'iskeyword'* *'isk'* -'iskeyword' 'isk' string (Vim default for MS-DOS and Win32: - "@,48-57,_,128-167,224-235" - otherwise: "@,48-57,_,192-255" - Vi default: "@,48-57,_") +'iskeyword' 'isk' string (Vim default for + Win32: @,48-57,_,128-167,224-235 + otherwise: @,48-57,_,192-255 + Vi default: @,48-57,_) local to buffer {not in Vi} Keywords are used in searching and recognizing with many commands: @@ -4340,8 +4211,6 @@ A jump table for the options with a short description can be found at |Q_op|. '*', '"' and '|' (so that CTRL-] on a command finds the help for that command). When the 'lisp' option is on the '-' character is always included. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'isprint'* *'isp'* 'isprint' 'isp' string (default for MS-DOS, Win32, and Macintosh: @@ -4382,7 +4251,6 @@ A jump table for the options with a short description can be found at |Q_op|. Insert two spaces after a '.', '?' and '!' with a join command. When 'cpoptions' includes the 'j' flag, only do this after a '.'. Otherwise only one space is inserted. - NOTE: This option is set when 'compatible' is set. *'key'* 'key' Removed. {Nvim} @@ -4888,8 +4756,6 @@ A jump table for the options with a short description can be found at |Q_op|. If 'modeline' is on 'modelines' gives the number of lines that is checked for set commands. If 'modeline' is off or 'modelines' is zero no lines are checked. See |modeline|. - NOTE: 'modeline' is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'modifiable'* *'ma'* *'nomodifiable'* *'noma'* 'modifiable' 'ma' boolean (default on) @@ -4929,8 +4795,6 @@ A jump table for the options with a short description can be found at |Q_op|. When on, listings pause when the whole screen is filled. You will get the |more-prompt|. When this option is off there are no pauses, the listing continues until finished. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'mouse'* *E538* 'mouse' string (default "", "a" for GUI, MS-DOS and Win32) @@ -5121,7 +4985,7 @@ A jump table for the options with a short description can be found at |Q_op|. local to window Print the line number in front of each line. When the 'n' option is excluded from 'cpoptions' a wrapped line will not use the column of - line numbers (this is the default when 'compatible' isn't set). + line numbers. The 'numberwidth' option can be used to set the room used for the line number. When a long, wrapped line doesn't start with the first character, '-' @@ -5157,7 +5021,6 @@ A jump table for the options with a short description can be found at |Q_op|. is set. Thus with the Vim default of 4 there is room for a line number up to 999. When the buffer has 1000 lines five columns will be used. The minimum value is 1, the maximum value is 10. - NOTE: 'numberwidth' is reset to 8 when 'compatible' is set. *'omnifunc'* *'ofu'* 'omnifunc' 'ofu' string (default: empty) @@ -5274,8 +5137,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'patchexpr' 'pex' string (default "") global {not in Vi} - {not available when compiled without the |+diff| - feature} Expression which is evaluated to apply a patch to a file and generate the resulting new version of the file. See |diff-patchexpr|. @@ -5368,7 +5229,6 @@ A jump table for the options with a short description can be found at |Q_op|. a Tab. NOTE: When using ">>" multiple times the resulting indent is a mix of tabs and spaces. You might not like this. - NOTE: 'preserveindent' is reset when 'compatible' is set. Also see 'copyindent'. Use |:retab| to clean up white space. @@ -5537,8 +5397,7 @@ A jump table for the options with a short description can be found at |Q_op|. having to calculate it yourself. Especially useful in combination with other commands (e.g. y d c < > gq gw =). When the 'n' option is excluded from 'cpoptions' a wrapped - line will not use the column of line numbers (this is the default when - 'compatible' isn't set). + line will not use the column of line numbers. The 'numberwidth' option can be used to set the room used for the line number. When a long, wrapped line doesn't start with the first character, '-' @@ -5594,7 +5453,7 @@ A jump table for the options with a short description can be found at |Q_op|. Inserting characters in Insert mode will work backwards. See "typing backwards" |ins-reverse|. This option can be toggled with the CTRL-_ command in Insert mode, when 'allowrevins' is set. - NOTE: This option is reset when 'compatible' or 'paste' is set. + NOTE: This option is reset when 'paste' is set. *'rightleft'* *'rl'* *'norightleft'* *'norl'* 'rightleft' 'rl' boolean (default off) @@ -5653,7 +5512,6 @@ A jump table for the options with a short description can be found at |Q_op|. This option is reset when the 'paste' option is set. If you don't want to see the ruler all the time but want to know where you are, use "g CTRL-G" |g_CTRL-G|. - NOTE: This option is reset when 'compatible' is set. *'rulerformat'* *'ruf'* 'rulerformat' 'ruf' string (default empty) @@ -5771,7 +5629,6 @@ A jump table for the options with a short description can be found at |Q_op|. When set to a negative number from -1 to -100 this is used as the percentage of the window height. Thus -50 scrolls half the window height. - NOTE: This option is set to 1 when 'compatible' is set. *'scrolloff'* *'so'* 'scrolloff' 'so' number (default 0) @@ -5783,7 +5640,6 @@ A jump table for the options with a short description can be found at |Q_op|. in the middle of the window (except at the start or end of the file or when long lines wrap). For scrolling horizontally see 'sidescrolloff'. - NOTE: This option is set to 0 when 'compatible' is set. *'scrollopt'* *'sbo'* 'scrollopt' 'sbo' string (default "ver,jump") @@ -6064,7 +5920,7 @@ A jump table for the options with a short description can be found at |Q_op|. if exists('+shellslash') < *'shelltemp'* *'stmp'* *'noshelltemp'* *'nostmp'* -'shelltemp' 'stmp' boolean (Vi default off, Vim default on) +'shelltemp' 'stmp' boolean (Vim default on, Vi default off) global {not in Vi} When on, use temp files for shell commands. When off use a pipe. @@ -6120,7 +5976,6 @@ A jump table for the options with a short description can be found at |Q_op|. Round indent to multiple of 'shiftwidth'. Applies to > and < commands. CTRL-T and CTRL-D in Insert mode always round the indent to a multiple of 'shiftwidth' (this is Vi compatible). - NOTE: This option is reset when 'compatible' is set. *'shiftwidth'* *'sw'* 'shiftwidth' 'sw' number (default 8) @@ -6180,9 +6035,6 @@ A jump table for the options with a short description can be found at |Q_op|. shm=a Abbreviation, but no loss of information. shm=at Abbreviation, and truncate message when necessary. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. - *'shortname'* *'sn'* *'noshortname'* *'nosn'* 'shortname' 'sn' Removed. {Nvim} @@ -6208,8 +6060,8 @@ A jump table for the options with a short description can be found at |Q_op|. "n" flag to 'cpoptions'. *'showcmd'* *'sc'* *'noshowcmd'* *'nosc'* -'showcmd' 'sc' boolean (Vim default: on, off for Unix, Vi default: - off) +'showcmd' 'sc' boolean (Vim default: on (off for Unix), + Vi default: off) global {not in Vi} {not available when compiled without the @@ -6223,8 +6075,6 @@ A jump table for the options with a short description can be found at |Q_op|. - When selecting more than one line, the number of lines. - When selecting a block, the size in screen characters: {lines}x{columns}. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'showfulltag'* *'sft'* *'noshowfulltag'* *'nosft'* 'showfulltag' 'sft' boolean (default off) @@ -6267,8 +6117,6 @@ A jump table for the options with a short description can be found at |Q_op|. When |XIM| may be used the message will include "XIM". But this doesn't mean XIM is really active, especially when 'imactivatekey' is not set. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'showtabline'* *'stal'* 'showtabline' 'stal' number (default 1) @@ -6308,7 +6156,6 @@ A jump table for the options with a short description can be found at |Q_op|. to a large value (like 999) has the effect of keeping the cursor horizontally centered in the window, as long as one does not come too close to the beginning of the line. - NOTE: This option is set to 0 when 'compatible' is set. Example: Try this together with 'sidescroll' and 'listchars' as in the following example to never allow the cursor to move @@ -6328,7 +6175,6 @@ A jump table for the options with a short description can be found at |Q_op|. ":g" and ":s". Not used for "*", "#", "gd", tag search, etc. After "*" and "#" you can make 'smartcase' used by doing a "/" command, recalling the search pattern from history and hitting <Enter>. - NOTE: This option is reset when 'compatible' is set. *'smartindent'* *'si'* *'nosmartindent'* *'nosi'* 'smartindent' 'si' boolean (default off) @@ -6355,8 +6201,7 @@ A jump table for the options with a short description can be found at |Q_op|. mapping: ":inoremap # X^H#", where ^H is entered with CTRL-V CTRL-H. When using the ">>" command, lines starting with '#' are not shifted right. - NOTE: 'smartindent' is reset when 'compatible' is set. When 'paste' - is set smart indenting is disabled. + NOTE: When 'paste' is set smart indenting is disabled. *'smarttab'* *'sta'* *'nosmarttab'* *'nosta'* 'smarttab' 'sta' boolean (default off) @@ -6372,7 +6217,6 @@ A jump table for the options with a short description can be found at |Q_op|. What gets inserted (a <Tab> or spaces) depends on the 'expandtab' option. Also see |ins-expandtab|. When 'expandtab' is not set, the number of spaces is minimized by using <Tab>s. - NOTE: This option is reset when 'compatible' is set. *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) @@ -6391,7 +6235,6 @@ A jump table for the options with a short description can be found at |Q_op|. spaces is minimized by using <Tab>s. The 'L' flag in 'cpoptions' changes how tabs are used when 'list' is set. - NOTE: This option is set to 0 when 'compatible' is set. *'spell'* *'nospell'* 'spell' boolean (default off) @@ -6585,7 +6428,6 @@ A jump table for the options with a short description can be found at |Q_op|. only has a line number, e.g., ":25" or ":+". In case of buffer changing commands the cursor is placed at the column where it was the last time the buffer was edited. - NOTE: This option is set when 'compatible' is set. *'statusline'* *'stl'* *E540* *E542* 'statusline' 'stl' string (default empty) @@ -6998,8 +6840,6 @@ A jump table for the options with a short description can be found at |Q_op|. {not in Vi} If on and using a tags file in another directory, file names in that tags file are relative to the directory where the tags file is. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'tags'* *'tag'* *E433* 'tags' 'tag' string (default "./tags,tags", when compiled with @@ -7124,7 +6964,6 @@ A jump table for the options with a short description can be found at |Q_op|. 'textwidth' is zero, 'wrapmargin' may be used. See also 'formatoptions' and |ins-textwidth|. When 'formatexpr' is set it will be used to break the line. - NOTE: This option is set to 0 when 'compatible' is set. *'thesaurus'* *'tsr'* 'thesaurus' 'tsr' string (default "") @@ -7150,7 +6989,6 @@ A jump table for the options with a short description can be found at |Q_op|. global {not in Vi} When on: The tilde command "~" behaves like an operator. - NOTE: This option is reset when 'compatible' is set. *'timeout'* *'to'* *'notimeout'* *'noto'* 'timeout' 'to' boolean (default on) @@ -7184,8 +7022,6 @@ A jump table for the options with a short description can be found at |Q_op|. sequences not timing out in 1 second, set the 'ttimeout' option and reset the 'timeout' option. - NOTE: 'ttimeout' is reset when 'compatible' is set. - *'timeoutlen'* *'tm'* 'timeoutlen' 'tm' number (default 1000) global @@ -7361,16 +7197,7 @@ A jump table for the options with a short description can be found at |Q_op|. xterm entries...). *'ttyfast'* *'tf'* *'nottyfast'* *'notf'* -'ttyfast' 'tf' boolean (default on) - global - {not in Vi} - Indicates a fast terminal connection. More characters will be sent to - the screen for redrawing, instead of using insert/delete line - commands. Improves smoothness of redrawing when there are multiple - windows and the terminal does not support a scrolling region. - Also enables the extra writing of characters at the end of each screen - line for lines that wrap. This helps when using copy/paste with the - mouse in an xterm and other terminals. +'ttyfast' 'tf' Removed. {Nvim} *'ttymouse'* *'ttym'* 'ttymouse' 'ttym' string (default depends on 'term') @@ -7611,9 +7438,10 @@ A jump table for the options with a short description can be found at |Q_op|. but the Windows version of Vim can source unix format scripts. *'viminfo'* *'vi'* *E526* *E527* *E528* -'viminfo' 'vi' string (Vi default: "", Vim default for MS-DOS, - Windows: '100,<50,s10,h,rA:,rB:, - for others: '100,<50,s10,h) +'viminfo' 'vi' string (Vim default for + Win32: '100,<50,s10,h,rA:,rB: + others: '100,<50,s10,h + Vi default: "") global {not in Vi} {not available when compiled without the |+viminfo| @@ -7811,8 +7639,6 @@ A jump table for the options with a short description can be found at |Q_op|. When 'l' is included and it is used after an operator at the end of a line then it will not move to the next line. This makes "dl", "cl", "yl" etc. work normally. - NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. *'wildchar'* *'wc'* 'wildchar' 'wc' number (Vim default: <Tab>, Vi default: CTRL-E) @@ -7825,8 +7651,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'wildcharm' for that. Although 'wc' is a number option, you can set it to a special key: > :set wc=<Esc> -< NOTE: This option is set to the Vi default value when 'compatible' is - set and to the Vim default value when 'compatible' is reset. +< *'wildcharm'* *'wcm'* 'wildcharm' 'wcm' number (default: none (0)) @@ -8161,8 +7986,6 @@ A jump table for the options with a short description can be found at |Q_op|. fail (and make sure not to exit Vim until the write was successful). See |backup-table| for another explanation. When the 'backupskip' pattern matches, a backup is not made anyway. - NOTE: This option is set to the default value when 'compatible' is - set. *'writedelay'* *'wd'* 'writedelay' 'wd' number (default 0) diff --git a/runtime/doc/os_unix.txt b/runtime/doc/os_unix.txt index 49f19c6661..73e955e834 100644 --- a/runtime/doc/os_unix.txt +++ b/runtime/doc/os_unix.txt @@ -30,8 +30,7 @@ can be changed at compile time. Because terminal updating under Unix is often slow (e.g. serial line terminal, shell window in suntools), the 'showcmd' and 'ruler' options -are default off. If you have a fast terminal, try setting them on. You might -also want to set 'ttyfast'. +are off by default. When using Vim in an xterm the mouse clicks can be used by Vim by setting 'mouse' to "a". If there is access to an X-server gui style copy/paste will diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 5cde4685fc..3b3214456b 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -911,7 +911,6 @@ Short explanation of each option: *option-list* 'ttimeout' time out on mappings 'ttimeoutlen' 'ttm' time out time for key codes in milliseconds 'ttybuiltin' 'tbi' use built-in termcap before external termcap -'ttyfast' 'tf' indicates a fast terminal connection 'ttymouse' 'ttym' type of mouse codes generated 'ttyscroll' 'tsl' maximum number of lines for a scroll 'ttytype' 'tty' alias for 'term' @@ -1134,7 +1133,7 @@ Context-sensitive completion on the command-line: |-e| -e Ex mode, start vim in Ex mode |-R| -R Read-only mode, implies -n |-m| -m modifications not allowed (resets 'write' option) -|-d| -d diff mode |diff| +|-d| -d |diff-mode| |-b| -b binary mode |-l| -l lisp mode |-A| -A Arabic mode ('arabic' is set) diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 65533cab66..78d7b5477f 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -568,8 +568,8 @@ following procedure is recommended: When the Myspell files are updated you can merge the differences: 1. Obtain the new Myspell files as xx_YY.new.aff and xx_UU.new.dic. -2. Use Vimdiff to see what changed: > - vimdiff xx_YY.orig.dic xx_YY.new.dic +2. Use |diff-mode| to see what changed: > + nvim -d xx_YY.orig.dic xx_YY.new.dic 3. Take over the changes you like in xx_YY.dic. You may also need to change xx_YY.aff. 4. Rename xx_YY.new.dic to xx_YY.orig.dic and xx_YY.new.aff to xx_YY.new.aff. diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 502d122a7c..39cb522002 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -99,8 +99,6 @@ rvim vim -Z Like "vim", but in restricted mode (see |-Z|) *rvim* rview vim -RZ Like "view", but in restricted mode. *rview* rgvim vim -gZ Like "gvim", but in restricted mode. *rgvim* rgview vim -RgZ Like "gview", but in restricted mode. *rgview* -vimdiff vim -d Start in diff mode |diff-mode| -gvimdiff vim -gd Start in diff mode |diff-mode| Additional characters may follow, they are ignored. For example, you can have "gvim-5" to start the GUI. You must have an executable by that name then, of @@ -397,9 +395,7 @@ argument. of terminal you are using. (See |terminal-info|.) {not in Vi} *-d* --d Start in diff mode, like |vimdiff|. - {not in Vi} {not available when compiled without the |+diff| - feature} +-d Start in |diff-mode|. *-f* -f GUI: Do not disconnect from the program that started Vim. @@ -988,10 +984,10 @@ vimrc file. These commands will write ":map" and ":set" commands to a file, in such a way that when these commands are executed, the current key mappings and options will be set to the same values. The options 'columns', 'endofline', -'fileformat', 'lines', 'modified', 'scroll', 'term', 'ttyfast' and 'ttymouse' -are not included, because these are terminal or file dependent. Note that the -options 'binary', 'paste' and 'readonly' are included, this might not always -be what you want. +'fileformat', 'lines', 'modified', 'scroll', 'term', and 'ttymouse' are not +included, because these are terminal or file dependent. Note that the options +'binary', 'paste' and 'readonly' are included, this might not always be what +you want. When special keys are used in mappings, The 'cpoptions' option will be temporarily set to its Vim default, to avoid the mappings to be diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 1841f83214..c1f7e06f0e 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4761,6 +4761,9 @@ DiffChange diff mode: Changed line |diff.txt| DiffDelete diff mode: Deleted line |diff.txt| *hl-DiffText* DiffText diff mode: Changed text within a changed line |diff.txt| + *hl-EndOfBuffer* +EndOfBuffer filler lines (~) after the end of the buffer. + By default, this is highlighted like |hl-NonText|. *hl-ErrorMsg* ErrorMsg error messages on the command line *hl-VertSplit* @@ -4789,10 +4792,10 @@ ModeMsg 'showmode' message (e.g., "-- INSERT --") *hl-MoreMsg* MoreMsg |more-prompt| *hl-NonText* -NonText '~' and '@' at the end of the window, characters from - 'showbreak' and other characters that do not really exist in - the text (e.g., ">" displayed when a double-wide character - doesn't fit at the end of the line). +NonText '@' at the end of the window, characters from 'showbreak' + and other characters that do not really exist in the text + (e.g., ">" displayed when a double-wide character doesn't + fit at the end of the line). See also |hl-EndOfBuffer|. *hl-Normal* Normal normal text *hl-Pmenu* diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index ea272031d6..49eb429319 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -537,11 +537,6 @@ If the characters from the terminal are arriving with more than 1 second between them you might want to set the 'timeout' and/or 'ttimeout' option. See the "Options" chapter |options|. -If your terminal does not support a scrolling region, but it does support -insert/delete line commands, scrolling with multiple windows may make the -lines jump up and down. If you don't want this set the 'ttyfast' option. -This will redraw the window instead of scroll it. - If your terminal scrolls very slowly, but redrawing is not slow, set the 'ttyscroll' option to a small number, e.g., 3. This will make Vim redraw the screen instead of scrolling, when there are more than 3 lines to be scrolled. diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index bfe67c2757..6f35118fa8 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1882,9 +1882,6 @@ In gvim the backspace key produces a backspace character, but on Linux the VERASE key is Delete. Set VERASE to Backspace? (patch by Stephane Chazelas, 2007 Oct 16) -TermResponse autocommand isn't always triggered when using vimdiff. (Aron -Griffis, 2007 Sep 19) - Create a gvimtutor.1 file and change Makefiles to install it. When 'encoding' is utf-8 typing text at the end of the line causes previously diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt index ef26b46eb7..d1f3fbd49d 100644 --- a/runtime/doc/usr_08.txt +++ b/runtime/doc/usr_08.txt @@ -15,7 +15,7 @@ side by side. All this is possible with split windows. |08.4| Vertical splits |08.5| Moving windows |08.6| Commands for all windows -|08.7| Viewing differences with vimdiff +|08.7| Viewing differences with diff mode |08.8| Various |08.9| Tab pages @@ -335,15 +335,15 @@ The "-O" argument is used to get vertically split windows. file in the argument list. ":vertical all" does it with vertical splits. ============================================================================== -*08.7* Viewing differences with vimdiff +*08.7* Viewing differences with diff mode -There is a special way to start Vim, which shows the differences between two +There is a special way to start Nvim, which shows the differences between two files. Let's take a file "main.c" and insert a few characters in one line. Write this file with the 'backup' option set, so that the backup file "main.c~" will contain the previous version of the file. - Type this command in a shell (not in Vim): > +Type this command in a shell to start Nvim in diff mode: > - vimdiff main.c~ main.c + nvim -d main.c~ main.c Vim will start, with two windows side by side. You will only see the line in which you added characters, and a few lines above and below it. @@ -367,8 +367,7 @@ in which you added characters, and a few lines above and below it. | | +-----------------------------------------+ -(This picture doesn't show the highlighting, use the vimdiff command for a -better look.) +(This picture doesn't show the highlighting, use "nvim -d" for that.) The lines that were not modified have been collapsed into one line. This is called a closed fold. They are indicated in the picture with "<- fold". Thus @@ -472,7 +471,7 @@ Since there are no changes left now, Vim puts all text in a closed fold. "do" stands for "diff obtain". "dg" would have been better, but that already has a different meaning ("dgg" deletes from the cursor until the first line). -For details about diff mode, see |vimdiff|. +For details about diff mode, see |diff-mode|. ============================================================================== *08.8* Various diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt index ec404f3301..2a810af6c2 100644 --- a/runtime/doc/usr_11.txt +++ b/runtime/doc/usr_11.txt @@ -44,7 +44,7 @@ To be on the safe side, write this file under another name: > :write help.txt.recovered Compare the file with the original file to check if you ended up with what you -expected. Vimdiff is very useful for this |08.7|. For example: > +expected. Diff mode is very useful for this |08.7|. For example: > :write help.txt.recovered :edit # diff --git a/runtime/doc/usr_28.txt b/runtime/doc/usr_28.txt index 46db1b90af..d29b384437 100644 --- a/runtime/doc/usr_28.txt +++ b/runtime/doc/usr_28.txt @@ -376,7 +376,7 @@ More about folding by expression in the reference manual: |fold-expr| *28.9* Folding unchanged lines This is useful when you set the 'diff' option in the same window. The -|vimdiff| command does this for you. Example: > +|-d| option does this for you. Example: > :setlocal diff foldmethod=diff scrollbind nowrap foldlevel=1 diff --git a/runtime/doc/usr_90.txt b/runtime/doc/usr_90.txt index 3579e1f430..4be0e93718 100644 --- a/runtime/doc/usr_90.txt +++ b/runtime/doc/usr_90.txt @@ -438,7 +438,6 @@ example for when "/usr/local" was used as the root: > rm /usr/local/bin/gview rm /usr/local/bin/gvim rm /usr/local/bin/gvim - rm /usr/local/bin/gvimdiff rm /usr/local/bin/rgview rm /usr/local/bin/rgvim rm /usr/local/bin/rview @@ -446,20 +445,17 @@ example for when "/usr/local" was used as the root: > rm /usr/local/bin/rvim rm /usr/local/bin/view rm /usr/local/bin/vim - rm /usr/local/bin/vimdiff rm /usr/local/bin/vimtutor rm /usr/local/bin/xxd rm /usr/local/man/man1/ex.1 rm /usr/local/man/man1/gview.1 rm /usr/local/man/man1/gvim.1 - rm /usr/local/man/man1/gvimdiff.1 rm /usr/local/man/man1/rgview.1 rm /usr/local/man/man1/rgvim.1 rm /usr/local/man/man1/rview.1 rm /usr/local/man/man1/rvim.1 rm /usr/local/man/man1/view.1 rm /usr/local/man/man1/vim.1 - rm /usr/local/man/man1/vimdiff.1 rm /usr/local/man/man1/vimtutor.1 rm /usr/local/man/man1/xxd.1 diff --git a/runtime/doc/usr_toc.txt b/runtime/doc/usr_toc.txt index 9e4bf5ca93..2a72e6ea86 100644 --- a/runtime/doc/usr_toc.txt +++ b/runtime/doc/usr_toc.txt @@ -133,7 +133,7 @@ Read this from start to end to learn the essential commands. |08.4| Vertical splits |08.5| Moving windows |08.6| Commands for all windows - |08.7| Viewing differences with vimdiff + |08.7| Viewing differences with diff mode |08.8| Various |usr_09.txt| Using the GUI diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 2e61079805..ced0c24434 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -325,7 +325,6 @@ m *+debug* Compiled for debugging. N *+dialog_gui* Support for |:confirm| with GUI dialog. N *+dialog_con* Support for |:confirm| with console dialog. N *+dialog_con_gui* Support for |:confirm| with GUI and console dialog. -N *+diff* |vimdiff| and 'diff' N *+digraphs* |digraphs| *E196* *+dnd* Support for DnD into the "~ register |quote_~|. B *+emacs_tags* |emacs-tags| files diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt index 9ce94696e4..b4e621e516 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -192,7 +192,7 @@ Folding. |folding| Folds can be created manually, from the syntax of the file, by indent, etc. -Diff mode. |diff| +Diff mode. |diff-mode| Vim can show two versions of a file with the differences highlighted. Parts of the text that are equal are folded away. Commands can be used to move text from one version to the other. @@ -826,7 +826,7 @@ Only Vim is able to accept options in between and after the file names. loading the edit buffer. Vim: allow up to 10 "-c" arguments --d Vim: start with 'diff' set. |vimdiff| +-d Vim: start with 'diff' set. |diff-mode| -D Vim: debug mode. diff --git a/runtime/optwin.vim b/runtime/optwin.vim index eb96bb7da3..073de3876a 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -520,8 +520,6 @@ call append("$", "ttytype\talias for 'term'") call <SID>OptionG("tty", &tty) call append("$", "ttybuiltin\tcheck built-in termcaps first") call <SID>BinOptionG("tbi", &tbi) -call append("$", "ttyfast\tterminal connection is fast") -call <SID>BinOptionG("tf", &tf) call append("$", "weirdinvert\tterminal that requires extra redrawing") call <SID>BinOptionG("wiv", &wiv) call append("$", "esckeys\trecognize keys that start with <Esc> in Insert mode") diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 47d74090d1..5614a3f2f6 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -30,19 +30,19 @@ syn keyword vimStdPlugin contained DiffOrig Man N[ext] P[rint] S TOhtml XMLent X syn keyword vimOption contained acd ambiwidth arabicshape autowriteall backupdir bdlay binary breakat bufhidden cd ci cinw co commentstring confirm cpoptions cscoperelative csre cursorcolumn delcombine diffopt ea efm ep et fdc fdo ffs filetype fo foldlevelstart formatexpr ft gfw gtt guipty hh hkmap ic imaf iminsert includeexpr inf isident langmap lines lmap ma matchtime mco ml modeline mousefocus mousetime nrformats ofu para pdev pi previewwindow printmbfont qe relativenumber rightleftcmd ru sbr scrolloff selection shellcmdflag shellxescape showbreak si smartcase softtabstop spelllang sps sta su swb synmaxcol tagbsearch tbi termencoding thesaurus titlestring tpm ttm ttytype undodir ut vfile vop wcm whichwrap wildignore winaltkeys winminwidth wmnu write syn keyword vimOption contained ai ambw ari aw backupext beval brk buflisted cdpath cin cinwords cocu compatible cpt cscopetag cst cursorline dex digraph ead ei equalalways eventignore fde fdt fic fillchars foldclose foldmarker formatlistpat gcr ghr guicursor guitablabel hi hkmapp icon imak ims incsearch infercase isk keymap langmenu linespace loadplugins macatsui maxcombine mef mls modelines mousehide mp nu omnifunc paragraphs penc pm printdevice printoptions quoteescape remap rl ruf sc scrollopt selectmode shellpipe shellxquote showcmd sidescroll smartindent sol spellsuggest sr stal sua swf syntax taglength tbidi terse tildeop tl tr tty tw undofile vb vi wa wd wi wildignorecase window winwidth wmw writeany syn keyword vimOption contained akm anti arshape awa backupskip bex browsedir buftype cedit cindent clipboard cole complete crb cscopetagorder csto cwh dg dip eadirection ek equalprg ex fdi fen fileencoding fk foldcolumn foldmethod formatoptions gd go guifont guitabtooltip hid hkp iconstring imc imsearch inde insertmode iskeyword keymodel laststatus lisp lpl magic maxfuncdepth menuitems mm modifiable mousem mps number opendevice paste pex pmbcs printencoding prompt rdt report rlc ruler scb scs sessionoptions shellquote shiftround showfulltag sidescrolloff smarttab sp spf srr startofline suffixes switchbuf ta tagrelative tbis textauto timeout tm ts ttybuiltin tx undolevels vbs viewdir wak weirdinvert wic wildmenu winfixheight wiv wop writebackup -syn keyword vimOption contained al antialias autochdir background balloondelay bexpr bk bs casemap cf cink cmdheight colorcolumn completefunc copyindent cscopeverbose csverb debug dict dir eb enc errorbells expandtab fdl fenc fileencodings fkmap foldenable foldminlines formatprg gdefault gp guifontset helpfile hidden hl ignorecase imcmdline imsf indentexpr is isp keywordprg lazyredraw lispwords ls makeef maxmapdepth mfd mmd modified mousemodel msm numberwidth operatorfunc pastetoggle pexpr pmbfn printexpr pt re restorescreen rnu rulerformat scr sect sft shellredir shiftwidth showmatch siso smc spc spl ss statusline suffixesadd sws tabline tags tbs textmode timeoutlen to tsl ttyfast uc undoreload vdir viewoptions warn wfh wig wildmode winfixwidth wiw wrap writedelay +syn keyword vimOption contained al antialias autochdir background balloondelay bexpr bk bs casemap cf cink cmdheight colorcolumn completefunc copyindent cscopeverbose csverb debug dict dir eb enc errorbells expandtab fdl fenc fileencodings fkmap foldenable foldminlines formatprg gdefault gp guifontset helpfile hidden hl ignorecase imcmdline imsf indentexpr is isp keywordprg lazyredraw lispwords ls makeef maxmapdepth mfd mmd modified mousemodel msm numberwidth operatorfunc pastetoggle pexpr pmbfn printexpr pt re restorescreen rnu rulerformat scr sect sft shellredir shiftwidth showmatch siso smc spc spl ss statusline suffixesadd sws tabline tags tbs textmode timeoutlen to tsl uc undoreload vdir viewoptions warn wfh wig wildmode winfixwidth wiw wrap writedelay syn keyword vimOption contained aleph ar autoindent backspace ballooneval bg bkc bsdir cb cfu cinkeys cmdwinheight columns completeopt cot cscopepathcomp cspc cuc deco dictionary directory ed encoding errorfile exrc fdls fencs fileformat flp foldexpr foldnestmax fp gfm grepformat guifontwide helpheight highlight hlg im imd imstatusfunc indentkeys isf isprint km lbr list lsp makeprg maxmem mh mmp more mouses mzq nuw opfunc patchexpr pfn popt printfont pumheight readonly revins ro runtimepath scroll sections sh shellslash shm showmode sj smd spell splitbelow ssl stl sw sxe tabpagemax tagstack tenc textwidth title toolbar tsr ttym udf updatecount ve viminfo wb wfw wildchar wildoptions winheight wm wrapmargin ws -syn keyword vimOption contained allowrevins arab autoread backup balloonexpr bh bl bsk cc ch cino cmp com concealcursor cp cscopeprg csprg cul def diff display edcompatible endofline errorformat fcl fdm fex fileformats fml foldignore foldopen fs gfn grepprg guiheadroom helplang history hls imactivatefunc imdisable inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev patchmode ph preserveindent printheader pvh redrawtime ri rs sb scrollbind secure shcf shelltemp shortmess showtabline slm spellcapcheck splitright ssop stmp swapfile sxq tabstop tal term tf titlelen toolbariconsize ttimeout ttymouse udir updatetime verbose virtualedit wc wh wildcharm wim winminheight wmh wrapscan ww +syn keyword vimOption contained allowrevins arab autoread backup balloonexpr bh bl bsk cc ch cino cmp com concealcursor cp cscopeprg csprg cul def diff display edcompatible endofline errorformat fcl fdm fex fileformats fml foldignore foldopen fs gfn grepprg guiheadroom helplang history hls imactivatefunc imdisable inc indk isfname joinspaces kmp lcs listchars lw mat maxmempattern mis mmt mouse mouseshape mzquantum odev patchmode ph preserveindent printheader pvh redrawtime ri rs sb scrollbind secure shcf shelltemp shortmess showtabline slm spellcapcheck splitright ssop stmp swapfile sxq tabstop tal term titlelen toolbariconsize ttimeout ttymouse udir updatetime verbose virtualedit wc wh wildcharm wim winminheight wmh wrapscan ww syn keyword vimOption contained altkeymap arabic autowrite backupcopy bdir bin bomb bt ccv charconvert cinoptions cms comments conceallevel cpo cscopequickfix csqf cursorbind define diffexpr dy ef eol esckeys fcs fdn ff fileignorecase fmr foldlevel foldtext fsync gfs gtl guioptions hf hk hlsearch imactivatekey imi include inex isi js kp linebreak lm lz matchpairs maxmemtot mkspellmem mod mousef mouset nf pa path pheader previewheight printmbcharset pvw regexpengine rightleft rtp sbo scrolljump sel shell shq sm so spellfile spr st sts swapsync syn tag tb termbidi tgst titleold top ttimeoutlen ttyscroll ul ur unnamedclip unc verbosefile visualbell " vimOptions: These are the turn-off setting variants {{{2 syn keyword vimOption contained noacd noallowrevins noantialias noarabic noarshape noautoread noaw noballooneval nobinary nobk nobuflisted nocin noconfirm nocopyindent nocscoperelative nocsre nocuc nocursorcolumn nodelcombine nodigraph noed noendofline noerrorbells noex nofen nofk nogd nohid nohkmap nohkp nohlsearch noicon noim noimcmdline noimdisable noinf noinsertmode nojoinspaces nolazyredraw nolinebreak nolist nolpl noma nomagic noml nomodeline nomodified nomousef nomousehide nonumber noopendevice nopi nopreviewwindow nopvw norelativenumber norestorescreen nori norl noro noru nosb noscb noscs nosft noshelltemp noshowfulltag noshowmode nosm nosmartindent nosmd nosol nosplitbelow nospr nossl nostartofline noswapfile nota notagrelative notbi notbs noterse notextmode notgst notimeout noto notr nottybuiltin notx noundofile novisualbell nowarn noweirdinvert nowfw nowildignorecase nowinfixheight nowiv nowrap nowrite nowritebackup -syn keyword vimOption contained noai noaltkeymap noar noarabicshape noautochdir noautowrite noawa nobeval nobl nocf nocindent nocp nocscopetag nocst nocul nocursorline nodg noea noedcompatible noeol noesckeys noexpandtab nofic nofkmap nogdefault nohidden nohkmapp nohls noic noignorecase noimc noimd noincsearch noinfercase nois nojs nolbr nolisp noloadplugins nolz nomacatsui nomh nomod nomodifiable nomore nomousefocus nonu noodev nopaste nopreserveindent noprompt noreadonly noremap norevins norightleft nornu nors noruler nosc noscrollbind nosecure noshellslash noshiftround noshowcmd noshowmatch nosi nosmartcase nosmarttab nospell nosplitright nosr nosta nostmp noswf notagbsearch notagstack notbidi notermbidi notextauto notf notildeop notitle notop nottimeout nottyfast noudf novb nowa nowb nowfh nowic nowildmenu nowinfixwidth nowmnu nowrapscan nowriteany nows +syn keyword vimOption contained noai noaltkeymap noar noarabicshape noautochdir noautowrite noawa nobeval nobl nocf nocindent nocp nocscopetag nocst nocul nocursorline nodg noea noedcompatible noeol noesckeys noexpandtab nofic nofkmap nogdefault nohidden nohkmapp nohls noic noignorecase noimc noimd noincsearch noinfercase nois nojs nolbr nolisp noloadplugins nolz nomacatsui nomh nomod nomodifiable nomore nomousefocus nonu noodev nopaste nopreserveindent noprompt noreadonly noremap norevins norightleft nornu nors noruler nosc noscrollbind nosecure noshellslash noshiftround noshowcmd noshowmatch nosi nosmartcase nosmarttab nospell nosplitright nosr nosta nostmp noswf notagbsearch notagstack notbidi notermbidi notextauto notildeop notitle notop nottimeout noudf novb nowa nowb nowfh nowic nowildmenu nowinfixwidth nowmnu nowrapscan nowriteany nows syn keyword vimOption contained noakm noanti noarab noari noautoindent noautowriteall nobackup nobin nobomb noci nocompatible nocrb nocscopeverbose nocsverb nocursorbind nodeco nodiff noeb noek noequalalways noet noexrc nofileignorecase nofoldenable noguipty nohk " vimOptions: These are the invertible variants {{{2 syn keyword vimOption contained invacd invallowrevins invantialias invarabic invarshape invautoread invaw invballooneval invbinary invbk invbuflisted invcin invconfirm invcopyindent invcscoperelative invcsre invcuc invcursorcolumn invdelcombine invdigraph inved invendofline inverrorbells invex invfen invfk invgd invhid invhkmap invhkp invhlsearch invicon invim invimcmdline invimdisable invinf invinsertmode invjoinspaces invlazyredraw invlinebreak invlist invlpl invma invmagic invml invmodeline invmodified invmousef invmousehide invnumber invopendevice invpi invpreviewwindow invpvw invrelativenumber invrestorescreen invri invrl invro invru invsb invscb invscs invsft invshelltemp invshowfulltag invshowmode invsm invsmartindent invsmd invsol invsplitbelow invspr invssl invstartofline invswapfile invta invtagrelative invtbi invtbs invterse invtextmode invtgst invtimeout invto invtr invttybuiltin invtx invundofile invvisualbell invwarn invweirdinvert invwfw invwildignorecase invwinfixheight invwiv invwrap invwrite invwritebackup -syn keyword vimOption contained invai invaltkeymap invar invarabicshape invautochdir invautowrite invawa invbeval invbl invcf invcindent invcp invcscopetag invcst invcul invcursorline invdg invea invedcompatible inveol invesckeys invexpandtab invfic invfkmap invgdefault invhidden invhkmapp invhls invic invignorecase invimc invimd invincsearch invinfercase invis invjs invlbr invlisp invloadplugins invlz invmacatsui invmh invmod invmodifiable invmore invmousefocus invnu invodev invpaste invpreserveindent invprompt invreadonly invremap invrevins invrightleft invrnu invrs invruler invsc invscrollbind invsecure invshellslash invshiftround invshowcmd invshowmatch invsi invsmartcase invsmarttab invspell invsplitright invsr invsta invstmp invswf invtagbsearch invtagstack invtbidi invtermbidi invtextauto invtf invtildeop invtitle invtop invttimeout invttyfast invudf invvb invwa invwb invwfh invwic invwildmenu invwinfixwidth invwmnu invwrapscan invwriteany invws +syn keyword vimOption contained invai invaltkeymap invar invarabicshape invautochdir invautowrite invawa invbeval invbl invcf invcindent invcp invcscopetag invcst invcul invcursorline invdg invea invedcompatible inveol invesckeys invexpandtab invfic invfkmap invgdefault invhidden invhkmapp invhls invic invignorecase invimc invimd invincsearch invinfercase invis invjs invlbr invlisp invloadplugins invlz invmacatsui invmh invmod invmodifiable invmore invmousefocus invnu invodev invpaste invpreserveindent invprompt invreadonly invremap invrevins invrightleft invrnu invrs invruler invsc invscrollbind invsecure invshellslash invshiftround invshowcmd invshowmatch invsi invsmartcase invsmarttab invspell invsplitright invsr invsta invstmp invswf invtagbsearch invtagstack invtbidi invtermbidi invtextauto invtildeop invtitle invtop invttimeout invudf invvb invwa invwb invwfh invwic invwildmenu invwinfixwidth invwmnu invwrapscan invwriteany invws syn keyword vimOption contained invakm invanti invarab invari invautoindent invautowriteall invbackup invbin invbomb invci invcompatible invcrb invcscopeverbose invcsverb invcursorbind invdeco invdiff inveb invek invequalalways invet invexrc invfileignorecase invfoldenable invguipty invhk " termcap codes (which can also be set) {{{2 @@ -68,7 +68,7 @@ syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePo syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo " Default highlighting groups {{{2 -syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineNr DiffAdd DiffChange DiffDelete DiffText Directory ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual VisualNOS WarningMsg WildMenu +syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineNr DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual VisualNOS WarningMsg WildMenu syn match vimHLGroup contained "Conceal" syn case match diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 4c9a141bd2..c4bc2d156a 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -15,8 +15,8 @@ usage() { >&2 echo " ${0} vim-revision" >&2 echo >&2 echo "Options:" - >&2 echo " --help Show this message." - >&2 echo " --list Show list of Vim patches missing from Neovim." + >&2 echo " -h, --help Show this message." + >&2 echo " -l, --list Show list of Vim patches missing from Neovim." >&2 echo >&2 echo "vim-revision can be a version number in format '7.4.xxx'" >&2 echo "or a Mercurial commit hash." @@ -181,14 +181,14 @@ list_vim_patches() { echo " './scripts/vim-patch.sh 1e8ebf870720e7b671f98f22d653009826304c4f'" } -if [[ ${#} != 1 || "${1}" == "--help" ]]; then +if [[ ${#} != 1 || "${1}" == "--help" || "${1}" == "-h" ]]; then usage exit 1 fi get_vim_sources -if [[ "${1}" == "--list" ]]; then +if [[ "${1}" == "--list" || "${1}" == "-l" ]]; then list_vim_patches else get_vim_patch ${1} diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index d78bd5f711..6fc5022d48 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -82,7 +82,6 @@ set(CONV_SOURCES syntax.c tag.c ui.c - version.c window.c) foreach(sfile ${CONV_SOURCES}) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 45cc3c530b..b7b2f7630c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -552,6 +552,18 @@ Integer vim_name_to_color(String name) return name_to_color((uint8_t *)name.data); } +Dictionary vim_get_color_map(void) +{ + Dictionary colors = ARRAY_DICT_INIT; + + for (int i = 0; color_name_table[i].name != NULL; i++) { + PUT(colors, color_name_table[i].name, + INTEGER_OBJ(color_name_table[i].color)); + } + return colors; +} + + Array vim_get_api_info(uint64_t channel_id) { Array rv = ARRAY_DICT_INIT; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index f6527db69b..9b48398d96 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3219,27 +3219,15 @@ get_address ( } if (!skip) { - /* - * When search follows another address, start from - * there. - */ - if (lnum != MAXLNUM) - pos.lnum = lnum; - else - pos.lnum = curwin->w_cursor.lnum; - - /* - * Start the search just like for the above - * do_search(). - */ - if (*cmd != '?') - pos.col = MAXCOL; - else - pos.col = 0; + // When search follows another address, start from there. + pos.lnum = (lnum != MAXLNUM) ? lnum : curwin->w_cursor.lnum; + // Start the search just like for the above do_search(). + pos.col = (*cmd != '?') ? MAXCOL : 0; + pos.coladd = 0; if (searchit(curwin, curbuf, &pos, - *cmd == '?' ? BACKWARD : FORWARD, - (char_u *)"", 1L, SEARCH_MSG, - i, (linenr_T)0, NULL) != FAIL) + *cmd == '?' ? BACKWARD : FORWARD, + (char_u *)"", 1L, SEARCH_MSG, + i, (linenr_T)0, NULL) != FAIL) lnum = pos.lnum; else { cmd = NULL; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 863e44ed9c..2667d13b78 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -6828,7 +6828,7 @@ void unblock_autocmds(void) /* When v:termresponse was set while autocommands were blocked, trigger * the autocommands now. Esp. useful when executing a shell command - * during startup (vimdiff). */ + * during startup (nvim -d). */ if (autocmd_blocked == 0 && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse) apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 73bcdea226..1aa90777fa 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -405,7 +405,8 @@ EXTERN int no_check_timestamps INIT(= 0); /* Don't check timestamps */ typedef enum { HLF_8 = 0 /* Meta & special keys listed with ":map", text that is displayed different from what it is */ - , HLF_AT /* @ and ~ characters at end of screen, characters that + , HLF_EOB // after the last line in the buffer + , HLF_AT /* @ characters at end of screen, characters that don't really exist in the text */ , HLF_D /* directories in CTRL-D listing */ , HLF_E /* error messages */ @@ -451,10 +452,10 @@ typedef enum { /* The HL_FLAGS must be in the same order as the HLF_ enums! * When changing this also adjust the default for 'highlight'. */ -#define HL_FLAGS {'8', '@', 'd', 'e', 'i', 'l', 'm', 'M', 'n', 'N', 'r', 's', \ - 'S', 'c', 't', 'v', 'V', 'w', 'W', 'f', 'F', 'A', 'C', 'D', \ - 'T', '-', '>', 'B', 'P', 'R', 'L', '+', '=', 'x', 'X', '*', \ - '#', '_', '!', '.', 'o'} +#define HL_FLAGS {'8', '~', '@', 'd', 'e', 'i', 'l', 'm', 'M', 'n', 'N', 'r', \ + 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', 'f', 'F', 'A', 'C', \ + 'D', 'T', '-', '>', 'B', 'P', 'R', 'L', '+', '=', 'x', 'X', \ + '*', '#', '_', '!', '.', 'o'} EXTERN int highlight_attr[HLF_COUNT]; /* Highl. attr for each context. */ # define USER_HIGHLIGHT diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 843cbcf6f9..09f4ecf519 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -1646,7 +1646,6 @@ static void cs_print_tags_priv(char **matches, char **cntxts, int num_matches) char *fname, *lno, *extra, *tbuf; int i, idx, num; char *globalcntx = "GLOBAL"; - char *cntxformat = " <<%s>>"; char *context; char *cstag_msg = _("Cscope tag: %s"); @@ -1706,7 +1705,11 @@ static void cs_print_tags_priv(char **matches, char **cntxts, int num_matches) context = cntxts[idx]; else context = globalcntx; - newsize = strlen(context) + strlen(cntxformat); + + const char *cntxformat = " <<%s>>"; + // '%s' won't appear in result string, so: + // newsize = len(cntxformat) - 2 + len(context) + 1 (for NUL). + newsize = strlen(context) + strlen(cntxformat) - 1; if (bufsize < newsize) { buf = xrealloc(buf, newsize); diff --git a/src/nvim/main.c b/src/nvim/main.c index 083da9bee9..6e24806c56 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -204,7 +204,7 @@ int main(int argc, char **argv) /* * Figure out the way to work from the command name argv[0]. - * "vimdiff" starts diff mode, "rvim" sets "restricted", etc. + * "view" sets "readonlymode", "rvim" sets "restricted", etc. */ parse_command_name(¶ms); @@ -454,7 +454,7 @@ int main(int argc, char **argv) edit_buffers(¶ms); if (params.diff_mode) { - /* set options in each window for "vimdiff". */ + /* set options in each window for "nvim -d". */ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { diff_win_options(wp, TRUE); } @@ -867,11 +867,10 @@ static void init_locale(void) #endif /* - * Check for: [r][g][vi|vim|view][diff][ex[im]] + * Check for: [r][g][vi|vim|view][ex[im]] * If the executable name starts with "r" we disable shell commands. * If the next character is "g" we run the GUI version. * If the next characters are "view" we start in readonly mode. - * If the next characters are "diff" or "vimdiff" we start in diff mode. * If the next characters are "ex" we start in Ex mode. If it's followed * by "im" use improved Ex mode. */ @@ -902,10 +901,6 @@ static void parse_command_name(mparm_T *parmp) parse_string(&initstr, "vim", 3); /* consume "vim" if it's there */ } - /* Catch "[r][g]vimdiff" and "[r][g]viewdiff". */ - if (parse_string(&initstr, "diff", 4)) - parmp->diff_mode = TRUE; - if (parse_string(&initstr, "ex", 2)) { if (parse_string(&initstr, "im", 2)) exmode_active = EXMODE_VIM; @@ -2057,93 +2052,65 @@ static void mainerr(int n, const char *str) } -/* - * print a message with three spaces prepended and '\n' appended. - */ -static void main_msg(char *s) -{ - mch_msg(" "); - mch_msg(s); - mch_msg("\n"); -} - -/* - * Print messages for "vim -h" or "vim --help" and exit. - */ +/// Prints help message and exits; used for 'nvim -h' & 'nvim --help' static void usage(void) { - int i; - static char *(use[]) = - { - N_("[file ..] edit specified file(s)"), - N_("- read text from stdin"), - N_("-t tag edit file where tag is defined"), - N_("-q [errorfile] edit file with first error") - }; - - signal_stop(); /* kill us with CTRL-C here, if you like */ - - mch_msg(longVersion); - mch_msg(_("\n\nusage:")); - for (i = 0;; ++i) { - mch_msg(_(" vim [arguments] ")); - mch_msg(_(use[i])); - if (i == ARRAY_SIZE(use) - 1) - break; - mch_msg(_("\n or:")); - } + signal_stop(); // kill us with CTRL-C here, if you like - mch_msg(_("\n\nArguments:\n")); - main_msg(_("--\t\t\tOnly file names after this")); + mch_msg(_("Usage:\n")); + mch_msg(_(" nvim [arguments] [file ...] Edit specified file(s)\n")); + mch_msg(_(" nvim [arguments] - Read text from stdin\n")); + mch_msg(_(" nvim [arguments] -t <tag> Edit file where tag is defined\n")); + mch_msg(_(" nvim [arguments] -q [errorfile] Edit file with first error\n")); + mch_msg(_("\nArguments:\n")); + mch_msg(_(" -- Only file names after this\n")); #if !defined(UNIX) - main_msg(_("--literal\t\tDon't expand wildcards")); + mch_msg(_(" --literal Don't expand wildcards\n")); #endif - main_msg(_("-v\t\t\tVi mode (like \"vi\")")); - main_msg(_("-e\t\t\tEx mode (like \"ex\")")); - main_msg(_("-E\t\t\tImproved Ex mode")); - main_msg(_("-s\t\t\tSilent (batch) mode (only for \"ex\")")); - main_msg(_("-d\t\t\tDiff mode (like \"vimdiff\")")); - main_msg(_("-R\t\t\tReadonly mode (like \"view\")")); - main_msg(_("-Z\t\t\tRestricted mode (like \"rvim\")")); - main_msg(_("-m\t\t\tModifications (writing files) not allowed")); - main_msg(_("-M\t\t\tModifications in text not allowed")); - main_msg(_("-b\t\t\tBinary mode")); - main_msg(_("-l\t\t\tLisp mode")); - main_msg(_("-V[N][fname]\t\tBe verbose [level N] [log messages to fname]")); - main_msg(_("-D\t\t\tDebugging mode")); - main_msg(_("-n\t\t\tNo swap file, use memory only")); - main_msg(_("-r\t\t\tList swap files and exit")); - main_msg(_("-r (with file name)\tRecover crashed session")); - main_msg(_("-L\t\t\tSame as -r")); - main_msg(_("-A\t\t\tstart in Arabic mode")); - main_msg(_("-H\t\t\tStart in Hebrew mode")); - main_msg(_("-F\t\t\tStart in Farsi mode")); - main_msg(_("-T <terminal>\tSet terminal type to <terminal>")); - main_msg(_("-u <vimrc>\t\tUse <vimrc> instead of any .vimrc")); - main_msg(_("--noplugin\t\tDon't load plugin scripts")); - main_msg(_("-p[N]\t\tOpen N tab pages (default: one for each file)")); - main_msg(_("-o[N]\t\tOpen N windows (default: one for each file)")); - main_msg(_("-O[N]\t\tLike -o but split vertically")); - main_msg(_("+\t\t\tStart at end of file")); - main_msg(_("+<lnum>\t\tStart at line <lnum>")); - main_msg(_("--cmd <command>\tExecute <command> before loading any vimrc file")); - main_msg(_("-c <command>\t\tExecute <command> after loading the first file")); - main_msg(_( - "-S <session>\t\tSource file <session> after loading the first file")); - main_msg(_("-s <scriptin>\tRead Normal mode commands from file <scriptin>")); - main_msg(_("-w <scriptout>\tAppend all typed commands to file <scriptout>")); - main_msg(_("-W <scriptout>\tWrite all typed commands to file <scriptout>")); - main_msg(_("--startuptime <file>\tWrite startup timing messages to <file>")); - main_msg(_("-i <viminfo>\t\tUse <viminfo> instead of .viminfo")); - main_msg(_("--api-info\t\tDump API metadata serialized to msgpack and exit")); - main_msg(_("--embed\t\tUse stdin/stdout as a msgpack-rpc channel. " - "This can be used for embedding Neovim into other programs")); - main_msg(_("-h or --help\tPrint Help (this message) and exit")); - main_msg(_("--version\t\tPrint version information and exit")); + mch_msg(_(" -v Vi mode (like \"vi\")\n")); + mch_msg(_(" -e Ex mode (like \"ex\")\n")); + mch_msg(_(" -E Improved Ex mode\n")); + mch_msg(_(" -s Silent (batch) mode (only for \"ex\")\n")); + mch_msg(_(" -d Diff mode\n")); + mch_msg(_(" -R Readonly mode (like \"view\")\n")); + mch_msg(_(" -Z Restricted mode (like \"rvim\")\n")); + mch_msg(_(" -m Modifications (writing files) not allowed\n")); + mch_msg(_(" -M Modifications in text not allowed\n")); + mch_msg(_(" -b Binary mode\n")); + mch_msg(_(" -l Lisp mode\n")); + mch_msg(_(" -V[N][file] Be verbose [level N][log messages to file]\n")); + mch_msg(_(" -D Debugging mode\n")); + mch_msg(_(" -n No swap file, use memory only\n")); + mch_msg(_(" -r List swap files and exit\n")); + mch_msg(_(" -r <file> Recover crashed session\n")); + mch_msg(_(" -A Start in Arabic mode\n")); + mch_msg(_(" -F Start in Farsi mode\n")); + mch_msg(_(" -H Start in Hebrew mode\n")); + mch_msg(_(" -T <terminal> Set terminal type to <terminal>\n")); + mch_msg(_(" -u <nvimrc> Use <nvimrc> instead of any .nvimrc\n")); + mch_msg(_(" --noplugin Don't load plugin scripts\n")); + mch_msg(_(" -p[N] Open N tab pages (default: one for each file)\n")); + mch_msg(_(" -o[N] Open N windows (default: one for each file)\n")); + mch_msg(_(" -O[N] Like -o but split vertically\n")); + mch_msg(_(" + Start at end of file\n")); + mch_msg(_(" +<lnum> Start at line <lnum>\n")); + mch_msg(_(" --cmd <command> Execute <command> before loading any nvimrc\n")); + mch_msg(_(" -c <command> Execute <command> after loading the first file\n")); + mch_msg(_(" -S <session> Source file <session> after loading the first file\n")); + mch_msg(_(" -s <scriptin> Read Normal mode commands from file <scriptin>\n")); + mch_msg(_(" -w <scriptout> Append all typed commands to file <scriptout>\n")); + mch_msg(_(" -W <scriptout> Write all typed commands to file <scriptout>\n")); + mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); + mch_msg(_(" -i <nviminfo> Use <nviminfo> instead of .nviminfo\n")); + mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n")); + mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); + mch_msg(_(" --version Print version information and exit\n")); + mch_msg(_(" -h | --help Print this help message and exit\n")); mch_exit(0); } + /* * Check the result of the ATTENTION dialog: * When "Quit" selected, exit Vim. diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index a702d4f256..54e8b83cd0 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -86,8 +86,8 @@ bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) FUNC_ATTR_NONNULL_ALL { - *arg = obj->via.dec; - return obj->type == MSGPACK_OBJECT_DOUBLE; + *arg = obj->via.f64; + return obj->type == MSGPACK_OBJECT_FLOAT; } bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) @@ -120,7 +120,7 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) arg->type = kObjectTypeInteger; return msgpack_rpc_to_integer(obj, &arg->data.integer); - case MSGPACK_OBJECT_DOUBLE: + case MSGPACK_OBJECT_FLOAT: arg->type = kObjectTypeFloat; return msgpack_rpc_to_float(obj, &arg->data.floating); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index f140922082..e147280723 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -834,6 +834,7 @@ getcount: ca.cmdchar = Ctrl_BSL; ca.nchar = c; idx = find_command(ca.cmdchar); + assert(idx >= 0); } } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 0199c5fc6c..182834c4f3 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -365,8 +365,8 @@ typedef struct vimoption { # define ISP_LATIN1 (char_u *)"@,161-255" #define HIGHLIGHT_INIT \ - "8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search," \ - "m:MoreMsg,M:ModeMsg,n:LineNr,N:CursorLineNr,r:Question,s:StatusLine," \ + "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch," \ + "l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,N:CursorLineNr,r:Question,s:StatusLine," \ "S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \ "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete," \ "T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare," \ @@ -1583,9 +1583,6 @@ static vimoption_T {"ttybuiltin", "tbi", P_BOOL|P_VI_DEF, (char_u *)&p_tbi, PV_NONE, {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"ttyfast", "tf", P_BOOL|P_NO_MKRC|P_VI_DEF, - (char_u *)&p_tf, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, {"ttymouse", "ttym", P_STRING|P_NODEFAULT|P_NO_MKRC|P_VI_DEF, #if defined(FEAT_MOUSE) && defined(UNIX) (char_u *)&p_ttym, PV_NONE, diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 99e8e645b6..c8b63cd155 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -569,7 +569,6 @@ EXTERN char_u *p_tsr; /* 'thesaurus' */ EXTERN int p_ttimeout; /* 'ttimeout' */ EXTERN long p_ttm; /* 'ttimeoutlen' */ EXTERN int p_tbi; /* 'ttybuiltin' */ -EXTERN int p_tf; /* 'ttyfast' */ EXTERN long p_ttyscroll; /* 'ttyscroll' */ #if defined(FEAT_MOUSE) && defined(UNIX) EXTERN char_u *p_ttym; /* 'ttymouse' */ diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index f7b47f9569..d674db951f 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -13,6 +13,7 @@ * changed beyond recognition. */ +#include <assert.h> #include <errno.h> #include <inttypes.h> #include <stdbool.h> @@ -1158,14 +1159,30 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, free(tempname); goto notfound; } - fseek(fd, 0L, SEEK_END); - len = ftell(fd); /* get size of temp file */ + int fseek_res = fseek(fd, 0L, SEEK_END); + if (fseek_res < 0) { + free(tempname); + fclose(fd); + return FAIL; + } + long long templen = ftell(fd); /* get size of temp file */ + if (templen < 0) { + free(tempname); + fclose(fd); + return FAIL; + } +#if SIZEOF_LONG_LONG > SIZEOF_SIZE_T + assert(templen <= (long long)SIZE_MAX); +#endif + len = (size_t)templen; fseek(fd, 0L, SEEK_SET); buffer = xmalloc(len + 1); - i = fread((char *)buffer, 1, len, fd); + // fread() doesn't terminate buffer with NUL; + // appropiate termination (not always NUL) is done below. + size_t readlen = fread((char *)buffer, 1, len, fd); fclose(fd); os_remove((char *)tempname); - if (i != (int)len) { + if (readlen != len) { /* unexpected read error */ EMSG2(_(e_notread), tempname); free(tempname); @@ -1174,8 +1191,6 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, } free(tempname); - - /* file names are separated with Space */ if (shell_style == STYLE_ECHO) { buffer[len] = '\n'; /* make sure the buffer ends in NL */ @@ -1235,6 +1250,8 @@ int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, if (len) ++i; /* count last entry */ } + assert(buffer[len] == NUL || buffer[len] == '\n'); + if (i == 0) { /* * Can happen when using /bin/sh and typing ":e $NO_SUCH_VAR^I". diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 6d25c4359b..7e7a7c1148 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1651,7 +1651,7 @@ static void win_update(win_T *wp) /* make sure the rest of the screen is blank */ /* put '~'s on rows that aren't part of the file. */ - win_draw_end(wp, '~', ' ', row, wp->w_height, HLF_AT); + win_draw_end(wp, '~', ' ', row, wp->w_height, HLF_EOB); } /* Reset the type of redrawing required, the window has been updated. */ @@ -4196,8 +4196,7 @@ win_line ( * Don't do this for double-width characters. * Don't do this for a window not at the right screen border. */ - if (p_tf - && !(has_mbyte + if (!(has_mbyte && ((*mb_off2cells)(LineOffset[screen_row], LineOffset[screen_row] + screen_Columns) == 2 @@ -7102,8 +7101,9 @@ static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int de return retval; } - if (wp->w_next != NULL && p_tf) /* don't delete/insert on fast terminal */ + if (wp->w_next != NULL) { return FAIL; + } return MAYBE; } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index f24b2aa80a..c88088f25f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5781,6 +5781,7 @@ static char *(highlight_init_both[]) = "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"), CENT("StatusLineNC term=reverse cterm=reverse", "StatusLineNC term=reverse cterm=reverse gui=reverse"), + "default link EndOfBuffer NonText", CENT("VertSplit term=reverse cterm=reverse", "VertSplit term=reverse cterm=reverse gui=reverse"), CENT("DiffText term=reverse cterm=bold ctermbg=Red", @@ -7644,184 +7645,181 @@ char_u *get_highlight_name(expand_T *xp, int idx) return HL_TABLE()[idx].sg_name; } +#define RGB(r, g, b) ((r << 16) | (g << 8) | b) +color_name_table_T color_name_table[] = { + // Color names taken from + // http://www.rapidtables.com/web/color/RGB_Color.htm + {"Maroon", RGB(0x80, 0x00, 0x00)}, + {"DarkRed", RGB(0x8b, 0x00, 0x00)}, + {"Brown", RGB(0xa5, 0x2a, 0x2a)}, + {"Firebrick", RGB(0xb2, 0x22, 0x22)}, + {"Crimson", RGB(0xdc, 0x14, 0x3c)}, + {"Red", RGB(0xff, 0x00, 0x00)}, + {"Tomato", RGB(0xff, 0x63, 0x47)}, + {"Coral", RGB(0xff, 0x7f, 0x50)}, + {"IndianRed", RGB(0xcd, 0x5c, 0x5c)}, + {"LightCoral", RGB(0xf0, 0x80, 0x80)}, + {"DarkSalmon", RGB(0xe9, 0x96, 0x7a)}, + {"Salmon", RGB(0xfa, 0x80, 0x72)}, + {"LightSalmon", RGB(0xff, 0xa0, 0x7a)}, + {"OrangeRed", RGB(0xff, 0x45, 0x00)}, + {"DarkOrange", RGB(0xff, 0x8c, 0x00)}, + {"Orange", RGB(0xff, 0xa5, 0x00)}, + {"Gold", RGB(0xff, 0xd7, 0x00)}, + {"DarkGoldenRod", RGB(0xb8, 0x86, 0x0b)}, + {"GoldenRod", RGB(0xda, 0xa5, 0x20)}, + {"PaleGoldenRod", RGB(0xee, 0xe8, 0xaa)}, + {"DarkKhaki", RGB(0xbd, 0xb7, 0x6b)}, + {"Khaki", RGB(0xf0, 0xe6, 0x8c)}, + {"Olive", RGB(0x80, 0x80, 0x00)}, + {"Yellow", RGB(0xff, 0xff, 0x00)}, + {"YellowGreen", RGB(0x9a, 0xcd, 0x32)}, + {"DarkOliveGreen", RGB(0x55, 0x6b, 0x2f)}, + {"OliveDrab", RGB(0x6b, 0x8e, 0x23)}, + {"LawnGreen", RGB(0x7c, 0xfc, 0x00)}, + {"ChartReuse", RGB(0x7f, 0xff, 0x00)}, + {"GreenYellow", RGB(0xad, 0xff, 0x2f)}, + {"DarkGreen", RGB(0x00, 0x64, 0x00)}, + {"Green", RGB(0x00, 0x80, 0x00)}, + {"ForestGreen", RGB(0x22, 0x8b, 0x22)}, + {"Lime", RGB(0x00, 0xff, 0x00)}, + {"LimeGreen", RGB(0x32, 0xcd, 0x32)}, + {"LightGreen", RGB(0x90, 0xee, 0x90)}, + {"PaleGreen", RGB(0x98, 0xfb, 0x98)}, + {"DarkSeaGreen", RGB(0x8f, 0xbc, 0x8f)}, + {"MediumSpringGreen", RGB(0x00, 0xfa, 0x9a)}, + {"SpringGreen", RGB(0x00, 0xff, 0x7f)}, + {"SeaGreen", RGB(0x2e, 0x8b, 0x57)}, + {"MediumAquamarine", RGB(0x66, 0xcd, 0xaa)}, + {"MediumSeaGreen", RGB(0x3c, 0xb3, 0x71)}, + {"LightSeaGreen", RGB(0x20, 0xb2, 0xaa)}, + {"DarkSlateGray", RGB(0x2f, 0x4f, 0x4f)}, + {"Teal", RGB(0x00, 0x80, 0x80)}, + {"DarkCyan", RGB(0x00, 0x8b, 0x8b)}, + {"Aqua", RGB(0x00, 0xff, 0xff)}, + {"Cyan", RGB(0x00, 0xff, 0xff)}, + {"LightCyan", RGB(0xe0, 0xff, 0xff)}, + {"DarkTurquoise", RGB(0x00, 0xce, 0xd1)}, + {"Turquoise", RGB(0x40, 0xe0, 0xd0)}, + {"MediumTurquoise", RGB(0x48, 0xd1, 0xcc)}, + {"PaleTurquoise", RGB(0xaf, 0xee, 0xee)}, + {"Aquamarine", RGB(0x7f, 0xff, 0xd4)}, + {"PowderBlue", RGB(0xb0, 0xe0, 0xe6)}, + {"CadetBlue", RGB(0x5f, 0x9e, 0xa0)}, + {"SteelBlue", RGB(0x46, 0x82, 0xb4)}, + {"CornFlowerBlue", RGB(0x64, 0x95, 0xed)}, + {"DeepSkyBlue", RGB(0x00, 0xbf, 0xff)}, + {"DodgerBlue", RGB(0x1e, 0x90, 0xff)}, + {"LightBlue", RGB(0xad, 0xd8, 0xe6)}, + {"SkyBlue", RGB(0x87, 0xce, 0xeb)}, + {"LightSkyBlue", RGB(0x87, 0xce, 0xfa)}, + {"MidnightBlue", RGB(0x19, 0x19, 0x70)}, + {"Navy", RGB(0x00, 0x00, 0x80)}, + {"DarkBlue", RGB(0x00, 0x00, 0x8b)}, + {"MediumBlue", RGB(0x00, 0x00, 0xcd)}, + {"Blue", RGB(0x00, 0x00, 0xff)}, + {"RoyalBlue", RGB(0x41, 0x69, 0xe1)}, + {"BlueViolet", RGB(0x8a, 0x2b, 0xe2)}, + {"Indigo", RGB(0x4b, 0x00, 0x82)}, + {"DarkSlateBlue", RGB(0x48, 0x3d, 0x8b)}, + {"SlateBlue", RGB(0x6a, 0x5a, 0xcd)}, + {"MediumSlateBlue", RGB(0x7b, 0x68, 0xee)}, + {"MediumPurple", RGB(0x93, 0x70, 0xdb)}, + {"DarkMagenta", RGB(0x8b, 0x00, 0x8b)}, + {"DarkViolet", RGB(0x94, 0x00, 0xd3)}, + {"DarkOrchid", RGB(0x99, 0x32, 0xcc)}, + {"MediumOrchid", RGB(0xba, 0x55, 0xd3)}, + {"Purple", RGB(0x80, 0x00, 0x80)}, + {"Thistle", RGB(0xd8, 0xbf, 0xd8)}, + {"Plum", RGB(0xdd, 0xa0, 0xdd)}, + {"Violet", RGB(0xee, 0x82, 0xee)}, + {"Magenta", RGB(0xff, 0x00, 0xff)}, + {"Fuchsia", RGB(0xff, 0x00, 0xff)}, + {"Orchid", RGB(0xda, 0x70, 0xd6)}, + {"MediumVioletRed", RGB(0xc7, 0x15, 0x85)}, + {"PaleVioletRed", RGB(0xdb, 0x70, 0x93)}, + {"DeepPink", RGB(0xff, 0x14, 0x93)}, + {"HotPink", RGB(0xff, 0x69, 0xb4)}, + {"LightPink", RGB(0xff, 0xb6, 0xc1)}, + {"Pink", RGB(0xff, 0xc0, 0xcb)}, + {"AntiqueWhite", RGB(0xfa, 0xeb, 0xd7)}, + {"Beige", RGB(0xf5, 0xf5, 0xdc)}, + {"Bisque", RGB(0xff, 0xe4, 0xc4)}, + {"BlanchedAlmond", RGB(0xff, 0xeb, 0xcd)}, + {"Wheat", RGB(0xf5, 0xde, 0xb3)}, + {"Cornsilk", RGB(0xff, 0xf8, 0xdc)}, + {"LemonChiffon", RGB(0xff, 0xfa, 0xcd)}, + {"LightGoldenRodYellow", RGB(0xfa, 0xfa, 0xd2)}, + {"LightYellow", RGB(0xff, 0xff, 0xe0)}, + {"SaddleBrown", RGB(0x8b, 0x45, 0x13)}, + {"Sienna", RGB(0xa0, 0x52, 0x2d)}, + {"Chocolate", RGB(0xd2, 0x69, 0x1e)}, + {"Peru", RGB(0xcd, 0x85, 0x3f)}, + {"SandyBrown", RGB(0xf4, 0xa4, 0x60)}, + {"BurlyWood", RGB(0xde, 0xb8, 0x87)}, + {"Tan", RGB(0xd2, 0xb4, 0x8c)}, + {"RosyBrown", RGB(0xbc, 0x8f, 0x8f)}, + {"Moccasin", RGB(0xff, 0xe4, 0xb5)}, + {"NavajoWhite", RGB(0xff, 0xde, 0xad)}, + {"PeachPuff", RGB(0xff, 0xda, 0xb9)}, + {"MistyRose", RGB(0xff, 0xe4, 0xe1)}, + {"LavenderBlush", RGB(0xff, 0xf0, 0xf5)}, + {"Linen", RGB(0xfa, 0xf0, 0xe6)}, + {"Oldlace", RGB(0xfd, 0xf5, 0xe6)}, + {"PapayaWhip", RGB(0xff, 0xef, 0xd5)}, + {"SeaShell", RGB(0xff, 0xf5, 0xee)}, + {"MintCream", RGB(0xf5, 0xff, 0xfa)}, + {"SlateGray", RGB(0x70, 0x80, 0x90)}, + {"LightSlateGray", RGB(0x77, 0x88, 0x99)}, + {"LightSteelBlue", RGB(0xb0, 0xc4, 0xde)}, + {"Lavender", RGB(0xe6, 0xe6, 0xfa)}, + {"FloralWhite", RGB(0xff, 0xfa, 0xf0)}, + {"AliceBlue", RGB(0xf0, 0xf8, 0xff)}, + {"GhostWhite", RGB(0xf8, 0xf8, 0xff)}, + {"Honeydew", RGB(0xf0, 0xff, 0xf0)}, + {"Ivory", RGB(0xff, 0xff, 0xf0)}, + {"Azure", RGB(0xf0, 0xff, 0xff)}, + {"Snow", RGB(0xff, 0xfa, 0xfa)}, + {"Black", RGB(0x00, 0x00, 0x00)}, + {"DimGray", RGB(0x69, 0x69, 0x69)}, + {"DimGrey", RGB(0x69, 0x69, 0x69)}, + {"Gray", RGB(0x80, 0x80, 0x80)}, + {"Grey", RGB(0x80, 0x80, 0x80)}, + {"DarkGray", RGB(0xa9, 0xa9, 0xa9)}, + {"DarkGrey", RGB(0xa9, 0xa9, 0xa9)}, + {"Silver", RGB(0xc0, 0xc0, 0xc0)}, + {"LightGray", RGB(0xd3, 0xd3, 0xd3)}, + {"LightGrey", RGB(0xd3, 0xd3, 0xd3)}, + {"Gainsboro", RGB(0xdc, 0xdc, 0xdc)}, + {"WhiteSmoke", RGB(0xf5, 0xf5, 0xf5)}, + {"White", RGB(0xff, 0xff, 0xff)}, + // The color names below were taken from gui_x11.c in vim source + {"LightRed", RGB(0xff, 0xbb, 0xbb)}, + {"LightMagenta",RGB(0xff, 0xbb, 0xff)}, + {"DarkYellow", RGB(0xbb, 0xbb, 0x00)}, + {"Gray10", RGB(0x1a, 0x1a, 0x1a)}, + {"Grey10", RGB(0x1a, 0x1a, 0x1a)}, + {"Gray20", RGB(0x33, 0x33, 0x33)}, + {"Grey20", RGB(0x33, 0x33, 0x33)}, + {"Gray30", RGB(0x4d, 0x4d, 0x4d)}, + {"Grey30", RGB(0x4d, 0x4d, 0x4d)}, + {"Gray40", RGB(0x66, 0x66, 0x66)}, + {"Grey40", RGB(0x66, 0x66, 0x66)}, + {"Gray50", RGB(0x7f, 0x7f, 0x7f)}, + {"Grey50", RGB(0x7f, 0x7f, 0x7f)}, + {"Gray60", RGB(0x99, 0x99, 0x99)}, + {"Grey60", RGB(0x99, 0x99, 0x99)}, + {"Gray70", RGB(0xb3, 0xb3, 0xb3)}, + {"Grey70", RGB(0xb3, 0xb3, 0xb3)}, + {"Gray80", RGB(0xcc, 0xcc, 0xcc)}, + {"Grey80", RGB(0xcc, 0xcc, 0xcc)}, + {"Gray90", RGB(0xe5, 0xe5, 0xe5)}, + {"Grey90", RGB(0xe5, 0xe5, 0xe5)}, + {NULL, 0}, +}; RgbValue name_to_color(uint8_t *name) { -#define RGB(r, g, b) ((r << 16) | (g << 8) | b) - static struct { - char *name; - RgbValue color; - } color_name_table[] = { - // Color names taken from - // http://www.rapidtables.com/web/color/RGB_Color.htm - {"Maroon", RGB(0x80, 0x00, 0x00)}, - {"DarkRed", RGB(0x8b, 0x00, 0x00)}, - {"Brown", RGB(0xa5, 0x2a, 0x2a)}, - {"Firebrick", RGB(0xb2, 0x22, 0x22)}, - {"Crimson", RGB(0xdc, 0x14, 0x3c)}, - {"Red", RGB(0xff, 0x00, 0x00)}, - {"Tomato", RGB(0xff, 0x63, 0x47)}, - {"Coral", RGB(0xff, 0x7f, 0x50)}, - {"IndianRed", RGB(0xcd, 0x5c, 0x5c)}, - {"LightCoral", RGB(0xf0, 0x80, 0x80)}, - {"DarkSalmon", RGB(0xe9, 0x96, 0x7a)}, - {"Salmon", RGB(0xfa, 0x80, 0x72)}, - {"LightSalmon", RGB(0xff, 0xa0, 0x7a)}, - {"OrangeRed", RGB(0xff, 0x45, 0x00)}, - {"DarkOrange", RGB(0xff, 0x8c, 0x00)}, - {"Orange", RGB(0xff, 0xa5, 0x00)}, - {"Gold", RGB(0xff, 0xd7, 0x00)}, - {"DarkGoldenRod", RGB(0xb8, 0x86, 0x0b)}, - {"GoldenRod", RGB(0xda, 0xa5, 0x20)}, - {"PaleGoldenRod", RGB(0xee, 0xe8, 0xaa)}, - {"DarkKhaki", RGB(0xbd, 0xb7, 0x6b)}, - {"Khaki", RGB(0xf0, 0xe6, 0x8c)}, - {"Olive", RGB(0x80, 0x80, 0x00)}, - {"Yellow", RGB(0xff, 0xff, 0x00)}, - {"YellowGreen", RGB(0x9a, 0xcd, 0x32)}, - {"DarkOliveGreen", RGB(0x55, 0x6b, 0x2f)}, - {"OliveDrab", RGB(0x6b, 0x8e, 0x23)}, - {"LawnGreen", RGB(0x7c, 0xfc, 0x00)}, - {"ChartReuse", RGB(0x7f, 0xff, 0x00)}, - {"GreenYellow", RGB(0xad, 0xff, 0x2f)}, - {"DarkGreen", RGB(0x00, 0x64, 0x00)}, - {"Green", RGB(0x00, 0x80, 0x00)}, - {"ForestGreen", RGB(0x22, 0x8b, 0x22)}, - {"Lime", RGB(0x00, 0xff, 0x00)}, - {"LimeGreen", RGB(0x32, 0xcd, 0x32)}, - {"LightGreen", RGB(0x90, 0xee, 0x90)}, - {"PaleGreen", RGB(0x98, 0xfb, 0x98)}, - {"DarkSeaGreen", RGB(0x8f, 0xbc, 0x8f)}, - {"MediumSpringGreen", RGB(0x00, 0xfa, 0x9a)}, - {"SpringGreen", RGB(0x00, 0xff, 0x7f)}, - {"SeaGreen", RGB(0x2e, 0x8b, 0x57)}, - {"MediumAquamarine", RGB(0x66, 0xcd, 0xaa)}, - {"MediumSeaGreen", RGB(0x3c, 0xb3, 0x71)}, - {"LightSeaGreen", RGB(0x20, 0xb2, 0xaa)}, - {"DarkSlateGray", RGB(0x2f, 0x4f, 0x4f)}, - {"Teal", RGB(0x00, 0x80, 0x80)}, - {"DarkCyan", RGB(0x00, 0x8b, 0x8b)}, - {"Aqua", RGB(0x00, 0xff, 0xff)}, - {"Cyan", RGB(0x00, 0xff, 0xff)}, - {"LightCyan", RGB(0xe0, 0xff, 0xff)}, - {"DarkTurquoise", RGB(0x00, 0xce, 0xd1)}, - {"Turquoise", RGB(0x40, 0xe0, 0xd0)}, - {"MediumTurquoise", RGB(0x48, 0xd1, 0xcc)}, - {"PaleTurquoise", RGB(0xaf, 0xee, 0xee)}, - {"Aquamarine", RGB(0x7f, 0xff, 0xd4)}, - {"PowderBlue", RGB(0xb0, 0xe0, 0xe6)}, - {"CadetBlue", RGB(0x5f, 0x9e, 0xa0)}, - {"SteelBlue", RGB(0x46, 0x82, 0xb4)}, - {"CornFlowerBlue", RGB(0x64, 0x95, 0xed)}, - {"DeepSkyBlue", RGB(0x00, 0xbf, 0xff)}, - {"DodgerBlue", RGB(0x1e, 0x90, 0xff)}, - {"LightBlue", RGB(0xad, 0xd8, 0xe6)}, - {"SkyBlue", RGB(0x87, 0xce, 0xeb)}, - {"LightSkyBlue", RGB(0x87, 0xce, 0xfa)}, - {"MidnightBlue", RGB(0x19, 0x19, 0x70)}, - {"Navy", RGB(0x00, 0x00, 0x80)}, - {"DarkBlue", RGB(0x00, 0x00, 0x8b)}, - {"MediumBlue", RGB(0x00, 0x00, 0xcd)}, - {"Blue", RGB(0x00, 0x00, 0xff)}, - {"RoyalBlue", RGB(0x41, 0x69, 0xe1)}, - {"BlueViolet", RGB(0x8a, 0x2b, 0xe2)}, - {"Indigo", RGB(0x4b, 0x00, 0x82)}, - {"DarkSlateBlue", RGB(0x48, 0x3d, 0x8b)}, - {"SlateBlue", RGB(0x6a, 0x5a, 0xcd)}, - {"MediumSlateBlue", RGB(0x7b, 0x68, 0xee)}, - {"MediumPurple", RGB(0x93, 0x70, 0xdb)}, - {"DarkMagenta", RGB(0x8b, 0x00, 0x8b)}, - {"DarkViolet", RGB(0x94, 0x00, 0xd3)}, - {"DarkOrchid", RGB(0x99, 0x32, 0xcc)}, - {"MediumOrchid", RGB(0xba, 0x55, 0xd3)}, - {"Purple", RGB(0x80, 0x00, 0x80)}, - {"Thistle", RGB(0xd8, 0xbf, 0xd8)}, - {"Plum", RGB(0xdd, 0xa0, 0xdd)}, - {"Violet", RGB(0xee, 0x82, 0xee)}, - {"Magenta", RGB(0xff, 0x00, 0xff)}, - {"Fuchsia", RGB(0xff, 0x00, 0xff)}, - {"Orchid", RGB(0xda, 0x70, 0xd6)}, - {"MediumVioletRed", RGB(0xc7, 0x15, 0x85)}, - {"PaleVioletRed", RGB(0xdb, 0x70, 0x93)}, - {"DeepPink", RGB(0xff, 0x14, 0x93)}, - {"HotPink", RGB(0xff, 0x69, 0xb4)}, - {"LightPink", RGB(0xff, 0xb6, 0xc1)}, - {"Pink", RGB(0xff, 0xc0, 0xcb)}, - {"AntiqueWhite", RGB(0xfa, 0xeb, 0xd7)}, - {"Beige", RGB(0xf5, 0xf5, 0xdc)}, - {"Bisque", RGB(0xff, 0xe4, 0xc4)}, - {"BlanchedAlmond", RGB(0xff, 0xeb, 0xcd)}, - {"Wheat", RGB(0xf5, 0xde, 0xb3)}, - {"Cornsilk", RGB(0xff, 0xf8, 0xdc)}, - {"LemonChiffon", RGB(0xff, 0xfa, 0xcd)}, - {"LightGoldenRodYellow", RGB(0xfa, 0xfa, 0xd2)}, - {"LightYellow", RGB(0xff, 0xff, 0xe0)}, - {"SaddleBrown", RGB(0x8b, 0x45, 0x13)}, - {"Sienna", RGB(0xa0, 0x52, 0x2d)}, - {"Chocolate", RGB(0xd2, 0x69, 0x1e)}, - {"Peru", RGB(0xcd, 0x85, 0x3f)}, - {"SandyBrown", RGB(0xf4, 0xa4, 0x60)}, - {"BurlyWood", RGB(0xde, 0xb8, 0x87)}, - {"Tan", RGB(0xd2, 0xb4, 0x8c)}, - {"RosyBrown", RGB(0xbc, 0x8f, 0x8f)}, - {"Moccasin", RGB(0xff, 0xe4, 0xb5)}, - {"NavajoWhite", RGB(0xff, 0xde, 0xad)}, - {"PeachPuff", RGB(0xff, 0xda, 0xb9)}, - {"MistyRose", RGB(0xff, 0xe4, 0xe1)}, - {"LavenderBlush", RGB(0xff, 0xf0, 0xf5)}, - {"Linen", RGB(0xfa, 0xf0, 0xe6)}, - {"Oldlace", RGB(0xfd, 0xf5, 0xe6)}, - {"PapayaWhip", RGB(0xff, 0xef, 0xd5)}, - {"SeaShell", RGB(0xff, 0xf5, 0xee)}, - {"MintCream", RGB(0xf5, 0xff, 0xfa)}, - {"SlateGray", RGB(0x70, 0x80, 0x90)}, - {"LightSlateGray", RGB(0x77, 0x88, 0x99)}, - {"LightSteelBlue", RGB(0xb0, 0xc4, 0xde)}, - {"Lavender", RGB(0xe6, 0xe6, 0xfa)}, - {"FloralWhite", RGB(0xff, 0xfa, 0xf0)}, - {"AliceBlue", RGB(0xf0, 0xf8, 0xff)}, - {"GhostWhite", RGB(0xf8, 0xf8, 0xff)}, - {"Honeydew", RGB(0xf0, 0xff, 0xf0)}, - {"Ivory", RGB(0xff, 0xff, 0xf0)}, - {"Azure", RGB(0xf0, 0xff, 0xff)}, - {"Snow", RGB(0xff, 0xfa, 0xfa)}, - {"Black", RGB(0x00, 0x00, 0x00)}, - {"DimGray", RGB(0x69, 0x69, 0x69)}, - {"DimGrey", RGB(0x69, 0x69, 0x69)}, - {"Gray", RGB(0x80, 0x80, 0x80)}, - {"Grey", RGB(0x80, 0x80, 0x80)}, - {"DarkGray", RGB(0xa9, 0xa9, 0xa9)}, - {"DarkGrey", RGB(0xa9, 0xa9, 0xa9)}, - {"Silver", RGB(0xc0, 0xc0, 0xc0)}, - {"LightGray", RGB(0xd3, 0xd3, 0xd3)}, - {"LightGrey", RGB(0xd3, 0xd3, 0xd3)}, - {"Gainsboro", RGB(0xdc, 0xdc, 0xdc)}, - {"WhiteSmoke", RGB(0xf5, 0xf5, 0xf5)}, - {"White", RGB(0xff, 0xff, 0xff)}, - // The color names below were taken from gui_x11.c in vim source - {"LightRed", RGB(0xff, 0xbb, 0xbb)}, - {"LightMagenta",RGB(0xff, 0xbb, 0xff)}, - {"DarkYellow", RGB(0xbb, 0xbb, 0x00)}, - {"Gray10", RGB(0x1a, 0x1a, 0x1a)}, - {"Grey10", RGB(0x1a, 0x1a, 0x1a)}, - {"Gray20", RGB(0x33, 0x33, 0x33)}, - {"Grey20", RGB(0x33, 0x33, 0x33)}, - {"Gray30", RGB(0x4d, 0x4d, 0x4d)}, - {"Grey30", RGB(0x4d, 0x4d, 0x4d)}, - {"Gray40", RGB(0x66, 0x66, 0x66)}, - {"Grey40", RGB(0x66, 0x66, 0x66)}, - {"Gray50", RGB(0x7f, 0x7f, 0x7f)}, - {"Grey50", RGB(0x7f, 0x7f, 0x7f)}, - {"Gray60", RGB(0x99, 0x99, 0x99)}, - {"Grey60", RGB(0x99, 0x99, 0x99)}, - {"Gray70", RGB(0xb3, 0xb3, 0xb3)}, - {"Grey70", RGB(0xb3, 0xb3, 0xb3)}, - {"Gray80", RGB(0xcc, 0xcc, 0xcc)}, - {"Grey80", RGB(0xcc, 0xcc, 0xcc)}, - {"Gray90", RGB(0xe5, 0xe5, 0xe5)}, - {"Grey90", RGB(0xe5, 0xe5, 0xe5)}, - {NULL, 0}, - }; if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index 9a284c8a8d..93088180b9 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -38,6 +38,12 @@ #define HL_CONCEAL 0x20000 /* can be concealed */ #define HL_CONCEALENDS 0x40000 /* can be concealed */ +typedef struct { + char *name; + RgbValue color; +} color_name_table_T; +extern color_name_table_T color_name_table[]; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "syntax.h.generated.h" #endif diff --git a/src/nvim/version.c b/src/nvim/version.c index 6056a9d2a7..8a5a3dc07d 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -4,6 +4,8 @@ /// Vim originated from Stevie version 3.6 (Fish disk 217) by GRWalter (Fred). #include <inttypes.h> +#include <assert.h> +#include <limits.h> #include "nvim/vim.h" #include "nvim/ascii.h" @@ -57,7 +59,6 @@ static char *(features[]) = { "+cursorbind", "+cursorshape", "+dialog_con", - "+diff", "+digraphs", "-dnd", "-ebcdic", @@ -1008,8 +1009,8 @@ void maybe_intro_message(void) void intro_message(int colon) { int i; - int row; - int blanklines; + long row; + long blanklines; int sponsor; char *p; static char *(lines[]) = { @@ -1027,7 +1028,10 @@ void intro_message(int colon) }; // blanklines = screen height - # message lines - blanklines = (int)Rows - (ARRAY_SIZE(lines) - 1); + size_t lines_size = ARRAY_SIZE(lines); + assert(lines_size <= LONG_MAX); + + blanklines = Rows - ((long)lines_size - 1l); // Don't overwrite a statusline. Depends on 'cmdheight'. if (p_ls > 1) { @@ -1073,13 +1077,14 @@ void intro_message(int colon) // Make the wait-return message appear just below the text. if (colon) { - msg_row = row; + assert(row <= INT_MAX); + msg_row = (int)row; } } -static void do_intro_line(int row, char_u *mesg, int attr) +static void do_intro_line(long row, char_u *mesg, int attr) { - int col; + long col; char_u *p; int l; int clen; @@ -1106,7 +1111,8 @@ static void do_intro_line(int row, char_u *mesg, int attr) clen += byte2cells(p[l]); } } - screen_puts_len(p, l, row, col, *p == '<' ? hl_attr(HLF_8) : attr); + assert(row <= INT_MAX && col <= INT_MAX); + screen_puts_len(p, l, (int)row, (int)col, *p == '<' ? hl_attr(HLF_8) : attr); col += clen; } } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index ea98ff4ce3..e63c79ec80 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,3 +1,8 @@ +if jit then + -- Disable JIT because of random errors on Travis with OS X. + jit.off(true, true) +end + require('coxpcall') local Loop = require('nvim.loop') local MsgpackStream = require('nvim.msgpack_stream') diff --git a/test/functional/shell/viml_system_spec.lua b/test/functional/shell/viml_system_spec.lua index 25d2b5bc2c..d24646e712 100644 --- a/test/functional/shell/viml_system_spec.lua +++ b/test/functional/shell/viml_system_spec.lua @@ -248,7 +248,7 @@ describe('systemlist()', function() end) it('`yes` and is directly interrupted with CTRL-C', function() - feed(':call systemlist("echo")<cr><c-c>') + feed(':call systemlist("yes | xargs")<cr><c-c>') screen:expect([[ ^ | ~ | @@ -268,7 +268,7 @@ describe('systemlist()', function() end) it('`yes` and is a little bit later interrupted with CTRL-C', function() - feed(':call systemlist("echo")<cr>') + feed(':call systemlist("yes | xargs")<cr>') feed('<c-c>') screen:expect([[ ^ | diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 15d5839ca5..52ab3cb5bf 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -29,19 +29,19 @@ end) describe('Default highlight groups', function() -- Test the default attributes for highlight groups shown by the :highlight -- command - local screen, hlgroup_colors + local screen - setup(function() - hlgroup_colors = { - NonText = nvim('name_to_color', 'Blue'), - Question = nvim('name_to_color', 'SeaGreen') - } - end) + local hlgroup_colors = { + NonText = Screen.colors.Blue, + Question = Screen.colors.SeaGreen + } before_each(function() clear() screen = Screen.new() screen:attach() + --ignore highligting of ~-lines + screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}} ) end) after_each(function() @@ -52,8 +52,6 @@ describe('Default highlight groups', function() [1] = {reverse = true, bold = true}, -- StatusLine [2] = {reverse = true} -- StatusLineNC }) - --ignore highligting of ~-lines - screen:set_default_attr_ignore( {{}, {bold=true, foreground=hlgroup_colors.NonText}} ) execute('sp', 'vsp', 'vsp') screen:expect([[ ^ {2:|} {2:|} | diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 9530e13453..296487fc9c 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -4,13 +4,12 @@ local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim local insert, execute = helpers.insert, helpers.execute describe('Mouse input', function() - local screen, hlgroup_colors + local screen - setup(function() - hlgroup_colors = { - Visual = nvim('name_to_color', 'LightGrey'), - } - end) + local hlgroup_colors = { + NonText = Screen.colors.Blue, + Visual = Screen.colors.LightGrey + } before_each(function() clear() @@ -21,8 +20,10 @@ describe('Mouse input', function() screen = Screen.new(25, 5) screen:attach() screen:set_default_attr_ids({ - [1] = {background = hlgroup_colors.Visual} + [1] = {background = hlgroup_colors.Visual}, + [2] = {bold = true} }) + screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}} ) feed('itesting<cr>mouse<cr>support and selection<esc>') screen:expect([[ testing | @@ -72,7 +73,7 @@ describe('Mouse input', function() mo{1:us}^ | support and selection | ~ | - -- VISUAL -- | + {2:-- VISUAL --} | ]]) feed('<LeftDrag><2,2>') screen:expect([[ @@ -80,7 +81,7 @@ describe('Mouse input', function() mo{1:use } | {1:su}^port and selection | ~ | - -- VISUAL -- | + {2:-- VISUAL --} | ]]) feed('<LeftDrag><0,0>') screen:expect([[ @@ -88,7 +89,7 @@ describe('Mouse input', function() {1:mou}se | support and selection | ~ | - -- VISUAL -- | + {2:-- VISUAL --} | ]]) end) @@ -99,7 +100,7 @@ describe('Mouse input', function() mouse | {1:suppor}^ and selection | ~ | - -- VISUAL -- | + {2:-- VISUAL --} | ]]) end) @@ -110,7 +111,7 @@ describe('Mouse input', function() mouse | {1:su}^{1:port and selection } | ~ | - -- VISUAL LINE -- | + {2:-- VISUAL LINE --} | ]]) end) @@ -121,7 +122,7 @@ describe('Mouse input', function() mouse | su^port and selection | ~ | - -- VISUAL BLOCK -- | + {2:-- VISUAL BLOCK --} | ]]) end) @@ -140,7 +141,7 @@ describe('Mouse input', function() {1:mouse } | {1:su}^port and selection | ~ | - -- VISUAL -- | + {2:-- VISUAL --} | ]]) end) @@ -153,7 +154,7 @@ describe('Mouse input', function() ing | Press ENTER or type comma| nd to continue^ | - ]]) + ]],nil,true) feed('<cr>') end) @@ -171,6 +172,8 @@ describe('Mouse input', function() ]]) screen:try_resize(53, 14) execute('sp', 'vsp') + screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}, + {reverse=true}, {bold=true, reverse=true}} ) screen:expect([[ lines |lines | to |to | diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index ef99c2a536..cd8c2bc399 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -54,12 +54,18 @@ -- having the exact same set of attributes will be substituted by "{K:S}", -- where K is a key associated the attribute set via the second argument of -- "expect". +-- If a transformation table is present, unexpected attribute sets in the final +-- state is considered an error. To make testing simpler, a list of attribute +-- sets that should be ignored can be passed as a third argument. Alternatively, +-- this third argument can be "true" to indicate that all unexpected attribute +-- sets should be ignored. -- --- Too illustrate how this works, let's say that in the above example we wanted +-- To illustrate how this works, let's say that in the above example we wanted -- to assert that the "-- INSERT --" string is highlighted with the bold -- attribute(which normally is), here's how the call to "expect" should look -- like: -- +-- NonText = Screen.colors.Blue -- screen:expect([[ -- hello screen \ -- ~ \ @@ -71,11 +77,34 @@ -- ~ \ -- ~ \ -- {b:-- INSERT --} \ --- ]], {b = {bold = true}}) +-- ]], {b = {bold = true}}, {{bold = true, foreground = NonText}}) -- -- In this case "b" is a string associated with the set composed of one -- attribute: bold. Note that since the {b:} markup is not a real part of the --- screen, the delimiter(|) had to be moved right +-- screen, the delimiter(|) had to be moved right. Also, the highlighting of the +-- NonText markers (~) is ignored in this test. +-- +-- Multiple expect:s will likely share a group of attribute sets to test. +-- Therefore these could be specified at the beginning of a test like this: +-- NonText = Screen.colors.Blue +-- screen:set_default_attr_ids( { +-- [1] = {reverse = true, bold = true}, +-- [2] = {reverse = true} +-- }) +-- screen:set_default_attr_ignore( {{}, {bold=true, foreground=NonText}} ) +-- These can be overridden for a specific expect expression, by passing +-- different sets as parameters. +-- +-- To help writing screen tests, there is a utility function +-- "screen:snapshot_util()", that can be placed in a test file at any point an +-- "expect(...)" should be. It will wait a short amount of time and then dump +-- the current state of the screen, in the form of an "expect(..)" expression +-- that would match it exactly. "snapshot_util" optionally also take the +-- transformation and ignore set as parameters, like expect, or uses the default +-- set. It will generate a larger attribute transformation set, if needed. +-- To generate a text-only test without highlight checks, +-- use `screen:snapshot_util({},true)` + local helpers = require('test.functional.helpers') local request, run, stop = helpers.request, helpers.run, helpers.stop local eq, dedent = helpers.eq, helpers.dedent @@ -90,6 +119,16 @@ if os.getenv('VALGRIND') then default_screen_timeout = 7500 end +local colors = request('vim_get_color_map') +local colornames = {} +for name, rgb in pairs(colors) do + -- we disregard the case that colornames might not be unique, as + -- this is just a helper to get any canonical name of a color + colornames[rgb] = name +end + +Screen.colors = colors + function Screen.debug(command) if not command then command = 'pynvim -n -g -c ' @@ -158,19 +197,11 @@ function Screen:expect(expected, attr_ids, attr_ignore) table.insert(expected_rows, row) end local ids = attr_ids or self._default_attr_ids - if attr_ignore == nil and self._default_attr_ignore ~= nil then - attr_ignore = {} - -- don't ignore something we specified in attr_ids - for i,a in pairs(self._default_attr_ignore) do - if attr_index(ids, a) == nil then - table.insert(attr_ignore, a) - end - end - end + local ignore = attr_ignore or self._default_attr_ignore self:wait(function() for i = 1, self._height do local expected_row = expected_rows[i] - local actual_row = self:_row_repr(self._rows[i], ids, attr_ignore) + local actual_row = self:_row_repr(self._rows[i], ids, ignore) if expected_row ~= actual_row then return 'Row '..tostring(i)..' didnt match.\nExpected: "'.. expected_row..'"\nActual: "'..actual_row..'"' @@ -417,12 +448,16 @@ function Screen:snapshot_util(attrs, ignore) end end - for i = 1, self._height do - local row = self._rows[i] - for j = 1, self._width do - local attr = row[j].attrs - if attr_index(attrs, attr) == nil and attr_index(ignore, attr) == nil then - table.insert(attrs, attr) + if ignore ~= true then + for i = 1, self._height do + local row = self._rows[i] + for j = 1, self._width do + local attr = row[j].attrs + if attr_index(attrs, attr) == nil and attr_index(ignore, attr) == nil then + if not equal_attrs(attr, {}) then + table.insert(attrs, attr) + end + end end end end @@ -438,11 +473,7 @@ function Screen:snapshot_util(attrs, ignore) if self._default_attr_ids == nil or self._default_attr_ids[i] ~= a then alldefault = false end - local items = {} - for f, v in pairs(a) do - table.insert(items, f.." = "..tostring(v)) - end - local dict = "{"..table.concat(items, ", ").."}" + local dict = "{"..pprint_attrs(a).."}" table.insert(attrstrs, "["..tostring(i).."] = "..dict) end local attrstr = "{"..table.concat(attrstrs, ", ").."}" @@ -455,6 +486,19 @@ function Screen:snapshot_util(attrs, ignore) end end +function pprint_attrs(attrs) + local items = {} + for f, v in pairs(attrs) do + local desc = tostring(v) + if f == "foreground" or f == "background" then + if colornames[v] ~= nil then + desc = "Screen.colors."..colornames[v] + end + end + table.insert(items, f.." = "..desc) + end + return table.concat(items, ", ") +end function backward_find_meaningful(tbl, from) for i = from or #tbl, 1, -1 do @@ -465,7 +509,7 @@ function backward_find_meaningful(tbl, from) return from end -function get_attr_id(attr_ids, attr_ignore, attrs) +function get_attr_id(attr_ids, ignore, attrs) if not attr_ids then return end @@ -474,11 +518,12 @@ function get_attr_id(attr_ids, attr_ignore, attrs) return id end end - if attr_ignore == nil or attr_index(attr_ignore, attrs) ~= nil then + if equal_attrs(attrs, {}) or + ignore == true or attr_index(ignore, attrs) ~= nil then -- ignore this attrs return nil end - return "UNEXPECTED" + return "UNEXPECTED "..pprint_attrs(attrs) end function equal_attrs(a, b) diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index 90f5a0b7de..7051ed3797 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -262,7 +262,7 @@ describe('fs function', function() it('owner of a file may change the group of the file to any group of which that owner is a member', function() -- Some systems may not have `id` utility. - if (os.execute('id -G &> /dev/null') == 0) then + if (os.execute('id -G > /dev/null 2>&1') == 0) then local file_gid = lfs.attributes(filename, 'gid') -- Gets ID of any group of which current user is a member except the diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 8c7eef49d6..19fc348c04 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -51,31 +51,24 @@ include(ExternalProject) set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.2.0.tar.gz) set(LIBUV_SHA1 38d1ba349fcfc1b221140523ba3d7cf3ea38c20b) -set(LIBUV_MD5 d41d8cd98f00b204e9800998ecf8427e) -set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/ecf4b09acd29746829b6a02939db91dfdec635b4.tar.gz) -set(MSGPACK_SHA1 c160ff99f20d9d0a25bea0a57f4452f1c9bde370) -set(MSGPACK_MD5 3599eaf904b8ba0c36cea7ed80973364) +set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/b4eba4ba090e8cd6d8332410e252296a62b7e56d.tar.gz) +set(MSGPACK_SHA1 768e60e3936cd0d0e4f1d58ca86a78eeb895931b) set(LUAJIT_URL http://luajit.org/download/LuaJIT-2.0.3.tar.gz) set(LUAJIT_SHA1 2db39e7d1264918c2266b0436c313fbd12da4ceb) -set(LUAJIT_MD5 f14e9104be513913810cd59c8c658dc0) set(LUAROCKS_URL https://github.com/keplerproject/luarocks/archive/0587afbb5fe8ceb2f2eea16f486bd6183bf02f29.tar.gz) set(LUAROCKS_SHA1 61a894fd5d61987bf7e7f9c3e0c5de16ba4b68c4) -set(LUAROCKS_MD5 0f53f42909fbcd2c88be303e8f970516) set(LIBUNIBILIUM_URL https://github.com/mauke/unibilium/archive/520abbc8b26910e2580619f669b5cc2c4ef7f864.tar.gz) set(LIBUNIBILIUM_SHA1 c546e5e8861380f5c109a256f25c93419e4076bf) -set(LIBUNIBILIUM_MD5 d80d1fc45b22b1e92bebd5bf76e8a98b) set(LIBTERMKEY_URL https://github.com/neovim/libtermkey/archive/7b3bdafdf589d08478f2493273d4d75636ecc183.tar.gz) set(LIBTERMKEY_SHA1 28bfe54dfd9269910a132b51dee7725a2121578d) -set(LIBTERMKEY_MD5 f0bac9c2467cc80c821be937ea5c13bc) set(LIBTICKIT_URL https://github.com/neovim/libtickit/archive/33f4afb3891df05955429acbf5b406dfe87ec22b.tar.gz) set(LIBTICKIT_SHA1 3aab459b9fb3cd83e85ac2e08f05e5f162c8c9d2) -set(LIBTICKIT_MD5 19ee9271c16716620d0906db74158ec6) if(USE_BUNDLED_LIBUNIBILIUM) ExternalProject_Add(libunibilium @@ -87,7 +80,6 @@ if(USE_BUNDLED_LIBUNIBILIUM) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libunibilium -DURL=${LIBUNIBILIUM_URL} -DEXPECTED_SHA1=${LIBUNIBILIUM_SHA1} - -DEXPECTED_MD5=${LIBUNIBILIUM_MD5} -DTARGET=libunibilium -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -109,7 +101,6 @@ if(USE_BUNDLED_LIBTERMKEY) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libtermkey -DURL=${LIBTERMKEY_URL} -DEXPECTED_SHA1=${LIBTERMKEY_SHA1} - -DEXPECTED_MD5=${LIBTERMKEY_MD5} -DTARGET=libtermkey -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -134,7 +125,6 @@ if(USE_BUNDLED_LIBTICKIT) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libtickit -DURL=${LIBTICKIT_URL} -DEXPECTED_SHA1=${LIBTICKIT_SHA1} - -DEXPECTED_MD5=${LIBTICKIT_MD5} -DTARGET=libtickit -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -159,7 +149,6 @@ if(USE_BUNDLED_LIBUV) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libuv -DURL=${LIBUV_URL} -DEXPECTED_SHA1=${LIBUV_SHA1} - -DEXPECTED_MD5=${LIBUV_MD5} -DTARGET=libuv -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND sh ${DEPS_BUILD_DIR}/src/libuv/autogen.sh && @@ -180,7 +169,6 @@ if(USE_BUNDLED_MSGPACK) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/msgpack -DURL=${MSGPACK_URL} -DEXPECTED_SHA1=${MSGPACK_SHA1} - -DEXPECTED_MD5=${MSGPACK_MD5} -DTARGET=msgpack -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND cmake ${DEPS_BUILD_DIR}/src/msgpack @@ -206,7 +194,6 @@ if(USE_BUNDLED_LUAJIT) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luajit -DURL=${LUAJIT_URL} -DEXPECTED_SHA1=${LUAJIT_SHA1} - -DEXPECTED_MD5=${LUAJIT_MD5} -DTARGET=luajit -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake CONFIGURE_COMMAND "" @@ -235,7 +222,6 @@ if(USE_BUNDLED_LUAROCKS) -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/luarocks -DURL=${LUAROCKS_URL} -DEXPECTED_SHA1=${LUAROCKS_SHA1} - -DEXPECTED_MD5=${LUAROCKS_MD5} -DTARGET=luarocks -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake BUILD_IN_SOURCE 1 diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/third-party/cmake/DownloadAndExtractFile.cmake index b0d28355cb..875d45795d 100644 --- a/third-party/cmake/DownloadAndExtractFile.cmake +++ b/third-party/cmake/DownloadAndExtractFile.cmake @@ -10,8 +10,8 @@ if(NOT DEFINED DOWNLOAD_DIR) message(FATAL_ERROR "DOWNLOAD_DIR must be defined.") endif() -if((NOT DEFINED EXPECTED_SHA1) OR (NOT DEFINED EXPECTED_MD5)) - message(FATAL_ERROR "EXPECTED_SHA1 or EXPECTED_MD5 must be defined.") +if(NOT DEFINED EXPECTED_SHA1) + message(FATAL_ERROR "EXPECTED_SHA1 must be defined.") endif() if(NOT DEFINED TARGET) @@ -58,16 +58,6 @@ message(STATUS "downloading... dst='${file}' timeout='${timeout_msg}'") -if((DEFINED EXPECTED_SHA1) AND (${CMAKE_VERSION} VERSION_GREATER 2.8.10)) - if(NOT (EXPECTED_SHA1 STREQUAL "0000000000000000000000000000000000000000")) - set(hash_args EXPECTED_HASH SHA1=${EXPECTED_SHA1}) - endif() -else() - if(NOT (EXPECTED_MD5 STREQUAL "00000000000000000000000000000000")) - set(hash_args EXPECTED_MD5 ${EXPECTED_MD5}) - endif() -endif() - file(DOWNLOAD ${URL} ${file} ${timeout_args} ${hash_args} @@ -85,6 +75,33 @@ if(NOT status_code EQUAL 0) ") endif() +set(NULL_SHA1 "0000000000000000000000000000000000000000") + +# Allow users to use "SKIP" or "skip" as the sha1 to skip checking the hash. +# You can still use the all zeros hash too. +if((EXPECTED_SHA1 STREQUAL "SKIP") OR (EXPECTED_SHA1 STREQUAL "skip")) + set(EXPECTED_SHA1 ${NULL_SHA1}) +endif() + +# We could avoid computing the SHA1 entirely if a NULL_SHA1 was given, +# but we want to warn users of an empty file. +file(SHA1 ${file} ACTUAL_SHA1) +if(ACTUAL_SHA1 STREQUAL "da39a3ee5e6b4b0d3255bfef95601890afd80709") + # File was empty. It's likely due to lack of SSL support. + message(FATAL_ERROR + "Failed to download ${URL}. The file is empty and likely means CMake " + "was built without SSL support. Please use a version of CMake with " + "proper SSL support. See " + "https://github.com/neovim/neovim/wiki/Building-Neovim#build-prerequisites " + "for more information.") +elseif((NOT EXPECTED_SHA1 STREQUAL NULL_SHA1) AND + (NOT EXPECTED_SHA1 STREQUAL ACTUAL_SHA1)) + # Wasn't a NULL SHA1 and we didn't match, so we fail. + message(FATAL_ERROR + "Failed to download ${URL}. Expected a SHA1 of " + "${EXPECTED_SHA1} but got ${ACTUAL_SHA1} instead.") +endif() + message(STATUS "downloading... done") # Slurped from a generated extract-TARGET.cmake file. @@ -140,4 +157,3 @@ message(STATUS "extracting... [clean up]") file(REMOVE_RECURSE "${ut_dir}") message(STATUS "extracting... done") - |