Moximxxx ยท GitHub

Environment

  • TeXstudio: 4.9.6 (the faulty code is also present on current master)
  • Qt: any (platform-independent logic bug)
  • OS: Windows, Linux (all platforms affected)
  • TeX distribution: any (not distribution-related)

Expected behavior

"Remove Column" removes the column and the application stays responsive.

Actual behavior

The application freezes permanently (infinite loop on the UI thread) and has to be killed. The same freeze happens with "Cut Column", which shares the same code path.

How to reproduce

  1. Create a new document:
\documentclass{article}
\begin{document}
\begin{tabular}{|cll|c|}
\hline
& & & \\ \hline
& & & \\ \hline
\end{tabular}
\end{document}
  1. Place the cursor at the end of the last \hline line, click "Add Row" (toolbar)
  2. Click "Align Columns"
  3. Click "Remove Column" โ†’ the application freezes

Root cause analysis

Step 3 ("Align Columns") strips the trailing \\ from the last table row: in LatexTableModel::getAlignedLines() (src/tablemanipulation.cpp), the "smart removal of break at final line" treats a row consisting only of & separators and spaces as non-empty, so the last row loses its \\ terminator.

Step 4 then hits an infinite loop in LatexTables::removeColumn(), in its inner "move to next row" loop:

while(tkResult.length==1 || tkResult.length<0 /* first column*/){
    if(tkResult.length>0) findNextColumn(cur,tkResult);   // return value ignored!
    tkResult = findColumn(cur, env);
    ...
}

On the final row (no \\ terminator), after the last & is handled the cursor sits exactly on that token's start. findColumn() uses a strict tk.start < col comparison, so it returns the very same & under the cursor again (length==1, loop condition stays true). findNextColumn() returns false when it reaches \end{tabular}, but the return value is ignored, so no state ever advances โ†’ infinite loop on the UI thread.

addColumn() had the identical loop and was fixed in #4254 by checking the findNextColumn() return value; removeColumn() was missed and is still unfixed on master.

Suggested fix

Mirror the #4254 fix in removeColumn() (src/tablemanipulation.cpp):

while(tkResult.length==1 || tkResult.length<0 /* first column*/){
    if(tkResult.length>0){
        bool success=findNextColumn(cur,tkResult);
        if(!success){
            // end of tabular reached (e.g. final row without "\\")
            break;
        }
    }
    tkResult = findColumn(cur, env);
    ...
}

The inner loop is navigation-only (each row's column removal happens before it), so breaking early cannot skip any removal. After the break, the existing breakLoop=!findNextColumn(...) check exits the outer loop cleanly.

A regression test for TableManipulationTest::remCol_data() covering a final row without \\:

QTest::newRow("rem col, last row without linebreak")
    << "\\begin{tabular}{ll}\na&b\\\\\nc&d\\\\\ne&f\n\\end{tabular}\n"
    << 1 << 0
    << "\\begin{tabular}{l}\nb\\\\\nd\\\\\nf\n\\end{tabular}\n";

Read the original on github.com โ†—