Used when pasting a fragment of table into another one
976976
function insertTableFragmentAtRange(
    opts: Options,
    change: Change,
    range: Range,
    // This fragment should contain only one table,
    // with a valid number of cells
    fragment: Document
): Change {
    const insertedTable = fragment.nodes.first();
    if (
        !(
            fragment.nodes.size === 1 &&
            insertedTable &&
            insertedTable.type === opts.typeTable
        )
    ) {
        throw new Error('Expected to insert a fragment containing one table');
    }

    const { value } = change;
    const targetPosition = TablePosition.create(
        opts,
        value.document,
        value.selection.startKey
    );

    const fragmentRows = insertedTable.nodes;
    const fragmentHeight = fragmentRows.size;
    const fragmentWidth = fragmentRows.first().nodes.size;

    // Insert columns and rows to accomodate the incoming pasted cells
    const missingWidth =
        fragmentWidth +
        targetPosition.getColumnIndex() -
        targetPosition.getWidth();
    const missingHeight =
        fragmentHeight +
        targetPosition.getRowIndex() -
        targetPosition.getHeight();

    if (missingWidth > 0) {
        // Add columns
        Array(missingWidth)
            .fill()
            .forEach(() => {
                insertColumn(opts, change, targetPosition.getWidth());
            });
    }
    if (missingHeight > 0) {
        // Add rows
        Array(missingHeight)
            .fill()
            .forEach(() => {
                insertRow(opts, change, targetPosition.getHeight());
            });
    }

    // Patch the inserted table over the target table, overwritting the cells
    const existingTable = change.value.document.getDescendant(
        targetPosition.table.key
    );

    fragmentRows.forEach((fragmentRow, fragmentRowIndex) => {
        fragmentRow.nodes.forEach((newCell, fragmentColumnIndex) => {
            const existingCell = existingTable.nodes
                .get(targetPosition.getRowIndex() + fragmentRowIndex)
                .nodes.get(
                    targetPosition.getColumnIndex() + fragmentColumnIndex
                );

            change.replaceNodeByKey(existingCell.key, newCell, {
                normalize: false
            });
        });
    });

    const lastPastedCell = fragmentRows.last().nodes.last();
    return change.collapseToEndOf(lastPastedCell);
}
84407
def paste_format(self):
        

        row, col, tab = self.grid.actions.cursor

        selection = self.get_selection()
        if selection:
            # Use selection rather than cursor for top left cell if present
            row, col = [tl if tl is not None else 0
                        for tl in selection.get_bbox()[0]]

        cell_attributes = self.grid.code_array.cell_attributes

        string_data = self.grid.main_window.clipboard.get_clipboard()
        format_data = ast.literal_eval(string_data)

        ca = format_data["cell_attributes"]
        rh = format_data["row_heights"]
        cw = format_data["col_widths"]

        assert isinstance(ca, types.ListType)
        assert isinstance(rh, types.DictType)
        assert isinstance(cw, types.DictType)

        # Cell attributes

        for selection_params, tab, attrs in ca:
            base_selection = Selection(*selection_params)
            shifted_selection = base_selection.shifted(row, col)
            if "merge_area" not in attrs:
                # Do not paste merge areas because this may have
                # inintended consequences for existing merge areas
                new_cell_attribute = shifted_selection, tab, attrs
                cell_attributes.append(new_cell_attribute)

        # Row heights
        row_heights = self.grid.code_array.row_heights
        for __row, __tab in rh:
            row_heights[__row+row, tab] = rh[__row, __tab]

        # Column widths
        col_widths = self.grid.code_array.col_widths
        for __col, __tab in cw:
            col_widths[__col+col, tab] = cw[(__col, __tab)]
1336238
def paste_action_callback(self, *event):
        """"""
        if react_to_event(self.view, self.tree_view, event) and self.active_entry_widget is None:
            _, dict_paths = self.get_view_selection()
            selected_data_list = rafcon.gui.clipboard.global_clipboard.get_semantic_dictionary_list()

            # enforce paste on root level if semantic data empty or nothing is selected
            if not dict_paths and not self.model.state.semantic_data:
                dict_paths = [[]]

            for target_dict_path_as_list in dict_paths:
                prev_value = self.model.state.semantic_data
                value = self.model.state.semantic_data
                for path_element in target_dict_path_as_list:
                    prev_value = value
                    value = value[path_element]
                if not isinstance(value, dict) and len(dict_paths) <= 1:  # if one selection take parent
                    target_dict_path_as_list.pop(-1)
                    value = prev_value
                if isinstance(value, dict):
                    for key_to_paste, value_to_add in selected_data_list:
                        self.model.state.add_semantic_data(target_dict_path_as_list, value_to_add, key_to_paste)
            self.reload_tree_store_data()
1094946
private void organizeCells(ArrayList<TextPiece> linesOfAPage,
	    TableCandidate tc) {
	float captionY = linesOfAPage.get(tc.getCaptionStartLine()).getY();
	float captionEndY = linesOfAPage.get(tc.getCaptionEndLine()).getEndY();
	ArrayList<TextPiece> rawCells = new ArrayList<TextPiece>();
	/*
	 * Copy & sort
	 */
	for (int i = tc.getBodyStartLine(); i <= tc.getBodyEndLine(); i++) {
	    rawCells.add(linesOfAPage.get(i));
	}

	Collections.sort(rawCells);
	assert rawCells.size() >= 4; // due to previous check
	TableRow currentRow = null; // creates first row

	for (TextPiece cell : rawCells) {
	    /*
	     * Skips lines beyond caption //TODO: remove the far Y
	     */
	    if (tc.isTopCaption() && (cell.getY() < captionY))
		continue;
	    if (!tc.isTopCaption() && (cell.getEndY() > captionEndY))
		continue;

	    /*
	     * Lines too close to each other are treated as a single line
	     */
	    if (currentRow != null
		    && cell.getY() - currentRow.getY() < m_docInfo
			    .getAverageLineGap() / 2.0)
		currentRow.addCell(cell);
	    else {
		/*
		 * Creates a new row
		 */
		currentRow = new TableRow();
		currentRow.setY(cell.getY());
		if (cell.isSuperScriptBeginning())
		    currentRow.setSuperScriptRow(true);
		currentRow.addCell(cell);
		tc.addRow(currentRow);
	    }
	}
	if (tc.getRows().size() < 2)
	    tc.setValid(false);
    }
1172073
def paste(**kwargs):
    """"""
    window = Tk()
    window.withdraw()
    d = window.selection_get(selection = 'CLIPBOARD')
    return d
997410
function processFragment(match) {
    var hpos = match.index;
    var hlen = match[0].length;

    currentFragment._txt += srctext.slice(0, hpos);

    if (pos || hpos) {
      currentFragment = new Fragment();
      fragments.push(currentFragment);
    }

    currentFragment._hdr = noHeaders ? '' : match[0];
    currentFragment._txt = noHeaders ? match[0] : '';

    var label = u.trim(match[1]);
    if (label) { currentFragment._lbl = label; }

    srctext = srctext.slice(hpos + hlen);
    pos += hpos + hlen;
  }
503494
def putcellslice(self, rownr, value, blc, trc, inc=[]):
        """"""
        return self._table.putcellslice(self._column, rownr, value, blc, trc, inc)
271843
function( range, action, docFrag, mergeThen ) {
			range.optimizeBookmark();

			var startNode = range.startContainer;
			var endNode = range.endContainer;

			var startOffset = range.startOffset;
			var endOffset = range.endOffset;

			var removeStartNode;
			var removeEndNode;

			// For text containers, we must simply split the node and point to the
			// second part. The removal will be handled by the rest of the code .
			if ( endNode.type == CKEDITOR.NODE_TEXT )
				endNode = endNode.split( endOffset );
			else {
				// If the end container has children and the offset is pointing
				// to a child, then we should start from it.
				if ( endNode.getChildCount() > 0 ) {
					// If the offset points after the last node.
					if ( endOffset >= endNode.getChildCount() ) {
						// Let's create a temporary node and mark it for removal.
						endNode = endNode.append( range.document.createText( '' ) );
						removeEndNode = true;
					} else {
						endNode = endNode.getChild( endOffset );
					}
				}
			}

			// For text containers, we must simply split the node. The removal will
			// be handled by the rest of the code .
			if ( startNode.type == CKEDITOR.NODE_TEXT ) {
				startNode.split( startOffset );

				// In cases the end node is the same as the start node, the above
				// splitting will also split the end, so me must move the end to
				// the second part of the split.
				if ( startNode.equals( endNode ) )
					endNode = startNode.getNext();
			} else {
				// If the start container has children and the offset is pointing
				// to a child, then we should start from its previous sibling.

				// If the offset points to the first node, we don't have a
				// sibling, so let's use the first one, but mark it for removal.
				if ( !startOffset ) {
					// Let's create a temporary node and mark it for removal.
					startNode = startNode.append( range.document.createText( '' ), 1 );
					removeStartNode = true;
				} else if ( startOffset >= startNode.getChildCount() ) {
					// Let's create a temporary node and mark it for removal.
					startNode = startNode.append( range.document.createText( '' ) );
					removeStartNode = true;
				} else {
					startNode = startNode.getChild( startOffset ).getPrevious();
				}
			}

			// Get the parent nodes tree for the start and end boundaries.
			var startParents = startNode.getParents();
			var endParents = endNode.getParents();

			// Compare them, to find the top most siblings.
			var i, topStart, topEnd;

			for ( i = 0; i < startParents.length; i++ ) {
				topStart = startParents[ i ];
				topEnd = endParents[ i ];

				// The compared nodes will match until we find the top most
				// siblings (different nodes that have the same parent).
				// "i" will hold the index in the parents array for the top
				// most element.
				if ( !topStart.equals( topEnd ) )
					break;
			}

			var clone = docFrag,
				levelStartNode, levelClone, currentNode, currentSibling;

			// Remove all successive sibling nodes for every node in the
			// startParents tree.
			for ( var j = i; j < startParents.length; j++ ) {
				levelStartNode = startParents[ j ];

				// For Extract and Clone, we must clone this level.
				if ( clone && !levelStartNode.equals( startNode ) ) // action = 0 = Delete
				levelClone = clone.append( levelStartNode.clone() );

				currentNode = levelStartNode.getNext();

				while ( currentNode ) {
					// Stop processing when the current node matches a node in the
					// endParents tree or if it is the endNode.
					if ( currentNode.equals( endParents[ j ] ) || currentNode.equals( endNode ) )
						break;

					// Cache the next sibling.
					currentSibling = currentNode.getNext();

					// If cloning, just clone it.
					if ( action == 2 ) // 2 = Clone
					clone.append( currentNode.clone( true ) );
					else {
						// Both Delete and Extract will remove the node.
						currentNode.remove();

						// When Extracting, move the removed node to the docFrag.
						if ( action == 1 ) // 1 = Extract
						clone.append( currentNode );
					}

					currentNode = currentSibling;
				}

				if ( clone )
					clone = levelClone;
			}

			clone = docFrag;

			// Remove all previous sibling nodes for every node in the
			// endParents tree.
			for ( var k = i; k < endParents.length; k++ ) {
				levelStartNode = endParents[ k ];

				// For Extract and Clone, we must clone this level.
				if ( action > 0 && !levelStartNode.equals( endNode ) ) // action = 0 = Delete
				levelClone = clone.append( levelStartNode.clone() );

				// The processing of siblings may have already been done by the parent.
				if ( !startParents[ k ] || levelStartNode.$.parentNode != startParents[ k ].$.parentNode ) {
					currentNode = levelStartNode.getPrevious();

					while ( currentNode ) {
						// Stop processing when the current node matches a node in the
						// startParents tree or if it is the startNode.
						if ( currentNode.equals( startParents[ k ] ) || currentNode.equals( startNode ) )
							break;

						// Cache the next sibling.
						currentSibling = currentNode.getPrevious();

						// If cloning, just clone it.
						if ( action == 2 ) // 2 = Clone
						clone.$.insertBefore( currentNode.$.cloneNode( true ), clone.$.firstChild );
						else {
							// Both Delete and Extract will remove the node.
							currentNode.remove();

							// When Extracting, mode the removed node to the docFrag.
							if ( action == 1 ) // 1 = Extract
							clone.$.insertBefore( currentNode.$, clone.$.firstChild );
						}

						currentNode = currentSibling;
					}
				}

				if ( clone )
					clone = levelClone;
			}

			if ( action == 2 ) // 2 = Clone.
			{
				// No changes in the DOM should be done, so fix the split text (if any).

				var startTextNode = range.startContainer;
				if ( startTextNode.type == CKEDITOR.NODE_TEXT ) {
					startTextNode.$.data += startTextNode.$.nextSibling.data;
					startTextNode.$.parentNode.removeChild( startTextNode.$.nextSibling );
				}

				var endTextNode = range.endContainer;
				if ( endTextNode.type == CKEDITOR.NODE_TEXT && endTextNode.$.nextSibling ) {
					endTextNode.$.data += endTextNode.$.nextSibling.data;
					endTextNode.$.parentNode.removeChild( endTextNode.$.nextSibling );
				}
			} else {
				// Collapse the range.

				// If a node has been partially selected, collapse the range between
				// topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
				if ( topStart && topEnd && ( startNode.$.parentNode != topStart.$.parentNode || endNode.$.parentNode != topEnd.$.parentNode ) ) {
					var endIndex = topEnd.getIndex();

					// If the start node is to be removed, we must correct the
					// index to reflect the removal.
					if ( removeStartNode && topEnd.$.parentNode == startNode.$.parentNode )
						endIndex--;

					// Merge splitted parents.
					if ( mergeThen && topStart.type == CKEDITOR.NODE_ELEMENT ) {
						var span = CKEDITOR.dom.element.createFromHtml( '<span ' +
							'data-cke-bookmark="1" style="display:none">&nbsp;</span>', range.document );
						span.insertAfter( topStart );
						topStart.mergeSiblings( false );
						range.moveToBookmark( { startNode: span } );
					} else {
						range.setStart( topEnd.getParent(), endIndex );
					}
				}

				// Collapse it to the start.
				range.collapse( true );
			}

			// Cleanup any marked node.
			if ( removeStartNode )
				startNode.remove();

			if ( removeEndNode && endNode.$.parentNode )
				endNode.remove();
		}
501292
private void paste(final DiffBlock curB)
	{

		String text = bufferMap.remove(curB.getId());

		// Paste (C S B)
		DiffPart action = new DiffPart(DiffAction.PASTE);

		// S
		action.setStart(version.length());
		codecData.checkBlocksizeS(version.length());

		// B
		action.setText(Integer.toString(curB.getId()));
		codecData.checkBlocksizeB(curB.getId());

		diff.add(action);

		version.append(text);
	}
360760
private void doPaintRows(final WTable table, final WebXmlRenderContext renderContext) {
		TableRepeater repeater = table.getRepeater();

		WComponent row = repeater.getRepeatedComponent();

		List<RowIdWrapper> wrappers = repeater.getBeanList();

		Set<?> otherSelectedRows = new HashSet<>(table.getSelectedRows());

		int index = -1;
		for (RowIdWrapper wrapper : wrappers) {
			index++;

			Object rowKey = wrapper.getRowKey();

			if (table.getSelectedRows().contains(rowKey)) {
				otherSelectedRows.remove(rowKey);
			}

			// Only rendering top level rows
			// Child rows handled by the layout, so dont paint the row
			if (wrapper.getParent() != null) {
				continue;
			}

			// Each row has its own context. This is why we can reuse the same
			// WComponent instance for each row.
			UIContext rowContext = repeater.getRowContext(wrapper, index);

			UIContextHolder.pushContext(rowContext);

			try {
				row.paint(renderContext);
			} finally {
				UIContextHolder.popContext();
			}
		}
		this.selectedOnOther = otherSelectedRows.size();
	}
1302052
function (instance, td, row, col, prop, keyboardProxy, useOriginalValue, suffix) {
    if (texteditor.isCellEdited) {
      return;
    }

    keyboardProxy.on('cut.editor', function (event) {
      event.stopPropagation();
    });

    keyboardProxy.on('paste.editor', function (event) {
      event.stopPropagation();
    });

    var $td = $(td);

    if (!instance.getCellMeta(row, col).isWritable) {
      return;
    }

    texteditor.isCellEdited = true;

    if (useOriginalValue) {
      var original = instance.getDataAtCell(row, prop);
      original = Handsontable.helper.stringify(original) + (suffix || '');
      keyboardProxy.val(original);
      texteditor.setCaretPosition(keyboardProxy, original.length);
    }
    else {
      keyboardProxy.val('');
    }

    texteditor.refreshDimensions(instance, $td, keyboardProxy);
    keyboardProxy.parent().removeClass('htHidden');

    instance.rootElement.triggerHandler('beginediting.handsontable');

    setTimeout(function () {
      //async fix for Firefox 3.6.28 (needs manual testing)
      keyboardProxy.parent().css({
        overflow: 'visible'
      });
    }, 1);
  }
18802
function appendColorRow( rangeA, rangeB )
			{
				for ( var i = rangeA ; i < rangeA + 3 ; i++ )
				{
					var row = table.$.insertRow( -1 );

					for ( var j = rangeB ; j < rangeB + 3 ; j++ )
					{
						for ( var n = 0 ; n < 6 ; n++ )
						{
							appendColorCell( row, '#' + aColors[j] + aColors[n] + aColors[i] );
						}
					}
				}
			}
714130
private void extractTablesFromAPage(ArrayList<TextPiece> linesOfAPage,
    		ArrayList<Table> tables, ArrayList<TextPiece> wordsOfAPage, int i, File pdfFile, ArrayList<ArrayList<TextPiece>> linesByPage) {
    	    	if (linesOfAPage.size() > 30) {
    	    		/*
    	    		 * Checks for table keywords
    	    		 */
    			Pattern tblKP = Pattern.compile("^\\(?(Table|lable|Tble|Tabl)",Pattern.CASE_INSENSITIVE);
    			Pattern tocKP = Pattern.compile("TableOfContent(s)?",Pattern.CASE_INSENSITIVE);
    			Pattern falsePos1 = Pattern.compile("Table");
    			Pattern falsePos2 = Pattern.compile("Table(s)?\\s+[\\d-]+(\\s+)?[\\.:-A-Z]");

    			boolean tableExists = false;
    			boolean tableCaptionFormat = false;
    			int lineNumber = 0; int endCurrentTable = 0;
    			int prevLineEndIndex = 0;
    			
    			TextPiece prevLine = null;
    	    		for (TextPiece t : linesOfAPage) {
    				// remove spaces
    				if(tableExists && lineNumber < endCurrentTable) { 
    					lineNumber++; prevLine = t; continue;
    				}
    				
    				String textFromPage = t.getText();
    	    		String textWithoutSpace = textFromPage.replace(" ", "");
    	    		if(falsePos2.matcher(textFromPage).groupCount() > 0) {
    	    			tableCaptionFormat = true;
    	    		}
    				// match for keywords
    				Matcher tblKM = tblKP.matcher(textWithoutSpace);
    				if(tblKM.find()) {
    					// string which matched
    					String matchString = textWithoutSpace.substring(tblKM.start(0),tblKM.end(0));
    					// string should not match table of contents
    					if(!tocKP.matcher(textWithoutSpace).find()) {
    						if(prevLine != null && t.getY() == prevLine.getY()) {
    							prevLine = t; lineNumber++; continue;
    						 }
    						TableCandidate tc = new TableCandidate();
    						tc.setKeyword(matchString);
    						endCurrentTable = extractOneTable(lineNumber,linesOfAPage, matchString, tc, wordsOfAPage, i, prevLineEndIndex, linesByPage);
    						boolean falsePositive = false;
    						if(tableCaptionFormat) {
    							if(!falsePos2.matcher(tc.getCaption()).find()) {
    							
    								falsePositive = true;
    							}
    						}
    						if(falsePos1.matcher(tc.getCaption()).groupCount() > 1) {
    							falsePositive = true;
    						}
    						
    						if(tc.isValid()) {
    							if(tc.isTopCaption()) {
    								prevLineEndIndex = tc.getBodyEndLine();
    							}
    							else {
    								prevLineEndIndex = tc.getCaptionEndLine();
    							}
    						}
    						String metaStructLevel = tc.getMetadata_StructureLevel();
    						String tableMeta_thisDoc = m_docInfo.getTableMetaInThisDoc();
    						String tableMetaHighLevel = tc.getMetadata_HighLevel();
    						String latestMetadata = tableMeta_thisDoc+tableMetaHighLevel+metaStructLevel;
    						m_docInfo.setTableMetadata(latestMetadata);
    						if(!falsePositive) {
    							Table singleTable = new Table();
    							singleTable.setPageNumber(i+1);
    							singleTable.setValidTable(tc);
    							tables.add(singleTable);
    						}
    					}
    					else { prevLine = t; lineNumber++; continue; }
    				} // match not found	
    				prevLine = t; lineNumber++;
    			} // end for
    	    	} // end if page size

    	   }
695049
public static void appendTable(ParagraphAlignment[] alignment, int rows, int cols, String[][] values, int[]
            rowHeight, int[] colWidth, Map<String, Integer> cellMargins, Map<String, Object> styles) {
        createXwpfDocumentIfNull();
        xwpfDocument.createParagraph();
        XWPFTable table = xwpfDocument.createTable(rows, cols);
        XWPFParagraph paragraph;
        XWPFTableRow row;
        XWPFTableCell cell;
        CTTcPr cellPr;
        XWPFRun run;
        if (Checker.isNotEmpty(cellMargins)) {
            int top = MsUtils.checkInteger(cellMargins.get("top"));
            int left = MsUtils.checkInteger(cellMargins.get("left"));
            int bottom = MsUtils.checkInteger(cellMargins.get("bottom"));
            int right = MsUtils.checkInteger(cellMargins.get("right"));
            table.setCellMargins(top, left, bottom, right);
        }
        int minRow = Math.min(rows, values.length);
        for (int i = 0; i < minRow; i++) {
            row = table.getRow(i);
            int height = i < rowHeight.length ? rowHeight[i] : rowHeight[rowHeight.length - 1];
            row.setHeight(height);
            String[] value = values[i];
            int minCol = Math.min(cols, value.length);
            for (int j = 0; j < minCol; j++) {
                cell = row.getCell(j);
                cellPr = cell.getCTTc().addNewTcPr();
                int width = j < colWidth.length ? colWidth[j] : colWidth[colWidth.length - 1];
                cellPr.addNewTcW().setW(BigInteger.valueOf(width));
                paragraph = cell.getParagraphs().get(0);
                int idx = i * cols + j;
                ParagraphAlignment align = idx < alignment.length ? alignment[idx] : alignment[alignment.length - 1];
                paragraph.setAlignment(align);
                run = paragraph.createRun();
                run.setText(value[j]);
                setStyle(run, styles);
            }
        }
    }
586589
def insertText(self, data, parent=None):
        """"""
        if parent is None:
            parent = self.openElements[-1]

        if (not self.insertFromTable or (self.insertFromTable and
                                         self.openElements[-1].name
                                         not in tableInsertModeElements)):
            parent.insertText(data)
        else:
            # We should be in the InTable mode. This means we want to do
            # special magic element rearranging
            parent, insertBefore = self.getTableMisnestedNodePosition()
            parent.insertText(data, insertBefore)
1228178
def OnInsertTabs(self, event):
        """"""

        with undo.group(_("Insert table")):
            self.grid.actions.insert_tabs(self.grid.current_table - 1, 1)

        self.grid.GetTable().ResetView()
        self.grid.actions.zoom()

        event.Skip()
1058421
def paste_clipboard_data(self, data, paste_mode=PasteMode.EMACS, count=1):
        
        assert isinstance(data, ClipboardData)
        assert paste_mode in (PasteMode.VI_BEFORE, PasteMode.VI_AFTER, PasteMode.EMACS)

        before = (paste_mode == PasteMode.VI_BEFORE)
        after = (paste_mode == PasteMode.VI_AFTER)

        if data.type == SelectionType.CHARACTERS:
            if after:
                new_text = (self.text[:self.cursor_position + 1] + data.text * count +
                            self.text[self.cursor_position + 1:])
            else:
                new_text = self.text_before_cursor + data.text * count + self.text_after_cursor

            new_cursor_position = self.cursor_position + len(data.text) * count
            if before:
                new_cursor_position -= 1

        elif data.type == SelectionType.LINES:
            l = self.cursor_position_row
            if before:
                lines = self.lines[:l] + [data.text] * count + self.lines[l:]
                new_text = '\n'.join(lines)
                new_cursor_position = len(''.join(self.lines[:l])) + l
            else:
                lines = self.lines[:l + 1] + [data.text] * count + self.lines[l + 1:]
                new_cursor_position = len(''.join(self.lines[:l + 1])) + l + 1
                new_text = '\n'.join(lines)

        elif data.type == SelectionType.BLOCK:
            lines = self.lines[:]
            start_line = self.cursor_position_row
            start_column = self.cursor_position_col + (0 if before else 1)

            for i, line in enumerate(data.text.split('\n')):
                index = i + start_line
                if index >= len(lines):
                    lines.append('')

                lines[index] = lines[index].ljust(start_column)
                lines[index] = lines[index][:start_column] + line * count + lines[index][start_column:]

            new_text = '\n'.join(lines)
            new_cursor_position = self.cursor_position + (0 if before else 1)

        return Document(text=new_text, cursor_position=new_cursor_position)