Repository navigation
Conversation
The dbmate and goose markers, the migration file names and the Supabase project detection of `init` move into a `migrations` module of pgls_workspace, with one adapter per tool behind a `MigrationTool` trait. A document gets the adapter from the settings of its file when it is opened, and again when the settings change, and keeps the sections of its content instead of computing them on every request.
This was referenced Oct 26, 2026
psteinroe
commented
Nov 5, 2026
psteinroe
left a comment
Collaborator
Author
There was a problem hiding this comment.
Supabase and the others should be handled by a single model. This is still too loose.
…luded `MigrationTool` now describes everything the language server knows about a tool: how `init` finds its projects and what it sets for them, its default migrations folder, which files are migrations and their sequence numbers, and how a file splits into sections with which marker diagnostics. Plain files and Supabase run every file as one migration, dbmate and goose split it at their markers. `init` goes through `find_projects`, which asks every tool, instead of the Supabase helpers. It also detects dbmate projects (a first migration in `db/migrations` with `-- migrate:up`). goose has no default folder or configuration file, so it isn't detected.
# Conflicts: # crates/pgls_workspace/src/workspace/server.rs
…s own crate Move the migration tools out of pgls_workspace into the new leaf crate pgls_migrations. Every tool (plain SQL, Supabase, dbmate, goose) implements every method of the trait: id, name, detect, is_tool_directory, default_dir, file, version and read. What tools share are helpers they call: naming, directives, units and detect. read() returns a Script with the units of the file (replacing pgls_analyser::MigrationSection) and the marker problems; the document keeps it per version. file() returns the role and version of a path, used for the migration-only rules and migrations.after. init uses detect::find_projects and writes the configuration itself. No behaviour change.
This was referenced Nov 9, 2026
… history tables in database - `is_tool_directory` is gone: Supabase's `detect` also finds the project when `init` runs inside its `supabase` folder, and says so with `Detected::configuration_directory` (`..`). `init` takes the folder of the configuration from the project it found. - `MigrationTool::database` tells where the tool records the migrations it ran (`History::Table` or `History::Schema`). Completions rank the history of every tool low, instead of the hard-coded `supabase_migrations` schema; sqlx's `_sqlx_migrations` stays as an extra until sqlx is a tool.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The migration tool support had grown into one file per concern rather than one place per tool: the dbmate and goose markers shared a single function with a
matchonSectionMarkersin every loop, the file name parsing lived next to them inworkspace/server/migration.rs, and the Supabase detection lived in theinitcommand. The server rebuilt the sections from the settings on every request (document_sections), up to three times per diagnostics pull. This PR is the structural cleanup asked for on #949, without behaviour changes, so that ENVSUB and the next tools have a place to go.What changed
pgls_workspace::migrationsmodule:MigrationTooltrait withsections(text)andmarker_diagnostics(text, sections, statements), andmigration_tool(SectionMarkers)to pick the adapter from the settings (Nonefor files that are one migration).dbmate.rsandgoose.rs, one adapter each, with their marker parsing and tests. Each describes its markers (MarkerSyntax: tool name, first marker, what a look-alike line means), andmarkers.rsholds what they share: the line scan, building the sections, andSectionMarkerDiagnosticwith the "statements before the first marker" and look-alike line checks.files.rs: which files are migrations and their sequence numbers (get_migration,is_in_migrations_dir), used formigrations.afterand the migration-only rules. Moved unchanged.supabase.rs: the Supabase project detection frominit(folder lookup,config.tomlport), moved unchanged, plusSupabaseProject::configure, which fills thedbandmigrationssettings thatinitused to fill inline.initkeeps the console output.tomlmoves frompgls_clitopgls_workspacewith it.Documentgets the migration tool from the settings of its file when it's opened (Document::open), keeps the sections of its content, and recomputes them onupdate_content. When the root or nested settings of a project change,update_settingsandupdate_nested_settingsapply the new tool to the project's open documents. For that, the document remembers its project key. The analyser,walk_statementsand the marker diagnostics readdoc.migration_sections()instead of recomputing them.fix_filebuilds its intermediate documents with the tool of the open one.The configuration (
sectionMarkers,lintDown,migrationsDir,after) andMigrationSectioninpgls_analyserare unchanged.Cost
The sections are computed once per document version instead of once or more per request: a line scan, O(file). On a 20,000-statement file (1.3 MB, release build) that's 0.6 ms with goose and 1.7 ms with dbmate per edit, and a request no longer pays it. A settings change rescans the open documents of the project.
Tests
The tests of the old module moved with the code into
dbmate.rs,goose.rs,files.rsandsupabase.rs, with the same inputs and assertions; the ones that covered both tools are split per tool. New:section_markers_follow_settings_changesopens a file, then switchessectionMarkersto dbmate and back to none, and checks that the marker diagnostics follow without reopening the file.cargo test -p pgls_workspace -p pgls_cli -p pgls_lsppasses, and no snapshots changed.Not covered
MigrationSectionandDirectionstay inpgls_analyser, which consumes them, andSectionMarkersstays the name of the setting.pgls_lexeris not part of this. feat(workspace): analyse goose ENVSUB expansions as parameters #949 builds the goose ENVSUB preprocessing on top of this structure.Update (review follow-up): Every migration tool, Supabase included, is now one
MigrationTool(pgls_workspace::migrations), which describes everything the language server knows about it:migrations.sectionMarkersvalue that selects it,supabase/migrations, dbmatedb/migrations, none for goose),initfinds its projects (detect, andis_tool_directoryfor running inside thesupabasefolder),is_migration,migration, used formigrationsDirandafter),sections,marker_diagnostics).The defaults fit a tool that runs every file as one migration, so
Plain(no tool) andSupabaseonly override what's theirs, and dbmate and goose add their markers.migration_tool(sectionMarkers)always returns a tool (Plainfor"none"), and a document always has one, so the workspace doesn't special-case files without markers.migrations::projectholds whatinitneeds from any tool:find_projectsasks every tool in the folder and, at the root of a monorepo, one or two levels below it; aProjectcarries the tool, its folder, its migrations folder, its section markers, its local database and a warning, andProject::configurefillsdbandmigrations.initonly prints the result. The Supabase helpers (SupabaseProject,find_supabase_directories, …) are gone.Behaviour changes, all in
init:<version>_<name>.sqlindb/migrationswith a-- migrate:upline, which dbmate requires in every migration.initthen setsmigrationsDirtodb/migrationsandsectionMarkersto"dbmate", and leaves the connection alone, since dbmate reads it fromDATABASE_URL. Also found one or two levels below the root, like Supabase projects.-dir/GOOSE_MIGRATION_DIR,.by default), so there's no reliable sign of a goose project.inittreats them like several Supabase projects: it sets nothing and lists them ("Found several migration projects (supabase: Supabase, db: dbmate)"). The message for several Supabase projects is unchanged.The Supabase output and settings are unchanged, and so is the workspace: Supabase has no markers, so its files run with
Plain, as before.Tests: the existing ones moved with the code. New:
selects_the_tool_of_the_section_markers,supabase_files_are_one_migration,migration_file_names(dbmate), and inmigrations::projectdetection of Supabase and dbmate,db/migrationswithout dbmate markers, projects below the root of a monorepo, projects at the root taking precedence, and the folder abovesupabase/. CLI:init_with_dbmate_project_sets_the_migrations_and_section_markers,init_with_supabase_and_dbmate_projects_leaves_the_configuration_alone.Update (redesign): Updated with main, and the migration tools are now one trait without defaults, in a crate of their own (
pgls_migrations):MigrationToolhasid,name,detect,is_tool_directory,default_dir,file,versionandread, and every tool (plain SQL, Supabase, dbmate, goose, intools/) implements all of them. Nothing is inherited: what tools share are helpers they call,naming(<number>_<name>.sqland its folder form),directives(the line scan and the look-alike marker lines),unitsanddetect(the monorepo search).read(text)returns aScript: the units of the file (Unit, which replacespgls_analyser::MigrationSection) and the marker problems. The document keeps it per version, as before.file(path, migrationsDir)returns the role (migration or not) and the version, which the migration-only rules andmigrations.afteruse.initgetsDetectedprojects fromdetect::find_projectsand writes the configuration itself;Project::configureis gone from the workspace.pgls_analyserandpgls_workspacedepend onpgls_migrations; it depends on neither, nor onpgls_configuration.sectionMarkersstill selects the tool, so Supabase isn't selectable yet (that's feat(configuration): rename migrations.sectionMarkers to migrations.tool #1036).No behaviour change: the tests moved with the code, and the marker messages are unchanged.
Update: Two more pieces of the design:
is_tool_directoryis gone. Supabase'sdetectalso finds the project wheninitruns inside itssupabasefolder, and returns it withDetected::configuration_directoryset to..;initwrites the configuration there. One difference: insidesupabase/, only the Supabase project is found now. Before, every tool was detected in the folder above, so a dbmatedb/migrationsnext tosupabase/made it ambiguous and nothing was configured.configures_the_project_above_the_supabase_foldercovers it.MigrationTool::databasesays where the tool records the migrations it ran:History::Schema("supabase_migrations"),History::Table("schema_migrations")for dbmate,History::Table("goose_db_version")for goose, none for plain SQL. Completions rank the history of every tool low, instead of the hard-coded schema;_sqlx_migrationsstays as an extra until sqlx is a tool. New: dbmate's and goose's tables are ranked low too (ranks_the_history_tables_of_migration_tools_low).