From 960e84c0825ed657e99b3d10e71636c450fb30e4 Mon Sep 17 00:00:00 2001 From: Terry Yin Date: Fri, 31 Jan 2025 14:21:34 +0800 Subject: [PATCH] vue js support --- lizard_languages/__init__.py | 2 + lizard_languages/vue.py | 137 +++++++++++++++++++++++++++++++++ prompt-history.md | 6 +- test/test_languages/testVue.py | 78 +++++++++++++++++++ 4 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 lizard_languages/vue.py create mode 100644 test/test_languages/testVue.py diff --git a/lizard_languages/__init__.py b/lizard_languages/__init__.py index 2b624e6f..fdada48f 100644 --- a/lizard_languages/__init__.py +++ b/lizard_languages/__init__.py @@ -23,6 +23,7 @@ from .solidity import SolidityReader from .jsx import JSXReader from .tsx import TSXReader +from .vue import VueReader def languages(): @@ -50,6 +51,7 @@ def languages(): ZigReader, JSXReader, TSXReader, + VueReader, ] diff --git a/lizard_languages/vue.py b/lizard_languages/vue.py new file mode 100644 index 00000000..f0f35331 --- /dev/null +++ b/lizard_languages/vue.py @@ -0,0 +1,137 @@ +''' +Language parser for Vue.js +''' + +from .typescript import TypeScriptReader, TypeScriptStates +from .javascript import JavaScriptReader, JSTokenizer, isidentifier +from .code_reader import CodeReader +from .clike import CCppCommentsMixin +from .js_style_regex_expression import js_style_regex_expression +from .js_style_language_states import JavaScriptStyleLanguageStates, ES6ObjectStates + + +class VueReader(CodeReader, CCppCommentsMixin): + # pylint: disable=R0903 + + ext = ['vue'] + language_names = ['vue'] + + @staticmethod + @js_style_regex_expression + def generate_tokens(source_code, addition='', token_class=None): + # Extract script content between ' in line: + break + if in_script: + script_content += line + '\n' + + # If no script content found, return empty generator + if not script_content: + return iter(()) + + # Add common JavaScript/TypeScript patterns + addition = addition + r"|(?:\$\w+)|`.*?`" + + # For TypeScript, add type-specific patterns + if 'lang="ts"' in script_tag or 'lang="typescript"' in script_tag: + addition = addition + r"|(?:\w+\?)|(?::\s*\w+)" + + # Use JavaScript tokenizer for proper token handling + js_tokenizer = JSTokenizer() + for token in CodeReader.generate_tokens(script_content, addition, token_class): + for tok in js_tokenizer(token): + yield tok + + def __init__(self, context): + super(VueReader, self).__init__(context) + self.context = context + self.context.script_tag = None + self.parallel_states = [VueStates(context)] + + def __call__(self, tokens, reader): + # Convert tokens to list to avoid exhausting the iterator + token_list = list(tokens) + + # Extract script tag from the tokens and store in context + for token in token_list: + if token.startswith('': + break + + # Set up the appropriate state machine based on the script tag + if self.context.script_tag and ('lang="ts"' in self.context.script_tag or 'lang="typescript"' in self.context.script_tag): + self.parallel_states = [TypeScriptStates(self.context)] + else: + self.parallel_states = [VueStates(self.context)] + + # Use the base class's __call__ method to process tokens + return super(VueReader, self).__call__(iter(token_list), reader) + + +class VueStates(TypeScriptStates): + def _state_global(self, token): + if token.startswith('': + self.next(self._state_global) + else: + # Delegate to TypeScript/JavaScript states + super(VueStates, self)._state_global(token) + + def _process_template_content(self, token): + if token == '': + self.next(self._state_global) + + def read_object(self): + # Check the stored script tag instead of context.current_file + if self.context.script_tag and ('lang="ts"' in self.context.script_tag or 'lang="typescript"' in self.context.script_tag): + self.sub_state(TypeScriptObjectStates(self.context)) + else: + self.sub_state(ES6ObjectStates(self.context)) + + +class TypeScriptObjectStates(ES6ObjectStates): + def _state_global(self, token): + if token == ':': + self.function_name = self.last_tokens + elif token == '(': + self._function(self.last_tokens) + self.next(self._function, token) + elif token == ')': + self.next(self._function_return_type) + else: + super(TypeScriptObjectStates, self)._state_global(token) + + def _function_return_type(self, token): + if token == '{': + self.next(self._state_global, token) + elif token == ';': + self.next(self._state_global) + else: + # Skip over the return type and continue + self.next(self._function_return_type) \ No newline at end of file diff --git a/prompt-history.md b/prompt-history.md index 853c3cdd..a2723f0c 100644 --- a/prompt-history.md +++ b/prompt-history.md @@ -6,4 +6,8 @@ please add support for TSX file similar to @jsx.py and add test similar to @test --------- -one test failed. It seems the problem is not in @tsx.py but the basic typescript @typescript.py support is incorrect. Please fix it. Make sure both @testTSX.py and @testTypeScript.py still pass. \ No newline at end of file +one test failed. It seems the problem is not in @tsx.py but the basic typescript @typescript.py support is incorrect. Please fix it. Make sure both @testTSX.py and @testTypeScript.py still pass. + +--------- + +based on @tsx.py and @jsx.py , as well as @testTSX.py @testJSX.py , please add support for VueJS files, which should support both lang="js" and lang="ts". Don't forget @__init__.py \ No newline at end of file diff --git a/test/test_languages/testVue.py b/test/test_languages/testVue.py new file mode 100644 index 00000000..bbfadb71 --- /dev/null +++ b/test/test_languages/testVue.py @@ -0,0 +1,78 @@ +import unittest +from lizard import analyze_file, FileAnalyzer, get_extensions +from lizard_languages import VueReader + + +def get_vue_function_list(source_code): + return analyze_file.analyze_source_code("a.vue", source_code).function_list + +class Test_tokenizing_Vue(unittest.TestCase): + + def check_tokens(self, expect, source): + tokens = list(VueReader.generate_tokens(source)) + self.assertEqual(expect, tokens) + + def test_js_script(self): + code = ''' + + ''' + functions = get_vue_function_list(code) + self.assertEqual("test", functions[0].name) + + def xtest_ts_script(self): + code = ''' + + ''' + functions = get_vue_function_list(code) + self.assertEqual("test", functions[0].name) + + def test_complex_component(self): + code = ''' + + + + ''' + functions = get_vue_function_list(code) + self.assertEqual(2, len(functions)) + self.assertEqual("data", functions[0].name) + self.assertEqual("updateMessage", functions[1].name) + + def test_template_ignored(self): + code = ''' + + ''' + functions = get_vue_function_list(code) + self.assertEqual(0, len(functions)) \ No newline at end of file