Skip to content

Prefix LSP/BSP extension methods with sourcekit/#48

Open
rintaro wants to merge 2 commits into
swiftlang:mainfrom
rintaro:sourcekit-extensions
Open

Prefix LSP/BSP extension methods with sourcekit/#48
rintaro wants to merge 2 commits into
swiftlang:mainfrom
rintaro:sourcekit-extensions

Conversation

@rintaro
Copy link
Copy Markdown
Member

@rintaro rintaro commented Apr 29, 2026

  • Rename all SourceKit-LSP–specific LSP/BSP requests and notifications to use the sourcekit/ prefix (e.g. workspace/testssourcekit/workspace/tests)
  • Add method: String parameter to Connection.send<R>(_:method:id:reply:) defaulted to R.method, but overridable with legacy names.
  • Add legacyNames: [String: String] parameter to MessageRegistry.init so the registry dispatches incoming messages from existing clients using either current or legacy method names.
  • Add LegacyNameFallbackConnection: a Connection wrapper that retries requests with the legacy name on methodNotFound, with a flag so subsequent requests skip the primary name once a legacy peer is detected.

see swiftlang/sourcekit-lsp#2632


/// Set to `true` once the peer successfully responds to a legacy method name,
/// after which requests that have a legacy name are sent using that name directly.
private let prefersLegacyMethodNames: AtomicBool = .init(initialValue: false)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a per-request name tracking here in case the client has migrated one request name but not another? I don’t think that we should make any cross request name assumptions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought clients should migrate all at once. But yeah, per request name might be safer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now this is methodNamesPreferringLegacyName: ThreadSafeBox<Set<String>>

///
/// Calls are recorded in `calls`. `responses` maps method names to explicit
/// results; any method not in the dictionary defaults to `.methodNotFound`.
private final class MockConnection: Connection, @unchecked Sendable {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @unchecked Sendable seems not correct to me. If we need it, we should have a very strong argument for why it’s needed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just for tests. It's safe because we never use an instance concurrently. I don't think it's worth to make it real Sendable.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think we should set a bad example / bad precedence here with @unchecked Sendable. I think it would be fairly simple to store all the mutable state in a struct and then just put it in a ThreadSafeBox to get proper concurrency safety.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, done

@Suite struct LegacyNameFallbackConnectionTests {

// No legacyName: pass through regardless of the reply – no retries, flag never set.
@Test func testNoLegacyMethods_passesThroughWithoutRetry() async {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mixed snake case and a snake case seems like an odd naming choice to me.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention here is test<WhatToTest>_<expectedOutcome>. They are originally generated names, but I thought it's kinda nice. But I agree it's not a convention we use, and I'm happy to fix it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to follow the conventions that other repos in the swiftlang org have (eg. swift-build) for consistency and I don’t think they use underscores. Correct me if I’m wrong.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed

@rintaro rintaro force-pushed the sourcekit-extensions branch from e63179f to 4b8d2b2 Compare May 7, 2026 18:13
rintaro added 2 commits May 13, 2026 20:13
- Rename all SourceKit-LSP–specific LSP/BSP requests and notifications
  to use the `sourcekit/` prefix (e.g. `workspace/tests` →
  `sourcekit/workspace/tests`)
- Add `method: String` parameter to
  `Connection.send<R>(_:method:id:reply:)` defaulted to `R.method`, but
  overridable with legacy names.
- Add `legacyNames: [String: String]` parameter to `MessageRegistry.init`
  so the registry dispatches incoming messages from existing clients using
  either current or legacy method names.
- Add `LegacyNameFallbackConnection`: a `Connection` wrapper that retries
  requests with the legacy name on `methodNotFound`, with a global flag
  so subsequent requests skip the primary name once a legacy peer is
  detected.
@rintaro rintaro force-pushed the sourcekit-extensions branch from 4b8d2b2 to 857a57f Compare May 14, 2026 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants