2025, Dec 08 07:00

How to Fix Merged Lines in a Python CLI Todo List: Append Newlines and Keep show Output Clean

Fix a Python CLI todo list newline bug: ensure each added item ends with \n so readlines/writelines keep one item per line. See the code fix explained.

When you build a simple Python CLI todo list, it’s easy to forget that files are line-oriented. One missing newline and your “show” output suddenly glues multiple entries together. That’s exactly what happens when you add items like “add bro” and then run “show”: the last two todos end up on the same line instead of appearing as separate items.

Reproducing the issue

The core loop reads and writes todos from a plain text file. The display logic strips newlines for printing, but the add branch appends the new item without a newline. The result is that two consecutive entries can end up concatenated in the file and therefore on screen.

while True:
    cmd_text = input("type add, show, edit, remove or exit ")
    cmd_text = cmd_text.strip()
    if 'add' in cmd_text:
        entry_text = cmd_text[4:]
        with open('todos.txt', 'r') as fh:
            items_list = fh.readlines()
        items_list.append(entry_text)
        with open('todos.txt', 'w') as fh:
            fh.writelines(items_list)
    elif 'show' in cmd_text:
        with open('todos.txt', 'r') as fh:
            items_list = fh.readlines()
        for pos, line_item in enumerate(items_list):
            line_item = line_item.strip("\n")
            pos = pos + 1
            line_out = f"{pos}-{line_item}"
            print(line_out)
    elif 'edit' in cmd_text:
        idx_num = int(cmd_text[5:])
        print(idx_num)
        idx_num = idx_num - 1
        with open('todos.txt', 'r') as fh:
            items_list = fh.readlines()
        updated_entry = input("Type the new todo:")
        items_list[idx_num] = updated_entry + "\n"
        with open('todos.txt', 'w') as fh:
            fh.writelines(items_list)
    elif 'remove' in cmd_text:
        idx_num = int(cmd_text[6:])
        with open('todos.txt', 'r') as fh:
            items_list = fh.readlines()
        pos = idx_num - 1
        removed_entry = items_list[pos].strip("\n")
        items_list.pop(pos)
        with open('todos.txt', 'w') as fh:
            fh.writelines(items_list)
        feedback = f"todo {removed_entry} was removed from the list"
        print(feedback)
    elif 'exit' in cmd_text:
        break
    else:
        print("Command is not valid ")
print("bye!")

What’s going wrong and why

The printing branch removes trailing newlines using strip("\n") so items look clean in the console. That’s fine. The problem is the add branch. It appends the new todo string without a line ending, then writes the entire list back with writelines. If the previous last item already lost its newline or the newly added one lacks it, the next write will place them on the same physical line. That’s how entries like “hii” and “bro” collapse into a single “hiibro” line, and subsequent additions keep merging.

The fix

Ensure every newly added item is persisted with a newline. Nothing else needs to change. By appending "\n" when adding, each todo becomes a proper line in todos.txt. The show branch can still strip the newline for clean output, and the file remains correctly structured.

if 'add' in cmd_text:
    entry_text = cmd_text[4:]
    with open('todos.txt', 'r') as fh:
        items_list = fh.readlines()
    items_list.append(entry_text + '\n')
    with open('todos.txt', 'w') as fh:
        fh.writelines(items_list)

Why this matters

When you treat a file as a sequence of lines, consistency of line endings is the contract. readlines relies on "\n" to determine boundaries. writelines writes exactly what you give it. If you omit the newline when persisting a record, the next record has no delimiter to separate it, and simple list-based storage breaks down. The fix is small, but it prevents subtle corruption of your todo list and keeps your CLI behavior predictable.

Practical closing notes

Keep each saved item terminated with a newline to preserve one-entry-per-line semantics. If you want to streamline the output logic later, you can start the enumeration from one directly, or harden the command parsing so that only commands like add and not adding or hello add trigger the add branch. For richer CLI behavior, dedicated modules are available that handle command loops and quality-of-life features such as remembering previous commands and highlighting; they can be introduced when the basic storage is solid.

The bottom line is simple: append "\n" when writing new todos. With that in place, “show” will print every item on its own line, exactly as intended.