A small, slightly overengineered line & function counter that tracks how your project grows (and mutates) over time.
It now:
- Counts lines for multiple languages
- Stores history in SQLite
- Lets you pick exactly which files/folders count
- Shows a function breakdown per file
- Lets you delete old snapshots from a nice little list
- And, of course, has more features, more buttons, and more bugs than ever before 🎉
- Code written by ChatGPT (GPT-5.1 Thinking).
- Human supervisor, button-clicker, and chaos director: Coverfish.
- Coverfish claims:
“I have no idea what I’m doing.”
which is still the core design philosophy of this project.
On every run, python linecounter.py:
-
Figures out your project root:
- The script lives somewhere like:
project_root/code/python linecounter.py - It scans one level above that (i.e.
project_root), recursively.
- The script lives somewhere like:
-
Discovers all files with supported extensions (see below), skipping big junk like
.git,venv,node_modules, etc. -
Applies your file selection (from the “Choose files” tab) so only the files you care about are counted.
-
For each selected file, counts:
- total lines
- non-empty lines
-
Saves a snapshot of those counts into
line_history.db(SQLite). -
Only creates a new snapshot if something actually changed since the last one.
-
Collects functions from selected files (Python, C/C++, JavaScript).
-
Opens a GUI window with four tabs:
- Snapshots (F1) – Text summary + snapshot list (with delete).
- Graph (F2) – Horizontal bar chart showing line diffs between snapshots.
- Functions (F3) – Per-file function list with line numbers.
- Choose files (F4) – Tree of your project where you can tick/untick files.
If nothing changed, it politely reuses the last snapshot instead of bloating your DB with identical copies.
The Snapshots tab is split:
-
Left side:
A big, colorized text view of all snapshots (newest first by default), showing:- Snapshot number and timestamp (and weekday labels like “MONDAY” for the last snapshot of each day)
- Per-file:
- total lines
- non-empty lines
- code percentage
- Totals per snapshot at the bottom.
-
Right side:
- Top: A scrollable list of snapshots in a
Treeview:- Rows like:
Snapshot 26 - 2025-12-11T22:42:17 | Thursday - Newest snapshots at the top.
- You can select one or many (Ctrl/Shift click).
- Rows like:
- Bottom: A big button:
“Delete selected snapshots”
This will:- Delete the selected snapshots from the SQLite DB
- Refresh the left text view
- Refresh the Graph tab, and keep navigation in sync
- Top: A scrollable list of snapshots in a
So yes: you now have a built-in snapshot trash can. Use with power and regret.
The Graph tab shows a horizontal bar chart for the currently selected snapshot:
- It compares the current snapshot to the previous one and displays, per file:
- baseline lines (previous snapshot)
- newly added lines
- removed lines
- Legend explains the colors:
- Blue: baseline
- Red: added
- Green: removed
- Totals (total lines / non-empty lines) are shown at the top.
Navigation:
- Up Arrow / ▲ / “newer snapshot” – move to a newer snapshot
- Down Arrow / ▼ / “older snapshot” – move to an older snapshot
- Also controlled by the buttons in the Graph tab.
It auto-sizes vertically and has its own scrollbar, so very large projects don’t completely murder your monitor.
The Functions tab scans only the currently selected files (from F4) and shows:
- A total function count across all files
- For each file:
- File path + number of functions
- Each function name
- The line number where the function is defined
Color scheme:
- Total header: bright green
- File headers: blue
- Function names: VS-Code-ish yellow
- Line numbers: green
- Background: dark theme (your eyes are welcome)
Right now:
- Python (
.py)- Uses the real Python AST (
astmodule) - Detects:
- Top-level functions
- Methods inside classes
- Nested functions
- Async functions
- Uses the real Python AST (
- JavaScript (
.js) (heuristic)- Recognises patterns like:
function myFunc(...) {export function myFunc(...) {const myFunc = function(...) {const myFunc = (...) => { ... }
- Recognises patterns like:
- C / C++ (
.c,.h,.cpp,.hpp) (also heuristic)- Looks for lines that look like function definitions with
{on the same line - Skips obvious preprocessor lines (
#include,#define, etc.)
- Looks for lines that look like function definitions with
HTML and CSS are line-counted but not function-counted (they kind of don’t have “functions” in this sense, only chaos).
Heuristics mean it might miss some exotic definitions or get confused by weird formatting, but for “what do I even have in this codebase?” it’s surprisingly useful.
This is the project selector brain of the whole thing.
- Shows a tree view of your project starting at the project root
(one level above the folder containing
python linecounter.py). - Each file line looks like:
☑ code/main.py☐ code/some_other_file.cpp
- Behaviour:
- Click the triangle next to a folder to expand/collapse it.
- Click a file row to toggle that file’s inclusion.
- Clicking on a folder can toggle all its descendants (depending on the exact version you’re running).
- Your choices are saved in the DB (
file_selectiontable), so the next time you run:- Only those files are:
- counted in snapshots
- shown in the functions tab
- Only those files are:
This lets you, for example, exclude things like:
- auto-generated code
- external libs
- one particularly cursed prototype folder
…without touching SUPPORTED_EXTENSIONS.
There are two related but different concepts:
These extensions are currently included in line counting:
SUPPORTED_EXTENSIONS = {
".py", ".c", ".h", ".cpp", ".hpp",
".html", ".css", ".js",
}So line stats are gathered for:
- Python (
.py) - C / C++ (
.c,.h,.cpp,.hpp) - HTML (
.html) - CSS (
.css) - JavaScript (
.js)
…and anything else you decide to add to that set.
Plus, the “Choose files” tree lets you narrow this down further.
Function detection is currently implemented for:
- Python:
.py - JavaScript:
.js - C / C++:
.c,.h,.cpp,.hpp
HTML and CSS files are ignored by the function counter (on purpose).
- Python 3.10+
- Standard library only:
pathlibsqlite3tkinterastosre
On some Linux distros you may need to install tkinter manually:
sudo apt install python3-tkNo pip install required. No virtualenv required.
(But you’ll probably end up with one anyway, because that’s life.)
git clone https://github.com/Coverfish/line-counter.git
cd line-counter(Optional) Test if tkinter works:
python -m tkinterIf a small demo window appears, you’re good.
The script expects something like this structure:
your-project/
code/
python linecounter.py
something_else/
more_code/
You run it from inside code/ (or via full path), and it will scan one level up
(your-project/) recursively.
Run:
python "python linecounter.py"On each run it will:
- Discover files from project root.
- Apply your saved file selection.
- Count lines and compare with the last snapshot.
- Only create a new snapshot if there are changes.
- Collect functions from the selected files.
- Open the GUI with all four tabs.
The database line_history.db lives next to python linecounter.py
(in the code/ folder).
- Feature: it works.
- Bug: sometimes it works too much.
- Feature: supports multiple languages.
- Bug: those languages did not ask for this.
If it:
- Counts the wrong thing → you have a graph of your mistakes.
- Misses a function → maybe that function deserved it.
- Breaks completely → you get to keep both pieces.
This project is basically:
Free to use in any way you see fit.
You can:
- Copy it
- Modify it
- Embed it into your own tools
- Use it to impress/horrify teammates
- Sacrifice it to the refactoring gods
There is no warranty of any kind.
If this script deletes all your snapshots because you mis-clicked the big button, that’s between you and your backups.
If you want a more official label, you can mentally treat this as:
Public domain / do whatever you want.
(Old screenshots still apply, but now imagine extra tabs and more chaos.)





