aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrad Lewis <22850972+BradLewis@users.noreply.github.com>2025-07-03 21:20:29 -0400
committerBrad Lewis <22850972+BradLewis@users.noreply.github.com>2025-07-04 20:39:31 -0400
commitd91f1da376e12f7ea8eef54528dc6f5ace6cf8bb (patch)
tree94314490b8abe972f369217b6e5991c558dcee25 /src
parentc7c76658be61af1532c0320dc0029e5b39f1c6fd (diff)
Enrich bit field hover documentation
Diffstat (limited to 'src')
-rw-r--r--src/server/analysis.odin25
-rw-r--r--src/server/ast.odin34
-rw-r--r--src/server/collector.odin31
-rw-r--r--src/server/documentation.odin108
-rw-r--r--src/server/symbol.odin10
5 files changed, 165 insertions, 43 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index 597c2cd..bfdf5ba 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -940,7 +940,7 @@ internal_resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Ex
case ^Proc_Type:
return make_symbol_procedure_from_ast(ast_context, node, v^, ast_context.field_name, {}, true), true
case ^Bit_Field_Type:
- return make_symbol_bit_field_from_ast(ast_context, v^, ast_context.field_name, true), true
+ return make_symbol_bit_field_from_ast(ast_context, v, ast_context.field_name, true), true
case ^Basic_Directive:
return resolve_basic_directive(ast_context, v^)
case ^Binary_Expr:
@@ -1409,7 +1409,7 @@ internal_resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ide
return_symbol, ok = make_symbol_bitset_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Bit_Field_Type:
- return_symbol, ok = make_symbol_bit_field_from_ast(ast_context, v^, node), true
+ return_symbol, ok = make_symbol_bit_field_from_ast(ast_context, v, node), true
return_symbol.name = node.name
case ^Proc_Lit:
if is_procedure_generic(v.type) {
@@ -1536,7 +1536,7 @@ internal_resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ide
return_symbol, ok = make_symbol_enum_from_ast(ast_context, v^, node), true
return_symbol.name = node.name
case ^Bit_Field_Type:
- return_symbol, ok = make_symbol_bit_field_from_ast(ast_context, v^, node), true
+ return_symbol, ok = make_symbol_bit_field_from_ast(ast_context, v, node), true
return_symbol.name = node.name
case ^Proc_Lit:
if is_procedure_generic(v.type) {
@@ -2843,10 +2843,11 @@ make_symbol_struct_from_ast :: proc(
make_symbol_bit_field_from_ast :: proc(
ast_context: ^AstContext,
- v: ast.Bit_Field_Type,
+ v: ^ast.Bit_Field_Type,
ident: ast.Ident,
inlined := false,
) -> Symbol {
+ construct_bit_field_field_docs(ast_context.file, v)
symbol := Symbol {
range = common.get_token_range(v, ast_context.file.src),
type = .Struct,
@@ -2862,19 +2863,29 @@ make_symbol_bit_field_from_ast :: proc(
names := make([dynamic]string, ast_context.allocator)
types := make([dynamic]^ast.Expr, ast_context.allocator)
ranges := make([dynamic]common.Range, 0, ast_context.allocator)
+ docs := make([dynamic]^ast.Comment_Group, 0, ast_context.allocator)
+ comments := make([dynamic]^ast.Comment_Group, 0, ast_context.allocator)
+ bit_sizes := make([dynamic]^ast.Expr, 0, ast_context.allocator)
for field in v.fields {
if identifier, ok := field.name.derived.(^ast.Ident); ok && field.type != nil {
append(&names, identifier.name)
append(&types, field.type)
append(&ranges, common.get_token_range(identifier, ast_context.file.src))
+ append(&docs, field.docs)
+ append(&comments, field.comments)
+ append(&bit_sizes, field.bit_size)
}
}
symbol.value = SymbolBitFieldValue {
- names = names[:],
- types = types[:],
- ranges = ranges[:],
+ backing_type = v.backing_type,
+ names = names[:],
+ types = types[:],
+ ranges = ranges[:],
+ docs = docs[:],
+ comments = comments[:],
+ bit_sizes = bit_sizes[:],
}
return symbol
diff --git a/src/server/ast.odin b/src/server/ast.odin
index 9abc1e5..81ec5c7 100644
--- a/src/server/ast.odin
+++ b/src/server/ast.odin
@@ -1236,6 +1236,40 @@ construct_struct_field_docs :: proc(file: ast.File, v: ^ast.Struct_Type) {
}
}
+construct_bit_field_field_docs :: proc(file: ast.File, v: ^ast.Bit_Field_Type) {
+ for field, i in v.fields {
+ // There is currently a bug in the odin parser where it adds line comments for a field to the
+ // docs of the following field, we address this problem here.
+ // see https://github.com/odin-lang/Odin/issues/5353
+ if field.comments == nil {
+ // We check if the comment is at the start of the next field
+ if i != len(v.fields) - 1 {
+ next_field := v.fields[i + 1]
+ if next_field.docs != nil && len(next_field.docs.list) > 0 {
+ list := next_field.docs.list
+ if list[0].pos.line == field.pos.line {
+ field.comments = ast.new(ast.Comment_Group, list[0].pos, parser.end_pos(list[0]))
+ field.comments.list = list[:1]
+ if len(list) > 1 {
+ next_field.docs = ast.new(
+ ast.Comment_Group,
+ list[1].pos,
+ parser.end_pos(list[len(list) - 2]),
+ )
+ next_field.docs.list = list[1:]
+ } else {
+ next_field.docs = nil
+ }
+ }
+ }
+ } else {
+ // We need to check the file to see if it contains a line comment as there is no next field
+ field.comments = get_file_comment(file, field.pos.line)
+ }
+ }
+ }
+}
+
// Retrives the comment group from the specified line of the file
get_file_comment :: proc(file: ast.File, line: int) -> ^ast.Comment_Group {
// TODO: linear scan might be a bit slow for files with lots of comments?
diff --git a/src/server/collector.odin b/src/server/collector.odin
index 22aba08..f4e4a1c 100644
--- a/src/server/collector.odin
+++ b/src/server/collector.odin
@@ -158,15 +158,19 @@ collect_struct_fields :: proc(
collect_bit_field_fields :: proc(
collection: ^SymbolCollection,
- fields: []^ast.Bit_Field_Field,
+ bit_field_type: ^ast.Bit_Field_Type,
package_map: map[string]string,
file: ast.File,
) -> SymbolBitFieldValue {
- names := make([dynamic]string, 0, len(fields), collection.allocator)
- types := make([dynamic]^ast.Expr, 0, len(fields), collection.allocator)
- ranges := make([dynamic]common.Range, 0, len(fields), collection.allocator)
-
- for field, i in fields {
+ construct_bit_field_field_docs(file, bit_field_type)
+ names := make([dynamic]string, 0, len(bit_field_type.fields), collection.allocator)
+ types := make([dynamic]^ast.Expr, 0, len(bit_field_type.fields), collection.allocator)
+ ranges := make([dynamic]common.Range, 0, len(bit_field_type.fields), collection.allocator)
+ docs := make([dynamic]^ast.Comment_Group, 0, collection.allocator)
+ comments := make([dynamic]^ast.Comment_Group, 0, collection.allocator)
+ bit_sizes := make([dynamic]^ast.Expr, 0, collection.allocator)
+
+ for field, i in bit_field_type.fields {
if ident, ok := field.name.derived.(^ast.Ident); ok {
append(&names, get_index_unique_string(collection, ident.name))
@@ -175,13 +179,20 @@ collect_bit_field_fields :: proc(
append(&types, cloned)
append(&ranges, common.get_token_range(ident, file.src))
+ append(&docs, clone_comment_group(field.docs, collection.allocator, &collection.unique_strings))
+ append(&comments, clone_comment_group(field.comments, collection.allocator, &collection.unique_strings))
+ append(&bit_sizes, clone_type(field.bit_size, collection.allocator, &collection.unique_strings))
}
}
value := SymbolBitFieldValue {
- names = names[:],
- types = types[:],
- ranges = ranges[:],
+ backing_type = clone_type(bit_field_type.backing_type, collection.allocator, &collection.unique_strings),
+ names = names[:],
+ types = types[:],
+ ranges = ranges[:],
+ docs = docs[:],
+ comments = comments[:],
+ bit_sizes = bit_sizes[:],
}
return value
@@ -547,7 +558,7 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri
case ^ast.Bit_Field_Type:
token = v^
token_type = .Struct
- symbol.value = collect_bit_field_fields(collection, v.fields, package_map, file)
+ symbol.value = collect_bit_field_fields(collection, v, package_map, file)
symbol.signature = "bit_field"
case ^ast.Map_Type:
token = v^
diff --git a/src/server/documentation.odin b/src/server/documentation.odin
index b52d259..b86147f 100644
--- a/src/server/documentation.odin
+++ b/src/server/documentation.odin
@@ -2,6 +2,7 @@ package server
import "core:fmt"
import "core:log"
+import "core:odin/ast"
import path "core:path/slashpath"
import "core:strings"
@@ -9,8 +10,7 @@ import "core:strings"
get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
is_variable := symbol.type == .Variable
- pointer_prefix := repeat("^", symbol.pointers, context.temp_allocator)
-
+ pointer_prefix := repeat("^", symbol.pointers, ast_context.allocator)
#partial switch v in symbol.value {
case SymbolEnumValue:
@@ -19,6 +19,13 @@ get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
append_variable_full_name(&sb, ast_context, symbol, pointer_prefix)
strings.write_string(&sb, " :: ")
}
+ if len(v.names) == 0 {
+ strings.write_string(&sb, "enum {}")
+ if symbol.comment != "" {
+ fmt.sbprintf(&sb, " %s", symbol.comment)
+ }
+ return strings.to_string(sb)
+ }
longestNameLen := 0
for name in v.names {
@@ -71,6 +78,10 @@ get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
append_variable_full_name(&sb, ast_context, symbol, pointer_prefix)
strings.write_string(&sb, " :: ")
}
+ if len(v.types) == 0 {
+ strings.write_string(&sb, "union {}")
+ return strings.to_string(sb)
+ }
strings.write_string(&sb, "union {\n")
for i in 0 ..< len(v.types) {
strings.write_string(&sb, "\t")
@@ -80,7 +91,7 @@ get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
strings.write_string(&sb, "}")
return strings.to_string(sb)
case SymbolAggregateValue:
- sb := strings.builder_make(context.temp_allocator)
+ sb := strings.builder_make(ast_context.allocator)
strings.write_string(&sb, "proc {\n")
for symbol in v.symbols {
if value, ok := symbol.value.(SymbolProcedureValue); ok {
@@ -91,6 +102,53 @@ get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
}
strings.write_string(&sb, "}")
return strings.to_string(sb)
+ case SymbolBitFieldValue:
+ sb := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&sb, ast_context, symbol, pointer_prefix)
+ strings.write_string(&sb, " :: ")
+ } else if symbol.type_name != "" {
+ if symbol.type_pkg == "" {
+ fmt.sbprintf(&sb, "%s%s :: ", pointer_prefix, symbol.type_name)
+ } else {
+ pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
+ fmt.sbprintf(&sb, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.type_name)
+ }
+ }
+ strings.write_string(&sb, "bit_field ")
+ build_string_node(v.backing_type, &sb, false)
+ if len(v.names) == 0 {
+ strings.write_string(&sb, " {}")
+ return strings.to_string(sb)
+ }
+ strings.write_string(&sb, " {\n")
+ longest_name_len := 0
+ for name in v.names {
+ if len(name) > longest_name_len {
+ longest_name_len = len(name)
+ }
+ }
+ longest_type_len := 0
+ type_names := make([dynamic]string, 0, len(v.types), ast_context.allocator)
+ for t in v.types {
+ type_name := node_to_string(t)
+ append(&type_names, type_name)
+ if len(type_name) > longest_type_len {
+ longest_type_len = len(type_name)
+ }
+ }
+
+ for name, i in v.names {
+ append_docs(&sb, v.docs, i)
+ fmt.sbprintf(&sb, "\t%s:%*s", v.names[i], longest_name_len - len(name) + 1, "")
+ fmt.sbprintf(&sb, "%s%*s| ", type_names[i], longest_type_len - len(type_names[i]) + 1, "")
+ build_string_node(v.bit_sizes[i], &sb, false)
+ strings.write_string(&sb, ",")
+ append_comments(&sb, v.comments, i)
+ strings.write_string(&sb, "\n")
+ }
+ strings.write_string(&sb, "}")
+ return strings.to_string(sb)
}
return get_short_signature(ast_context, symbol)
@@ -99,7 +157,7 @@ get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
get_short_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
is_variable := symbol.type == .Variable
- pointer_prefix := repeat("^", symbol.pointers, context.temp_allocator)
+ pointer_prefix := repeat("^", symbol.pointers, ast_context.allocator)
#partial switch v in symbol.value {
case SymbolBasicValue:
sb := strings.builder_make(ast_context.allocator)
@@ -139,7 +197,7 @@ get_short_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string
allocator = ast_context.allocator,
)
case SymbolProcedureValue:
- sb := strings.builder_make(context.temp_allocator)
+ sb := strings.builder_make(ast_context.allocator)
if symbol.type_pkg != "" && symbol.type_name != "" {
pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
fmt.sbprintf(&sb, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.type_name)
@@ -227,8 +285,8 @@ get_short_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string
return ""
}
-get_enum_field_signature :: proc(value: SymbolEnumValue, index: int) -> string {
- sb := strings.builder_make(context.temp_allocator)
+get_enum_field_signature :: proc(value: SymbolEnumValue, index: int, allocator := context.temp_allocator) -> string {
+ sb := strings.builder_make(allocator)
fmt.sbprintf(&sb, ".%s", value.names[index])
if index < len(value.values) && value.values[index] != nil {
strings.write_string(&sb, " = ")
@@ -342,12 +400,7 @@ write_struct_hover :: proc(ast_context: ^AstContext, sb: ^strings.Builder, v: Sy
using_index = index
}
}
- if i < len(v.docs) && v.docs[i] != nil {
- for c in v.docs[i].list {
- fmt.sbprintf(sb, "\t%s\n", c.text)
- }
- }
-
+ append_docs(sb, v.docs, i)
strings.write_string(sb, "\t")
name_len := len(v.names[i])
@@ -355,18 +408,11 @@ write_struct_hover :: proc(ast_context: ^AstContext, sb: ^strings.Builder, v: Sy
strings.write_string(sb, using_prefix)
name_len += len(using_prefix)
}
- strings.write_string(sb, v.names[i])
- fmt.sbprintf(sb, ":%*s", longestNameLen - name_len + 1, "")
+ fmt.sbprintf(sb, "%s:%*s", v.names[i], longestNameLen - name_len + 1, "")
build_string_node(v.types[i], sb, false)
strings.write_string(sb, ",")
-
- if i < len(v.comments) && v.comments[i] != nil {
- for c in v.comments[i].list {
- fmt.sbprintf(sb, " %s\n", c.text)
- }
- } else {
- strings.write_string(sb, "\n")
- }
+ append_comments(sb, v.comments, i)
+ strings.write_string(sb, "\n")
}
strings.write_string(sb, "}")
}
@@ -386,6 +432,22 @@ append_variable_full_name :: proc(
return
}
+append_docs :: proc(sb: ^strings.Builder, docs: []^ast.Comment_Group, index: int) {
+ if index < len(docs) && docs[index] != nil {
+ for c in docs[index].list {
+ fmt.sbprintf(sb, "\t%s\n", c.text)
+ }
+ }
+}
+
+append_comments :: proc(sb: ^strings.Builder, comments: []^ast.Comment_Group, index: int) {
+ if index < len(comments) && comments[index] != nil {
+ for c in comments[index].list {
+ fmt.sbprintf(sb, " %s", c.text)
+ }
+ }
+}
+
concatenate_symbol_information :: proc {
concatenate_raw_symbol_information,
concatenate_raw_string_information,
diff --git a/src/server/symbol.odin b/src/server/symbol.odin
index fddc775..e9b9037 100644
--- a/src/server/symbol.odin
+++ b/src/server/symbol.odin
@@ -39,9 +39,13 @@ SymbolStructValue :: struct {
}
SymbolBitFieldValue :: struct {
- names: []string,
- ranges: []common.Range,
- types: []^ast.Expr,
+ backing_type: ^ast.Expr,
+ names: []string,
+ ranges: []common.Range,
+ types: []^ast.Expr,
+ docs: []^ast.Comment_Group,
+ comments: []^ast.Comment_Group,
+ bit_sizes: []^ast.Expr,
}
SymbolPackageValue :: struct {