Skip to content

Support expansion of nested macros #1631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 18, 2024

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Aug 16, 2024

The basic idea is that a sourcekit-lsp://swift-macro-expansion URL should have sufficient information to reconstruct the contents of that macro buffer without relying on any state in SourceKit-LSP. The benefit of not having any cross-request state in SourceKit-LSP is that an editor might can send the workspace/getReferenceDocument request at any time and it will succeed independent of the previous requests. Furthermore, we can always get the contents of the macro expansion to form a DocumentSnapshot, which can be used to provide semantic functionality inside macro expansion buffers.

To do that, the sourcekit-lsp: URL scheme was changed to have a parent instead of a primary, which is the URI of the document that the buffer was expanded from. For nested macro expansions, this will be a sourcekit-lsp://swift-macro-expansion URL itself.

With that parent, we can reconstruct the macro expansion chain all the way from the primary source file. To avoid sending the same expand macro request to sourcekitd all the time, we introduce MacroExpansionManager, which caches the last 10 macro expansions.

SwiftLanguageService now has a latestSnapshot method that returns the contents of the reference document when asked for a reference document URL and only consults the document manager for other URIs. To support semantic functionality in macro expansion buffers, we need to call that latestSnapshot method so we have a document snapshot of the macro expansion buffer for position conversions and pass the following to the sourcekitd requests.

keys.sourceFile: snapshot.uri.sourcekitdSourceFile,
keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath,

We should consider if there’s a way to make the latestSnapshot method on documentManager less accessible so that the method which also returns snapshots for reference documents is the one being used by default.

CC @lokesh-tr

The basic idea is that a `sourcekit-lsp://swift-macro-expansion` URL should have sufficient information to reconstruct the contents of that macro buffer without relying on any state in SourceKit-LSP. The benefit of not having any cross-request state in SourceKit-LSP is that an editor might can send the `workspace/getReferenceDocument` request at any time and it will succeed independent of the previous requests. Furthermore, we can always get the contents of the macro expansion to form a `DocumentSnapshot`, which can be used to provide semantic functionality inside macro expansion buffers.

To do that, the `sourcekit-lsp:` URL scheme was changed to have a parent instead of a `primary`, which is the URI of the document that the buffer was expanded from. For nested macro expansions, this will be a `sourcekit-lsp://swift-macro-expansion` URL itself.

With that parent, we can reconstruct the macro expansion chain all the way from the primary source file. To avoid sending the same expand macro request to sourcekitd all the time, we introduce `MacroExpansionManager`, which caches the last 10 macro expansions.

`SwiftLanguageService` now has a `latestSnapshot` method that returns the contents of the reference document when asked for a reference document URL and only consults the document manager for other URIs. To support semantic functionality in macro expansion buffers, we need to call that `latestSnapshot` method so we have a document snapshot of the macro expansion buffer for position conversions and pass the following to the sourcekitd requests.
```
keys.sourceFile: snapshot.uri.sourcekitdSourceFile,
keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath,
```

We should consider if there’s a way to make the `latestSnapshot` method on `documentManager` less accessible so that the method which also returns snapshots for reference documents is the one being used by default.

Co-Authored-By: Lokesh T R <[email protected]>
@ahoppen ahoppen force-pushed the nested-macro-expansions branch from 015d626 to 90e0f3f Compare August 16, 2024 21:51
@ahoppen
Copy link
Member Author

ahoppen commented Aug 16, 2024

@swift-ci Please test

Copy link
Contributor

@lokesh-tr lokesh-tr left a comment

Choose a reason for hiding this comment

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

I love how you implemented it and am glad that my initial idea of having nested URLs is good and that the macro expansions storage idea took its shape into a Cache system which is pretty neat.

Thank you so much for lending me an helping hand. ❤️

I will look into building semantic functionality on top of this and see if there exists any issues in integrating this with VS Code / Extension.

@ahoppen
Copy link
Member Author

ahoppen commented Aug 18, 2024

@hamishknight @bnbarham If you have review comments, I will address them in a follow-up PR so that @lokesh-tr can continue building semantic functionality in macro expansion buffers on top of this.

@ahoppen ahoppen merged commit 5833322 into swiftlang:main Aug 18, 2024
3 checks passed
@ahoppen ahoppen deleted the nested-macro-expansions branch August 18, 2024 18:58
Copy link
Contributor

@lokesh-tr lokesh-tr left a comment

Choose a reason for hiding this comment

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

Just noticed a few things while working on the new PR, I will fix them.

@lokesh-tr
Copy link
Contributor

Here's the new one: #1634

///
/// The primary file is used to determine the workspace and language service that is used to generate the reference
/// document as well as getting the reference document's build settings.
var primaryFile: DocumentURI? {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like all-but-one uses of this immediately do ?? self, would it be worth making this non-optional? For the one use that cares about nil, maybe we could expose isPrimaryFile?

Copy link
Member Author

Choose a reason for hiding this comment

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

The usages that will be introduced by #1634 will expect primaryFile to be nil if the URI is an actual on-disk file, so I think keeping it optional makes sense.

guard let primaryFileURL = expandMacroCommand.textDocument.uri.fileURL else {
throw ResponseError.unknown("Given URI is not a file URL")
}
let primaryFileDisplayName =
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be worth renaming this to currentFileDisplayName/parentFileDisplayName or something to avoid confusion?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh yes, good suggestion.

Comment on lines +394 to +396
switch try? ReferenceDocumentURL(from: notification.textDocument.uri) {
case .macroExpansion:
break
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this be an early return? Same goes for open and close below

Copy link
Member Author

Choose a reason for hiding this comment

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

I want to migrate generated interfaces to reference documents next, which will allow us to provide semantic functionality in them. They will require logic for open/close/reopen, so I don’t think that the early return scales. That’s why I would just keep it as a switch like it is today.

ahoppen added a commit to ahoppen/sourcekit-lsp that referenced this pull request Aug 19, 2024
ahoppen added a commit that referenced this pull request Aug 20, 2024
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.

3 participants