Skip to content

Commit 3a0b5e5

Browse files
committed
Fix super class and protocol conformance parsing
1 parent 7dcd116 commit 3a0b5e5

File tree

4 files changed

+143
-32
lines changed

4 files changed

+143
-32
lines changed

Tool/Sources/FocusedCodeFinder/ObjectiveC/ObjectiveCCodeFinder.swift

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,26 +100,41 @@ public class ObjectiveCFocusedCodeFinder: KnownLanguageFocusedCodeFinder<
100100
if let nameNode = node.child(byFieldName: "name") {
101101
name = textProvider(.node(nameNode))
102102
}
103-
if let superClassNode = node.child(byFieldName: "superclass") {
104-
superClass = textProvider(.node(superClassNode))
105-
}
106103
if let categoryNode = node.child(byFieldName: "category") {
107104
category = textProvider(.node(categoryNode))
108105
}
109-
if let protocolsNode = node.child(byFieldName: "protocols") {
110-
for protocolNode in protocolsNode.children {
111-
let protocolName = textProvider(.node(protocolNode))
112-
if !protocolName.isEmpty {
113-
protocols.append(protocolName)
106+
107+
for i in 0..<node.childCount {
108+
guard let childNode = node.child(at: i) else { continue }
109+
switch ObjectiveCNodeType(rawValue: childNode.nodeType) {
110+
case .protocolQualifiers:
111+
var protocolNames = [String]()
112+
for j in 0..<childNode.childCount {
113+
guard let protocolNode = childNode.child(at: j) else { continue }
114+
guard ObjectiveCNodeType(rawValue: protocolNode.nodeType) == .identifier
115+
else { continue }
116+
protocolNames.append(
117+
textProvider(.node(protocolNode))
118+
.trimmingCharacters(in: .whitespacesAndNewlines)
119+
)
120+
}
121+
protocols = protocolNames.filter { $0 != "," && !$0.isEmpty }
122+
case .superclassReference:
123+
if let superClassNode = childNode.child(byFieldName: "name") {
124+
superClass = textProvider(.node(superClassNode))
125+
.trimmingCharacters(in: .whitespacesAndNewlines)
114126
}
127+
default:
128+
continue
115129
}
116130
}
131+
117132
var signature = "@interface \(name)"
118133
if !category.isEmpty {
119-
signature += "(\(category))"
134+
signature += " (\(category))"
120135
}
121136
if !protocols.isEmpty {
122-
signature += "<\(protocols.joined(separator: ","))>"
137+
signature += "<\(protocols.joined(separator: ", "))>"
123138
}
124139
if !superClass.isEmpty {
125140
signature += ": \(superClass)"
@@ -144,27 +159,41 @@ public class ObjectiveCFocusedCodeFinder: KnownLanguageFocusedCodeFinder<
144159
if let nameNode = node.child(byFieldName: "name") {
145160
name = textProvider(.node(nameNode))
146161
}
147-
if let superClassNode = node.child(byFieldName: "superclass") {
148-
superClass = textProvider(.node(superClassNode))
149-
}
150162
if let categoryNode = node.child(byFieldName: "category") {
151163
category = textProvider(.node(categoryNode))
152164
}
153-
if let protocolsNode = node.child(byFieldName: "protocols") {
154-
for protocolNode in protocolsNode.children {
155-
let protocolName = textProvider(.node(protocolNode))
156-
if !protocolName.isEmpty {
157-
protocols.append(protocolName)
165+
166+
for i in 0..<node.childCount {
167+
guard let childNode = node.child(at: i) else { continue }
168+
switch ObjectiveCNodeType(rawValue: childNode.nodeType) {
169+
case .protocolQualifiers:
170+
var protocolNames = [String]()
171+
for j in 0..<childNode.childCount {
172+
guard let protocolNode = childNode.child(at: j) else { continue }
173+
guard ObjectiveCNodeType(rawValue: protocolNode.nodeType) == .identifier
174+
else { continue }
175+
protocolNames.append(
176+
textProvider(.node(protocolNode))
177+
.trimmingCharacters(in: .whitespacesAndNewlines)
178+
)
158179
}
180+
protocols = protocolNames.filter { $0 != "," && !$0.isEmpty }
181+
case .superclassReference:
182+
if let superClassNode = childNode.child(byFieldName: "name") {
183+
superClass = textProvider(.node(superClassNode))
184+
.trimmingCharacters(in: .whitespacesAndNewlines)
185+
}
186+
default:
187+
continue
159188
}
160189
}
161190

162191
var signature = "@implementation \(name)"
163192
if !category.isEmpty {
164-
signature += "(\(category))"
193+
signature += " (\(category))"
165194
}
166195
if !protocols.isEmpty {
167-
signature += "<\(protocols.joined(separator: ","))>"
196+
signature += "<\(protocols.joined(separator: ", "))>"
168197
}
169198
if !superClass.isEmpty {
170199
signature += ": \(superClass)"

Tool/Sources/FocusedCodeFinder/ObjectiveC/ObjectiveCScopeHierarchySyntaxVisitor.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,27 +52,27 @@ final class ObjectiveCScopeHierarchySyntaxVisitor: ASTTreeVisitor {
5252
handleModuleImport(node)
5353
return .skipChildren
5454
case .classInterface, .categoryInterface, .protocolDeclaration:
55-
guard cursorRange.contains(range) else { return .skipChildren }
55+
guard cursorRange.strictlyContains(range) else { return .skipChildren }
5656
_scopeHierarchy.append(node)
5757
return .visitChildren
5858
case .classImplementation, .categoryImplementation:
59-
guard cursorRange.contains(range) else { return .skipChildren }
59+
guard cursorRange.strictlyContains(range) else { return .skipChildren }
6060
_scopeHierarchy.append(node)
6161
return .visitChildren
6262
case .methodDefinition:
63-
guard cursorRange.contains(range) else { return .skipChildren }
63+
guard cursorRange.strictlyContains(range) else { return .skipChildren }
6464
_scopeHierarchy.append(node)
6565
return .skipChildren
6666
case .typeDefinition:
67-
guard cursorRange.contains(range) else { return .skipChildren }
67+
guard cursorRange.strictlyContains(range) else { return .skipChildren }
6868
_scopeHierarchy.append(node)
6969
return .skipChildren
7070
case .structSpecifier, .enumSpecifier, .nsEnumSpecifier:
71-
guard cursorRange.contains(range) else { return .skipChildren }
71+
guard cursorRange.strictlyContains(range) else { return .skipChildren }
7272
_scopeHierarchy.append(node)
7373
return .skipChildren
7474
case .functionDefinition:
75-
guard cursorRange.contains(range) else { return .skipChildren }
75+
guard cursorRange.strictlyContains(range) else { return .skipChildren }
7676
_scopeHierarchy.append(node)
7777
return .skipChildren
7878
default:

Tool/Sources/FocusedCodeFinder/ObjectiveC/ObjectiveCSyntax.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,12 @@ enum ObjectiveCNodeType: String {
121121
case enumSpecifier = "enum_specifier"
122122
/// `NS_ENUM {}` and `NS_OPTIONS {}`.
123123
case nsEnumSpecifier = "ns_enum_specifier"
124-
/// fields inside a type definition.
124+
/// Fields inside a type definition.
125125
case fieldDeclarationList = "field_declaration_list"
126+
/// Protocols that a type conforms.
127+
case protocolQualifiers = "protocol_qualifiers"
128+
/// Superclass of a type.
129+
case superclassReference = "superclass_reference"
126130
}
127131

128132
extension ObjectiveCNodeType {

Tool/Tests/FocusedCodeFinderTests/ObjectiveCFocusedCodeFinderTests.swift

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ final class ObjectiveCFocusedCodeFinder_Selection_Tests: XCTestCase {
88
func test_selecting_a_line_inside_the_method_the_scope_should_be_the_method() {
99
let code = """
1010
@implementation Foo
11-
- (void)foo {
11+
- (void)fooWith:(NSInteger)foo {
1212
NSInteger foo = 0;
1313
NSLog(@"Hello");
1414
NSLog(@"World");
@@ -31,8 +31,8 @@ final class ObjectiveCFocusedCodeFinder_Selection_Tests: XCTestCase {
3131
range: .init(startPair: (0, 0), endPair: (6, 4))
3232
),
3333
.init(
34-
signature: "- (void)foo",
35-
name: "foo",
34+
signature: "- (void)fooWith:(NSInteger)foo",
35+
name: "fooWith:(NSInteger)foo",
3636
range: .init(startPair: (1, 0), endPair: (5, 1))
3737
),
3838
]),
@@ -49,7 +49,7 @@ final class ObjectiveCFocusedCodeFinder_Selection_Tests: XCTestCase {
4949

5050
func test_selecting_a_line_inside_a_function_the_scope_should_be_the_function() {
5151
let code = """
52-
void foo() {
52+
void foo(char name[]) {
5353
NSInteger foo = 0;
5454
NSLog(@"Hello");
5555
NSLog(@"World");
@@ -63,7 +63,7 @@ final class ObjectiveCFocusedCodeFinder_Selection_Tests: XCTestCase {
6363
XCTAssertEqual(context, .init(
6464
scope: .scope(signature: [
6565
.init(
66-
signature: "void foo()",
66+
signature: "void foo(char name[])",
6767
name: "foo",
6868
range: .init(startPair: (0, 0), endPair: (4, 1))
6969
),
@@ -78,5 +78,83 @@ final class ObjectiveCFocusedCodeFinder_Selection_Tests: XCTestCase {
7878
includes: []
7979
))
8080
}
81+
82+
func test_selecting_a_method_inside_an_implementation_the_scope_should_be_the_implementation() {
83+
let code = """
84+
@implementation Foo (Category)
85+
- (void)fooWith:(NSInteger)foo {
86+
NSInteger foo = 0;
87+
NSLog(@"Hello");
88+
NSLog(@"World");
89+
}
90+
@end
91+
"""
92+
let range = CursorRange(
93+
start: CursorPosition(line: 1, character: 0),
94+
end: CursorPosition(line: 5, character: 1)
95+
)
96+
let context = ObjectiveCFocusedCodeFinder().findFocusedCode(
97+
in: document(code: code),
98+
containingRange: range
99+
)
100+
XCTAssertEqual(context, .init(
101+
scope: .scope(signature: [
102+
.init(
103+
signature: "@implementation Foo (Category)",
104+
name: "Foo",
105+
range: .init(startPair: (0, 0), endPair: (6, 4))
106+
),
107+
]),
108+
contextRange: .init(startPair: (0, 0), endPair: (6, 4)),
109+
focusedRange: range,
110+
focusedCode: """
111+
- (void)fooWith:(NSInteger)foo {
112+
NSInteger foo = 0;
113+
NSLog(@"Hello");
114+
NSLog(@"World");
115+
}
116+
117+
""",
118+
imports: [],
119+
includes: []
120+
))
121+
}
122+
123+
func test_selecting_a_line_inside_an_interface_the_scope_should_be_the_interface() {
124+
let code = """
125+
@interface Foo<A, B>: NSObject
126+
- (void)fooWith:(NSInteger)foo;
127+
- (void)fooWith:(NSInteger)foo;
128+
- (void)fooWith:(NSInteger)foo;
129+
@end
130+
"""
131+
let range = CursorRange(
132+
start: CursorPosition(line: 1, character: 0),
133+
end: CursorPosition(line: 3, character: 31)
134+
)
135+
let context = ObjectiveCFocusedCodeFinder().findFocusedCode(
136+
in: document(code: code),
137+
containingRange: range
138+
)
139+
XCTAssertEqual(context, .init(
140+
scope: .scope(signature: [
141+
.init(
142+
signature: "@interface Foo<A, B>: NSObject",
143+
name: "Foo",
144+
range: .init(startPair: (0, 0), endPair: (4, 4))
145+
),
146+
]),
147+
contextRange: .init(startPair: (0, 0), endPair: (4, 4)),
148+
focusedRange: range,
149+
focusedCode: """
150+
- (void)fooWith:(NSInteger)foo;
151+
- (void)fooWith:(NSInteger)foo;
152+
- (void)fooWith:(NSInteger)foo;
153+
154+
""",
155+
imports: [],
156+
includes: []
157+
))
158+
}
81159
}
82160

0 commit comments

Comments
 (0)