Skip to content

Commit

Permalink
Merge pull request #388 from johnfairh/extension-nested-type
Browse files Browse the repository at this point in the history
Fix extensions of nested types
  • Loading branch information
jpsim authored May 22, 2017
2 parents d9e4059 + b66b457 commit ce9d965
Show file tree
Hide file tree
Showing 7 changed files with 524 additions and 146 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

##### Bug Fixes

* None.
* Fix handling of Swift extensions of nested types.
[John Fairhurst](https://github.com/johnfairh)

## 0.17.5

Expand Down
22 changes: 13 additions & 9 deletions Source/SourceKittenFramework/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -255,23 +255,27 @@ public final class File {
return [SwiftDocKey.name.rawValue: markName]
} else if let decl = SwiftDeclarationKind(rawValue: kind), decl != .varParameter {
// Update if kind is a declaration (but not a parameter)
let innerTypeNameOffset = SwiftDocKey.getName(dictionary)?.byteOffsetOfInnerTypeName() ?? 0
var updateDict = Request.send(cursorInfoRequest: cursorInfoRequest,
atOffset: SwiftDocKey.getNameOffset(dictionary)!) ?? [:]
atOffset: SwiftDocKey.getNameOffset(dictionary)! + innerTypeNameOffset) ?? [:]

// Skip kinds, since values from editor.open are more accurate than cursorinfo
updateDict.removeValue(forKey: SwiftDocKey.kind.rawValue)
File.untrustedCursorInfoKeys.forEach {
updateDict.removeValue(forKey: $0.rawValue)
}

// Skip offset and length.
// Their values are same with "key.nameoffset" and "key.namelength" in most case.
// When kind is extension, their values locate **the type's declaration** in their declared file.
// That may be different from the file declaring extension.
updateDict.removeValue(forKey: SwiftDocKey.offset.rawValue)
updateDict.removeValue(forKey: SwiftDocKey.length.rawValue)
return updateDict
}
return nil
}

/// Keys to ignore from cursorinfo when already have dictionary from editor.open
private static let untrustedCursorInfoKeys: [SwiftDocKey] = [
.kind, // values from editor.open are more accurate than cursorinfo
.offset, // usually same as nameoffset, but for extension, value locates **type's declaration** in type's file
.length, // usually same as namelength, but for extension, value locates **type's declaration** in type's file
.name // for extensions of nested types has just the inner name, prefer fully-qualified name
]

/**
Returns whether or not a doc should be inserted into a parent at the provided offset.

Expand Down
9 changes: 9 additions & 0 deletions Source/SourceKittenFramework/String+SourceKitten.swift
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,15 @@ extension String {
unwantedSet.insert(charactersIn: "{")
return trimmingCharacters(in: unwantedSet)
}

/// Returns the byte offset of the section of the string following the last dot ".", or 0 if no dots.
internal func byteOffsetOfInnerTypeName() -> Int64 {
guard let range = range(of: ".", options: .backwards) else {
return 0
}
let utf8pos = index(after: range.lowerBound).samePosition(in: utf8)
return Int64(utf8.distance(from: utf8.startIndex, to: utf8pos))
}
}

// MARK: - migration support
Expand Down
11 changes: 11 additions & 0 deletions Source/SourceKittenFramework/SwiftDocKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@ public enum SwiftDocKey: String {
return get(.length, dictionary)
}

/**
Get name string from dictionary.

- parameter dictionary: Dictionary to get value from.

- returns: Name string if successful.
*/
internal static func getName(_ dictionary: [String: SourceKitRepresentable]) -> String? {
return get(.name, dictionary)
}

/**
Get type name string from dictionary.

Expand Down
173 changes: 154 additions & 19 deletions Tests/SourceKittenFrameworkTests/Fixtures/Extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"key.substructure" : [
{
"key.accessibility" : "source.lang.swift.accessibility.internal",
"key.length" : 125,
"key.length" : 179,
"key.doc.type" : "Class",
"key.runtime_name" : "_TtC8__main__4Base",
"key.parsed_scope.start" : 2,
Expand Down Expand Up @@ -84,6 +84,39 @@
"key.bodylength" : 5,
"key.parsed_scope.end" : 9,
"key.namelength" : 15
},
{
"key.accessibility" : "source.lang.swift.accessibility.internal",
"key.length" : 20,
"key.doc.type" : "Class",
"key.parsed_scope.start" : 12,
"key.kind" : "source.lang.swift.decl.class",
"key.doc.full_as_xml" : "<Class file=\"Extension.swift\" line=\"12\" column=\"11\"><Name>Nested<\/Name><USR>s:CC9Extension4Base6Nested<\/USR><Declaration>class Nested<\/Declaration><Abstract><Para>Doc for Base.Nested<\/Para><\/Abstract><\/Class>",
"key.nameoffset" : 180,
"key.typename" : "Base.Nested.Type",
"key.doc.column" : 11,
"key.doc.comment" : "Doc for Base.Nested",
"key.bodyoffset" : 188,
"key.filepath" : "Extension.swift",
"key.doc.file" : "Extension.swift",
"key.annotated_decl" : "<Declaration>class Nested<\/Declaration>",
"key.fully_annotated_decl" : "<decl.class><syntaxtype.keyword>class<\/syntaxtype.keyword> <decl.name>Nested<\/decl.name><\/decl.class>",
"key.doc.declaration" : "class Nested",
"key.typeusr" : "_Tt",
"key.offset" : 174,
"key.doc.line" : 12,
"key.doc.name" : "Nested",
"key.actionable" : [
{
"key.actionname" : "Rename"
}
],
"key.name" : "Nested",
"key.usr" : "s:CC9Extension4Base6Nested",
"key.parsed_declaration" : "class Nested",
"key.bodylength" : 5,
"key.parsed_scope.end" : 13,
"key.namelength" : 6
}
],
"key.typeusr" : "_Tt",
Expand All @@ -98,8 +131,8 @@
"key.name" : "Base",
"key.usr" : "s:C9Extension4Base",
"key.parsed_declaration" : "class Base",
"key.bodylength" : 112,
"key.parsed_scope.end" : 10,
"key.bodylength" : 166,
"key.parsed_scope.end" : 14,
"key.namelength" : 4
},
{
Expand All @@ -118,12 +151,12 @@
"key.doc.declaration" : "typealias ExtendedIndex = Double",
"key.length" : 13,
"key.doc.type" : "Other",
"key.parsed_scope.start" : 15,
"key.parsed_scope.start" : 19,
"key.typeusr" : "_Tt",
"key.kind" : "source.lang.swift.decl.typealias",
"key.doc.full_as_xml" : "<Other file=\"Extension.swift\" line=\"15\" column=\"15\"><Name>ExtendedIndex<\/Name><USR>s:C9Extension4Base13ExtendedIndex<\/USR><Declaration>typealias ExtendedIndex = Double<\/Declaration><Abstract><Para>Doc for Base.ExtendedIndex<\/Para><\/Abstract><\/Other>",
"key.offset" : 211,
"key.doc.line" : 15,
"key.doc.full_as_xml" : "<Other file=\"Extension.swift\" line=\"19\" column=\"15\"><Name>ExtendedIndex<\/Name><USR>s:C9Extension4Base13ExtendedIndex<\/USR><Declaration>typealias ExtendedIndex = Double<\/Declaration><Abstract><Para>Doc for Base.ExtendedIndex<\/Para><\/Abstract><\/Other>",
"key.offset" : 265,
"key.doc.line" : 19,
"key.typename" : "Double.Type",
"key.actionable" : [
{
Expand All @@ -136,20 +169,20 @@
"key.doc.comment" : "Doc for Base.ExtendedIndex",
"key.usr" : "s:C9Extension4Base13ExtendedIndex",
"key.parsed_declaration" : "typealias ExtendedIndex = Double",
"key.parsed_scope.end" : 15
"key.parsed_scope.end" : 19
},
{
"key.accessibility" : "source.lang.swift.accessibility.internal",
"key.length" : 44,
"key.doc.type" : "Function",
"key.parsed_scope.start" : 18,
"key.parsed_scope.start" : 22,
"key.kind" : "source.lang.swift.decl.function.method.instance",
"key.doc.full_as_xml" : "<Function file=\"Extension.swift\" line=\"18\" column=\"10\"><Name>extendedF(index:)<\/Name><USR>s:FC9Extension4Base9extendedFFT5indexSd_T_<\/USR><Declaration>func extendedF(index: ExtendedIndex)<\/Declaration><Abstract><Para>Doc for Base.extendedF<\/Para><\/Abstract><\/Function>",
"key.nameoffset" : 275,
"key.doc.full_as_xml" : "<Function file=\"Extension.swift\" line=\"22\" column=\"10\"><Name>extendedF(index:)<\/Name><USR>s:FC9Extension4Base9extendedFFT5indexSd_T_<\/USR><Declaration>func extendedF(index: ExtendedIndex)<\/Declaration><Abstract><Para>Doc for Base.extendedF<\/Para><\/Abstract><\/Function>",
"key.nameoffset" : 329,
"key.typename" : "(Base) -> (Double) -> ()",
"key.doc.column" : 10,
"key.doc.comment" : "Doc for Base.extendedF",
"key.bodyoffset" : 308,
"key.bodyoffset" : 362,
"key.filepath" : "Extension.swift",
"key.doc.file" : "Extension.swift",
"key.annotated_decl" : "<Declaration>func extendedF(index: <Type usr=\"s:C9Extension4Base13ExtendedIndex\">ExtendedIndex<\/Type>)<\/Declaration>",
Expand All @@ -159,8 +192,8 @@

],
"key.typeusr" : "_TtFT5indexSd_T_",
"key.offset" : 270,
"key.doc.line" : 18,
"key.offset" : 324,
"key.doc.line" : 22,
"key.doc.name" : "extendedF(index:)",
"key.actionable" : [
{
Expand All @@ -171,16 +204,16 @@
"key.usr" : "s:FC9Extension4Base9extendedFFT5indexSd_T_",
"key.parsed_declaration" : "func extendedF(index: ExtendedIndex)",
"key.bodylength" : 5,
"key.parsed_scope.end" : 19,
"key.parsed_scope.end" : 23,
"key.namelength" : 31
}
],
"key.doc.type" : "Class",
"key.typeusr" : "_Tt",
"key.kind" : "source.lang.swift.decl.extension",
"key.offset" : 144,
"key.offset" : 198,
"key.doc.full_as_xml" : "<Class file=\"Extension.swift\" line=\"2\" column=\"7\"><Name>Base<\/Name><USR>s:C9Extension4Base<\/USR><Declaration>class Base<\/Declaration><Abstract><Para>Doc for Base<\/Para><\/Abstract><\/Class>",
"key.nameoffset" : 154,
"key.nameoffset" : 208,
"key.doc.line" : 2,
"key.typename" : "Base.Type",
"key.actionable" : [
Expand All @@ -192,12 +225,114 @@
"key.doc.column" : 7,
"key.name" : "Base",
"key.usr" : "s:C9Extension4Base",
"key.bodyoffset" : 160,
"key.bodyoffset" : 214,
"key.bodylength" : 155,
"key.namelength" : 4
},
{
"key.filepath" : "Extension.swift",
"key.doc.file" : "Extension.swift",
"key.annotated_decl" : "<Declaration>class Nested<\/Declaration>",
"key.fully_annotated_decl" : "<decl.class><syntaxtype.keyword>class<\/syntaxtype.keyword> <decl.name>Nested<\/decl.name><\/decl.class>",
"key.doc.declaration" : "class Nested",
"key.length" : 25,
"key.doc.type" : "Class",
"key.typeusr" : "_Tt",
"key.kind" : "source.lang.swift.decl.extension",
"key.offset" : 413,
"key.doc.full_as_xml" : "<Class file=\"Extension.swift\" line=\"12\" column=\"11\"><Name>Nested<\/Name><USR>s:CC9Extension4Base6Nested<\/USR><Declaration>class Nested<\/Declaration><Abstract><Para>Doc for Base.Nested<\/Para><\/Abstract><\/Class>",
"key.nameoffset" : 423,
"key.doc.line" : 12,
"key.typename" : "Base.Nested.Type",
"key.actionable" : [
{
"key.actionname" : "Rename"
}
],
"key.doc.name" : "Nested",
"key.doc.column" : 11,
"key.name" : "Base.Nested",
"key.usr" : "s:CC9Extension4Base6Nested",
"key.bodyoffset" : 436,
"key.bodylength" : 1,
"key.namelength" : 11
},
{
"key.filepath" : "Extension.swift",
"key.parsed_declaration" : "class 🐽",
"key.annotated_decl" : "<Declaration>class 🐽<\/Declaration>",
"key.fully_annotated_decl" : "<decl.class><syntaxtype.keyword>class<\/syntaxtype.keyword> <decl.name>🐽<\/decl.name><\/decl.class>",
"key.accessibility" : "source.lang.swift.accessibility.internal",
"key.length" : 38,
"key.substructure" : [
{
"key.filepath" : "Extension.swift",
"key.parsed_declaration" : "struct 🐧",
"key.annotated_decl" : "<Declaration>struct 🐧<\/Declaration>",
"key.fully_annotated_decl" : "<decl.struct><syntaxtype.keyword>struct<\/syntaxtype.keyword> <decl.name>🐧<\/decl.name><\/decl.struct>",
"key.accessibility" : "source.lang.swift.accessibility.internal",
"key.length" : 19,
"key.parsed_scope.start" : 32,
"key.typeusr" : "_Tt",
"key.kind" : "source.lang.swift.decl.struct",
"key.offset" : 457,
"key.nameoffset" : 464,
"key.typename" : "🐽.🐧.Type",
"key.actionable" : [
{
"key.actionname" : "Rename"
}
],
"key.name" : "🐧",
"key.usr" : "s:VC9ExtensionX4ipIhX4voIh",
"key.bodyoffset" : 470,
"key.bodylength" : 5,
"key.parsed_scope.end" : 33,
"key.namelength" : 4
}
],
"key.runtime_name" : "_TtC8__main__4🐽",
"key.parsed_scope.start" : 31,
"key.typeusr" : "_Tt",
"key.kind" : "source.lang.swift.decl.class",
"key.offset" : 440,
"key.nameoffset" : 446,
"key.typename" : "🐽.Type",
"key.actionable" : [
{
"key.actionname" : "Rename"
}
],
"key.name" : "🐽",
"key.usr" : "s:C9ExtensionX4ipIh",
"key.bodyoffset" : 452,
"key.bodylength" : 25,
"key.parsed_scope.end" : 34,
"key.namelength" : 4
},
{
"key.filepath" : "Extension.swift",
"key.annotated_decl" : "<Declaration>struct 🐧<\/Declaration>",
"key.fully_annotated_decl" : "<decl.struct><syntaxtype.keyword>struct<\/syntaxtype.keyword> <decl.name>🐧<\/decl.name><\/decl.struct>",
"key.length" : 23,
"key.typeusr" : "_Tt",
"key.kind" : "source.lang.swift.decl.extension",
"key.offset" : 480,
"key.nameoffset" : 490,
"key.typename" : "🐽.🐧.Type",
"key.actionable" : [
{
"key.actionname" : "Rename"
}
],
"key.name" : "🐽.🐧",
"key.usr" : "s:VC9ExtensionX4ipIhX4voIh",
"key.bodyoffset" : 501,
"key.bodylength" : 1,
"key.namelength" : 9
}
],
"key.offset" : 0,
"key.length" : 317
"key.length" : 504
}
}
17 changes: 17 additions & 0 deletions Tests/SourceKittenFrameworkTests/Fixtures/Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ class Base {
/// Doc for Base.f
func f(index: Index) {
}

/// Doc for Base.Nested
class Nested {
}
}

extension Base {
Expand All @@ -18,3 +22,16 @@ extension Base {
func extendedF(index: ExtendedIndex) {
}
}

// Tests for extensions of nested types

extension Base.Nested {
}

class 🐽 {
struct 🐧 {
}
}

extension 🐽.🐧 {
}
Loading

0 comments on commit ce9d965

Please sign in to comment.