diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b2e036..7839225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Adjust History icon and reorder toolbar items * Try to address checkbox/bullet size issues (again) * Fix HTML export of markdown (with newlines, tables and other styling preserved) + * Remove table tool # 0.5.1 diff --git a/bouquin/locales/en.json b/bouquin/locales/en.json index d8b428d..eb5dd83 100644 --- a/bouquin/locales/en.json +++ b/bouquin/locales/en.json @@ -50,7 +50,6 @@ "backup_failed": "Backup failed", "quit": "Quit", "cancel": "Cancel", - "ok": "OK", "save": "Save", "help": "Help", "saved": "Saved", @@ -266,8 +265,6 @@ "cut": "Cut", "copy": "Copy", "paste": "Paste", - "edit_table": "Edit table", - "toolbar_insert_table": "Insert table", "start": "Start", "pause": "Pause", "resume": "Resume", @@ -292,10 +289,5 @@ "friday": "Friday", "saturday": "Saturday", "sunday": "Sunday", - "day": "Day", - "add_row": "Add row", - "add_column": "Add column", - "delete_row": "Delete row", - "delete_column": "Delete column", - "column": "Column" + "day": "Day" } diff --git a/bouquin/locales/fr.json b/bouquin/locales/fr.json index 8dc0b48..3ba5ba6 100644 --- a/bouquin/locales/fr.json +++ b/bouquin/locales/fr.json @@ -50,7 +50,6 @@ "backup_failed": "Échec de la sauvegarde", "quit": "Quitter", "cancel": "Annuler", - "ok": "OK", "save": "Enregistrer", "help": "Aide", "saved": "Enregistré", @@ -265,8 +264,6 @@ "cut": "Couper", "copy": "Copier", "paste": "Coller", - "edit_table": "Modifier le tableau", - "toolbar_insert_table": "Insérer un tableau", "start": "Démarrer", "pause": "Pause", "resume": "Reprendre", @@ -289,10 +286,5 @@ "friday": "Vendredi", "saturday": "Samedi", "sunday": "Dimanche", - "day": "Jour", - "add_row": "Ajouter une ligne", - "add_column": "Ajouter une colonne", - "delete_row": "Supprimer la ligne", - "delete_column": "Supprimer la colonne", - "column": "Colonne" + "day": "Jour" } diff --git a/bouquin/locales/it.json b/bouquin/locales/it.json index 6e33083..6be0955 100644 --- a/bouquin/locales/it.json +++ b/bouquin/locales/it.json @@ -40,7 +40,6 @@ "today": "Oggi", "show": "Mostra", "history": "Cronologia", - "view_history": "Visualizza cronologia", "export_accessible_flag": "&Esporta", "export_entries": "Esporta voci", "export_complete": "Esportazione completata", @@ -128,7 +127,6 @@ "check_for_updates": "Controlla aggiornamenti", "close": "Chiudi", "send": "Invia", - "yes": "Sì", "time_log": "Registro Attività", "time_log_no_entries": "Nessuna Attività", "close_tab": "Chiudi scheda", diff --git a/bouquin/main_window.py b/bouquin/main_window.py index 480d0f8..0e5e454 100644 --- a/bouquin/main_window.py +++ b/bouquin/main_window.py @@ -1103,7 +1103,6 @@ class MainWindow(QMainWindow): self._tb_checkboxes = lambda: self._call_editor("toggle_checkboxes") self._tb_alarm = self._on_alarm_requested self._tb_timer = self._on_timer_requested - self._tb_table = self._on_table_requested self._tb_font_larger = self._on_font_larger_requested self._tb_font_smaller = self._on_font_smaller_requested @@ -1117,7 +1116,6 @@ class MainWindow(QMainWindow): tb.checkboxesRequested.connect(self._tb_checkboxes) tb.alarmRequested.connect(self._tb_alarm) tb.timerRequested.connect(self._tb_timer) - tb.tableRequested.connect(self._tb_table) tb.insertImageRequested.connect(self._on_insert_image) tb.historyRequested.connect(self._open_history) tb.fontSizeLargerRequested.connect(self._tb_font_larger) @@ -1333,36 +1331,6 @@ class MainWindow(QMainWindow): timer.start(msecs) self._reminder_timers.append(timer) - # ----------- Table handler ------------# - def _on_table_requested(self): - """Insert a basic markdown table template.""" - editor = getattr(self, "editor", None) - if editor is None: - return - - # Basic 3x3 table template - table_template = """| Column 1 | Column 2 | Column 3 | -| --- | --- | --- | -| Cell 1 | Cell 2 | Cell 3 | -| Cell 4 | Cell 5 | Cell 6 | -""" - - cursor = editor.textCursor() - cursor.insertText(table_template) - - # Move cursor to first cell for easy editing - # Find the start of "Column 1" text - cursor.movePosition( - QTextCursor.Left, QTextCursor.MoveAnchor, len(table_template) - ) - cursor.movePosition(QTextCursor.Right, QTextCursor.MoveAnchor, 2) # After "| " - cursor.movePosition( - QTextCursor.Right, QTextCursor.KeepAnchor, 8 - ) # Select "Column 1" - editor.setTextCursor(cursor) - - editor.setFocus() - # ----------- History handler ------------# def _open_history(self): if hasattr(self.editor, "current_date"): diff --git a/bouquin/markdown_editor.py b/bouquin/markdown_editor.py index ac8a849..9f48858 100644 --- a/bouquin/markdown_editor.py +++ b/bouquin/markdown_editor.py @@ -1250,23 +1250,6 @@ class MarkdownEditor(QTextEdit): menu = QMenu(self) cursor = self.cursorForPosition(event.pos()) - # Check if we're in a table - text = self.toPlainText() - cursor_pos = cursor.position() - - from .table_editor import find_table_at_cursor - - table_info = find_table_at_cursor(text, cursor_pos) - - if table_info: - # Add table editing action - edit_table_action = QAction(strings._("edit_table"), self) - edit_table_action.triggered.connect( - lambda: self._edit_table_at_cursor(cursor_pos) - ) - menu.addAction(edit_table_action) - menu.addSeparator() - # Check if we're in a code block block = cursor.block() if self._is_inside_code_block(block): @@ -1302,30 +1285,6 @@ class MarkdownEditor(QTextEdit): menu.exec(event.globalPos()) - def _edit_table_at_cursor(self, cursor_pos: int): - """Open table editor dialog for the table at cursor position.""" - from .table_editor import find_table_at_cursor, TableEditorDialog - from PySide6.QtWidgets import QDialog - - text = self.toPlainText() - table_info = find_table_at_cursor(text, cursor_pos) - - if not table_info: - return - - start_pos, end_pos, table_text = table_info - - # Open table editor - dlg = TableEditorDialog(table_text, self) - if dlg.exec() == QDialog.Accepted: - # Replace the table with edited version - new_table = dlg.get_markdown_table() - - cursor = QTextCursor(self.document()) - cursor.setPosition(start_pos) - cursor.setPosition(end_pos, QTextCursor.KeepAnchor) - cursor.insertText(new_table) - def _set_code_block_language(self, block, language: str): """Set the language for a code block and store metadata.""" if not hasattr(self, "_code_metadata"): diff --git a/bouquin/table_editor.py b/bouquin/table_editor.py deleted file mode 100644 index 100b738..0000000 --- a/bouquin/table_editor.py +++ /dev/null @@ -1,255 +0,0 @@ -from __future__ import annotations - -import re -from typing import Optional - -from PySide6.QtCore import Slot -from PySide6.QtWidgets import ( - QDialog, - QVBoxLayout, - QHBoxLayout, - QTableWidget, - QTableWidgetItem, - QPushButton, - QHeaderView, - QWidget, -) - -from . import strings - - -class TableEditorDialog(QDialog): - """Dialog for editing markdown tables visually.""" - - def __init__(self, table_text: str, parent: Optional[QWidget] = None): - super().__init__(parent) - self.setWindowTitle(strings._("edit_table")) - self.setMinimumSize(600, 400) - - layout = QVBoxLayout(self) - - # Parse the table - self.table_widget = QTableWidget() - self._parse_table(table_text) - - # Allow editing - self.table_widget.horizontalHeader().setSectionResizeMode( - QHeaderView.Interactive - ) - layout.addWidget(self.table_widget) - - # Buttons for table operations - btn_layout = QHBoxLayout() - - add_row_btn = QPushButton(strings._("add_row")) - add_row_btn.clicked.connect(self._add_row) - btn_layout.addWidget(add_row_btn) - - add_col_btn = QPushButton(strings._("add_column")) - add_col_btn.clicked.connect(self._add_column) - btn_layout.addWidget(add_col_btn) - - del_row_btn = QPushButton(strings._("delete_row")) - del_row_btn.clicked.connect(self._delete_row) - btn_layout.addWidget(del_row_btn) - - del_col_btn = QPushButton(strings._("delete_column")) - del_col_btn.clicked.connect(self._delete_column) - btn_layout.addWidget(del_col_btn) - - layout.addLayout(btn_layout) - - # OK/Cancel buttons - btn_layout2 = QHBoxLayout() - btn_layout2.addStretch() - - ok_btn = QPushButton(strings._("ok")) - ok_btn.clicked.connect(self.accept) - ok_btn.setDefault(True) - btn_layout2.addWidget(ok_btn) - - cancel_btn = QPushButton(strings._("cancel")) - cancel_btn.clicked.connect(self.reject) - btn_layout2.addWidget(cancel_btn) - - layout.addLayout(btn_layout2) - - def _parse_table(self, text: str): - """Parse markdown table into QTableWidget.""" - lines = [line.strip() for line in text.split("\n") if line.strip()] - - if len(lines) < 1: - return - - # Parse header - header_line = lines[0] - # Split by | and remove first/last empty strings from leading/trailing pipes - header_parts = header_line.split("|") - if len(header_parts) > 0 and not header_parts[0].strip(): - header_parts = header_parts[1:] - if len(header_parts) > 0 and not header_parts[-1].strip(): - header_parts = header_parts[:-1] - headers = [cell.strip() for cell in header_parts] - - # Check if line[1] is a separator line (contains ---) - # If not, treat all lines after header as data - start_data_idx = 1 - if len(lines) > 1: - separator_check = lines[1] - # Split by | and remove first/last empty strings - sep_parts = separator_check.split("|") - if len(sep_parts) > 0 and not sep_parts[0].strip(): - sep_parts = sep_parts[1:] - if len(sep_parts) > 0 and not sep_parts[-1].strip(): - sep_parts = sep_parts[:-1] - cells = [cell.strip() for cell in sep_parts] - # Check if this looks like a separator (contains --- or :--: etc) - if cells and all(re.match(r"^:?-+:?$", cell) for cell in cells): - start_data_idx = 2 # Skip separator line - - # Parse data rows - data_rows = [] - for line in lines[start_data_idx:]: - # Split by | and remove first/last empty strings from leading/trailing pipes - parts = line.split("|") - if len(parts) > 0 and not parts[0].strip(): - parts = parts[1:] - if len(parts) > 0 and not parts[-1].strip(): - parts = parts[:-1] - cells = [cell.strip() for cell in parts] - data_rows.append(cells) - - # Set up table - self.table_widget.setColumnCount(len(headers)) - self.table_widget.setHorizontalHeaderLabels(headers) - self.table_widget.setRowCount(len(data_rows)) - - # Populate cells - for row_idx, row_data in enumerate(data_rows): - for col_idx, cell_text in enumerate(row_data): - if col_idx < len(headers): - item = QTableWidgetItem(cell_text) - self.table_widget.setItem(row_idx, col_idx, item) - - @Slot() - def _add_row(self): - """Add a new row to the table.""" - row_count = self.table_widget.rowCount() - self.table_widget.insertRow(row_count) - - # Add empty items - for col in range(self.table_widget.columnCount()): - self.table_widget.setItem(row_count, col, QTableWidgetItem("")) - - @Slot() - def _add_column(self): - """Add a new column to the table.""" - col_count = self.table_widget.columnCount() - self.table_widget.insertColumn(col_count) - self.table_widget.setHorizontalHeaderItem( - col_count, QTableWidgetItem(strings._("column") + f"{col_count + 1}") - ) - - # Add empty items - for row in range(self.table_widget.rowCount()): - self.table_widget.setItem(row, col_count, QTableWidgetItem("")) - - @Slot() - def _delete_row(self): - """Delete the currently selected row.""" - current_row = self.table_widget.currentRow() - if current_row >= 0: - self.table_widget.removeRow(current_row) - - @Slot() - def _delete_column(self): - """Delete the currently selected column.""" - current_col = self.table_widget.currentColumn() - if current_col >= 0: - self.table_widget.removeColumn(current_col) - - def get_markdown_table(self) -> str: - """Convert the table back to markdown format.""" - if self.table_widget.rowCount() == 0 or self.table_widget.columnCount() == 0: - return "" - - lines = [] - - # Header - headers = [] - for col in range(self.table_widget.columnCount()): - header_item = self.table_widget.horizontalHeaderItem(col) - headers.append( - header_item.text() - if header_item - else strings._("column") + f"{col + 1}" - ) - lines.append("| " + " | ".join(headers) + " |") - - # Separator - lines.append("| " + " | ".join(["---"] * len(headers)) + " |") - - # Data rows - for row in range(self.table_widget.rowCount()): - cells = [] - for col in range(self.table_widget.columnCount()): - item = self.table_widget.item(row, col) - cells.append(item.text() if item else "") - lines.append("| " + " | ".join(cells) + " |") - - return "\n".join(lines) - - -def find_table_at_cursor(text: str, cursor_pos: int) -> Optional[tuple[int, int, str]]: - """ - Find a markdown table containing the cursor position. - Returns (start_pos, end_pos, table_text) or None. - """ - lines = text.split("\n") - - # Find which line the cursor is on - current_pos = 0 - cursor_line_idx = 0 - for i, line in enumerate(lines): - if current_pos + len(line) >= cursor_pos: - cursor_line_idx = i - break - current_pos += len(line) + 1 # +1 for newline - - # Check if cursor line is part of a table - if not _is_table_line(lines[cursor_line_idx]): - return None - - # Find table start - start_idx = cursor_line_idx - while start_idx > 0 and _is_table_line(lines[start_idx - 1]): - start_idx -= 1 - - # Find table end - end_idx = cursor_line_idx - while end_idx < len(lines) - 1 and _is_table_line(lines[end_idx + 1]): - end_idx += 1 - - # Extract table text - table_lines = lines[start_idx : end_idx + 1] - table_text = "\n".join(table_lines) - - # Calculate character positions - start_pos = sum(len(lines[i]) + 1 for i in range(start_idx)) - end_pos = start_pos + len(table_text) - - return (start_pos, end_pos, table_text) - - -def _is_table_line(line: str) -> bool: - """Check if a line is part of a markdown table.""" - stripped = line.strip() - if not stripped: - return False - - # Table lines start and end with | - if not (stripped.startswith("|") and stripped.endswith("|")): - return False - - # Must have at least one | in between - return stripped.count("|") >= 2 diff --git a/bouquin/toolbar.py b/bouquin/toolbar.py index c4274a4..8873ffd 100644 --- a/bouquin/toolbar.py +++ b/bouquin/toolbar.py @@ -20,7 +20,6 @@ class ToolBar(QToolBar): insertImageRequested = Signal() alarmRequested = Signal() timerRequested = Signal() - tableRequested = Signal() fontSizeLargerRequested = Signal() fontSizeSmallerRequested = Signal() @@ -121,11 +120,6 @@ class ToolBar(QToolBar): self.actTimer.setToolTip(strings._("toolbar_pomodoro_timer")) self.actTimer.triggered.connect(self.timerRequested) - # Table - self.actTable = QAction("⊞", self) - self.actTable.setToolTip(strings._("toolbar_insert_table")) - self.actTable.triggered.connect(self.tableRequested) - # Set exclusive buttons in QActionGroups self.grpHeadings = QActionGroup(self) self.grpHeadings.setExclusive(True) @@ -162,7 +156,6 @@ class ToolBar(QToolBar): self.actBullets, self.actNumbers, self.actCheckboxes, - self.actTable, self.actInsertImg, self.actAlarm, self.actTimer, @@ -192,7 +185,6 @@ class ToolBar(QToolBar): self._style_letter_button(self.actCheckboxes, "☐") self._style_letter_button(self.actAlarm, "⏰") self._style_letter_button(self.actTimer, "⌛") - self._style_letter_button(self.actTable, "⊞") # History self._style_letter_button(self.actHistory, "🔁") diff --git a/tests/test_main_window.py b/tests/test_main_window.py index 6b0d6a5..bfe0972 100644 --- a/tests/test_main_window.py +++ b/tests/test_main_window.py @@ -2244,44 +2244,6 @@ def test_close_current_tab(qtbot, app, tmp_db_cfg, fresh_db): assert window.tab_widget.count() == initial_count - 1 -def test_table_insertion(qtbot, app, tmp_db_cfg, fresh_db): - """Test inserting a table template.""" - s = get_settings() - s.setValue("db/default_db", str(tmp_db_cfg.path)) - s.setValue("db/key", tmp_db_cfg.key) - s.setValue("ui/idle_minutes", 0) - s.setValue("ui/theme", "light") - s.setValue("ui/move_todos", True) - s.setValue("ui/tags", True) - s.setValue("ui/time_log", True) - s.setValue("ui/reminders", True) - s.setValue("ui/locale", "en") - s.setValue("ui/font_size", 11) - - themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT)) - window = MainWindow(themes=themes) - qtbot.addWidget(window) - window.show() - - # Open a date - today = date.today().isoformat() - window._open_date_in_tab(QDate.fromString(today, "yyyy-MM-dd")) - - # Ensure we have an editor - editor = window.editor - assert editor is not None - - # Insert table - window._on_table_requested() - - # Verify table was inserted - text = editor.toPlainText() - assert "Column 1" in text - assert "Column 2" in text - assert "Column 3" in text - assert "---" in text - - def test_parse_today_alarms(qtbot, app, tmp_db_cfg, fresh_db): """Test parsing inline alarms from markdown (⏰ HH:MM format).""" from PySide6.QtCore import QTime diff --git a/tests/test_markdown_editor.py b/tests/test_markdown_editor.py index 0309341..cc02ad8 100644 --- a/tests/test_markdown_editor.py +++ b/tests/test_markdown_editor.py @@ -1802,26 +1802,6 @@ def test_render_images_with_corrupted_data(qtbot, app): assert len(text) >= 0 -def test_editor_with_tables(qtbot, app): - """Test editor with markdown tables""" - themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT)) - editor = MarkdownEditor(theme_manager=themes) - qtbot.addWidget(editor) - editor.show() - - table_markdown = """ -| Header 1 | Header 2 | -|----------|----------| -| Cell 1 | Cell 2 | -| Cell 3 | Cell 4 | -""" - editor.from_markdown(table_markdown) - qtbot.wait(50) - - result = editor.to_markdown() - assert "Header 1" in result or "|" in result - - def test_editor_with_code_blocks(qtbot, app): """Test editor with code blocks""" themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT)) diff --git a/tests/test_table_editor.py b/tests/test_table_editor.py deleted file mode 100644 index ac6887f..0000000 --- a/tests/test_table_editor.py +++ /dev/null @@ -1,384 +0,0 @@ -from bouquin.table_editor import TableEditorDialog, find_table_at_cursor, _is_table_line - - -def test_table_editor_init_simple_table(qtbot, app): - """Test initialization with a simple markdown table.""" - table_text = """| Header1 | Header2 | Header3 | -| --- | --- | --- | -| Cell1 | Cell2 | Cell3 | -| Cell4 | Cell5 | Cell6 |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - assert dialog.table_widget.columnCount() == 3 - assert dialog.table_widget.rowCount() == 2 - assert dialog.table_widget.horizontalHeaderItem(0).text() == "Header1" - assert dialog.table_widget.horizontalHeaderItem(1).text() == "Header2" - assert dialog.table_widget.item(0, 0).text() == "Cell1" - assert dialog.table_widget.item(1, 2).text() == "Cell6" - - -def test_table_editor_no_separator_line(qtbot, app): - """Test parsing table without separator line.""" - table_text = """| Header1 | Header2 | -| Cell1 | Cell2 | -| Cell3 | Cell4 |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - assert dialog.table_widget.columnCount() == 2 - assert dialog.table_widget.rowCount() == 2 - assert dialog.table_widget.item(0, 0).text() == "Cell1" - - -def test_table_editor_empty_table(qtbot, app): - """Test initialization with empty table text.""" - dialog = TableEditorDialog("") - qtbot.addWidget(dialog) - - # Should have no columns/rows - assert dialog.table_widget.columnCount() == 0 or dialog.table_widget.rowCount() == 0 - - -def test_table_editor_single_header_line(qtbot, app): - """Test table with only header line.""" - table_text = "| Header1 | Header2 | Header3 |" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - assert dialog.table_widget.columnCount() == 3 - assert dialog.table_widget.rowCount() == 0 - - -def test_add_row(qtbot, app): - """Test adding a row to the table.""" - table_text = """| H1 | H2 | -| --- | --- | -| A | B |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - initial_rows = dialog.table_widget.rowCount() - dialog._add_row() - - assert dialog.table_widget.rowCount() == initial_rows + 1 - # New row should have empty items - assert dialog.table_widget.item(initial_rows, 0).text() == "" - assert dialog.table_widget.item(initial_rows, 1).text() == "" - - -def test_add_column(qtbot, app): - """Test adding a column to the table.""" - table_text = """| H1 | H2 | -| --- | --- | -| A | B |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - initial_cols = dialog.table_widget.columnCount() - dialog._add_column() - - assert dialog.table_widget.columnCount() == initial_cols + 1 - # New column should have header and empty items - assert "Column" in dialog.table_widget.horizontalHeaderItem(initial_cols).text() - assert dialog.table_widget.item(0, initial_cols).text() == "" - - -def test_delete_row(qtbot, app): - """Test deleting a row from the table.""" - table_text = """| H1 | H2 | -| --- | --- | -| A | B | -| C | D |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - initial_rows = dialog.table_widget.rowCount() - dialog.table_widget.setCurrentCell(0, 0) - dialog._delete_row() - - assert dialog.table_widget.rowCount() == initial_rows - 1 - - -def test_delete_row_no_selection(qtbot, app): - """Test deleting a row when nothing is selected.""" - table_text = """| H1 | H2 | -| --- | --- | -| A | B |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - initial_rows = dialog.table_widget.rowCount() - dialog.table_widget.setCurrentCell(-1, -1) # No selection - dialog._delete_row() - - # Row count should remain the same - assert dialog.table_widget.rowCount() == initial_rows - - -def test_delete_column(qtbot, app): - """Test deleting a column from the table.""" - table_text = """| H1 | H2 | H3 | -| --- | --- | --- | -| A | B | C |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - initial_cols = dialog.table_widget.columnCount() - dialog.table_widget.setCurrentCell(0, 1) - dialog._delete_column() - - assert dialog.table_widget.columnCount() == initial_cols - 1 - - -def test_delete_column_no_selection(qtbot, app): - """Test deleting a column when nothing is selected.""" - table_text = """| H1 | H2 | -| --- | --- | -| A | B |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - initial_cols = dialog.table_widget.columnCount() - dialog.table_widget.setCurrentCell(-1, -1) # No selection - dialog._delete_column() - - # Column count should remain the same - assert dialog.table_widget.columnCount() == initial_cols - - -def test_get_markdown_table(qtbot, app): - """Test converting table back to markdown.""" - table_text = """| Name | Age | City | -| --- | --- | --- | -| Alice | 30 | NYC | -| Bob | 25 | LA |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - result = dialog.get_markdown_table() - - assert "| Name | Age | City |" in result - assert "| --- | --- | --- |" in result - assert "| Alice | 30 | NYC |" in result - assert "| Bob | 25 | LA |" in result - - -def test_get_markdown_table_empty(qtbot, app): - """Test getting markdown from empty table.""" - dialog = TableEditorDialog("") - qtbot.addWidget(dialog) - - result = dialog.get_markdown_table() - assert result == "" - - -def test_get_markdown_table_with_modifications(qtbot, app): - """Test getting markdown after modifying table.""" - table_text = """| H1 | H2 | -| --- | --- | -| A | B |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - # Modify a cell - dialog.table_widget.item(0, 0).setText("Modified") - - result = dialog.get_markdown_table() - assert "Modified" in result - - -def test_find_table_at_cursor_middle_of_table(qtbot, app): - """Test finding table when cursor is in the middle.""" - text = """Some text before - -| H1 | H2 | -| --- | --- | -| A | B | -| C | D | - -Some text after""" - - # Cursor position in the middle of the table - cursor_pos = text.find("| A |") + 2 - result = find_table_at_cursor(text, cursor_pos) - - assert result is not None - start, end, table_text = result - assert "| H1 | H2 |" in table_text - assert "| A | B |" in table_text - assert "Some text before" not in table_text - assert "Some text after" not in table_text - - -def test_find_table_at_cursor_first_line(qtbot, app): - """Test finding table when cursor is on the first line.""" - text = """| H1 | H2 | -| --- | --- | -| A | B |""" - - cursor_pos = 5 # In the first line - result = find_table_at_cursor(text, cursor_pos) - - assert result is not None - start, end, table_text = result - assert "| H1 | H2 |" in table_text - - -def test_find_table_at_cursor_not_in_table(qtbot, app): - """Test finding table when cursor is not in a table.""" - text = """Just some regular text -No tables here - -| H1 | H2 | -| --- | --- | -| A | B |""" - - cursor_pos = 10 # In "Just some regular text" - result = find_table_at_cursor(text, cursor_pos) - - assert result is None - - -def test_find_table_at_cursor_empty_text(qtbot, app): - """Test finding table in empty text.""" - result = find_table_at_cursor("", 0) - assert result is None - - -def test_find_table_at_cursor_multiple_tables(qtbot, app): - """Test finding correct table when there are multiple tables.""" - text = """| Table1 | H1 | -| --- | --- | - -Some text - -| Table2 | H2 | -| --- | --- | -| Data | Here |""" - - # Cursor in second table - cursor_pos = text.find("| Data |") - result = find_table_at_cursor(text, cursor_pos) - - assert result is not None - start, end, table_text = result - assert "Table2" in table_text - assert "Table1" not in table_text - - -def test_is_table_line_valid(qtbot, app): - """Test identifying valid table lines.""" - assert _is_table_line("| Header | Header2 |") is True - assert _is_table_line("| --- | --- |") is True - assert _is_table_line("| Cell | Cell2 | Cell3 |") is True - - -def test_is_table_line_invalid(qtbot, app): - """Test identifying invalid table lines.""" - assert _is_table_line("Just regular text") is False - assert _is_table_line("") is False - assert _is_table_line(" ") is False - assert _is_table_line("| Only one pipe") is False - assert _is_table_line("Only one pipe |") is False - assert _is_table_line("No pipes at all") is False - - -def test_is_table_line_edge_cases(qtbot, app): - """Test edge cases for table line detection.""" - assert _is_table_line("| | |") is True # Minimal valid table - assert ( - _is_table_line(" | Header | Data | ") is True - ) # With leading/trailing spaces - - -def test_table_with_alignment_indicators(qtbot, app): - """Test parsing table with alignment indicators.""" - table_text = """| Left | Center | Right | -| :--- | :---: | ---: | -| L1 | C1 | R1 |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - assert dialog.table_widget.columnCount() == 3 - assert dialog.table_widget.rowCount() == 1 - assert dialog.table_widget.item(0, 0).text() == "L1" - - -def test_accept_dialog(qtbot, app): - """Test accepting the dialog.""" - table_text = "| H1 | H2 |\n| --- | --- |\n| A | B |" - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - # Find and click the OK button - for child in dialog.findChildren(type(dialog.findChild(type(None)))): - if hasattr(child, "text") and callable(child.text): - try: - if "ok" in child.text().lower() or "OK" in child.text(): - child.click() - break - except: - pass - - -def test_reject_dialog(qtbot, app): - """Test rejecting the dialog.""" - table_text = "| H1 | H2 |\n| --- | --- |\n| A | B |" - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - # Find and click the Cancel button - for child in dialog.findChildren(type(dialog.findChild(type(None)))): - if hasattr(child, "text") and callable(child.text): - try: - if "cancel" in child.text().lower(): - child.click() - break - except: - pass - - -def test_table_with_uneven_columns(qtbot, app): - """Test parsing table with uneven number of columns in rows.""" - table_text = """| H1 | H2 | H3 | -| --- | --- | --- | -| A | B | -| C | D | E | F |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - # Should handle gracefully - assert dialog.table_widget.columnCount() == 3 - assert dialog.table_widget.rowCount() == 2 - - -def test_table_with_empty_cells(qtbot, app): - """Test parsing table with empty cells.""" - table_text = """| H1 | H2 | H3 | -| --- | --- | --- | -| | B | | -| C | | E |""" - - dialog = TableEditorDialog(table_text) - qtbot.addWidget(dialog) - - assert dialog.table_widget.item(0, 0).text() == "" - assert dialog.table_widget.item(0, 1).text() == "B" - assert dialog.table_widget.item(0, 2).text() == "" - assert dialog.table_widget.item(1, 0).text() == "C" - assert dialog.table_widget.item(1, 1).text() == "" - assert dialog.table_widget.item(1, 2).text() == "E"