1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
package server
import "core:odin/ast"
import "src:common"
get_document_symbols :: proc(document: ^Document) -> []DocumentSymbol {
ast_context := make_ast_context(
document.ast,
document.imports,
document.package_name,
document.uri.uri,
document.fullpath,
)
get_globals(document.ast, &ast_context)
symbols := make([dynamic]DocumentSymbol, context.temp_allocator)
package_symbol: DocumentSymbol
if len(document.ast.decls) == 0 {
return {}
}
for k, global in ast_context.globals {
symbol: DocumentSymbol
symbol.selectionRange = common.get_token_range(global.name_expr, ast_context.file.src)
symbol.range = common.get_token_range(global.expr, ast_context.file.src)
ensure_selection_range_contained(&symbol.range, symbol.selectionRange)
symbol.name = k
#partial switch v in global.expr.derived {
case ^ast.Struct_Type, ^ast.Bit_Field_Type:
// TODO: this only does the top level fields, we may want to travers all the way down in the future
if s, ok := resolve_type_expression(&ast_context, global.expr); ok {
#partial switch v in s.value {
case SymbolStructValue:
children := make([dynamic]DocumentSymbol, context.temp_allocator)
for name, i in v.names {
if name == "" {
continue
}
child: DocumentSymbol
child.range = v.ranges[i]
child.selectionRange = v.ranges[i]
child.name = name
child.kind = .Field
append(&children, child)
}
symbol.children = children[:]
case SymbolBitFieldValue:
children := make([dynamic]DocumentSymbol, context.temp_allocator)
for name, i in v.names {
if name == "" {
continue
}
child: DocumentSymbol
child.range = v.ranges[i]
child.selectionRange = v.ranges[i]
child.name = name
child.kind = .Field
append(&children, child)
}
symbol.children = children[:]
}
}
symbol.kind = .Struct
case ^ast.Proc_Lit, ^ast.Proc_Group:
symbol.kind = .Function
case ^ast.Enum_Type, ^ast.Union_Type:
symbol.kind = .Enum
case ^ast.Comp_Lit:
if s, ok := resolve_type_expression(&ast_context, v); ok {
ranges :: struct {
range: common.Range,
selection_range: common.Range,
}
name_map := make(map[string]ranges)
for elem in v.elems {
if field_value, ok := elem.derived.(^ast.Field_Value); ok {
if name, ok := field_value.field.derived.(^ast.Ident); ok {
selection_range := common.get_token_range(name, ast_context.file.src)
range := common.get_token_range(field_value, ast_context.file.src)
ensure_selection_range_contained(&range, selection_range)
name_map[name.name] = {
range = range,
selection_range = selection_range,
}
}
}
}
#partial switch v in s.value {
case SymbolStructValue:
children := make([dynamic]DocumentSymbol, context.temp_allocator)
for name, i in v.names {
child: DocumentSymbol
if range, ok := name_map[name]; ok {
child.range = range.range
child.selectionRange = range.selection_range
child.name = name
child.kind = .Field
append(&children, child)
}
}
symbol.children = children[:]
case SymbolBitFieldValue:
children := make([dynamic]DocumentSymbol, context.temp_allocator)
for name, i in v.names {
child: DocumentSymbol
if range, ok := name_map[name]; ok {
child.range = range.range
child.selectionRange = range.selection_range
child.name = name
child.kind = .Field
append(&children, child)
}
}
symbol.children = children[:]
}
}
case:
symbol.kind = .Variable
}
append(&symbols, symbol)
}
return symbols[:]
}
@(private="file")
ensure_selection_range_contained :: proc(range: ^common.Range, selection_range: common.Range) {
// selection range must be contained with range, so we set the range start to be the selection range start
range.start = selection_range.start
// if the range end is somehow before the selection_range end, we set it to the end of the selection range
if range.end.line < selection_range.end.line {
range.end = selection_range.end
} else if range.end.line == selection_range.end.line && range.end.character < selection_range.end.character {
range.end = selection_range.end
}
}
|