aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2025-06-28 11:52:44 +0200
committerGitHub <noreply@github.com>2025-06-28 11:52:44 +0200
commitd7c08dca5bee9f9ddda7cde4fb5d428251b025a5 (patch)
tree0bc1535342af07c6b50ba1b60ea6b2f62d797d80
parent4eae668a145df223c6cfc7b8929cdd49d436756d (diff)
parentc8630395d0c3314c356db0cc40b4b79904909c6d (diff)
Merge pull request #684 from BradLewis/feat/rework-symbol-documentation-writing
Add field comments information on hover
-rw-r--r--src/server/analysis.odin300
-rw-r--r--src/server/completion.odin163
-rw-r--r--src/server/documentation.odin360
-rw-r--r--src/server/hover.odin6
-rw-r--r--src/server/methods.odin4
-rw-r--r--src/server/signature.odin69
-rw-r--r--tests/completions_test.odin52
-rw-r--r--tests/objc_test.odin6
8 files changed, 454 insertions, 506 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index b43dce7..9703ece 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -226,10 +226,8 @@ resolve_type_comp_literal :: proc(
return {}, nil, false
}
-
set_ast_package_set_scoped(ast_context, current_symbol.pkg)
-
for elem, element_index in current_comp_lit.elems {
if !position_in_node(elem, position_context.position) {
continue
@@ -3753,53 +3751,6 @@ clear_locals :: proc(ast_context: ^AstContext) {
clear(&ast_context.usings)
}
-concatenate_symbol_information :: proc {
- concatenate_raw_symbol_information,
- concatenate_raw_string_information,
-}
-
-concatenate_raw_symbol_information :: proc(ast_context: ^AstContext, symbol: Symbol, is_completion: bool) -> string {
- return concatenate_raw_string_information(
- ast_context,
- symbol.pkg,
- symbol.name,
- symbol.signature,
- symbol.type,
- symbol.comment,
- is_completion,
- )
-}
-
-concatenate_raw_string_information :: proc(
- ast_context: ^AstContext,
- pkg: string,
- name: string,
- signature: string,
- type: SymbolType,
- comment: string,
- is_completion: bool,
-) -> string {
- pkg := path.base(pkg, false, context.temp_allocator)
-
- if type == .Package {
- return fmt.tprintf("%v: package", name)
- } else if type == .Keyword && is_completion {
- return name
- } else if type == .Function {
- if comment != "" {
- return fmt.tprintf("%v\n%v.%v: %v", comment, pkg, name, signature)
- }
- return fmt.tprintf("%v.%v: %v", pkg, name, signature)
-
- } else {
- if signature != "" {
- return fmt.tprintf("%v.%v: %v", pkg, name, signature)
- } else {
- return fmt.tprintf("%v.%v", pkg, name)
- }
- }
-}
-
unwrap_procedure_until_struct_bit_field_or_package :: proc(
ast_context: ^AstContext,
node: ^ast.Expr,
@@ -3925,257 +3876,6 @@ unwrap_bitset :: proc(ast_context: ^AstContext, bitset_symbol: Symbol) -> (Symbo
return {}, false
}
-append_variable_full_name :: proc(
- sb: ^strings.Builder,
- ast_context: ^AstContext,
- symbol: Symbol,
- pointer_prefix: string,
-) {
- pkg_name := get_symbol_pkg_name(ast_context, symbol)
- if pkg_name == "" {
- fmt.sbprintf(sb, "%s%s :: ", pointer_prefix, symbol.name)
- return
- }
- fmt.sbprintf(sb, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.name)
- return
-}
-
-make_comment_map :: proc(groups: []^ast.Comment_Group, allocator: mem.Allocator) -> map[int]^ast.Comment_Group {
- comment_map := make(map[int]^ast.Comment_Group, allocator = allocator)
-
- for cg in groups {
- comment_map[cg.pos.line] = cg
- }
-
- return comment_map
-}
-
-get_signature :: proc(
- ast_context: ^AstContext,
- symbol: Symbol,
- was_variable := false,
- short_signature := false,
-) -> string {
- if symbol.type == .Function {
- return symbol.signature
- }
-
- if .Distinct in symbol.flags {
- return symbol.name
- }
-
- is_variable := symbol.type == .Variable
- is_field := symbol.type == .Field
-
- pointer_prefix := repeat("^", symbol.pointers, context.temp_allocator)
-
-
- #partial switch v in symbol.value {
- case SymbolBasicValue:
- return strings.concatenate({pointer_prefix, node_to_string(v.ident)}, ast_context.allocator)
- case SymbolBitSetValue:
- return strings.concatenate(
- a = {pointer_prefix, "bit_set[", node_to_string(v.expr), "]"},
- allocator = ast_context.allocator,
- )
- case SymbolEnumValue:
- if short_signature {
- builder := strings.builder_make(ast_context.allocator)
- if is_variable {
- append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
- }
- strings.write_string(&builder, "enum")
- return strings.to_string(builder)
- }
- builder := strings.builder_make(ast_context.allocator)
- if is_variable {
- append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
- }
- strings.write_string(&builder, "enum {\n")
- for i in 0 ..< len(v.names) {
- strings.write_string(&builder, "\t")
- strings.write_string(&builder, v.names[i])
- strings.write_string(&builder, ",\n")
- }
- strings.write_string(&builder, "}")
- return strings.to_string(builder)
- case SymbolMapValue:
- return strings.concatenate(
- a = {pointer_prefix, "map[", node_to_string(v.key), "]", node_to_string(v.value)},
- allocator = ast_context.allocator,
- )
- case SymbolProcedureValue:
- return "proc"
- case SymbolStructValue:
- if short_signature {
- builder := strings.builder_make(ast_context.allocator)
- if is_variable {
- append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
- }
- strings.write_string(&builder, "struct")
- return strings.to_string(builder)
- }
- builder := strings.builder_make(ast_context.allocator)
- if is_variable {
- append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
- } else if is_field {
- pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
- if pkg_name == "" {
- fmt.sbprintf(&builder, "%s%s", pointer_prefix, symbol.type_name)
- } else {
- fmt.sbprintf(&builder, "%s%s.%s", pointer_prefix, pkg_name, symbol.type_name)
- }
- if symbol.comment != "" {
- fmt.sbprintf(&builder, " %s", symbol.comment)
- }
- return strings.to_string(builder)
- } else if symbol.type_name != "" {
- if symbol.type_pkg == "" {
- fmt.sbprintf(&builder, "%s%s :: ", pointer_prefix, symbol.type_name)
- } else {
- pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
- fmt.sbprintf(&builder, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.type_name)
- }
- }
- if len(v.names) == 0 {
- strings.write_string(&builder, "struct {}")
- return strings.to_string(builder)
- }
- write_struct_hover(ast_context, &builder, v)
- return strings.to_string(builder)
- case SymbolUnionValue:
- if short_signature {
- builder := strings.builder_make(ast_context.allocator)
- if is_variable {
- append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
- }
- strings.write_string(&builder, "union")
- return strings.to_string(builder)
- }
- builder := strings.builder_make(ast_context.allocator)
- if is_variable {
- append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
- }
- strings.write_string(&builder, "union {\n")
- for i in 0 ..< len(v.types) {
- strings.write_string(&builder, "\t")
- build_string_node(v.types[i], &builder, false)
- strings.write_string(&builder, ",\n")
- }
- strings.write_string(&builder, "}")
- return strings.to_string(builder)
- case SymbolBitFieldValue:
- if is_variable {
- return strings.concatenate({pointer_prefix, symbol.name}, ast_context.allocator)
- } else {
- return "bit_field"
- }
- case SymbolMultiPointerValue:
- return strings.concatenate(
- a = {pointer_prefix, "[^]", node_to_string(v.expr)},
- allocator = ast_context.allocator,
- )
- case SymbolDynamicArrayValue:
- return strings.concatenate(
- a = {pointer_prefix, "[dynamic]", node_to_string(v.expr)},
- allocator = ast_context.allocator,
- )
- case SymbolSliceValue:
- return strings.concatenate(
- a = {pointer_prefix, "[]", node_to_string(v.expr)},
- allocator = ast_context.allocator,
- )
- case SymbolFixedArrayValue:
- return strings.concatenate(
- a = {pointer_prefix, "[", node_to_string(v.len), "]", node_to_string(v.expr)},
- allocator = ast_context.allocator,
- )
- case SymbolMatrixValue:
- return strings.concatenate(
- a = {
- pointer_prefix,
- "matrix",
- "[",
- node_to_string(v.x),
- ",",
- node_to_string(v.y),
- "]",
- node_to_string(v.expr),
- },
- allocator = ast_context.allocator,
- )
- case SymbolPackageValue:
- return "package"
- case SymbolUntypedValue:
- switch v.type {
- case .Float:
- return "float"
- case .String:
- return "string"
- case .Bool:
- return "bool"
- case .Integer:
- return "int"
- }
- }
-
- return ""
-}
-
-write_struct_hover :: proc(ast_context: ^AstContext, sb: ^strings.Builder, v: SymbolStructValue) {
- using_prefix := "using "
- longestNameLen := 0
- for name, i in v.names {
- l := len(name)
- if _, ok := v.usings[i]; ok {
- l += len(using_prefix)
- }
- if l > longestNameLen {
- longestNameLen = len(name)
- }
- }
-
- using_index := -1
-
- strings.write_string(sb, "struct {\n")
- for i in 0 ..< len(v.names) {
- if i < len(v.from_usings) {
- if index := v.from_usings[i]; index != using_index {
- fmt.sbprintf(sb, "\n\t// from `using %s: ", v.names[index])
- build_string_node(v.types[index], sb, false)
- strings.write_string(sb, "`\n")
- 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)
- }
- }
-
- strings.write_string(sb, "\t")
-
- name_len := len(v.names[i])
- if _, ok := v.usings[i]; ok {
- 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, "")
- 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")
- }
- }
- strings.write_string(sb, "}")
-}
-
position_in_proc_decl :: proc(position_context: ^DocumentPositionContext) -> bool {
if position_context.value_decl == nil {
return false
diff --git a/src/server/completion.odin b/src/server/completion.odin
index 2e4c0a9..4b8fd35 100644
--- a/src/server/completion.odin
+++ b/src/server/completion.odin
@@ -140,7 +140,6 @@ get_completion_list :: proc(
}
}
-
switch completion_type {
case .Comp_Lit:
get_comp_lit_completion(&ast_context, &position_context, &list)
@@ -585,15 +584,19 @@ get_selector_completion :: proc(
continue
}
+ symbol.type_pkg = symbol.pkg
+ symbol.type_name = symbol.name
+ symbol.name = name
+ symbol.pkg = selector.name
+ symbol.type = .Field
+ symbol.doc = get_doc(v.docs[i], context.temp_allocator)
+ symbol.comment = get_comment(v.comments[i])
+ symbol.signature = get_short_signature(ast_context, symbol)
+
item := CompletionItem {
label = name,
kind = .Field,
- detail = fmt.tprintf(
- "%v.%v: %v",
- selector.name,
- name,
- type_to_string(ast_context, v.types[i]),
- ),
+ detail = concatenate_symbol_information(ast_context, symbol),
documentation = symbol.doc,
}
@@ -662,12 +665,12 @@ get_selector_completion :: proc(
}
resolve_unresolved_symbol(ast_context, &symbol)
- build_procedure_symbol_signature(&symbol)
+ symbol.signature = get_short_signature(ast_context, symbol)
item := CompletionItem {
label = symbol.name,
kind = symbol_type_to_completion_kind(symbol.type),
- detail = concatenate_symbol_information(ast_context, symbol, true),
+ detail = concatenate_symbol_information(ast_context, symbol),
documentation = symbol.doc,
}
@@ -1210,15 +1213,9 @@ get_identifier_completion :: proc(
list: ^CompletionList,
) {
CombinedResult :: struct {
- score: f32,
- snippet: Snippet_Info,
- name: string,
- type: SymbolType,
- doc: string,
- comment: string,
- pkg: string,
- signature: string,
- flags: SymbolFlags,
+ score: f32,
+ snippet: Snippet_Info,
+ symbol: Symbol,
}
items := make([dynamic]CompletionItem, context.temp_allocator)
@@ -1250,23 +1247,11 @@ get_identifier_completion :: proc(
for r in results {
r := r
resolve_unresolved_symbol(ast_context, &r.symbol)
- build_procedure_symbol_signature(&r.symbol)
+ r.symbol.signature = get_short_signature(ast_context, r.symbol)
uri, _ := common.parse_uri(r.symbol.uri, context.temp_allocator)
if uri.path != ast_context.fullpath {
- append(
- &combined,
- CombinedResult {
- score = r.score,
- type = r.symbol.type,
- name = r.symbol.name,
- doc = r.symbol.doc,
- comment = r.symbol.comment,
- flags = r.symbol.flags,
- signature = r.symbol.signature,
- pkg = r.symbol.pkg,
- },
- )
+ append(&combined, CombinedResult{score = r.score, symbol = r.symbol})
}
}
}
@@ -1280,7 +1265,7 @@ get_identifier_completion :: proc(
//combined is sorted and should do binary search instead.
for result in combined {
- if result.name == k {
+ if result.symbol.name == k {
continue global
}
}
@@ -1293,24 +1278,11 @@ get_identifier_completion :: proc(
ident.name = k
if symbol, ok := resolve_type_identifier(ast_context, ident^); ok {
- symbol.signature = get_signature(ast_context, symbol, short_signature = true)
-
- build_procedure_symbol_signature(&symbol)
+ symbol.name = k
+ symbol.signature = get_short_signature(ast_context, symbol)
if score, ok := common.fuzzy_match(matcher, ident.name); ok == 1 {
- append(
- &combined,
- CombinedResult {
- score = score * 1.1,
- type = symbol.type,
- name = ident.name,
- doc = symbol.doc,
- comment = symbol.comment,
- flags = symbol.flags,
- pkg = symbol.pkg,
- signature = symbol.signature,
- },
- )
+ append(&combined, CombinedResult{score = score * 1.1, symbol = symbol})
}
}
}
@@ -1335,24 +1307,11 @@ get_identifier_completion :: proc(
ident.name = k
if symbol, ok := resolve_type_identifier(ast_context, ident^); ok {
- symbol.signature = get_signature(ast_context, symbol, short_signature = true)
-
- build_procedure_symbol_signature(&symbol)
+ symbol.signature = get_short_signature(ast_context, symbol)
if score, ok := common.fuzzy_match(matcher, ident.name); ok == 1 {
- append(
- &combined,
- CombinedResult {
- score = score * 1.7,
- type = symbol.type,
- name = clean_ident(ident.name),
- doc = symbol.doc,
- comment = symbol.comment,
- flags = symbol.flags,
- pkg = symbol.pkg,
- signature = symbol.signature,
- },
- )
+ symbol.name = clean_ident(ident.name)
+ append(&combined, CombinedResult{score = score * 1.7, symbol = symbol})
}
}
}
@@ -1369,19 +1328,7 @@ get_identifier_completion :: proc(
}
if score, ok := common.fuzzy_match(matcher, symbol.name); ok == 1 {
- append(
- &combined,
- CombinedResult {
- score = score * 1.1,
- type = symbol.type,
- name = symbol.name,
- doc = symbol.doc,
- comment = symbol.comment,
- flags = symbol.flags,
- signature = symbol.signature,
- pkg = symbol.pkg,
- },
- )
+ append(&combined, CombinedResult{score = score * 1.1, symbol = symbol})
}
}
@@ -1392,19 +1339,7 @@ get_identifier_completion :: proc(
}
if score, ok := common.fuzzy_match(matcher, keyword); ok == 1 {
- append(
- &combined,
- CombinedResult {
- score = score,
- type = symbol.type,
- name = symbol.name,
- doc = symbol.doc,
- comment = symbol.comment,
- flags = symbol.flags,
- signature = symbol.signature,
- pkg = symbol.pkg,
- },
- )
+ append(&combined, CombinedResult{score = score, symbol = symbol})
}
}
@@ -1415,26 +1350,17 @@ get_identifier_completion :: proc(
}
if score, ok := common.fuzzy_match(matcher, keyword); ok == 1 {
- append(
- &combined,
- CombinedResult {
- score = score * 1.1,
- type = symbol.type,
- name = symbol.name,
- doc = symbol.doc,
- comment = symbol.comment,
- flags = symbol.flags,
- signature = symbol.signature,
- pkg = symbol.pkg,
- },
- )
+ append(&combined, CombinedResult{score = score * 1.1, symbol = symbol})
}
}
if common.config.enable_snippets {
for k, v in snippets {
if score, ok := common.fuzzy_match(matcher, k); ok == 1 {
- append(&combined, CombinedResult{score = score * 1.1, snippet = v, name = k})
+ symbol := Symbol {
+ name = k,
+ }
+ append(&combined, CombinedResult{score = score * 1.1, snippet = v, symbol = symbol})
}
}
}
@@ -1451,18 +1377,18 @@ get_identifier_completion :: proc(
//Skip procedures when the position is in proc decl
if position_in_proc_decl(position_context) &&
- result.type == .Function &&
+ result.symbol.type == .Function &&
common.config.enable_procedure_context {
continue
}
if result.snippet.insert != "" {
item := CompletionItem {
- label = result.name,
+ label = result.symbol.name,
insertText = result.snippet.insert,
kind = .Snippet,
detail = result.snippet.detail,
- documentation = result.doc,
+ documentation = result.symbol.doc,
insertTextFormat = .Snippet,
}
@@ -1480,31 +1406,22 @@ get_identifier_completion :: proc(
append(&items, item)
} else {
item := CompletionItem {
- label = result.name,
- documentation = result.doc,
+ label = result.symbol.name,
+ documentation = result.symbol.doc,
}
- item.kind = symbol_type_to_completion_kind(result.type)
+ item.kind = symbol_type_to_completion_kind(result.symbol.type)
- if result.type == .Function && common.config.enable_snippets && common.config.enable_procedure_snippet {
+ if result.symbol.type == .Function && common.config.enable_snippets && common.config.enable_procedure_snippet {
item.insertText = fmt.tprintf("%v($0)", item.label)
item.insertTextFormat = .Snippet
- item.deprecated = .Deprecated in result.flags
+ item.deprecated = .Deprecated in result.symbol.flags
item.command = Command {
command = "editor.action.triggerParameterHints",
}
}
- item.detail = concatenate_symbol_information(
- ast_context,
- result.pkg,
- result.name,
- result.signature,
- result.type,
- result.comment,
- true,
- )
-
+ item.detail = concatenate_symbol_information(ast_context, result.symbol)
append(&items, item)
}
}
diff --git a/src/server/documentation.odin b/src/server/documentation.odin
new file mode 100644
index 0000000..663d32a
--- /dev/null
+++ b/src/server/documentation.odin
@@ -0,0 +1,360 @@
+package server
+
+import "core:fmt"
+import "core:log"
+import path "core:path/slashpath"
+import "core:strings"
+
+
+get_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
+ if .Distinct in symbol.flags {
+ return symbol.name
+ }
+
+ is_variable := symbol.type == .Variable
+
+ pointer_prefix := repeat("^", symbol.pointers, context.temp_allocator)
+
+
+ #partial switch v in symbol.value {
+ case SymbolEnumValue:
+ builder := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ strings.write_string(&builder, " :: ")
+ }
+ strings.write_string(&builder, "enum {\n")
+ for i in 0 ..< len(v.names) {
+ strings.write_string(&builder, "\t")
+ strings.write_string(&builder, v.names[i])
+ strings.write_string(&builder, ",\n")
+ }
+ strings.write_string(&builder, "}")
+ return strings.to_string(builder)
+ case SymbolStructValue:
+ builder := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ strings.write_string(&builder, " :: ")
+ } else if symbol.type_name != "" {
+ if symbol.type_pkg == "" {
+ fmt.sbprintf(&builder, "%s%s :: ", pointer_prefix, symbol.type_name)
+ } else {
+ pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
+ fmt.sbprintf(&builder, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.type_name)
+ }
+ }
+ if len(v.names) == 0 {
+ strings.write_string(&builder, "struct {}")
+ if symbol.comment != "" {
+ fmt.sbprintf(&builder, " %s", symbol.comment)
+ }
+ return strings.to_string(builder)
+ }
+ write_struct_hover(ast_context, &builder, v)
+ return strings.to_string(builder)
+ case SymbolUnionValue:
+ builder := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ strings.write_string(&builder, " :: ")
+ }
+ strings.write_string(&builder, "union {\n")
+ for i in 0 ..< len(v.types) {
+ strings.write_string(&builder, "\t")
+ build_string_node(v.types[i], &builder, false)
+ strings.write_string(&builder, ",\n")
+ }
+ strings.write_string(&builder, "}")
+ return strings.to_string(builder)
+ case SymbolAggregateValue:
+ builder := strings.builder_make(context.temp_allocator)
+ strings.write_string(&builder, "proc {\n")
+ for symbol in v.symbols {
+ if value, ok := symbol.value.(SymbolProcedureValue); ok {
+ fmt.sbprintf(&builder, "\t%s :: ", symbol.name)
+ write_procedure_symbol_signature(&builder, value)
+ strings.write_string(&builder, ",\n")
+ }
+ }
+ strings.write_string(&builder, "}")
+ return strings.to_string(builder)
+ }
+
+ return get_short_signature(ast_context, symbol)
+}
+
+get_short_signature :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
+ if .Distinct in symbol.flags {
+ return symbol.name
+ }
+
+ is_variable := symbol.type == .Variable
+
+ pointer_prefix := repeat("^", symbol.pointers, context.temp_allocator)
+
+
+ #partial switch v in symbol.value {
+ case SymbolBasicValue:
+ builder := strings.builder_make(ast_context.allocator)
+ fmt.sbprintf(&builder, "%s%s", pointer_prefix, node_to_string(v.ident))
+ if symbol.type == .Field && symbol.comment != "" {
+ fmt.sbprintf(&builder, " %s", symbol.comment)
+ }
+ return strings.to_string(builder)
+ case SymbolBitSetValue:
+ return strings.concatenate(
+ a = {pointer_prefix, "bit_set[", node_to_string(v.expr), "]"},
+ allocator = ast_context.allocator,
+ )
+ case SymbolEnumValue:
+ builder := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ } else {
+ strings.write_string(&builder, "enum")
+ }
+ return strings.to_string(builder)
+ case SymbolMapValue:
+ return strings.concatenate(
+ a = {pointer_prefix, "map[", node_to_string(v.key), "]", node_to_string(v.value)},
+ allocator = ast_context.allocator,
+ )
+ case SymbolProcedureValue:
+ builder := strings.builder_make(context.temp_allocator)
+ if symbol.type_pkg != "" {
+ pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
+ fmt.sbprintf(&builder, "%s%s.%s :: ", pointer_prefix, pkg_name, symbol.type_name)
+ }
+ write_procedure_symbol_signature(&builder, v)
+ return strings.to_string(builder)
+ case SymbolAggregateValue:
+ return "proc"
+ case SymbolStructValue:
+ builder := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ } else if symbol.type_name != "" {
+ pkg_name := get_pkg_name(ast_context, symbol.type_pkg)
+ if pkg_name == "" {
+ fmt.sbprintf(&builder, "%s%s", pointer_prefix, symbol.type_name)
+ } else {
+ fmt.sbprintf(&builder, "%s%s.%s", pointer_prefix, pkg_name, symbol.type_name)
+ }
+ } else {
+ strings.write_string(&builder, "struct")
+ }
+ if symbol.comment != "" {
+ fmt.sbprintf(&builder, " %s", symbol.comment)
+ }
+ return strings.to_string(builder)
+ case SymbolUnionValue:
+ builder := strings.builder_make(ast_context.allocator)
+ if is_variable {
+ append_variable_full_name(&builder, ast_context, symbol, pointer_prefix)
+ } else {
+ strings.write_string(&builder, "union")
+ }
+ return strings.to_string(builder)
+ case SymbolBitFieldValue:
+ if is_variable {
+ return strings.concatenate({pointer_prefix, symbol.name}, ast_context.allocator)
+ } else {
+ return "bit_field"
+ }
+ case SymbolMultiPointerValue:
+ return strings.concatenate(
+ a = {pointer_prefix, "[^]", node_to_string(v.expr)},
+ allocator = ast_context.allocator,
+ )
+ case SymbolDynamicArrayValue:
+ return strings.concatenate(
+ a = {pointer_prefix, "[dynamic]", node_to_string(v.expr)},
+ allocator = ast_context.allocator,
+ )
+ case SymbolSliceValue:
+ return strings.concatenate(
+ a = {pointer_prefix, "[]", node_to_string(v.expr)},
+ allocator = ast_context.allocator,
+ )
+ case SymbolFixedArrayValue:
+ return strings.concatenate(
+ a = {pointer_prefix, "[", node_to_string(v.len), "]", node_to_string(v.expr)},
+ allocator = ast_context.allocator,
+ )
+ case SymbolMatrixValue:
+ return strings.concatenate(
+ a = {
+ pointer_prefix,
+ "matrix",
+ "[",
+ node_to_string(v.x),
+ ",",
+ node_to_string(v.y),
+ "]",
+ node_to_string(v.expr),
+ },
+ allocator = ast_context.allocator,
+ )
+ case SymbolPackageValue:
+ return "package"
+ case SymbolUntypedValue:
+ switch v.type {
+ case .Float:
+ return "float"
+ case .String:
+ return "string"
+ case .Bool:
+ return "bool"
+ case .Integer:
+ return "int"
+ }
+ }
+
+ return ""
+}
+
+write_procedure_symbol_signature :: proc(sb: ^strings.Builder, value: SymbolProcedureValue) {
+ strings.write_string(sb, "proc")
+ strings.write_string(sb, "(")
+ for arg, i in value.orig_arg_types {
+ strings.write_string(sb, node_to_string(arg))
+ if i != len(value.orig_arg_types) - 1 {
+ strings.write_string(sb, ", ")
+ }
+ }
+ strings.write_string(sb, ")")
+
+ if len(value.orig_return_types) != 0 {
+ strings.write_string(sb, " -> ")
+
+ if len(value.orig_return_types) > 1 {
+ strings.write_string(sb, "(")
+ }
+
+ for arg, i in value.orig_return_types {
+ strings.write_string(sb, node_to_string(arg))
+ if i != len(value.orig_return_types) - 1 {
+ strings.write_string(sb, ", ")
+ }
+ }
+
+ if len(value.orig_return_types) > 1 {
+ strings.write_string(sb, ")")
+ }
+ } else if value.diverging {
+ strings.write_string(sb, " -> !")
+ }
+}
+
+write_struct_hover :: proc(ast_context: ^AstContext, sb: ^strings.Builder, v: SymbolStructValue) {
+ using_prefix := "using "
+ longestNameLen := 0
+ for name, i in v.names {
+ l := len(name)
+ if _, ok := v.usings[i]; ok {
+ l += len(using_prefix)
+ }
+ if l > longestNameLen {
+ longestNameLen = len(name)
+ }
+ }
+
+ using_index := -1
+
+ strings.write_string(sb, "struct {\n")
+ for i in 0 ..< len(v.names) {
+ if i < len(v.from_usings) {
+ if index := v.from_usings[i]; index != using_index {
+ fmt.sbprintf(sb, "\n\t// from `using %s: ", v.names[index])
+ build_string_node(v.types[index], sb, false)
+ strings.write_string(sb, "`\n")
+ 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)
+ }
+ }
+
+ strings.write_string(sb, "\t")
+
+ name_len := len(v.names[i])
+ if _, ok := v.usings[i]; ok {
+ 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, "")
+ 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")
+ }
+ }
+ strings.write_string(sb, "}")
+}
+
+append_variable_full_name :: proc(
+ sb: ^strings.Builder,
+ ast_context: ^AstContext,
+ symbol: Symbol,
+ pointer_prefix: string,
+) {
+ pkg_name := get_symbol_pkg_name(ast_context, symbol)
+ if pkg_name == "" {
+ fmt.sbprintf(sb, "%s%s", pointer_prefix, symbol.name)
+ return
+ }
+ fmt.sbprintf(sb, "%s%s.%s", pointer_prefix, pkg_name, symbol.name)
+ return
+}
+
+concatenate_symbol_information :: proc {
+ concatenate_raw_symbol_information,
+ concatenate_raw_string_information,
+}
+
+concatenate_raw_symbol_information :: proc(ast_context: ^AstContext, symbol: Symbol) -> string {
+ return concatenate_raw_string_information(
+ ast_context,
+ symbol.pkg,
+ symbol.name,
+ symbol.signature,
+ symbol.type,
+ symbol.comment,
+ )
+}
+
+concatenate_raw_string_information :: proc(
+ ast_context: ^AstContext,
+ pkg: string,
+ name: string,
+ signature: string,
+ type: SymbolType,
+ comment: string,
+) -> string {
+ pkg := path.base(pkg, false, context.temp_allocator)
+
+ if type == .Package {
+ return fmt.tprintf("%v: package", name)
+ //} else if type == .Keyword {
+ // return name
+ } else {
+ sb := strings.builder_make()
+ if type == .Function && comment != "" {
+ fmt.sbprintf(&sb, "%s\n", comment)
+ }
+ fmt.sbprintf(&sb, "%v.%v", pkg, name)
+ if signature != "" {
+ fmt.sbprintf(&sb, ": %v", signature)
+ }
+ return strings.to_string(sb)
+ }
+}
diff --git a/src/server/hover.odin b/src/server/hover.odin
index e69249b..52663a8 100644
--- a/src/server/hover.odin
+++ b/src/server/hover.odin
@@ -34,9 +34,7 @@ write_hover_content :: proc(ast_context: ^AstContext, symbol: Symbol) -> MarkupC
}
}
- build_procedure_symbol_signature(&symbol, false)
-
- cat := concatenate_symbol_information(ast_context, symbol, false)
+ cat := concatenate_symbol_information(ast_context, symbol)
if cat != "" {
content.kind = "markdown"
@@ -146,7 +144,7 @@ get_hover_information :: proc(document: ^Document, position: common.Position) ->
}
}
- symbol.signature = get_signature(&ast_context, symbol)
+ symbol.signature = get_short_signature(&ast_context, symbol)
hover.contents = write_hover_content(&ast_context, symbol)
return hover, true, true
}
diff --git a/src/server/methods.odin b/src/server/methods.odin
index 1dae9a0..f17be51 100644
--- a/src/server/methods.odin
+++ b/src/server/methods.odin
@@ -67,7 +67,7 @@ append_method_completion :: proc(
if symbols, ok := &v.methods[method]; ok {
for &symbol in symbols {
resolve_unresolved_symbol(ast_context, &symbol)
- build_procedure_symbol_signature(&symbol)
+ symbol.signature = get_short_signature(ast_context, symbol)
range, ok := get_range_from_selection_start_to_dot(position_context)
@@ -129,7 +129,7 @@ append_method_completion :: proc(
item := CompletionItem {
label = symbol.name,
kind = symbol_type_to_completion_kind(symbol.type),
- detail = concatenate_symbol_information(ast_context, symbol, true),
+ detail = get_short_signature(ast_context, symbol),
additionalTextEdits = remove_edit,
textEdit = TextEdit{newText = new_text, range = {start = range.end, end = range.end}},
insertTextFormat = .Snippet,
diff --git a/src/server/signature.odin b/src/server/signature.odin
index 07e487e..00e6fb9 100644
--- a/src/server/signature.odin
+++ b/src/server/signature.odin
@@ -46,67 +46,6 @@ ParameterInformation :: struct {
label: string,
}
-/*
- Lazily build the signature and returns from ast.Nodes
-*/
-build_procedure_symbol_signature :: proc(symbol: ^Symbol, short_signature := true) {
- if value, ok := symbol.value.(SymbolProcedureValue); ok {
- builder := strings.builder_make(context.temp_allocator)
- write_procedure_symbol_signature(&builder, &value)
- symbol.signature = strings.to_string(builder)
- } else if value, ok := symbol.value.(SymbolAggregateValue); ok {
- if short_signature {
- symbol.signature = "proc"
- return
- }
-
- builder := strings.builder_make(context.temp_allocator)
- strings.write_string(&builder, "proc {\n")
- for symbol in value.symbols {
- if value, ok := symbol.value.(SymbolProcedureValue); ok {
- fmt.sbprintf(&builder, "\t%s :: ", symbol.name)
- write_procedure_symbol_signature(&builder, &value)
- strings.write_string(&builder, ",\n")
- }
- }
- strings.write_string(&builder, "}")
- symbol.signature = strings.to_string(builder)
- }
-}
-
-write_procedure_symbol_signature :: proc(sb: ^strings.Builder, value: ^SymbolProcedureValue) {
- strings.write_string(sb, "proc")
- strings.write_string(sb, "(")
- for arg, i in value.orig_arg_types {
- strings.write_string(sb, node_to_string(arg))
- if i != len(value.orig_arg_types) - 1 {
- strings.write_string(sb, ", ")
- }
- }
- strings.write_string(sb, ")")
-
- if len(value.orig_return_types) != 0 {
- strings.write_string(sb, " -> ")
-
- if len(value.orig_return_types) > 1 {
- strings.write_string(sb, "(")
- }
-
- for arg, i in value.orig_return_types {
- strings.write_string(sb, node_to_string(arg))
- if i != len(value.orig_return_types) - 1 {
- strings.write_string(sb, ", ")
- }
- }
-
- if len(value.orig_return_types) > 1 {
- strings.write_string(sb, ")")
- }
- } else if value.diverging {
- strings.write_string(sb, " -> !")
- }
-}
-
seperate_proc_field_arguments :: proc(procedure: ^Symbol) {
if value, ok := &procedure.value.(SymbolProcedureValue); ok {
types := make([dynamic]^ast.Field, context.temp_allocator)
@@ -195,10 +134,10 @@ get_signature_information :: proc(document: ^Document, position: common.Position
parameters[i].label = node_to_string(arg)
}
- build_procedure_symbol_signature(&call)
+ call.signature = get_short_signature(&ast_context, call)
info := SignatureInformation {
- label = concatenate_symbol_information(&ast_context, call, false),
+ label = concatenate_symbol_information(&ast_context, call),
documentation = call.doc,
parameters = parameters,
}
@@ -221,10 +160,10 @@ get_signature_information :: proc(document: ^Document, position: common.Position
parameters[i].label = node_to_string(arg)
}
- build_procedure_symbol_signature(&symbol)
+ symbol.signature = get_short_signature(&ast_context, symbol)
info := SignatureInformation {
- label = concatenate_symbol_information(&ast_context, symbol, false),
+ label = concatenate_symbol_information(&ast_context, symbol),
documentation = symbol.doc,
}
diff --git a/tests/completions_test.odin b/tests/completions_test.odin
index af1ac7e..2e078e8 100644
--- a/tests/completions_test.odin
+++ b/tests/completions_test.odin
@@ -13,7 +13,7 @@ ast_simple_struct_completion :: proc(t: ^testing.T) {
My_Struct :: struct {
one: int,
- two: int,
+ two: int, // test comment
three: int,
}
@@ -29,7 +29,7 @@ ast_simple_struct_completion :: proc(t: ^testing.T) {
t,
&source,
".",
- {"My_Struct.one: int", "My_Struct.two: int", "My_Struct.three: int"},
+ {"My_Struct.one: int", "My_Struct.two: int // test comment", "My_Struct.three: int"},
)
}
@@ -651,7 +651,7 @@ ast_for_in_identifier_completion :: proc(t: ^testing.T) {
}
- test.expect_completion_details(t, &source, "", {"test.my_element: test.My_Struct :: struct"})
+ test.expect_completion_details(t, &source, "", {"test.my_element: test.My_Struct"})
}
@(test)
@@ -675,7 +675,7 @@ ast_for_in_call_expr_completion :: proc(t: ^testing.T) {
}
- test.expect_completion_details(t, &source, ".", {"test.zstep: test.Step :: struct"})
+ test.expect_completion_details(t, &source, ".", {"test.zstep: test.Step"})
}
@@ -1661,7 +1661,7 @@ ast_new_clone_completion :: proc(t: ^testing.T) {
`,
}
- test.expect_completion_details(t, &source, "", {"test.adzz: ^test.Foo :: struct"})
+ test.expect_completion_details(t, &source, "", {"test.adzz: ^test.Foo"})
}
@(test)
@@ -1735,7 +1735,7 @@ ast_index_proc_parameter_completion :: proc(t: ^testing.T) {
packages = packages[:],
}
- test.expect_completion_details(t, &source, ".", {"my_package.param: my_package.My_Struct :: struct"})
+ test.expect_completion_details(t, &source, ".", {"my_package.param: my_package.My_Struct"})
}
@(test)
@@ -2048,7 +2048,7 @@ ast_procedure_in_procedure_non_mutable_completion :: proc(t: ^testing.T) {
packages = {},
}
- test.expect_completion_details(t, &source, "", {"Int"})
+ test.expect_completion_details(t, &source, "", {"test.Int: int"})
}
@(test)
@@ -2534,7 +2534,7 @@ ast_poly_struct_with_poly :: proc(t: ^testing.T) {
`,
}
- test.expect_completion_details(t, &source, "", {"test.first: ^test.Animal :: struct"})
+ test.expect_completion_details(t, &source, "", {"test.first: ^test.Animal"})
}
@(test)
@@ -3013,7 +3013,7 @@ ast_enumerated_array_range_completion :: proc(t: ^testing.T) {
`,
}
- test.expect_completion_details(t, &source, "", {"test.indezx: test.Enum :: enum"})
+ test.expect_completion_details(t, &source, "", {"test.indezx: test.Enum"})
}
@(test)
@@ -3306,3 +3306,37 @@ ast_completion_multi_pointer_nested :: proc(t: ^testing.T) {
test.expect_completion_details(t, &source, "", {"S3.s3: int"})
}
+
+@(test)
+ast_completion_struct_documentation :: proc(t: ^testing.T) {
+ packages := make([dynamic]test.Package, context.temp_allocator)
+
+ append(
+ &packages,
+ test.Package {
+ pkg = "my_package",
+ source = `package my_package
+ My_Struct :: struct {
+ }
+ `,
+ },
+ )
+ source := test.Source {
+ main = `package main
+
+ import "my_package"
+
+ Foo :: struct {
+ bazz: my_package.My_Struct // bazz
+ }
+
+ main :: proc() {
+ p := Foo{}
+ p.b{*}
+ }
+ `,
+ packages = packages[:],
+ }
+
+ test.expect_completion_details(t, &source, "", {"Foo.bazz: my_package.My_Struct // bazz"})
+}
diff --git a/tests/objc_test.odin b/tests/objc_test.odin
index 915a3b1..9d41377 100644
--- a/tests/objc_test.odin
+++ b/tests/objc_test.odin
@@ -43,7 +43,7 @@ objc_return_type_with_selector_expression :: proc(t: ^testing.T) {
t,
&source,
"->",
- {"Window.initWithContentRect: my_package.Window_initWithContentRect"},
+ {"Window.initWithContentRect: my_package.Window_initWithContentRect :: proc(self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: BOOL) -> ^Window"},
)
}
@@ -91,7 +91,7 @@ objc_return_type_with_selector_expression_2 :: proc(t: ^testing.T) {
t,
&source,
"->",
- {"Window.initWithContentRect: my_package.Window_initWithContentRect"},
+ {"Window.initWithContentRect: my_package.Window_initWithContentRect :: proc(self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: BOOL) -> ^Window"},
)
}
@@ -141,7 +141,7 @@ objc_hover_chained_selector :: proc(t: ^testing.T) {
test.expect_hover(
t,
&source,
- "Window.initWithContentRect: proc(self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: BOOL) -> ^Window",
+ "Window.initWithContentRect: my_package.Window_initWithContentRect :: proc(self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: BOOL) -> ^Window",
)
}