Skip to content

Add table parsing support in EntitiesHelper and TableSpan class#339

Merged
risin42 merged 10 commits intorisin42:devfrom
andyching168:dev
Apr 18, 2026
Merged

Add table parsing support in EntitiesHelper and TableSpan class#339
risin42 merged 10 commits intorisin42:devfrom
andyching168:dev

Conversation

@andyching168
Copy link
Copy Markdown
Contributor

@andyching168 andyching168 commented Apr 14, 2026

This pull request introduces support for rendering GitHub Flavored Markdown (GFM) tables in messages, specifically when the "Neko" markdown parser is enabled. It does this by detecting table markdown blocks, parsing them into a structured format, and rendering them as custom table spans within the message layout. The implementation includes a new TableSpan class for drawing the tables, as well as changes to the markdown parsing pipeline to integrate table detection and rendering. Additionally, some color and measurement logic for table rendering has been improved for better theme adaptation.

Markdown Table Support

  • Added detection and parsing of GFM-style tables in message text using a regular expression (TABLE_BLOCK_PATTERN) and a new parseTables method in EntitiesHelper.java. This method replaces matched table markdown blocks with a zero-width space and applies a custom TableSpan for rendering. [1] [2] [3]
  • Integrated table parsing into the message layout generation pipeline, so that tables are only parsed and rendered if the "Neko" markdown parser is selected (MARKDOWN_PARSER_NEKO).
  • Added logging to assist with debugging table detection and rendering. [1] [2]

Custom Table Rendering

  • Introduced a new TableSpan class that extends ReplacementSpan to render parsed tables as styled blocks within the message. This includes custom drawing of cell backgrounds, borders, and text, with support for header row styling and theme-aware colors.
  • Improved table drawing and measurement logic for better appearance and theme adaptation (light/dark), including color adjustments and dynamic sizing, as seen in the patch_table.py script which updates the relevant methods in TableSpan.java.

Code Integration and Refactoring

  • Updated imports in affected files to support the new functionality and dependencies. [1] [2]

These changes collectively enable rich table rendering in messages, enhancing the readability and presentation of structured data in chat.

Summary by CodeRabbit

  • New Features

    • Rich rendering of Markdown tables in messages: formatted grids with header styling, borders, wrapped cell text and automatic column sizing.
  • Bug Fixes

    • More reliable span/link/animated-emoji detection and accessibility by basing hit-testing and link resolution on the rendered display text.
    • Preserves original editable message text (tables won’t alter outgoing/edit text); added control to disable table parsing where needed.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

Walkthrough

Adds GFM-style table detection and rendering: new TableSpan renders tables; EntitiesHelper gains table parsing and a parseMarkdown overload; MessageObject exposes getMessageDisplayText(); ChatMessageCell and MediaDataController use the new display/text flow.

Changes

Cohort / File(s) Summary
Message display API
TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
Added public CharSequence getMessageDisplayText(); returns null if messageText is null, otherwise returns EntitiesHelper.parseTables(messageText) when the Neko markdown parser is selected, else the original messageText. generateLayout(...) now uses this display text for StaticLayout creation.
Cell rendering & span handling
TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
Switched span/link extraction, motion-event handling, click and accessibility link resolution to read from currentMessageObject.getMessageDisplayText() (cast to Spanned when applicable). Animated-emoji spans continue to be read from raw messageText when it is Spanned.
Markdown/table parsing
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java
Added overload parseMarkdown(CharSequence[] message, boolean allowStrike, boolean parseTables) and made existing parseMarkdown(message, allowStrike) delegate to it. Added public static CharSequence parseTables(CharSequence text) and package-private static String[][] parseTableRows(String tableBlock) to detect GFM table blocks, parse rows, replace blocks with placeholders, and attach TableSpan for parsed tables. Unparsed table blocks are left unchanged.
Table rendering span
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java
Introduced new TableSpan (extends ReplacementSpan) that measures columns, creates per-cell StaticLayouts, enforces width constraints, computes table height, and draws background, header shading, borders/grid lines, and cell text.
Outgoing message entity parsing
TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java
When MARKDOWN_PARSER_NEKO is active, getEntities(...) now calls EntitiesHelper.parseMarkdown(message, allowStrike, false) to disable table parsing for outgoing/edit text handling.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Chat UI
    participant MsgObj as MessageObject
    participant Helper as EntitiesHelper
    participant Span as TableSpan
    participant Cell as ChatMessageCell

    UI->>MsgObj: request layout/render
    MsgObj->>MsgObj: getMessageDisplayText()
    alt Neko parser selected and messageText != null
        MsgObj->>Helper: parseTables(messageText)
        Helper->>Helper: match TABLE_BLOCK_PATTERN, parseTableRows()
        alt table parsed
            Helper->>Span: create TableSpan(rows)
            Helper-->>MsgObj: return CharSequence with TableSpan placeholders
        else no table parsed
            Helper-->>MsgObj: return original messageText
        end
    else parser not Neko or messageText null
        MsgObj-->>UI: return original messageText
    end
    UI->>Cell: render using returned displayText
    Cell->>Cell: extract spans/clickable links from displayText (if Spanned)
    Cell->>Span: measurement & draw invoked during layout/draw
    Span-->>Cell: rendered table content embedded in layout
    Cell-->>UI: final rendered message
Loading
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main additions to the codebase: table parsing support via EntitiesHelper and a new TableSpan class for rendering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d969812-ca0a-4f1e-939f-f44bfc981de8

📥 Commits

Reviewing files that changed from the base of the PR and between 6283ee4 and de9456f.

📒 Files selected for processing (5)
  • TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java
  • patch_entities.patch
  • patch_table.py

Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java (2)

24-26: ⚠️ Potential issue | 🟠 Major

Still misses valid GFM tables without outer pipes.

Line 25 still requires every row to start and end with |, so a | b / ---|--- tables never reach parseTableRows(...) even though Lines 178-179 already normalize optional outer pipes.


154-157: ⚠️ Potential issue | 🟠 Major

Preserve the block newlines around the placeholder.

Line 155 collapses the whole block to one inline placeholder. If the match consumed the trailing newline, the following paragraph can render on the same logical line as the table instead of below it.

Suggested fix
-            builder.replace(start, end, "\u200B");
+            builder.replace(start, end, "\n\u200B\n");
             TableSpan span = new TableSpan(parsed, originalMarkdown);
-            builder.setSpan(span, start, start + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            builder.setSpan(span, start + 1, start + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java (1)

72-80: ⚠️ Potential issue | 🟠 Major

Clamp computed column widths before measuring layouts.

When colCount is large, Line 75 can make avgWidth zero or negative, which collapses cell positions and overlaps the grid. At the same time Line 80 always reports the full maxTableWidth, so narrow tables reserve much more inline width than they use. Clamp each column width and derive mTableWidth from the actual total.

Suggested fix
-        int maxTableWidth = AndroidUtilities.displaySize.x - dp(100);
-        int availableSpace = maxTableWidth - dp(CELL_PADDING) * 2 * colCount - dp(BORDER_WIDTH) * (colCount + 1);
-
-        int avgWidth = availableSpace / colCount;
-        for (int i = 0; i < colCount; i++) {
-            columnWidths[i] = avgWidth;
-        }
-
-        mTableWidth = maxTableWidth;
+        int maxTableWidth = AndroidUtilities.displaySize.x - dp(100);
+        int padding = dp(CELL_PADDING);
+        int border = dp(BORDER_WIDTH);
+        int availableSpace = Math.max(0, maxTableWidth - padding * 2 * colCount - border * (colCount + 1));
+        int avgWidth = Math.max(dp(MIN_COLUMN_WIDTH), availableSpace / Math.max(1, colCount));
+        int totalWidth = border;
+        for (int i = 0; i < colCount; i++) {
+            columnWidths[i] = Math.min(dp(MAX_COLUMN_WIDTH), avgWidth);
+            totalWidth += columnWidths[i] + padding * 2 + border;
+        }
+
+        mTableWidth = Math.min(maxTableWidth, totalWidth);

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e3ff1b8d-f7d7-4f0e-ad2f-44fe1ad2dde9

📥 Commits

Reviewing files that changed from the base of the PR and between de9456f and 74c63d1.

📒 Files selected for processing (2)
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java

Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (2)
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java (2)

131-135: ⚠️ Potential issue | 🟠 Major

Skip table detection inside mono/code spans.

The pattern matcher scans raw text without checking whether matched regions overlap with existing TextStyleSpan.isMono() or CodeHighlighting.Span spans. A code block containing table-like content (e.g., a markdown tutorial) will be incorrectly replaced with a TableSpan.

Apply the same overlap check used in the PATTERNS[] loop (lines 60-78) before recording table matches.

Suggested approach
+    // Parse GFM tables and replace with TableSpan using placeholder char
+    public static CharSequence parseTables(CharSequence text) {
+        if (text == null) return text;
+        
+        Spannable spannable = text instanceof Spannable 
+            ? (Spannable) text 
+            : Spannable.Factory.getInstance().newSpannable(text);
+        
         // Collect positions manually first
         var positions = new ArrayList<int[]>();
         var m = TABLE_BLOCK_PATTERN.matcher(text);
-        while (m.find()) {
-            positions.add(new int[]{m.start(), m.end()});
+        find:
+        while (m.find()) {
+            int start = m.start();
+            int end = m.end();
+            
+            // Skip if overlapping with mono spans
+            var textStyleSpans = spannable.getSpans(start, end, TextStyleSpan.class);
+            for (var span : textStyleSpans) {
+                if (span.isMono()) {
+                    int spanStart = spannable.getSpanStart(span);
+                    int spanEnd = spannable.getSpanEnd(span);
+                    if (spanStart <= start && spanEnd >= end) {
+                        continue find;
+                    }
+                }
+            }
+            
+            // Skip if overlapping with code highlighting spans
+            var codeSpans = spannable.getSpans(start, end, CodeHighlighting.Span.class);
+            for (var span : codeSpans) {
+                int spanStart = spannable.getSpanStart(span);
+                int spanEnd = spannable.getSpanEnd(span);
+                if (spanStart <= start && spanEnd >= end) {
+                    continue find;
+                }
+            }
+            
+            positions.add(new int[]{start, end});
         }

179-181: ⚠️ Potential issue | 🟠 Major

Handle escaped pipes when splitting cells.

line.split("\\|") treats \| as a real column separator. Valid table content like a \| b will inflate the column count, and TableSpan will misalign or truncate that row.

Parse the line character-by-character or use a regex that respects escapes.

Suggested approach using regex negative lookbehind
-            String[] cells = line.split("\\|");
+            // Split on | but not on escaped \|
+            String[] cells = line.split("(?<!\\\\)\\|");
+            // Also unescape \| in each cell
             // Trim each cell
             for (int j = 0; j < cells.length; j++) {
-                cells[j] = cells[j].trim();
+                cells[j] = cells[j].trim().replace("\\|", "|");
             }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d1922511-8ab6-416b-a478-68c935545ba7

📥 Commits

Reviewing files that changed from the base of the PR and between 74c63d1 and d3a8b96.

📒 Files selected for processing (2)
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java

Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java Outdated
Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 71055b8a-66f7-427d-a6d8-057646c41221

📥 Commits

Reviewing files that changed from the base of the PR and between d3a8b96 and 240c56c.

📒 Files selected for processing (2)
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java

Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java (2)

128-131: ⚠️ Potential issue | 🟠 Major

Skip table detection inside code/mono spans before recording positions.

Line 128 currently matches raw text blocks without checking overlap with TextStyleSpan.isMono() / CodeHighlighting.Span. Code blocks containing table-like lines can still be converted into TableSpan.


173-176: ⚠️ Potential issue | 🟠 Major

Preserve escaped pipes when splitting table cells.

Line 175 uses line.split("\\|"), so escaped \| is treated as a real delimiter. This corrupts column counts and misaligns rendering.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5a5eb634-41a2-4bae-b8aa-8c836d101f23

📥 Commits

Reviewing files that changed from the base of the PR and between 240c56c and 9b823f5.

📒 Files selected for processing (1)
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java

Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java Outdated
@andyching168
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: df5e3cf4-6eef-4187-afcf-1c4f76f9be6e

📥 Commits

Reviewing files that changed from the base of the PR and between 240c56c and 4aeb70a.

📒 Files selected for processing (1)
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java

@risin42
Copy link
Copy Markdown
Owner

risin42 commented Apr 15, 2026

Thanks.

The current approach of using a zero width space placeholder breaks the raw message text.
This means functions like copy, edit, or share won't work as expected. Sent tables would also just show up as an empty space.

There is also some unfinished/unused code and vibe leftovers that need cleaning up.

If the goal is just to render Markdown tables from AI bots, sure, it visually works, but the blast radius is too large.

I appreciate the effort and I don't mind AI use, but this PR needs more work before it can be merged. I need to keep the codebase maintainable but I can't take on maintenance of code in this state.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1ecd778f-aa37-48b0-8c0d-9039d15d9bc4

📥 Commits

Reviewing files that changed from the base of the PR and between 4aeb70a and 8aaeccc.

📒 Files selected for processing (4)
  • TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java
  • TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
  • TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java

Comment thread TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java Outdated
Comment thread TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java Outdated
Comment thread TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java (1)

2356-2399: ⚠️ Potential issue | 🔴 Critical

Links inside table cells are not clickable and lack accessibility support.

When tables are parsed via parseTables(), the entire table markdown is replaced with a zero-width placeholder and rendered as a TableSpan. This conversion discards all original spans (links, mentions, etc.) embedded in cell content, converting them to plain text strings. TableSpan is a pure rendering span with no touch event handling, click detection, or accessibility implementation—meaning ChatMessageCell's getSpans() lookup cannot find interactive elements that no longer exist in the buffer, and table cells remain inaccessible to screen readers.

To fix this, preserve ClickableSpans in cell content during parsing, or add hit-testing and accessibility support to TableSpan to expose and handle cell interactions.

Applies to lines 2356–2399, 27136–27270, 27676–27681.

♻️ Duplicate comments (2)
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java (1)

141-166: ⚠️ Potential issue | 🟠 Major

Skip code/mono blocks on exact boundary matches too.

The current check only skips when an existing CodeHighlighting.Span/mono TextStyleSpan extends past the candidate range. If the table-looking text is exactly the contents of a code span, skip stays false and literal code is still converted into a table. Use inclusive containment here (spanStart <= s && spanEnd >= e), or mirror the boundary logic from parseMarkdown(...).

TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java (1)

84-97: ⚠️ Potential issue | 🟠 Major

Re-fit after the per-column minimum clamp.

After scaling, each column is clamped back up to minColumnPx / 2, but the code never checks whether that pushed the sum over availableSpace again. On wide tables, mTableWidth can still exceed the intended max and overrun the bubble. Add a second fit pass or a hard fallback once the clamped widths no longer fit.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8f8ac64e-d2cc-4a27-bd67-3e32735b70a8

📥 Commits

Reviewing files that changed from the base of the PR and between 8aaeccc and 0dd4c6c.

📒 Files selected for processing (4)
  • TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java
  • TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/EntitiesHelper.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java

Comment thread TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java Outdated
Comment thread TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java (1)

81-87: ⚠️ Potential issue | 🟠 Major

Post-shrink width can still overflow available space.

At Line 85, enforcing minColumnPx / 2 after ratio shrink can push total content width back above availableSpace for wide tables (many columns). That still causes overflow/clipping risk.

Proposed fix
-        if (totalContentWidth > availableSpace) {
+        int minShrinkPx = Math.max(1, minColumnPx / 2);
+        if (totalContentWidth > availableSpace) {
             float ratio = (float) availableSpace / totalContentWidth;
             for (int c = 0; c < colCount; c++) {
                 columnWidths[c] = (int) (columnWidths[c] * ratio);
-                columnWidths[c] = Math.max(minColumnPx / 2, columnWidths[c]);
+                columnWidths[c] = Math.max(minShrinkPx, columnWidths[c]);
             }
         }
+
+        int adjustedContentWidth = 0;
+        for (int c = 0; c < colCount; c++) {
+            adjustedContentWidth += columnWidths[c];
+        }
+        if (adjustedContentWidth > availableSpace) {
+            float ratio = (float) availableSpace / adjustedContentWidth;
+            adjustedContentWidth = 0;
+            for (int c = 0; c < colCount; c++) {
+                columnWidths[c] = Math.max(1, (int) (columnWidths[c] * ratio));
+                adjustedContentWidth += columnWidths[c];
+            }
+        }
@@
-        mTableWidth = decorationsWidth;
-        for (int w : columnWidths) {
-            mTableWidth += w;
-        }
+        mTableWidth = Math.min(maxTableWidth, decorationsWidth + adjustedContentWidth);

Also applies to: 89-93


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0b3f0afe-d111-4e44-9b0b-1bf35298483f

📥 Commits

Reviewing files that changed from the base of the PR and between 0dd4c6c and be13cbc.

📒 Files selected for processing (2)
  • TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4fd2ed3e-ac3f-4adb-b2ee-b5b23af150c0

📥 Commits

Reviewing files that changed from the base of the PR and between be13cbc and 0ed4533.

📒 Files selected for processing (1)
  • TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/TableSpan.java

@andyching168
Copy link
Copy Markdown
Contributor Author

Hi, thanks for the candid review, and sorry for the churn/noise in the earlier revisions.

You were right about the blast-radius concerns. I went through another cleanup pass and addressed the core maintainability/data-integrity issues you called out:

  1. Raw text integrity is preserved
  • Table replacement is now display-only.
  • Outgoing/edit entity parsing explicitly disables table parsing, so send/edit/copy/share use original text and are no longer affected by placeholders.
  1. Stale display cache issue removed
  • Replaced the mutable messageDisplayText field with a derived getter from messageText, so no stale/null cache state between mutations and layout passes.
  1. Span/interaction correctness fixes
  • Widened guards from Spannable to Spanned where appropriate.
  • Fixed sponsored description link span source mismatch (layout text and span lookup now use the same backing source).
  1. Table rendering stability fixes
  • Removed the mFirstTop draw guard that could cause disappearing tables after relayout.
  • Added re-measure conditions for display width/text size changes.
  • Added post-shrink width normalization to avoid overflow/clipping in wide tables.
  1. Cleanup
  • Removed leftover patch artifact and unfinished dead paths from this change set.

I know the earlier revisions were not at merge quality; thanks for pushing on that.

@risin42 risin42 merged commit 28862c5 into risin42:dev Apr 18, 2026
1 check passed
@risin42
Copy link
Copy Markdown
Owner

risin42 commented Apr 18, 2026

de639f5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants