private void TerminateLastRow(BlockProcessor state, GridTableState tableState, Table gridTable, bool isLastRow) { var columns = tableState.ColumnSlices; TableRow currentRow = null; foreach (var columnSlice in columns) { if (columnSlice.CurrentCell != null) { if (currentRow == null) { currentRow = new TableRow(); } currentRow.Add(columnSlice.CurrentCell); columnSlice.BlockProcessor.Close(columnSlice.CurrentCell); } // Renew the block parser processor (or reset it for the last row) if (columnSlice.BlockProcessor != null) { columnSlice.BlockProcessor.ReleaseChild(); columnSlice.BlockProcessor = isLastRow ? null : state.CreateChild(); } // Create or erase the cell if (isLastRow || columnSlice.CurrentColumnSpan == 0) { // We don't need the cell anymore if we have a last row // Or the cell has a columnspan == 0 columnSlice.CurrentCell = null; } else { // Else we can create a new cell columnSlice.CurrentCell = new TableCell(this) { ColumnSpan = columnSlice.CurrentColumnSpan }; if (columnSlice.BlockProcessor == null) { columnSlice.BlockProcessor = state.CreateChild(); } // Ensure that the BlockParser is aware that the TableCell is the top-level container columnSlice.BlockProcessor.Open(columnSlice.CurrentCell); } } if (currentRow != null) { gridTable.Add(currentRow); } }
private static void Undo(BlockProcessor processor, GridTableState tableState, Table gridTable) { var parser = processor.Parsers.FindExact <ParagraphBlockParser>(); // Discard the grid table var parent = gridTable.Parent; processor.Discard(gridTable); var paragraphBlock = new ParagraphBlock(parser) { Lines = tableState.Lines, }; parent.Add(paragraphBlock); processor.Open(paragraphBlock); }
private static void TerminateCurrentRow(BlockProcessor processor, GridTableState tableState, Table gridTable, bool isLastRow) { var columns = tableState.ColumnSlices; TableRow currentRow = null; for (int i = 0; i < columns.Count; i++) { var columnSlice = columns[i]; if (columnSlice.CurrentCell != null) { if (currentRow == null) { currentRow = new TableRow(); } // If this cell does not already belong to a row if (columnSlice.CurrentCell.Parent == null) { currentRow.Add(columnSlice.CurrentCell); } // If the cell is not going to span through to the next row if (columnSlice.CurrentCell.AllowClose) { columnSlice.BlockProcessor.Close(columnSlice.CurrentCell); } } // Renew the block parser processor (or reset it for the last row) if (columnSlice.BlockProcessor != null && (columnSlice.CurrentCell == null || columnSlice.CurrentCell.AllowClose)) { columnSlice.BlockProcessor.ReleaseChild(); columnSlice.BlockProcessor = isLastRow ? null : processor.CreateChild(); } // Create or erase the cell if (isLastRow || columnSlice.CurrentColumnSpan == 0 || (columnSlice.CurrentCell != null && columnSlice.CurrentCell.AllowClose)) { // We don't need the cell anymore if we have a last row // Or the cell has a columnspan == 0 // And the cell does not have to be kept open to span rows columnSlice.CurrentCell = null; } } if (currentRow != null && currentRow.Count > 0) { gridTable.Add(currentRow); } }
private BlockState HandleNewRow(BlockProcessor processor, GridTableState tableState, Table gridTable) { var columns = tableState.ColumnSlices !; SetRowSpanState(columns, processor.Line, out bool isHeaderRow, out bool hasRowSpan); SetColumnSpanState(columns, processor.Line); TerminateCurrentRow(processor, tableState, gridTable, false); if (isHeaderRow) { for (int i = 0; i < gridTable.Count; i++) { var row = (TableRow)gridTable[i]; row.IsHeader = true; } } tableState.StartRowGroup = gridTable.Count; if (hasRowSpan) { HandleContents(processor, tableState, gridTable); } return(BlockState.ContinueDiscard); }
private BlockState HandleContents(BlockProcessor processor, GridTableState tableState, Table gridTable) { var isRowLine = processor.CurrentChar == '+'; var columns = tableState.ColumnSlices; var line = processor.Line; SetColumnSpanState(columns, line); if (!isRowLine && !CanContinueRow(columns)) { TerminateCurrentRow(processor, tableState, gridTable, false); } for (int i = 0; i < columns.Count;) { var columnSlice = columns[i]; var nextColumnIndex = i + columnSlice.CurrentColumnSpan; // If the span is 0, we exit if (nextColumnIndex == i) { break; } var nextColumn = nextColumnIndex < columns.Count ? columns[nextColumnIndex] : null; var sliceForCell = line; sliceForCell.Start = line.Start + columnSlice.Start + 1; if (nextColumn != null) { sliceForCell.End = line.Start + nextColumn.Start - 1; } else { var columnEnd = columns[columns.Count - 1].End; var columnEndChar = line.PeekCharExtra(columnEnd); // If there is a `|` (or a `+` in the case that we are dealing with a row line // with spanned contents) exactly at the expected end of the table row, we cut the line // otherwise we allow to have the last cell of a row to be open for longer cell content if (columnEndChar == '|' || (isRowLine && columnEndChar == '+')) { sliceForCell.End = line.Start + columnEnd - 1; } else if (line.PeekCharExtra(line.End) == '|') { sliceForCell.End = line.End - 1; } } sliceForCell.TrimEnd(); if (!isRowLine || !IsRowSeperator(sliceForCell)) { if (columnSlice.CurrentCell == null) { columnSlice.CurrentCell = new TableCell(this) { ColumnSpan = columnSlice.CurrentColumnSpan, ColumnIndex = i }; if (columnSlice.BlockProcessor == null) { columnSlice.BlockProcessor = processor.CreateChild(); } // Ensure that the BlockParser is aware that the TableCell is the top-level container columnSlice.BlockProcessor.Open(columnSlice.CurrentCell); } // Process the content of the cell columnSlice.BlockProcessor.LineIndex = processor.LineIndex; columnSlice.BlockProcessor.ProcessLine(sliceForCell); } // Go to next column i = nextColumnIndex; } return(BlockState.ContinueDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { // A grid table cannot start more than an indent if (processor.IsCodeIndent) { return(BlockState.None); } var line = processor.Line; GridTableState tableState = null; // Match the first row that should be of the minimal form: +--------------- var c = line.CurrentChar; var lineStart = line.Start; while (c == '+') { var columnStart = line.Start; line.NextChar(); line.TrimStart(); // if we have reached the end of the line, exit c = line.CurrentChar; if (c == 0) { break; } // Parse a column alignment TableColumnAlign?columnAlign; if (!TableHelper.ParseColumnHeader(ref line, '-', out columnAlign)) { return(BlockState.None); } tableState = tableState ?? new GridTableState { Start = processor.Start, ExpectRow = true }; tableState.AddColumn(columnStart - lineStart, line.Start - lineStart, columnAlign); c = line.CurrentChar; } if (c != 0 || tableState == null) { return(BlockState.None); } // Store the line (if we need later to build a ParagraphBlock because the GridTable was in fact invalid) tableState.AddLine(ref processor.Line); var table = new Table(this); table.SetData(typeof(GridTableState), tableState); // Calculate the total width of all columns int totalWidth = 0; foreach (var columnSlice in tableState.ColumnSlices) { totalWidth += columnSlice.End - columnSlice.Start - 1; } // Store the column width and alignment foreach (var columnSlice in tableState.ColumnSlices) { var columnDefinition = new TableColumnDefinition { // Column width proportional to the total width Width = (float)(columnSlice.End - columnSlice.Start - 1) * 100.0f / totalWidth, Alignment = columnSlice.Align }; table.ColumnDefinitions.Add(columnDefinition); } processor.NewBlocks.Push(table); return(BlockState.ContinueDiscard); }
private BlockState ParseRowSeparator(BlockProcessor state, GridTableState tableState, Table gridTable) { // A grid table must start with a line like this: // + ------------- + ------------ + ---------------------------------------- + // Spaces are optional var line = state.Line; var c = line.CurrentChar; bool isFirst = true; var delimiterChar = '\0'; while (true) { if (c == '+') { line.NextChar(); if (line.IsEmptyOrWhitespace()) { if (isFirst) { return(BlockState.None); } break; } TableColumnAlign align; if (TableHelper.ParseColumnHeaderDetect(ref line, ref delimiterChar, out align)) { isFirst = false; c = line.CurrentChar; continue; } } // If we have any other characters, this is an invalid line return(BlockState.None); } // If we have an header row var isHeader = delimiterChar == '='; // Terminate the current row TerminateLastRow(state, tableState, gridTable, false); // If we had a header row separator, we can mark all rows since last row separator // to be header rows if (isHeader) { for (int i = tableState.StartRowGroup; i < gridTable.Count; i++) { var row = (TableRow)gridTable[i]; row.IsHeader = true; } } // Makr the next start row group continue on the next row tableState.StartRowGroup = gridTable.Count; // We don't keep the line return(BlockState.ContinueDiscard); }
public override BlockState TryOpen(BlockProcessor processor) { // A grid table cannot start more than an indent if (processor.IsCodeIndent) { return(BlockState.None); } var line = processor.Line; // A grid table must start with a line like this: // + ------------- + ------------ + ---------------------------------------- + // Spaces are optional GridTableState tableState = null; var c = line.CurrentChar; var startPosition = processor.Start; while (true) { if (c == '+') { var startCharacter = line.Start; line.NextChar(); if (line.IsEmptyOrWhitespace()) { if (tableState == null) { return(BlockState.None); } break; } TableColumnAlign align; if (TableHelper.ParseColumnHeader(ref line, '-', out align)) { if (tableState == null) { tableState = new GridTableState() { Start = processor.Start, ExpectRow = true, }; } tableState.AddColumn(startCharacter - startPosition, line.Start - 1 - startPosition, align); c = line.CurrentChar; continue; } } // If we have any other characters, this is an invalid line return(BlockState.None); } // Store the line (if we need later to build a ParagraphBlock because the GridTable was in fact invalid) tableState.AddLine(ref processor.Line); // Create the grid table var table = new Table(this); table.SetData(typeof(GridTableState), tableState); // Calculate the total width of all columns int totalWidth = 0; foreach (var columnSlice in tableState.ColumnSlices) { totalWidth += columnSlice.End - columnSlice.Start; } // Store the column width and alignment foreach (var columnSlice in tableState.ColumnSlices) { var columnDefinition = new TableColumnDefinition { // Column width proportional to the total width Width = (float)(columnSlice.End - columnSlice.Start) * 100.0f / totalWidth, Alignment = columnSlice.Align, }; table.ColumnDefinitions.Add(columnDefinition); } processor.NewBlocks.Push(table); return(BlockState.ContinueDiscard); }