From bc9f2b22da0f1c8575081d4396512ab525a4aec0 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2014 14:16:31 +0000 Subject: [PATCH 01/23] Improve error output for failed ctags commands The current throwing/handling of errors resulting from failed ctags commands isn't ideal - the messages are not formatted and are often unhelpful in diagnosing issues. Fix this by making some small changes: - Use 'check_output' instead of 'Popen', as this throws an exception for failed commands. This is easier to use than manually building an exception - Format the output of errors on Windows machines, by decoding the Windows formatted strings and stripping carriage returns - Other small changes --- ctags.py | 11 +++-------- ctagsplugin.py | 9 ++++++++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ctags.py b/ctags.py index a72e8e2..47f5574 100644 --- a/ctags.py +++ b/ctags.py @@ -302,14 +302,9 @@ def build_ctags(path, tag_file=None, recursive=False, opts=None, cmd=None, cmd.append(os.path.join(path, '*')) # execute the command - p = subprocess.Popen(cmd, cwd=cwd, shell=False, env=env, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - - ret = p.wait() - - if ret: - raise EnvironmentError(ret, p.stdout.read()) + p = subprocess.check_output( + cmd, cwd=cwd, shell=False, env=env, stdin=subprocess.PIPE, + stderr=subprocess.STDOUT) if not tag_file: # Exuberant ctags defaults to ``tags`` filename. tag_file = os.path.join(cwd, 'tags') diff --git a/ctagsplugin.py b/ctagsplugin.py index f0b9087..6ece178 100644 --- a/ctagsplugin.py +++ b/ctagsplugin.py @@ -867,7 +867,14 @@ def tags_built(tag_file): recursive=recursive, opts=opts, cmd=command) except IOError as e: - error_message(str(e).rstrip()) + error_message(e.strerror) + return + except subprocess.CalledProcessError as e: + if sublime.platform() == 'windows': + str_err = e.output.decode(encoding='windows-1252').rstrip() + else: + str_err = e.output.rstrip() + error_message(str_err) return except EnvironmentError as e: if not isinstance(e.strerror, str): From d4e880e51fd3ab593164fa0032c44839906ab22b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2014 15:46:58 +0000 Subject: [PATCH 02/23] Backport 'check_output' to Python 2.6.x - 'subprocess.check_output' is not supported in Sublime Text 2, as it uses an older version of Python (2.6.5). Backport this function --- ctags.py | 11 ++++++++--- helpers/check_output.py | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 helpers/check_output.py diff --git a/ctags.py b/ctags.py index 47f5574..a42fd8c 100644 --- a/ctags.py +++ b/ctags.py @@ -5,10 +5,16 @@ import codecs import re import os +import sys import subprocess import bisect import mmap +if sys.version_info<(2,7,0): + from helpers.check_output import check_output +else: + from subprocess import check_output + """ Contants """ @@ -302,9 +308,8 @@ def build_ctags(path, tag_file=None, recursive=False, opts=None, cmd=None, cmd.append(os.path.join(path, '*')) # execute the command - p = subprocess.check_output( - cmd, cwd=cwd, shell=False, env=env, stdin=subprocess.PIPE, - stderr=subprocess.STDOUT) + check_output(cmd, cwd=cwd, shell=False, env=env, stdin=subprocess.PIPE, + stderr=subprocess.STDOUT) if not tag_file: # Exuberant ctags defaults to ``tags`` filename. tag_file = os.path.join(cwd, 'tags') diff --git a/helpers/check_output.py b/helpers/check_output.py new file mode 100644 index 0000000..dd20835 --- /dev/null +++ b/helpers/check_output.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +# Based on source from here: https://gist.github.com/edufelipe/1027906 + +"""Backport version of 'subprocess.check_output' for Python 2.6.x""" + +import subprocess + +def check_output(*popenargs, **kwargs): + r"""Run command with arguments and return its output as a byte string. + + Backported from Python 2.7 as it's implemented as pure python on stdlib. + + >>> check_output(['/usr/bin/python', '--version']) + Python 2.6.2 + """ + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + error = subprocess.CalledProcessError(retcode, cmd) + error.output = output + raise error + return output From 10bf036f6e394a16651d28b012226eb842fa40fe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2014 15:48:55 +0000 Subject: [PATCH 03/23] Remove unsupported 'encoding' argument - 'decode' does not support the above argument in Python 2.6.x. Remove this as it's unecessary. --- ctagsplugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctagsplugin.py b/ctagsplugin.py index 6ece178..26ba0ef 100644 --- a/ctagsplugin.py +++ b/ctagsplugin.py @@ -9,6 +9,7 @@ import re import string import threading +import subprocess from itertools import chain from operator import itemgetter as iget @@ -871,7 +872,7 @@ def tags_built(tag_file): return except subprocess.CalledProcessError as e: if sublime.platform() == 'windows': - str_err = e.output.decode(encoding='windows-1252').rstrip() + str_err = e.output.decode('windows-1252').rstrip() else: str_err = e.output.rstrip() error_message(str_err) From c21350b74afb927f8df651c23d5d76e041e36ad2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2014 16:01:31 +0000 Subject: [PATCH 04/23] Enable 'shell' in Popen - In order to garner useful information when an invalid CTags string is passed, we need to run the subprocess command through the 'shell'. This cause of the issue is described here: http://stackoverflow.com/q/22251830/ --- ctags.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ctags.py b/ctags.py index a42fd8c..80d5534 100644 --- a/ctags.py +++ b/ctags.py @@ -307,8 +307,13 @@ def build_ctags(path, tag_file=None, recursive=False, opts=None, cmd=None, else: # search all files in current directory cmd.append(os.path.join(path, '*')) + # workaround for the issue described here: + # http://bugs.python.org/issue6689 + if os.name == 'posix': + cmd = ' '.join(cmd) + # execute the command - check_output(cmd, cwd=cwd, shell=False, env=env, stdin=subprocess.PIPE, + check_output(cmd, cwd=cwd, shell=True, env=env, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) if not tag_file: # Exuberant ctags defaults to ``tags`` filename. From 13357213c98fdb7ece956c5053e1048d8955dbf3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2014 16:17:40 +0000 Subject: [PATCH 05/23] Make remaining build exception more general - There are likely special cases that occur in 'ctags_build' for which we have no handler. Replace the defunct 'EnvironmentError' with a general 'Exception' handler to catch these cases. --- ctagsplugin.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/ctagsplugin.py b/ctagsplugin.py index 26ba0ef..0083122 100644 --- a/ctagsplugin.py +++ b/ctagsplugin.py @@ -877,13 +877,9 @@ def tags_built(tag_file): str_err = e.output.rstrip() error_message(str_err) return - except EnvironmentError as e: - if not isinstance(e.strerror, str): - str_err = ' '.join(e.strerror.decode('utf-8').splitlines()) - else: - str_err = str(e).rstrip() - error_message(str_err) # show error_message - return + except Exception as e: + error_message("An unknown error occured.\nCheck the console for info.") + raise e tags_built(result) From ce0622cbe56e834e4ea8943ba0591ba94fac518a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2014 16:21:35 +0000 Subject: [PATCH 06/23] Strip all carriage returns from printed errors --- ctagsplugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctagsplugin.py b/ctagsplugin.py index 0083122..ead275d 100644 --- a/ctagsplugin.py +++ b/ctagsplugin.py @@ -872,7 +872,8 @@ def tags_built(tag_file): return except subprocess.CalledProcessError as e: if sublime.platform() == 'windows': - str_err = e.output.decode('windows-1252').rstrip() + str_err = ' '.join( + e.output.decode('windows-1252').splitlines()) else: str_err = e.output.rstrip() error_message(str_err) From f94e78fff62b28b567044aa0bcc411abb43350ad Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Mar 2014 22:11:21 +0000 Subject: [PATCH 07/23] Add regression tests for #209, #213 - Add some regression tests for these issues. As they are currently conflicting, they don't both work. --- test_ctags.py | 215 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 1 deletion(-) diff --git a/test_ctags.py b/test_ctags.py index f0ef24d..2ece786 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -27,7 +27,7 @@ def build_python_file(self): """Build a simple Python "program" that ctags can use. :Returns: - Path to a constructed, valid Java source file + Path to a constructed, valid Python source file """ path = '' @@ -45,6 +45,48 @@ def build_python_file(self): return path + def build_python_file__extended(self): + """Build a Python "program" demonstrating all common CTag types + + Build a Python program that demonstrates the following CTag types: + - ``f`` - function definitions + - ``v`` - variable definitions + - ``c`` - classes + - ``m`` - class, struct, and union members + - ``i`` - import + + This is mainly intended to regression test for issue #209. + + :Returns: + Path to a constructed, valid Python source file + """ + path = '' + + # the file created here is locked while open, hence we can't delete + # similarly, ctags appears to require an extension hence the suffix + with tempfile.NamedTemporaryFile(delete=False, suffix='.py') as temp: + try: + path = temp.name # store name for later use + temp.writelines([ + b'import os\n', + b'\n', + b'COLOR_RED = "\\c800080FF;"\t#red\n', + b'\n', + b'def my_function(first_name):\n', + b'\tprint("Hello {0}".format(first_name))\n', + b'\n', + b'class MyClass(object):\n', + b'\tlast_name = None\n', + b'\taddress = None\t# comment preceded by a tab\n', + b'\n', + b'\tdef my_method(self, last_name):\n', + b'\t\tself.last_name = last_name\n', + b'\t\tprint("Hello again, {0}".format(self.last_name))\n']) + finally: + temp.close() + + return path + def build_java_file(self): """Build a slightly detailed Java "program" that ctags can use. @@ -79,6 +121,38 @@ def build_java_file(self): return path + def build_c_file(self): + """Build a simple C "program" that ctags can use. + + This is mainly intended to regression test for issue #213. + + :Returns: + Path to a constructed, valid Java source file + """ + path = '' + + # the file created here is locked while open, hence we can't delete + # similarly, ctags appears to require an extension hence the suffix + with tempfile.NamedTemporaryFile(delete=False, suffix='.c') as temp: + try: + path = temp.name # store name for later use + temp.writelines([ + b'#define foo(x,y) x+y\n' + b'#define foobar 1\n' + b'\n' + b'void bar()\n' + b'{\n' + b'\tfoo(10,2);' + b'\n' + b'#if foobar\n' + b'\tfoo(2,3); \n' + b'}\n']) + finally: + temp.close() + + return path + + """ Test functions """ @@ -263,5 +337,144 @@ def test_post_process_tag__fields(self): self.assertEqual(result, expected_output) + """Tag class""" + + def test_parse_tag_lines__python(self): + """Test ``parse_tag_lines`` with a sample Python file""" + path = self.build_python_file__extended() + + tag_file = ctags.build_ctags(path=path) + + with codecs.open(tag_file, encoding='utf-8') as output: + try: + content = output.readlines() + filename = os.path.basename(path) + except: + self.failure("Setup of files for test failed") + finally: + output.close() + os.remove(path) # clean up + os.remove(tag_file) + + expected_outputs = { + 'MyClass': [{ + 'symbol': 'MyClass', + 'filename': filename, + 'ex_command': 'class MyClass(object):', + 'tag_path': (filename, 'MyClass'), + 'type': 'c', + 'fields': None}], + 'address': [{ + 'symbol': 'address', + 'filename': filename, + 'ex_command': '\taddress = None\t# comment preceded by a tab', + 'tag_path': (filename, 'MyClass\r', 'address'), + 'type': 'v', + 'fields': 'class:MyClass\r', + 'field_keys': ['class'], + 'class': 'MyClass\r'}], + 'last_name': [{ + 'symbol': 'last_name', + 'filename': filename, + 'ex_command': '\tlast_name = None', + 'tag_path': (filename, 'MyClass\r', 'last_name'), + 'type': 'v', + 'fields': 'class:MyClass\r', + 'field_keys': ['class'], + 'class': 'MyClass\r'}], + 'my_function': [{ + 'symbol': 'my_function', + 'filename': filename, + 'ex_command': 'def my_function(first_name):', + 'tag_path': (filename, 'my_function'), + 'type': 'f', + 'fields': None}], + 'my_method': [{ + 'symbol': 'my_method', + 'filename': filename, + 'ex_command': '\tdef my_method(self, last_name):', + 'tag_path': (filename, 'MyClass\r', 'my_method'), + 'type': 'm', + 'fields': 'class:MyClass\r', + 'field_keys': ['class'], + 'class': 'MyClass\r'}], + 'os': [{ + 'symbol': 'os', + 'filename': filename, + 'ex_command': 'import os', + 'tag_path': (filename, 'os'), + 'type': 'i', + 'fields': None}], + 'COLOR_RED': [{ + 'symbol': 'COLOR_RED', + 'filename': filename, + 'ex_command': 'COLOR_RED = "\\c800080FF;"\t#red', + 'tag_path': (filename, 'COLOR_RED'), + 'type': 'v', + 'fields': None}], + } + + result = ctags.parse_tag_lines(content) + + for key in expected_outputs: + self.assertEqual(result[key], expected_outputs[key]) + + for key in result: # don't forget - we might have missed something! + self.assertEqual(expected_outputs[key], result[key]) + + def test_parse_tag_lines__c(self): + """Test ``parse_tag_lines`` with a sample C file""" + path = self.build_c_file() + + tag_file = ctags.build_ctags(path=path) + + with codecs.open(tag_file, encoding='utf-8') as output: + try: + content = output.readlines() + filename = os.path.basename(path) + except IOError: + self.fail("Setup of files for test failed") + finally: + output.close() + os.remove(path) # clean up + os.remove(tag_file) + + expected_outputs = { + 'bar': [{ + 'symbol': 'bar', + 'filename': filename, + 'ex_command': 'void bar()', + 'tag_path': (filename, 'bar'), + 'type': 'f', + 'fields': None}], + 'foo': [{ + 'symbol': 'foo', + 'filename': filename, + 'ex_command': '1', + 'tag_path': (filename, 'foo'), + 'type': 'd', + 'fields': 'file:\r', + 'field_keys': ['file'], + 'file': '\r'}], + 'foobar': [{ + 'symbol': 'foobar', + 'filename': filename, + 'ex_command': '2', + 'tag_path': (filename, 'foobar'), + 'type': 'd', + 'fields': 'file:\r', + 'field_keys': ['file'], + 'file': '\r'}] + } + + result = ctags.parse_tag_lines(content) + + for key in expected_outputs: + self.assertEqual(result[key], expected_outputs[key]) + + for key in result: # don't forget - we might have missed something! + self.assertEqual(expected_outputs[key], result[key]) + + if __name__ == '__main__': unittest.main() From 02e45264fedea0bffb4a67bc46a7b92dec7d62f1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Mar 2014 23:19:40 +0000 Subject: [PATCH 08/23] Resolve #213 - Add special case to deal with line numbers (which aren't followed by a dollar sign, i.e. '$') --- ctags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctags.py b/ctags.py index e81933a..631fbdd 100644 --- a/ctags.py +++ b/ctags.py @@ -16,7 +16,7 @@ TAGS_RE = re.compile( r'(?P[^\t]+)\t' r'(?P[^\t]+)\t' - r'(?P.*?\$/);"\t' + r'(?P(\d|.*?\$/));"\t' r'(?P[^\t\r\n]+)' r'(?:\t(?P.*))?' ) From 3c07bcedf881ca230949250b478d5094d32a2748 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 11 Mar 2014 22:21:53 +0000 Subject: [PATCH 09/23] Update regex with changes from "forum" Update regex with decisions made here: https://github.com/SublimeText/CTags/issues/213 --- ctags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctags.py b/ctags.py index 631fbdd..c9083c2 100644 --- a/ctags.py +++ b/ctags.py @@ -16,7 +16,7 @@ TAGS_RE = re.compile( r'(?P[^\t]+)\t' r'(?P[^\t]+)\t' - r'(?P(\d|.*?\$/));"\t' + r'(?P(/.+/|\?.+\?|\d+));"\t' r'(?P[^\t\r\n]+)' r'(?:\t(?P.*))?' ) From 78d18518e3726f375687562df595cd64aa8d74a2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 11 Mar 2014 22:23:58 +0000 Subject: [PATCH 10/23] Fix typo in 'test_ctags.py' --- test_ctags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_ctags.py b/test_ctags.py index 2ece786..05a5f3d 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -350,7 +350,7 @@ def test_parse_tag_lines__python(self): content = output.readlines() filename = os.path.basename(path) except: - self.failure("Setup of files for test failed") + self.fail("Setup of files for test failed") finally: output.close() os.remove(path) # clean up From dfa7aedd4869d6be014881d6b365a145bea359d0 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 14 Mar 2014 20:54:51 +0000 Subject: [PATCH 11/23] Fix regression highlighted in #177 --- ctagsplugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ctagsplugin.py b/ctagsplugin.py index e350c84..0e8c176 100644 --- a/ctagsplugin.py +++ b/ctagsplugin.py @@ -687,6 +687,10 @@ def run(self, view, args, tags_file): region = view.sel()[0] if region.begin() == region.end(): # point region = view.word(region) + language = view.syntax_name(view.sel()[0].b) + endings = view.substr(sublime.Region(region.end(), region.end()+1)) + if self.scopes.match(language) and self.endings.match(endings): + region = sublime.Region(region.begin(), region.end()+1) symbol = view.substr(region) return JumpToDefinition.run(symbol, view, tags_file) From 1b3b2b3969272091578582d432b3581dbd30f421 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 14 Mar 2014 21:21:20 +0000 Subject: [PATCH 12/23] Update to work with Python3 (ST3) --- ctagsplugin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ctagsplugin.py b/ctagsplugin.py index 0e8c176..e10e0be 100644 --- a/ctagsplugin.py +++ b/ctagsplugin.py @@ -51,7 +51,6 @@ ENTITY_SCOPE = 'entity.name.function, entity.name.type, meta.toc-list' RUBY_SPECIAL_ENDINGS = '\?|!' -RUBY_SCOPES = '.*(ruby|rails).*' ON_LOAD = sublime_plugin.all_callbacks['on_load'] @@ -676,7 +675,6 @@ class NavigateToDefinition(sublime_plugin.TextCommand): def __init__(self, args): sublime_plugin.TextCommand.__init__(self, args) - self.scopes = re.compile(RUBY_SCOPES) self.endings = re.compile(RUBY_SPECIAL_ENDINGS) def is_visible(self): @@ -687,9 +685,12 @@ def run(self, view, args, tags_file): region = view.sel()[0] if region.begin() == region.end(): # point region = view.word(region) - language = view.syntax_name(view.sel()[0].b) + + # handle special line endings for Ruby + language = view.settings().get('syntax') endings = view.substr(sublime.Region(region.end(), region.end()+1)) - if self.scopes.match(language) and self.endings.match(endings): + + if 'Ruby' in language and self.endings.match(endings): region = sublime.Region(region.begin(), region.end()+1) symbol = view.substr(region) From 48eb5213c91bd90fa3a5fb92015be410cc5a52d5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 16:41:27 +0000 Subject: [PATCH 13/23] Resolve broken unit test --- test_ctags.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_ctags.py b/test_ctags.py index 05a5f3d..1bc7d0c 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -6,6 +6,7 @@ import tempfile import unittest import codecs +from subprocess import CalledProcessError try: import sublime @@ -195,7 +196,7 @@ def test_build_ctags__invalid_custom_command(self): # build_ctags requires a real path, so we create a temporary file as a # cross-platform way to get the temp directory with tempfile.NamedTemporaryFile() as temp: - with self.assertRaises(EnvironmentError): + with self.assertRaises(CalledProcessError): ctags.build_ctags(path=temp.name, cmd='ccttaaggss') def test_build_ctags__single_file(self): From cc55bd0f07f20358316b4e31d104014eec625a69 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 16:51:20 +0000 Subject: [PATCH 14/23] Add initial version of '.travis.yml' file --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7421275 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +# After changing this file, check it on: +# http://lint.travis-ci.org/ +language: python +python: + - "2.6" # sublime text 2 + - "3.3" # sublime text 3 +script: + - python -m unittest discover From d46b5cdc2a3e6dcf3cbdb708f635ffa62b106717 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 17:02:05 +0000 Subject: [PATCH 15/23] Resolve build issues with Python 2.6 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7421275..cc63bbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,5 @@ python: - "2.6" # sublime text 2 - "3.3" # sublime text 3 script: - - python -m unittest discover + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then python -m unittest2.__main__ discover; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then python -m unittest discover; fi From 4fe4b97a99ec6420e3e07b173daddd8cf03b1e09 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 17:04:38 +0000 Subject: [PATCH 16/23] Further fixes for Python 2.6 Further changes based on pyql travis config file https://github.com/enthought/pyql/blob/master/.travis.yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index cc63bbc..18aff13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ language: python python: - "2.6" # sublime text 2 - "3.3" # sublime text 3 +before_install: + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi script: - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then python -m unittest2.__main__ discover; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then python -m unittest discover; fi From 73d396531f7e15d7aba8d35435324d46c4191bb8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 17:14:19 +0000 Subject: [PATCH 17/23] Add requirement for 'exubrant-ctags' package --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 18aff13..f2c830d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "2.6" # sublime text 2 - "3.3" # sublime text 3 before_install: + - sudo apt-get install exuberant-ctags - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi script: - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then python -m unittest2.__main__ discover; fi From ba74343729cadd0950542e7dc5bda5f5b2e4af81 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 17:57:00 +0000 Subject: [PATCH 18/23] Account for different OS line endings in tests --- test_ctags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_ctags.py b/test_ctags.py index f0ef24d..d37366c 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -137,7 +137,7 @@ def test_build_ctags__single_file(self): self.assertEqual( content[-1], 'my_definition\t{0}\t/^def my_definition()' - ':$/;"\tf\r\n'.format(filename)) + ':$/;"\tf{1}'.format(filename, os.linesep)) finally: output.close() os.remove(path) # clean up @@ -156,7 +156,7 @@ def test_build_ctags__custom_tag_file(self): self.assertEqual( content[-1], 'my_definition\t{0}\t/^def my_definition()' - ':$/;"\tf\r\n'.format(filename)) + ':$/;"\tf{1}'.format(filename, os.linesep)) finally: output.close() os.remove(path) # clean up From 28d339e4f1d817b601a24aa48a8e1d7ec8e8f59f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 18:21:22 +0000 Subject: [PATCH 19/23] Replace calls to 'unittest' with 'unittest2' Backport new 'unittest' features to Python 2.6.5 (ST2) --- test_ctags.py | 7 ++++++- test_ctagsplugin.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test_ctags.py b/test_ctags.py index d37366c..94499ba 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -3,10 +3,15 @@ """Unit tests for ctags.py""" import os +import sys import tempfile -import unittest import codecs +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + try: import sublime diff --git a/test_ctagsplugin.py b/test_ctagsplugin.py index 43809ff..ad74d31 100644 --- a/test_ctagsplugin.py +++ b/test_ctagsplugin.py @@ -3,10 +3,15 @@ """Unit tests for ctagsplugin.py""" import os +import sys import tempfile -import unittest import shutil +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + try: import sublime From b25760a0498fe022be4845e13b063f5ba9e6235f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 18:28:38 +0000 Subject: [PATCH 20/23] Add image to README --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 4eb85fa..32f19bc 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,8 @@ CTags ===== +.. image:: https://travis-ci.org/SublimeText/CTags.png?branch=development :target: https://travis-ci.org/SublimeText/CTags + About ===== From 8d079814d0fdbe7630429875bc8c1ca25463d9c1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 20:12:10 +0000 Subject: [PATCH 21/23] Strip newlines from parsed taglines Strip newlines from parsed taglines, and update unit tests to reflect this change --- ctags.py | 2 ++ test_ctags.py | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ctags.py b/ctags.py index 269c4fa..d1a0a87 100644 --- a/ctags.py +++ b/ctags.py @@ -95,6 +95,8 @@ def parse_tag_lines(lines, order_by='symbol', tag_class=None, filters=[]): if isinstance(line, Tag): # handle both text and tag objects line = line.line + line = line.rstrip('\r\n') + search_obj = TAGS_RE.search(line) if not search_obj: diff --git a/test_ctags.py b/test_ctags.py index fc7468b..af54db8 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -374,20 +374,20 @@ def test_parse_tag_lines__python(self): 'symbol': 'address', 'filename': filename, 'ex_command': '\taddress = None\t# comment preceded by a tab', - 'tag_path': (filename, 'MyClass\r', 'address'), + 'tag_path': (filename, 'MyClass', 'address'), 'type': 'v', - 'fields': 'class:MyClass\r', + 'fields': 'class:MyClass', 'field_keys': ['class'], - 'class': 'MyClass\r'}], + 'class': 'MyClass'}], 'last_name': [{ 'symbol': 'last_name', 'filename': filename, 'ex_command': '\tlast_name = None', - 'tag_path': (filename, 'MyClass\r', 'last_name'), + 'tag_path': (filename, 'MyClass', 'last_name'), 'type': 'v', - 'fields': 'class:MyClass\r', + 'fields': 'class:MyClass', 'field_keys': ['class'], - 'class': 'MyClass\r'}], + 'class': 'MyClass'}], 'my_function': [{ 'symbol': 'my_function', 'filename': filename, @@ -399,11 +399,11 @@ def test_parse_tag_lines__python(self): 'symbol': 'my_method', 'filename': filename, 'ex_command': '\tdef my_method(self, last_name):', - 'tag_path': (filename, 'MyClass\r', 'my_method'), + 'tag_path': (filename, 'MyClass', 'my_method'), 'type': 'm', - 'fields': 'class:MyClass\r', + 'fields': 'class:MyClass', 'field_keys': ['class'], - 'class': 'MyClass\r'}], + 'class': 'MyClass'}], 'os': [{ 'symbol': 'os', 'filename': filename, @@ -459,18 +459,18 @@ def test_parse_tag_lines__c(self): 'ex_command': '1', 'tag_path': (filename, 'foo'), 'type': 'd', - 'fields': 'file:\r', + 'fields': 'file:', 'field_keys': ['file'], - 'file': '\r'}], + 'file': ''}], 'foobar': [{ 'symbol': 'foobar', 'filename': filename, 'ex_command': '2', 'tag_path': (filename, 'foobar'), 'type': 'd', - 'fields': 'file:\r', + 'fields': 'file:', 'field_keys': ['file'], - 'file': '\r'}] + 'file': ''}] } result = ctags.parse_tag_lines(content) From 80d9208111a6364f5fd3f2b95abde1b728502fab Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sat, 15 Mar 2014 21:14:37 +0000 Subject: [PATCH 22/23] Ignore imports for Python sample files - Some versions of ctags don't parse imports by default. This can caused artificially failed unit tests on some platforms. Fix this. --- test_ctags.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test_ctags.py b/test_ctags.py index af54db8..862c91d 100644 --- a/test_ctags.py +++ b/test_ctags.py @@ -349,7 +349,7 @@ def test_parse_tag_lines__python(self): """Test ``parse_tag_lines`` with a sample Python file""" path = self.build_python_file__extended() - tag_file = ctags.build_ctags(path=path) + tag_file = ctags.build_ctags(path=path, opts=['--python-kinds=-i']) with codecs.open(tag_file, encoding='utf-8') as output: try: @@ -404,13 +404,6 @@ def test_parse_tag_lines__python(self): 'fields': 'class:MyClass', 'field_keys': ['class'], 'class': 'MyClass'}], - 'os': [{ - 'symbol': 'os', - 'filename': filename, - 'ex_command': 'import os', - 'tag_path': (filename, 'os'), - 'type': 'i', - 'fields': None}], 'COLOR_RED': [{ 'symbol': 'COLOR_RED', 'filename': filename, From 3cfd53b819a8d3684f9ef286f5e7318ba39ca1eb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Mar 2014 21:04:33 +0000 Subject: [PATCH 23/23] Update Messages for Package Control --- messages.json | 3 ++- messages/0.3.7.md | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 messages/0.3.7.md diff --git a/messages.json b/messages.json index d9b0e93..96ea7fd 100644 --- a/messages.json +++ b/messages.json @@ -6,5 +6,6 @@ "0.3.3": "messages/0.3.3.md", "0.3.4": "messages/0.3.4.md", "0.3.5": "messages/0.3.5.md", - "0.3.6": "messages/0.3.6.md" + "0.3.6": "messages/0.3.6.md", + "0.3.7": "messages/0.3.7.md" } diff --git a/messages/0.3.7.md b/messages/0.3.7.md new file mode 100644 index 0000000..a48c189 --- /dev/null +++ b/messages/0.3.7.md @@ -0,0 +1,24 @@ +Changes in 0.3.7 +================ + +- Resolve regressions caused by multiple previous releases +- General improvements in error handling and other corner cases +- Bug Fixes + +Fixes +===== + +* Ruby: Exception and ? ignored., #177 +* Can't Jump to the definition which defined by #define, #213 + +Resolves +======== + +* Travis-ci Integration, #218 +* Tests aren't cross platform, #219 +* Better formatted build warnings #220 + +******************************************************************************* + +For more detailed information about these changes, run ``git v0.3.6..v0.3.7`` +on the Git repository found [here](https://github.com/SublimeText/CTags).