Description
Motivation
To be able to access information about Swift attributes through SourceKit requests such as editor.open
, its key.substructure
or document structure part of the response should include more information on attributes.
Missing attribute arguments
Currently, only the basic type of attribute, and its offset and length are provided in the response.
E.g. for this function
@available(swift 5.10)
func foo(){}
the key.substructure
response includes this
{
key.offset: 0,
key.length: 22,
key.attribute: source.decl.attribute.available
}
which does not include an information about the arguments of the attribute.
(reproduce using this request in sourcekitd-repl)
{
key.request: source.request.editor.open,
key.name: "myname",
key.enablesubstructure: 1,
key.sourcetext: "@available(swift 5.10) func foo(){}"
}
Missing custom attribute information
For custom attributes, the name of the custom attribute isn't available, it's just shown as source.decl.attribute._custom
.
Take this file:
@propertyWrapper
struct MyCustomAttribute{
var wrappedValue: Int
}
struct MyTopLevel {
@MyCustomAttribute
var i:Int
}
The key.substructure
response for the i
property of the MyTopLevel
struct includes
{
key.offset: 97,
key.length: 18,
key.attribute: source.decl.attribute._custom
}
(reproduce using this request in sourcekitd-repl)
{
key.request: source.request.editor.open,
key.name: "myname",
key.enablesubstructure: 1,
key.sourcetext: "@propertyWrapper\nstruct MyCustomAttribute{\n var wrappedValue: Int \n}\n\nstruct MyTopLevel {\n @MyCustomAttribute\n var i:Int\n}\n \n"
}
Little distinction between attributes and declaration modifiers
Currently, attributes and declaration modifiers are not distinguished in the key.substructure
response. They both live under the key.attributes
list and their UIDs both contain .attribute.
.
E.g. for
@available(swift 5.10) public func foo(){}
This is contained in the key.substructure
response:
key.attributes: [
{
key.offset: 23,
key.length: 6,
key.attribute: source.decl.attribute.public
},
{
key.offset: 0,
key.length: 22,
key.attribute: source.decl.attribute.available
}
]
(reproduce using this request in sourcekitd-repl)
{
key.request: source.request.editor.open,
key.name: "myname",
key.enablesubstructure: 1,
key.sourcetext: "@available(swift 5.10) public func foo(){}"
}
Unlike the first two kinds of missing information in this proposal, this one can be inferred, but I still feel it would be cleaner to mark these distinct ways of annotating functions.
Proposed solution
Missing attribute arguments
To support attribute arguments, two things need to be considered:
- How to structure a response about attribute arguments
- How to identify attribute arguments themselves
How to structure a response about attribute arguments
- One option would be to have the
key.attribute
list include an attribute with the samekey.attribute
kind multiple times, but a different argument each time, resulting in something like this:(this seems more consistent with the current implementation, but not very clean)key.attributes: [ { key.offset: 0, key.length: 22, key.attribute: source.decl.attribute.available, key.argument: ..., key.argumentoffset: ..., key.argumentlength: ... }, { key.offset: 0, key.length: 22, key.attribute: source.decl.attribute.available, key.argument: ..., key.argumentoffset: ..., key.argumentlength: ... } ]
- My preferred alternative would be a separate
key.arguments
list attached to an attribute:In my opinion, this seems cleaner and easier to access.key.attributes: [ { key.offset: 0, key.length: 22, key.attribute: source.decl.attribute.available, key.arguments: [ { key.argument: ..., key.offset: ..., key.length: ... }, { key.argument: ..., key.offset: ..., key.length: ... } ] } ]
How to identify attribute arguments themselves
I deliberately omitted the key.argument
values above, because it would need to be decided how these are to be represented. Options include:
- strings, possibly with spaces in them
- a list of strings
- UIDs, like attributes themselves
- etc.
As I'm not very familiar with the Swift language itself and don't know all the cases and trade-offs here I would like to leave this fully open to discussion.
Missing custom attribute information
I think the cleanest solution would be to add a key.name
or key.customattributename
for attributes with key.attribute: source.decl.attribute._custom
{
key.offset: 97,
key.length: 18,
key.attribute: source.decl.attribute._custom,
key.customattributename: "MyCustomAttribute"
}
Little distinction between attributes and declaration modifiers
Here there are again a few options:
- splitting the
key.attributes
list into akey.declarationmodifiers
andkey.attributes
(breaking change) - adding some kind of distinction UID key, e.g.
key.kind: attribute
/key.kind: modifier
/key.kind: declarationmodifier
(or an entirely newkey.xyz
) (non-breaking change) - Changing the
key.attribute
UIDs for declaration modifiers, e.g. (for the example above):(breaking change, but probably wouldn't break as much as 1.)key.attributes: [ { key.offset: 23, key.length: 6, key.attribute: source.decl.modifier.public }, { key.offset: 0, key.length: 22, key.attribute: source.decl.attribute.available } ]
Alternatives considered
For the Missing attribute arguments and Missing custom attribute information there aren't really any alternatives, except parsing the returned key.sourcetext
yourself, which I wouldn't consider structured information.
For Little distinction between attributes and declaration modifiers, it's currently possible to find out whether something is an attribute or a declaration modifiers by simply mapping the source.decl.attribute.x
UIDs to whether they are attributes/declaration modifiers respectively, as these are known ahead of time. But this isn't very clean, and doesn't allow for easy handling of just one of the two, which is why more explicit information in the response would be desirable.
Additional information
If these ideas are too varied and this issue too big, I'd be happy to split it up into multiple smaller issues and convert this one to a map of those issues.