Repository navigation
Conversation
psteinroe
force-pushed
the
feat/nested-configs
branch
from
October 19, 2026 12:35
818bd74 to
057e50f
Compare
psteinroe
force-pushed
the
feat/nested-configs
branch
from
October 19, 2026 13:05
057e50f to
1485b46
Compare
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.
Addresses #719 (see also #326 and #293).
Problem
The language server loaded one configuration file per LSP workspace folder, searching upward from the folder root. A
postgres-language-server.jsoncin a subdirectory was ignored. So a repo withsrc/db1andsrc/db2pointing at different databases could only be configured through a multi-root workspace. The CLI likewise used only the working directory's configuration.What changed
Each file now uses the nearest
postgres-language-server.jsoncat or above it, within the workspace root, like Biome's nested configs.pgls_workspace::configuration::register_nested_projects): finds configuration files below a root through the newFileSystem::find_files. On the OS file system it usesignore::WalkBuilder, so it respects.gitignore/.ignore, skips hidden directories, and skipsnode_modules/target. Each configuration file is registered as its own project viaregister_project_folder+update_settings, shallowest first. A directory that the project above it ignores (files.ignore/files.include) is skipped. The root project is the current project again afterwards.WorkspaceSettings::get_project_key_for_path): every per-file operation (diagnostics, formatting, completions, hover, code actions, execute statement,is_path_ignored, migrations) now uses the settings of the project with the deepest folder containing the path. Before, it used a mutable "current project" thatopen_fileswitched as a side effect. That was racy across documents and picked the first prefix match, not the longest. A relative path (CLI arguments) resolves against the current project's folder, which is the working directory. Paths outside every project keep using the current project, so single-project behaviour is unchanged.register_project_foldernow reuses an existing project with the same path instead of only comparing against the current one. Before, re-registering after switching projects added duplicates.didChangeWatchedFilesregistration now watches<root>/**/postgres-language-server.jsonc. Any config-file event reloads the settings and now refreshes the diagnostics of open documents. That refresh was commented out before, so diagnostics stayed stale until the next edit.checkandformatregister nested projects below the working directory. Env vars and CLI arguments are layered over each nested config, the same way as over the root config.check_database_connectionchecks every project's database.ConnectionKey), so per-project databases needed no new plumbing. Pools are created lazily (connect_lazy_with), so no connection is opened until a file of that project needs one. Projects pointing at the same database share the pool and snapshot.snapshot_beforenow takes the file'ssearch_pathexplicitly instead of reading the current project's.Inheritance: explicit only
A nested configuration inherits nothing implicitly; unset settings are defaults. To share settings, use the existing
extends:I chose this over Biome's
root: falsesemantics to keep it simple, predictable, and free of new configuration surface.extendsalready merges with the nested file's own settings taking precedence. Relative paths such asmigrations.migrationsDirresolve against the nested file's folder.Interaction with the user configuration (#878)
#878 falls back to a user-level config when no project config is found at or above the project directory. With nested configs:
load_configurationnever reaches the fallback for it. To use the user config there, extend it explicitly, e.g."extends": ["~/.config/postgres-language-server/postgres-language-server.jsonc"](~expansion is from feat(configuration): expand ~ in extends and fall back to a user configuration #878).~as a workspace on macOS would register~/Library/Application Support/postgres-language-server/as a nested project, because that folder isn't hidden.Covered by
nested_projects_do_not_merge_the_user_configuration: a workspace at the home directory with no project config and a user config that setsbanDropColumn: error. A file without a project config getserror. A nested{}project gets the defaultwarning. A nested project thatextends: ["~/…"]getserror. The user config dir is not registered as a project.Deliberately out of scope
--config-path(CLI) and the LSP's explicit configuration path: an explicitly chosen file still applies to everything, and nested files are not looked up.dblint(not file-based) uses the working directory's configuration.files.ignore/includeglobs are matched as before. The matcher does not anchor globs to the config's folder, which is existing behaviour and not changed here, so the docs don't promise it.invalidate_schema_cachewithoutall,pull_db_diagnostics) still use the current/root project.Risks
node_modules,target, gitignored folders, and folders ignored by the root config are excluded, which should cover most accidental cases.Tests
pgls_workspacesettings unit tests: longest-prefix selection (incl.db1vsdb10), relative paths, the unnamed project as the least specific, fallback to the current project, and removing the current project.pgls_workspaceserver tests:nested_projects_do_not_merge_the_user_configuration(see above).nested_projects_use_their_own_migrations_settingsputs the same dbmate migration in a nested project (sectionMarkers: dbmate,lintDown: false,migrationsDir,after) and in a root without migrations settings. Only the nested one skips the down section, andafteronly ignores files in the nestedmigrations/folder.nested_projects_use_the_nearest_configuration_filecovers per-directory linter settings, a deeper override, a config in a root-ignored folder that is skipped, and one undernode_modulesthat is never loaded.registering_a_project_folder_again_reuses_its_project.crates/pgls_lsp/tests/server.rs):nested_configuration_files_apply_to_their_directoriesuses a temp workspace with a root config and two nested configs. It checks differentbanDropColumnseverities per directory, picks up a config created at runtime, and re-publishes diagnostics after a nested config is deleted.assert_check.rs):check_uses_the_nearest_configuration_filecovers directory traversal and relative path arguments, including a nested config thatextendsthe root.The tests use linter settings rather than two databases. Database selection goes through the same per-file
Settingslookup, and pools/snapshots were already keyed by connection.