Skip to content
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

Implement completionItem/resolve #432

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

LebJe
Copy link
Contributor

@LebJe LebJe commented Oct 5, 2021

This pull request adds support for completionItem/resolve to SourceKit-LSP.

Unfortunately, the tests are disabled (and this PR is a draft) because SourceKit cannot provide cursorinfo via an USR for anything other than things like structs:

{
  key.internal_diagnostic: "Unable to resolve type from USR."
}

Test Code:

/// `a` (does not work)
let a = 1

/// `b` (does not work)
func b(c: Int) {}

/// `E` (works)
enum E: CaseIterable {
	/// `a` (does not work)
	case a
	
	/// `b` (does not work)
	case b
	
	/// `c` (does not work)
	case c
}

/// `A` (works)
struct A: Equatable {
	
	/// `a` (does not work)
	let a: String
		
  	/// - Parameters:
  	///    - a: `String`
	/// - Note: does not work
	init(a: String) {
		self.a = a
	}
}

To test the above code, type something like E.a, or A.init, and see if the `` is rendered as a code block in the completion popup.

Perhaps this is related to this issue.

@LebJe LebJe changed the title Implement completionItem/resolve. Implement completionItem/resolve Oct 5, 2021
Copy link
Collaborator

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Thanks a lot for looking into this @LebJe. I’ll need to look into why sourcektid doesn’t return cursor info for the USR. Maybe @benlangmuir has an idea if this is something that’s supported or what might be going wrong here from the top of his head.

For now, I’ve added some comments inline.

{
return TextDocumentIdentifier(DocumentURI(string: textDocURI))
} else {
fatalError("Missing textDocURI in CompletionItem.data: \(data ?? "(CompletionItem.data is nil)")")
Copy link
Collaborator

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 make any assumptions about the requests sent to us by the client and, in particular, not crash if it is malformed. Could we return a failure value (e.g.nil) here instead of fatalErroring?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is TextDocumentIdentifier(DocumentURI(string: "")) ok?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you are not using this computed property at all, so let’s just remove it.

}
}

public static var method: String = "completionItem/resolve"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the method property is always the first property in a request type. Could you move it up to match this style?

Sources/SourceKitLSP/Swift/CodeCompletion.swift Outdated Show resolved Hide resolved
@ahoppen ahoppen self-assigned this Oct 6, 2021
@benlangmuir
Copy link
Member

Unfortunately, the tests are disabled (and this PR is a draft) because SourceKit cannot provide cursorinfo via an USR for anything other than things like structs:

Cursor info on USRs only works on a subset of USRs for nominal types IIRC. Please don't use this functionality. We should never have added it.

The way I would implement this would be

  1. Use the existing sourcekitd completion items from the active completion session to fill in the data (if we aren't already keeping them alive, we could). This allows reducing the data we send in the original completion response (notably not serializing the brief documentation string).
  2. Over time we can make this more lazy by avoiding computing the full completion item in sourcekitd up front and add new requests to query the details.

@LebJe
Copy link
Contributor Author

LebJe commented Oct 8, 2021

@benlangmuir, are you saying I should:

  • Have a Dictionary<String, SKDResponseDictionary> that stores the codecomplete results.
  • Rewrite completionsFromSKDResponse so that it:
    • Clears the old completions form the dictionary, and fills it with <unique id>: <completion response>.
    • Returns the minimum amount of information to the LSP client.
  • Rewrite _completionResolve so that it:
    • Uses the completion dictionary to fill in the rest of the data, and do any necessary processing (like rewriting placeholders).

With this approach though, it is still impossible to get full documentation.

Is there something I'm not noticing in the codecomplete results that would help me get the full documentation?


Cursor info on USRs only works on a subset of USRs for nominal types IIRC. Please don't use this functionality. We should never have added it.

Was the implementation never finished, or is something causing cursor info via USRs on things other than nominal types to not work?

{
return TextDocumentIdentifier(DocumentURI(string: textDocURI))
} else {
fatalError("Missing textDocURI in CompletionItem.data: \(data ?? "(CompletionItem.data is nil)")")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you are not using this computed property at all, so let’s just remove it.

@@ -169,18 +169,23 @@ extension SwiftLanguageServer {
// Map SourceKit's not_recommended field to LSP's deprecated
let notRecommended = (value[self.keys.not_recommended] as Int?).map({ $0 != 0 })

let kind: sourcekitd_uid_t? = value[self.keys.kind]
let kind = (value[self.keys.kind] as sourcekitd_uid_t?)?.asCompletionItemKind(self.values) ?? .value
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason why you need to refactor kind anymore? I think the previous version read better 😉

@@ -169,18 +169,23 @@ extension SwiftLanguageServer {
// Map SourceKit's not_recommended field to LSP's deprecated
let notRecommended = (value[self.keys.not_recommended] as Int?).map({ $0 != 0 })

let kind: sourcekitd_uid_t? = value[self.keys.kind]
let kind = (value[self.keys.kind] as sourcekitd_uid_t?)?.asCompletionItemKind(self.values) ?? .value
var data: [String: LSPAny] = ["textDocURI": .string(snapshot.document.uri.stringValue)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it make sense to define the magic "textDocURI" string as a static property on CompletionItem instead of repeating it throughout the codebase? IIUC it’s not part of the LSP standard but just a key that you’re using for the opaque data dictionary.
Same for "usr"

@benlangmuir
Copy link
Member

@benlangmuir, are you saying I should:

Something along these lines is what I had in mind for an initial implementation, but I haven't thought through the details of whether it makes sense to clear it vs. extend the data, etc.

Is there something I'm not noticing in the codecomplete results that would help me get the full documentation?

No, that's correct in the current API. We should separate out two possible goals here:

  • Improve performance by being more lazy about returning all the information for a code-completion item.
  • Return the full documentation instead of the brief documentation.

I'm not sure one way or the other whether that second item is a good idea -- it's certainly reasonable to ask for the full documentation of a completion item, but doing so _by default using the existing API _ is more a question of how it corresponds to what LSP editors are expecting.

If we do want to return the full documentation, I think a better foundation for that is looking up information from a code-completion item in the current session, and potentially adding more lazy APIs to support that in sourcekitd, rather than via cursor info with a USR.

CC @rintaro

Cursor info on USRs only works on a subset of USRs for nominal types IIRC. Please don't use this functionality. We should never have added it.

Was the implementation never finished, or is something causing cursor info via USRs on things other than nominal types to not work?

It was never properly implemented. I think there were some partial attempts to extend it, but they were unmaintainable and later removed.

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.

None yet

5 participants