Skip to content

Commit 9cf6a37

Browse files
authored
analyzer: refactor folding ranges, support multi-resolve and grammar updates (#179)
1 parent f9e6c82 commit 9cf6a37

File tree

16 files changed

+277168
-250432
lines changed

16 files changed

+277168
-250432
lines changed

src/analyzer/psi/PsiFile.v

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,25 @@ pub fn (p &PsiFile) get_imports() []ImportSpec {
191191
}
192192

193193
pub fn (p &PsiFile) resolve_import_spec(name string) ?ImportSpec {
194+
specs := p.resolve_import_specs(name)
195+
if specs.len > 0 {
196+
return specs.first()
197+
}
198+
return none
199+
}
200+
201+
pub fn (p &PsiFile) resolve_import_specs(name string) []ImportSpec {
194202
imports := p.get_imports()
195203
if imports.len == 0 {
196-
return none
204+
return []
197205
}
198-
206+
mut result := []ImportSpec{cap: 2}
199207
for imp in imports {
200208
if imp.import_name() == name {
201-
return imp
209+
result << imp
202210
}
203211
}
204-
205-
return none
212+
return result
206213
}
207214

208215
pub fn (p &PsiFile) process_declarations(mut processor PsiScopeProcessor) bool {

src/analyzer/psi/PsiReference.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ module psi
33
pub interface PsiReference {
44
element() PsiElement
55
resolve() ?PsiElement
6+
multi_resolve() []PsiElement
67
}

src/analyzer/psi/ReferenceImpl.v

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,49 @@ pub fn (r &ReferenceImpl) resolve() ?PsiElement {
5959
return result
6060
}
6161

62+
pub fn (r &ReferenceImpl) multi_resolve() []PsiElement {
63+
file := r.file or { return [] }
64+
65+
if res := r.resolve_as_import_spec() {
66+
return res
67+
}
68+
69+
sub := SubResolver{
70+
containing_file: file
71+
element: r.element
72+
for_types: r.for_types
73+
for_attributes: r.for_attributes
74+
}
75+
mut processor := ResolveProcessor{
76+
containing_file: file
77+
ref: r.element
78+
ref_name: r.element.name()
79+
collect_all: true
80+
}
81+
82+
sub.process_resolve_variants(mut processor)
83+
84+
return processor.result
85+
}
86+
87+
fn (r &ReferenceImpl) resolve_as_import_spec() ?[]PsiElement {
88+
if r.element is Identifier {
89+
parent := r.element.parent()?
90+
if parent !is ImportName {
91+
return none
92+
}
93+
spec := parent.parent()?.parent()?
94+
if spec is ImportSpec {
95+
if ident := spec.identifier() {
96+
if ident.is_equal(parent) {
97+
return [spec]
98+
}
99+
}
100+
}
101+
}
102+
return none
103+
}
104+
62105
pub struct SubResolver {
63106
pub:
64107
containing_file ?&PsiFile
@@ -91,16 +134,22 @@ pub fn (r &SubResolver) process_qualifier_expression(qualifier PsiElement, mut p
91134

92135
if qualifier is ReferenceExpressionBase {
93136
resolved := qualifier.resolve() or { return true }
137+
94138
if resolved is ImportSpec {
95-
import_name := resolved.qualified_name()
96-
real_fqn := stubs_index.find_real_module_fqn(import_name)
139+
import_name := resolved.import_name()
140+
file := r.containing_file or { return true }
141+
specs := file.resolve_import_specs(import_name)
97142

98-
elements := stubs_index.get_all_declarations_from_module(real_fqn, r.for_types)
99-
for element in elements {
100-
if !processor.execute(element) {
143+
for _, spec in specs {
144+
target_fqn := spec.qualified_name()
145+
real_fqn := stubs_index.find_real_module_fqn(target_fqn)
146+
elements := stubs_index.get_all_declarations_from_module(real_fqn, r.for_types)
147+
148+
if !r.process_elements(elements, mut processor) {
101149
return false
102150
}
103151
}
152+
return true
104153
}
105154

106155
if resolved is ModuleClause {
@@ -821,7 +870,8 @@ pub struct ResolveProcessor {
821870
ref ReferenceExpressionBase
822871
ref_name string
823872
mut:
824-
result []PsiElement
873+
result []PsiElement
874+
collect_all bool
825875
}
826876

827877
fn (mut r ResolveProcessor) execute(element PsiElement) bool {
@@ -836,6 +886,9 @@ fn (mut r ResolveProcessor) execute(element PsiElement) bool {
836886
}
837887
if name == r.ref_name {
838888
r.result << element as PsiElement
889+
if r.collect_all {
890+
return true
891+
}
839892
return false
840893
}
841894
}

src/analyzer/psi/StubbedElementTypeImpl.v

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,14 @@ pub fn (s &StubbedElementType) create_stub(psi PsiElement, parent_stub &StubBase
572572
return declaration_stub(*psi, parent_stub, .import_spec, include_text: true)
573573
}
574574

575-
if node_type in [.import_list, .import_declaration, .import_path, .import_name, .import_alias] {
575+
if node_type in [
576+
.import_list,
577+
.import_declaration,
578+
.import_path,
579+
.import_name,
580+
.import_alias,
581+
.selective_import_list,
582+
] {
576583
stub_type := node_type_to_stub_type(node_type)
577584
return text_based_stub(psi, parent_stub, stub_type,
578585
include_text: node_type !in [

src/analyzer/psi/search/ReferencesSearch.v

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,17 @@ pub fn (r &ReferencesSearch) search_in(element psi.PsiNamedElement, search_root
274274
result << node
275275
}
276276
}
277+
if element is psi.ImportSpec && resolved is psi.ImportSpec {
278+
if element.import_name() == resolved.import_name() {
279+
if element_file := element.containing_file() {
280+
if resolved_file := resolved.containing_file() {
281+
if element_file.path == resolved_file.path {
282+
result << node
283+
}
284+
}
285+
}
286+
}
287+
}
277288
}
278289
}
279290
}

src/server/documentation/provider.v

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,11 @@ fn (mut p Provider) const_documentation(element psi.ConstantDefinition) ? {
267267
p.sb.write_string(' = ')
268268
if value := element.expression() {
269269
p.sb.write_string(value.get_text())
270+
if element.stub_based() {
271+
if mut file := value.containing_file() {
272+
file.free()
273+
}
274+
}
270275
}
271276
p.sb.write_string('\n')
272277
p.sb.write_string('```')

src/server/features_definition.v

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@ pub fn (mut ls LanguageServer) definition(params lsp.TextDocumentPositionParams)
1717
return none
1818
}
1919

20-
resolved := element.resolve() or {
21-
loglib.with_fields({
22-
'caller': @METHOD
23-
'name': element.name()
24-
}).warn('Cannot resolve reference')
20+
resolved_elements := element.reference().multi_resolve()
21+
if resolved_elements.len == 0 {
2522
return none
2623
}
2724

28-
containing_file := resolved.containing_file() or { return [] }
29-
data := new_resolve_result(containing_file, resolved) or { return [] }
30-
return [
31-
data.to_location_link(element.text_range()),
32-
]
25+
mut links := []lsp.LocationLink{cap: resolved_elements.len}
26+
27+
for resolved in resolved_elements {
28+
containing_file := resolved.containing_file() or { continue }
29+
if data := new_resolve_result(containing_file, resolved) {
30+
links << data.to_location_link(element.text_range())
31+
}
32+
}
33+
34+
return links
3335
}
3436

3537
struct ResolveResult {
Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,20 @@
11
module server
22

33
import lsp
4-
import analyzer.psi
4+
import loglib
5+
import server.folding
56

67
pub fn (mut ls LanguageServer) folding_range(params lsp.FoldingRangeParams) ?[]lsp.FoldingRange {
78
uri := params.text_document.uri.normalize()
8-
file := ls.get_file(uri)?
9-
10-
mut result := []lsp.FoldingRange{}
11-
mut walker := psi.new_tree_walker(file.psi_file.root().node())
12-
defer { walker.free() }
13-
14-
for {
15-
node := walker.next() or { break }
16-
17-
if node.type_name == .import_list {
18-
element := psi.create_element(node, file.psi_file)
19-
decls := element.find_children_by_type(.import_declaration)
20-
if decls.len < 2 {
21-
continue
22-
}
23-
24-
first := decls.first()
25-
range := element.text_range()
26-
27-
first_range := if first is psi.ImportDeclaration {
28-
spec := first.spec() or { continue }
29-
spec.text_range()
30-
} else {
31-
continue
32-
}
33-
34-
result << lsp.FoldingRange{
35-
start_line: first_range.line
36-
start_character: first_range.column
37-
end_line: range.end_line - 2
38-
end_character: range.end_column
39-
kind: lsp.folding_range_kind_imports
40-
}
41-
}
9+
file := ls.get_file(uri) or {
10+
loglib.with_fields({
11+
'uri': uri.str()
12+
}).warn('Folding range requested for unopened file')
13+
return []
4214
}
4315

44-
return result
16+
mut visitor := folding.FoldingVisitor.new(file.psi_file)
17+
ranges := visitor.accept(file.psi_file.root())
18+
19+
return ranges
4520
}

0 commit comments

Comments
 (0)