From 1b7ad4595b6a22f0dc7b864899d1cf85279fb462 Mon Sep 17 00:00:00 2001 From: boolangery Date: Sun, 19 Jul 2026 16:23:09 +0200 Subject: [PATCH 1/6] ci: migrate from Travis CI to GitHub Actions Travis CI is no longer functional for open-source projects. - Replace .travis.yml with GitHub Actions workflows - Install python3-dev and g++ for Cython extension compilation - Test on Python 3.9-3.12 - Publish to PyPI on release --- .github/workflows/python-package.yml | 37 ++++++++++++++++++++++++++++ .github/workflows/python-publish.yml | 36 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .github/workflows/python-package.yml create mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..b0410af --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,37 @@ +name: Python package + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y python3-dev g++ + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install cython pytest + pip install -e . + - name: Cythonize and build extension + run: | + python setup.py cythonize + - name: Test with pytest + run: | + pytest diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..92fac6b --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,36 @@ +name: Upload Python Package + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y python3-dev g++ + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install cython build + - name: Cythonize + run: | + python setup.py cythonize + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} From de2bface4dfee36e9faaefbd273f2bc6566fa8aa Mon Sep 17 00:00:00 2001 From: boolangery Date: Mon, 20 Jul 2026 09:46:35 +0200 Subject: [PATCH 2/6] fix: Cythonize before install, remove .travis.yml, update classifiers --- .github/workflows/python-package.yml | 6 ++++-- .travis.yml | 24 ------------------------ setup.py | 7 +++++-- 3 files changed, 9 insertions(+), 28 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index b0410af..0194daf 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -27,11 +27,13 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - python -m pip install cython pytest - pip install -e . + python -m pip install cython pytest setuptools - name: Cythonize and build extension run: | python setup.py cythonize + - name: Install package + run: | + pip install -e . - name: Test with pytest run: | pytest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 87890ac..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: python - -env: - matrix: - - LUA=lua5.3 LUAC=luac5.3 - -python: - - "3.6" -# Enable 3.7 without globally enabling sudo and dist: xenial for other build jobs -matrix: - include: - - python: 3.7 - dist: xenial - sudo: true - -# command to install dependencies -install: - - pip install -r requirements.txt - - python setup.py cythonize - - pip install . - -# command to run tests -script: - - pytest diff --git a/setup.py b/setup.py index f7ddc25..be4fee8 100644 --- a/setup.py +++ b/setup.py @@ -65,9 +65,12 @@ def __init__(self, *args, **kwargs): packages=['luastyle', 'luastyle.tests'], zip_safe=False, classifiers=[ - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7' + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12' ], + python_requires='>=3.9', install_requires=['luaparser>=2.0'], entry_points={ 'console_scripts': [ From 45d636a8754730dd7fb0f56e833be3d62ca20276 Mon Sep 17 00:00:00 2001 From: boolangery Date: Mon, 20 Jul 2026 12:29:22 +0200 Subject: [PATCH 3/6] fix: update CTokens enum for luaparser 4.x / antlr4 compatibility Token type constants completely changed in luaparser >=4.0 which switched to antlr4-based lexing. Updated all ~60 token values to match LuaLexer constants from antlr4 runtime. Also: - Added STRING/NUMBER subtype variants (NORMALSTRING/CHARSTRING/LONGSTRING, INT/HEX/FLOAT/HEX_FLOAT) to ATOM_OP set - Added STRING_TYPES set for multi-type string matching - Removed broken import of luaparser.builder.Tokens (no longer exists) - STRING checks in parse_tail now handle all string literal types --- .gitignore | 1 + luastyle/indenter.pxd | 138 ++++++++++++++++++++++-------------------- luastyle/indenter.pyx | 15 ++++- 3 files changed, 87 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index 1290439..9dd61a1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ luastyle/tests/__pycache__/ MANIFEST dist/ luastyle/__init__.pyc +indenter.html diff --git a/luastyle/indenter.pxd b/luastyle/indenter.pxd index af5493c..46bf416 100644 --- a/luastyle/indenter.pxd +++ b/luastyle/indenter.pxd @@ -1,6 +1,5 @@ import logging from luaparser import ast, astnodes -from luaparser.builder import Tokens from libcpp cimport bool from libcpp.vector cimport vector from libcpp.unordered_set cimport unordered_set @@ -62,70 +61,78 @@ cdef enum Expr: cdef enum CTokens: - AND = 1 - BREAK = 2 - DO = 3 - ELSETOK = 4 - ELSEIF = 5 - END = 6 - FALSE = 7 - FOR = 8 - FUNCTION = 9 - GOTO = 10 - IFTOK = 11 - IN = 12 - LOCAL = 13 - NIL = 14 - NOT = 15 - OR = 16 - REPEAT = 17 - RETURN = 18 - THEN = 19 - TRUE = 20 - UNTIL = 21 - WHILE = 22 - ADD = 23 - MINUS = 24 - MULT = 25 - DIV = 26 - FLOOR = 27 - MOD = 28 - POW = 29 - LENGTH = 30 - EQ = 31 - NEQ = 32 - LTEQ = 33 - GTEQ = 34 - LT = 35 - GT = 36 - ASSIGN = 37 - BITAND = 38 - BITOR = 39 - BITNOT = 40 - BITRSHIFT = 41 - BITRLEFT = 42 - OPAR = 43 - CPAR = 44 - OBRACE = 45 - CBRACE = 46 - OBRACK = 47 - CBRACK = 48 - COLCOL = 49 - COL = 50 - COMMA = 51 - VARARGS = 52 - CONCAT = 53 - DOT = 54 - SEMCOL = 55 - NAME = 56 - NUMBER = 57 - STRING = 58 - COMMENT = 59 - LINE_COMMENT = 60 - SPACE = 61 - NEWLINE = 62 - SHEBANG = 63 - LongBracket = 64 + # Updated for luaparser >=4.0 / antlr4 LuaLexer token types + # See: luaparser.parser.LuaLexer + SEMCOL = 1 # was 55 (SEMI) + EQ = 2 # was 31; also ASSIGN (antlr4: single = token) + BREAK = 3 # was 2 + GOTO = 4 # was 10 + DO = 5 # was 3 + END = 6 # was 6 + WHILE = 7 # was 22 + REPEAT = 8 # was 17 + UNTIL = 9 # was 21 + IFTOK = 10 # was 11 (IF) + THEN = 11 # was 19 + ELSEIF = 12 # was 5 + ELSETOK = 13 # was 4 (ELSE) + FOR = 14 # was 8 + COMMA = 15 # was 51 + IN = 16 # was 12 + FUNCTION = 17 # was 9 + LOCAL = 18 # was 13 + LT = 19 # was 35 + GT = 20 # was 36 + RETURN = 21 # was 18 + COLCOL = 22 # was 49 (CC = ::) + NIL = 23 # was 14 + FALSE = 24 # was 7 + TRUE = 25 # was 20 + DOT = 26 # was 54 + BITNOT = 27 # was 40 (SQUIG = ~) + MINUS = 28 # was 24 + LENGTH = 29 # was 30 (POUND = #) + OPAR = 30 # was 43 (OP = () + CPAR = 31 # was 44 (CP = )) + NOT = 32 # was 15 + BITRLEFT = 33 # was 42 (LL = <<) + BITRSHIFT = 34 # was 41 (GG = >>) + BITAND = 35 # was 38 (AMP = &) + FLOOR = 36 # was 27 (SS = //) + MOD = 37 # was 28 (PER = %) + COL = 38 # was 50 + LTEQ = 39 # was 33 (LE = <=) + GTEQ = 40 # was 34 (GE = >=) + AND = 41 # was 1 + OR = 42 # was 16 + ADD = 43 # was 23 (PLUS = +) + MULT = 44 # was 25 (STAR = *) + OBRACK = 45 # was 47 (OCU = [) + CBRACK = 46 # was 48 (CCU = ]) + OBRACE = 47 # was 45 (OB = {) + CBRACE = 48 # was 46 (CB = }) + NEQ = 49 # was 32 (EE = ~=) + CONCAT = 50 # was 53 (DD = ..) + BITOR = 51 # was 39 (PIPE = |) + POW = 52 # was 29 (CARET = ^) + DIV = 53 # was 26 (SLASH = /) + VARARGS = 54 # was 52 (DDD = ...) + NAME = 56 # was 56 + STRING = 57 # was 58 (NORMALSTRING) + NORMALSTRING = 57 + CHARSTRING = 58 + LONGSTRING = 59 + NUMBER = 60 # was 57 (INT, most common) + INT = 60 + HEX = 61 + FLOAT = 62 + HEX_FLOAT = 63 + COMMENT = 64 # was 59 + LINE_COMMENT = 65 # was 60 + SPACE = 66 # was 61 (WS) + NEWLINE = 67 # was 62 (NL) + SHEBANG = 68 # was 63 + ASSIGN = 2 # was 37, same as EQ (antlr4 single =) cdef struct ParseFieldResult: @@ -175,6 +182,7 @@ cdef class IndentProcessor: cdef unordered_set[int] MULT_OP cdef unordered_set[int] BITWISE_OP cdef unordered_set[int] ATOM_OP + cdef unordered_set[int] STRING_TYPES cdef unordered_set[int] COMMA_SEMCOL cdef inline void inc_level(self, int n=1) diff --git a/luastyle/indenter.pyx b/luastyle/indenter.pyx index 8c05027..bac97cd 100644 --- a/luastyle/indenter.pyx +++ b/luastyle/indenter.pyx @@ -152,7 +152,14 @@ cdef class IndentProcessor: self.ATOM_OP.insert(CTokens.VARARGS) self.ATOM_OP.insert(CTokens.NUMBER) + self.ATOM_OP.insert(CTokens.INT) + self.ATOM_OP.insert(CTokens.HEX) + self.ATOM_OP.insert(CTokens.FLOAT) + self.ATOM_OP.insert(CTokens.HEX_FLOAT) self.ATOM_OP.insert(CTokens.STRING) + self.ATOM_OP.insert(CTokens.NORMALSTRING) + self.ATOM_OP.insert(CTokens.CHARSTRING) + self.ATOM_OP.insert(CTokens.LONGSTRING) self.ATOM_OP.insert(CTokens.NIL) self.ATOM_OP.insert(CTokens.TRUE) self.ATOM_OP.insert(CTokens.FALSE) @@ -160,6 +167,10 @@ cdef class IndentProcessor: self.COMMA_SEMCOL.insert(CTokens.COMMA) self.COMMA_SEMCOL.insert(CTokens.SEMCOL) + self.STRING_TYPES.insert(CTokens.NORMALSTRING) + self.STRING_TYPES.insert(CTokens.CHARSTRING) + self.STRING_TYPES.insert(CTokens.LONGSTRING) + # init indentation token self._indentation_token.type = -2 # indentation token @@ -782,7 +793,7 @@ cdef class IndentProcessor: self.failure_save() if self.next_is_rc(CTokens.COL) and self.next_is_rc(CTokens.NAME): result.last_line = self._line_count - if self.next_is_rc(CTokens.STRING, False): + if self.next_is_rc(CTokens.STRING, False) or self.next_is_rc(CTokens.CHARSTRING, False) or self.next_is_rc(CTokens.LONGSTRING, False): self.success() return result @@ -808,7 +819,7 @@ cdef class IndentProcessor: return result self.failure_save() - if self.next_is_rc(CTokens.STRING, False): + if self.next_is_rc(CTokens.STRING, False) or self.next_is_rc(CTokens.CHARSTRING, False) or self.next_is_rc(CTokens.LONGSTRING, False): result.is_chainable = False self.success() return result From 3fe066dc6b0aafc0cb0ce89b52d2f6c590c19a04 Mon Sep 17 00:00:00 2001 From: boolangery Date: Mon, 20 Jul 2026 12:37:27 +0200 Subject: [PATCH 4/6] fix: correct CTokens to match actual antlr4 runtime token types Several LuaLexer constant names don't match their runtime values: - {/} are types 45/46 (not OB=47/CB=48) - [/] are types 47/48 (not OCU=45/CCU=46) - ~= is type 55 (SQEQ, not EE=49) - == is type 49 (EE, single token in antlr4) - = is type 2 (EQ, both assignment and equality char) --- luastyle/indenter.pxd | 68 ++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/luastyle/indenter.pxd b/luastyle/indenter.pxd index 46bf416..3511dc0 100644 --- a/luastyle/indenter.pxd +++ b/luastyle/indenter.pxd @@ -63,8 +63,10 @@ cdef enum Expr: cdef enum CTokens: # Updated for luaparser >=4.0 / antlr4 LuaLexer token types # See: luaparser.parser.LuaLexer - SEMCOL = 1 # was 55 (SEMI) - EQ = 2 # was 31; also ASSIGN (antlr4: single = token) + # Updated for luaparser >=4.0 — actual antlr4 token type values + # (NOT the LuaLexer constant names, which differ from runtime types) + SEMCOL = 1 # was 55; actual: SEMI + ASSIGN = 2 # was 37; actual: = (EQ constant) BREAK = 3 # was 2 GOTO = 4 # was 10 DO = 5 # was 3 @@ -72,10 +74,10 @@ cdef enum CTokens: WHILE = 7 # was 22 REPEAT = 8 # was 17 UNTIL = 9 # was 21 - IFTOK = 10 # was 11 (IF) + IFTOK = 10 # was 11; actual: IF THEN = 11 # was 19 ELSEIF = 12 # was 5 - ELSETOK = 13 # was 4 (ELSE) + ELSETOK = 13 # was 4; actual: ELSE FOR = 14 # was 8 COMMA = 15 # was 51 IN = 16 # was 12 @@ -84,55 +86,55 @@ cdef enum CTokens: LT = 19 # was 35 GT = 20 # was 36 RETURN = 21 # was 18 - COLCOL = 22 # was 49 (CC = ::) + COLCOL = 22 # was 49; actual: :: (CC constant) NIL = 23 # was 14 FALSE = 24 # was 7 TRUE = 25 # was 20 DOT = 26 # was 54 - BITNOT = 27 # was 40 (SQUIG = ~) + BITNOT = 27 # was 40; actual: ~ (SQUIG constant) MINUS = 28 # was 24 - LENGTH = 29 # was 30 (POUND = #) - OPAR = 30 # was 43 (OP = () - CPAR = 31 # was 44 (CP = )) + LENGTH = 29 # was 30; actual: # (POUND constant) + OPAR = 30 # was 43; actual: ( (OP constant) + CPAR = 31 # was 44; actual: ) (CP constant) NOT = 32 # was 15 - BITRLEFT = 33 # was 42 (LL = <<) - BITRSHIFT = 34 # was 41 (GG = >>) - BITAND = 35 # was 38 (AMP = &) - FLOOR = 36 # was 27 (SS = //) - MOD = 37 # was 28 (PER = %) + BITRLEFT = 33 # was 42; actual: << (LL constant) + BITRSHIFT = 34 # was 41; actual: >> (GG constant) + BITAND = 35 # was 38; actual: & (AMP constant) + FLOOR = 36 # was 27; actual: // (SS constant) + MOD = 37 # was 28; actual: % (PER constant) COL = 38 # was 50 - LTEQ = 39 # was 33 (LE = <=) - GTEQ = 40 # was 34 (GE = >=) + LTEQ = 39 # was 33; actual: <= (LE constant) + GTEQ = 40 # was 34; actual: >= (GE constant) AND = 41 # was 1 OR = 42 # was 16 - ADD = 43 # was 23 (PLUS = +) - MULT = 44 # was 25 (STAR = *) - OBRACK = 45 # was 47 (OCU = [) - CBRACK = 46 # was 48 (CCU = ]) - OBRACE = 47 # was 45 (OB = {) - CBRACE = 48 # was 46 (CB = }) - NEQ = 49 # was 32 (EE = ~=) - CONCAT = 50 # was 53 (DD = ..) - BITOR = 51 # was 39 (PIPE = |) - POW = 52 # was 29 (CARET = ^) - DIV = 53 # was 26 (SLASH = /) - VARARGS = 54 # was 52 (DDD = ...) + ADD = 43 # was 23; actual: + (PLUS constant) + MULT = 44 # was 25; actual: * (STAR constant) + OBRACE = 45 # was 45; actual: { (runtime type, NOT OB constant=47) + CBRACE = 46 # was 46; actual: } (runtime type, NOT CB constant=48) + OBRACK = 47 # was 47; actual: [ (runtime type, NOT OCU constant=45) + CBRACK = 48 # was 48; actual: ] (runtime type, NOT CCU constant=46) + EQ = 49 # was 31; actual: == (EE constant, single token in antlr4) + CONCAT = 50 # was 53; actual: .. (DD constant) + BITOR = 51 # was 39; actual: | (PIPE constant) + POW = 52 # was 29; actual: ^ (CARET constant) + DIV = 53 # was 26; actual: / (SLASH constant) + VARARGS = 54 # was 52; actual: ... (DDD constant) + NEQ = 55 # was 32; actual: ~= (SQEQ constant, runtime type) NAME = 56 # was 56 - STRING = 57 # was 58 (NORMALSTRING) + STRING = 57 # was 58; actual: NORMALSTRING NORMALSTRING = 57 CHARSTRING = 58 LONGSTRING = 59 - NUMBER = 60 # was 57 (INT, most common) + NUMBER = 60 # was 57; actual: INT INT = 60 HEX = 61 FLOAT = 62 HEX_FLOAT = 63 COMMENT = 64 # was 59 LINE_COMMENT = 65 # was 60 - SPACE = 66 # was 61 (WS) - NEWLINE = 67 # was 62 (NL) + SPACE = 66 # was 61; actual: WS + NEWLINE = 67 # was 62; actual: NL SHEBANG = 68 # was 63 - ASSIGN = 2 # was 37, same as EQ (antlr4 single =) cdef struct ParseFieldResult: From 1ad599d6969e672a888cb69916008d307864ad5e Mon Sep 17 00:00:00 2001 From: boolangery Date: Mon, 20 Jul 2026 12:40:19 +0200 Subject: [PATCH 5/6] fix: strip indentation from empty lines in handle_hidden_right In antlr4 lexer, SPACE tokens are separate from NL tokens. The render() function pushes indentation after each newline, but empty lines (consecutive newlines) should not have indent spaces. Pop the intermediate indent before rendering the second newline. --- luastyle/indenter.pyx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/luastyle/indenter.pyx b/luastyle/indenter.pyx index bac97cd..5d554ed 100644 --- a/luastyle/indenter.pyx +++ b/luastyle/indenter.pyx @@ -536,6 +536,14 @@ cdef class IndentProcessor: for t in tokens: # TODO: replace with a map if t.type == CTokens.NEWLINE: + # Strip indentation from previous empty line + # (antlr4 produces separate SPACE tokens before NL, + # but the render function pushes indent after each NL. + # For consecutive newlines, remove the intermediate indent.) + if self._src.size() >= 2 and self._src.back().type == -2: + prev = self._src[self._src.size() - 2] + if prev.type == CTokens.NEWLINE: + self._src.pop_back() # remove indent on empty line token.type = t.type token.text = t.text.encode('UTF-8') self.render(token) From a82bd2b63cc584bb77e0a22f416764afa6ec8146 Mon Sep 17 00:00:00 2001 From: boolangery Date: Mon, 20 Jul 2026 12:42:02 +0200 Subject: [PATCH 6/6] fix: strip indent after render for empty lines The render() function pushes indentation after each newline, but empty lines (consecutive newlines) should be indent-free. Check after render() whether indent was injected between two newlines and remove it. --- luastyle/indenter.pyx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/luastyle/indenter.pyx b/luastyle/indenter.pyx index 5d554ed..4debb32 100644 --- a/luastyle/indenter.pyx +++ b/luastyle/indenter.pyx @@ -536,17 +536,19 @@ cdef class IndentProcessor: for t in tokens: # TODO: replace with a map if t.type == CTokens.NEWLINE: - # Strip indentation from previous empty line - # (antlr4 produces separate SPACE tokens before NL, - # but the render function pushes indent after each NL. - # For consecutive newlines, remove the intermediate indent.) - if self._src.size() >= 2 and self._src.back().type == -2: - prev = self._src[self._src.size() - 2] - if prev.type == CTokens.NEWLINE: - self._src.pop_back() # remove indent on empty line token.type = t.type token.text = t.text.encode('UTF-8') self.render(token) + # render() pushes indentation after each newline. + # For consecutive newlines, remove the intermediate indent + # so empty lines don't carry over indentation. + if self._src.size() >= 3: + if (self._src.back().type == CTokens.NEWLINE and + self._src[self._src.size() - 2].type == -2 and + self._src[self._src.size() - 3].type == CTokens.NEWLINE): + self._src.pop_back() # remove NL + self._src.pop_back() # remove INDENT + self._src.push_back(token) # re-push NL is_newline = True elif t.type == CTokens.SPACE: if not is_newline: