diff options
| author | Brad Lewis <22850972+BradLewis@users.noreply.github.com> | 2025-06-25 21:23:29 -0400 |
|---|---|---|
| committer | Brad Lewis <22850972+BradLewis@users.noreply.github.com> | 2025-06-27 20:10:21 -0400 |
| commit | 747bd0539895fdf2ef9f47ba35238e86f3021fcc (patch) | |
| tree | aed05bd361f2db6f9186ab0eec47db4e9e09d4b5 /src | |
| parent | 4eae668a145df223c6cfc7b8929cdd49d436756d (diff) | |
Consolidate documentation writing code into new file and base it around symbols
Diffstat (limited to 'src')
| -rw-r--r-- | src/server/analysis.odin | 298 | ||||
| -rw-r--r-- | src/server/ast.odin | 234 | ||||
| -rw-r--r-- | src/server/completion.odin | 156 | ||||
| -rw-r--r-- | src/server/documentation.odin | 536 | ||||
| -rw-r--r-- | src/server/hover.odin | 7 | ||||
| -rw-r--r-- | src/server/methods.odin | 4 | ||||
| -rw-r--r-- | src/server/signature.odin | 69 |
7 files changed, 579 insertions, 725 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin index b43dce7..3798adc 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -3753,53 +3753,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 +3878,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/ast.odin b/src/server/ast.odin index 9abc1e5..629a04a 100644 --- a/src/server/ast.odin +++ b/src/server/ast.odin @@ -961,240 +961,6 @@ node_equal_node :: proc(a, b: ^ast.Node) -> bool { return false } -/* - Returns the string representation of a type. This allows us to print the signature without storing it in the indexer as a string(saving memory). -*/ - -node_to_string :: proc(node: ^ast.Node, remove_pointers := false) -> string { - builder := strings.builder_make(context.temp_allocator) - - build_string(node, &builder, remove_pointers) - - return strings.to_string(builder) -} - -build_string :: proc { - build_string_ast_array, - build_string_dynamic_array, - build_string_node, -} - -build_string_dynamic_array :: proc(array: $A/[]^$T, builder: ^strings.Builder, remove_pointers: bool) { - for elem, i in array { - build_string(elem, builder, remove_pointers) - } -} - -build_string_ast_array :: proc(array: $A/[dynamic]^$T, builder: ^strings.Builder, remove_pointers: bool) { - for elem, i in array { - build_string(elem, builder, remove_pointers) - } -} - -build_string_node :: proc(node: ^ast.Node, builder: ^strings.Builder, remove_pointers: bool) { - using ast - - if node == nil { - return - } - - #partial switch n in node.derived { - case ^Bad_Expr: - case ^Ident: - if strings.contains(n.name, "/") { - strings.write_string(builder, path.base(n.name, false, context.temp_allocator)) - } else { - strings.write_string(builder, n.name) - } - case ^Implicit: - strings.write_string(builder, n.tok.text) - case ^Undef: - case ^Basic_Lit: - strings.write_string(builder, n.tok.text) - case ^Basic_Directive: - strings.write_string(builder, "#") - strings.write_string(builder, n.name) - case ^Implicit_Selector_Expr: - strings.write_string(builder, ".") - build_string(n.field, builder, remove_pointers) - case ^Ellipsis: - strings.write_string(builder, "..") - build_string(n.expr, builder, remove_pointers) - case ^Proc_Lit: - build_string(n.type, builder, remove_pointers) - build_string(n.body, builder, remove_pointers) - case ^Comp_Lit: - build_string(n.type, builder, remove_pointers) - strings.write_string(builder, "{") - for elem, i in n.elems { - build_string(elem, builder, remove_pointers) - if len(n.elems) - 1 != i { - strings.write_string(builder, ", ") - } - } - strings.write_string(builder, "}") - case ^Tag_Expr: - build_string(n.expr, builder, remove_pointers) - case ^Unary_Expr: - strings.write_string(builder, n.op.text) - build_string(n.expr, builder, remove_pointers) - case ^Binary_Expr: - build_string(n.left, builder, remove_pointers) - strings.write_string(builder, " ") - strings.write_string(builder, n.op.text) - strings.write_string(builder, " ") - build_string(n.right, builder, remove_pointers) - case ^Paren_Expr: - strings.write_string(builder, "(") - build_string(n.expr, builder, remove_pointers) - strings.write_string(builder, ")") - case ^Call_Expr: - build_string(n.expr, builder, remove_pointers) - strings.write_string(builder, "(") - for arg, i in n.args { - build_string(arg, builder, remove_pointers) - if len(n.args) - 1 != i { - strings.write_string(builder, ", ") - } - } - strings.write_string(builder, ")") - case ^Selector_Expr: - build_string(n.expr, builder, remove_pointers) - strings.write_string(builder, ".") - build_string(n.field, builder, remove_pointers) - case ^Index_Expr: - build_string(n.expr, builder, remove_pointers) - strings.write_string(builder, "[") - build_string(n.index, builder, remove_pointers) - strings.write_string(builder, "]") - case ^Deref_Expr: - build_string(n.expr, builder, remove_pointers) - case ^Slice_Expr: - build_string(n.expr, builder, remove_pointers) - build_string(n.low, builder, remove_pointers) - build_string(n.high, builder, remove_pointers) - case ^Field_Value: - build_string(n.field, builder, remove_pointers) - strings.write_string(builder, ": ") - build_string(n.value, builder, remove_pointers) - case ^Type_Cast: - build_string(n.type, builder, remove_pointers) - build_string(n.expr, builder, remove_pointers) - case ^Bad_Stmt: - case ^Bad_Decl: - case ^Attribute: - build_string(n.elems, builder, remove_pointers) - case ^Field: - for name, i in n.names { - build_string(name, builder, remove_pointers) - if len(n.names) - 1 != i { - strings.write_string(builder, ", ") - } - } - - if len(n.names) > 0 && n.type != nil { - strings.write_string(builder, ": ") - build_string(n.type, builder, remove_pointers) - - if n.default_value != nil && n.type != nil { - strings.write_string(builder, " = ") - } - - } else if len(n.names) > 0 && n.default_value != nil { - strings.write_string(builder, " := ") - } else { - build_string(n.type, builder, remove_pointers) - } - - build_string(n.default_value, builder, remove_pointers) - case ^Field_List: - for field, i in n.list { - build_string(field, builder, remove_pointers) - if len(n.list) - 1 != i { - strings.write_string(builder, ",") - } - } - case ^Typeid_Type: - strings.write_string(builder, "typeid") - build_string(n.specialization, builder, remove_pointers) - case ^Helper_Type: - build_string(n.type, builder, remove_pointers) - case ^Distinct_Type: - build_string(n.type, builder, remove_pointers) - case ^Poly_Type: - strings.write_string(builder, "$") - - build_string(n.type, builder, remove_pointers) - - if n.specialization != nil { - strings.write_string(builder, "/") - build_string(n.specialization, builder, remove_pointers) - } - case ^Proc_Type: - strings.write_string(builder, "proc(") - build_string(n.params, builder, remove_pointers) - strings.write_string(builder, ")") - if n.results != nil { - strings.write_string(builder, " -> ") - build_string(n.results, builder, remove_pointers) - } - case ^Pointer_Type: - if !remove_pointers { - strings.write_string(builder, "^") - } - build_string(n.elem, builder, remove_pointers) - case ^Array_Type: - strings.write_string(builder, "[") - build_string(n.len, builder, remove_pointers) - strings.write_string(builder, "]") - build_string(n.elem, builder, remove_pointers) - case ^Dynamic_Array_Type: - strings.write_string(builder, "[dynamic]") - build_string(n.elem, builder, remove_pointers) - case ^Struct_Type: - build_string(n.poly_params, builder, remove_pointers) - build_string(n.align, builder, remove_pointers) - build_string(n.fields, builder, remove_pointers) - case ^Union_Type: - build_string(n.poly_params, builder, remove_pointers) - build_string(n.align, builder, remove_pointers) - build_string(n.variants, builder, remove_pointers) - case ^Enum_Type: - build_string(n.base_type, builder, remove_pointers) - build_string(n.fields, builder, remove_pointers) - case ^Bit_Set_Type: - strings.write_string(builder, "bit_set") - strings.write_string(builder, "[") - build_string(n.elem, builder, remove_pointers) - strings.write_string(builder, "]") - build_string(n.underlying, builder, remove_pointers) - case ^Map_Type: - strings.write_string(builder, "map") - strings.write_string(builder, "[") - build_string(n.key, builder, remove_pointers) - strings.write_string(builder, "]") - build_string(n.value, builder, remove_pointers) - case ^ast.Multi_Pointer_Type: - strings.write_string(builder, "[^]") - build_string(n.elem, builder, remove_pointers) - case ^ast.Bit_Field_Type: - strings.write_string(builder, "bit_field") - build_string(n.backing_type, builder, remove_pointers) - for field, i in n.fields { - build_string(field, builder, remove_pointers) - if len(n.fields) - 1 != i { - strings.write_string(builder, ",") - } - } - case ^ast.Bit_Field_Field: - build_string(n.name, builder, remove_pointers) - strings.write_string(builder, ": ") - build_string(n.type, builder, remove_pointers) - strings.write_string(builder, " | ") - build_string(n.bit_size, builder, remove_pointers) - } -} - repeat :: proc(value: string, count: int, allocator := context.allocator) -> string { if count <= 0 { return "" diff --git a/src/server/completion.odin b/src/server/completion.odin index 2e4c0a9..7d76fa9 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -584,16 +584,16 @@ get_selector_completion :: proc( if !position_context.arrow && .ObjC in selector.flags { continue } + symbol.name = name + symbol.pkg = selector.pkg + symbol.type = .Field + symbol.doc = get_doc(v.docs[i], context.temp_allocator) + symbol.comment = get_comment(v.comments[i]) item := CompletionItem { label = name, kind = .Field, - detail = fmt.tprintf( - "%v.%v: %v", - selector.name, - name, - type_to_string(ast_context, v.types[i]), - ), + detail = get_short_signature(ast_context, symbol), documentation = symbol.doc, } @@ -662,12 +662,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 = get_short_signature(ast_context, symbol), documentation = symbol.doc, } @@ -1210,15 +1210,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 +1244,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 +1262,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 +1275,10 @@ 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.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 +1303,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 +1324,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 +1335,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 +1346,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 +1373,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,30 +1402,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 = get_short_signature(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..57122d9 --- /dev/null +++ b/src/server/documentation.odin @@ -0,0 +1,536 @@ +package server + +import "core:fmt" +import "core:odin/ast" +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 + is_field := symbol.type == .Field + + 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, "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) + } 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: + 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 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 + 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: + 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) + 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) + 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) + } + strings.write_string(&builder, "struct") + 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, "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 +} + +/* + Returns the string representation of a type. This allows us to print the signature without storing it in the indexer as a string(saving memory). +*/ + +node_to_string :: proc(node: ^ast.Node, remove_pointers := false) -> string { + builder := strings.builder_make(context.temp_allocator) + + build_string(node, &builder, remove_pointers) + + return strings.to_string(builder) +} + +build_string :: proc { + build_string_ast_array, + build_string_dynamic_array, + build_string_node, +} + +build_string_dynamic_array :: proc(array: $A/[]^$T, builder: ^strings.Builder, remove_pointers: bool) { + for elem, i in array { + build_string(elem, builder, remove_pointers) + } +} + +build_string_ast_array :: proc(array: $A/[dynamic]^$T, builder: ^strings.Builder, remove_pointers: bool) { + for elem, i in array { + build_string(elem, builder, remove_pointers) + } +} + +build_string_node :: proc(node: ^ast.Node, builder: ^strings.Builder, remove_pointers: bool) { + using ast + + if node == nil { + return + } + + #partial switch n in node.derived { + case ^Bad_Expr: + case ^Ident: + if strings.contains(n.name, "/") { + strings.write_string(builder, path.base(n.name, false, context.temp_allocator)) + } else { + strings.write_string(builder, n.name) + } + case ^Implicit: + strings.write_string(builder, n.tok.text) + case ^Undef: + case ^Basic_Lit: + strings.write_string(builder, n.tok.text) + case ^Basic_Directive: + strings.write_string(builder, "#") + strings.write_string(builder, n.name) + case ^Implicit_Selector_Expr: + strings.write_string(builder, ".") + build_string(n.field, builder, remove_pointers) + case ^Ellipsis: + strings.write_string(builder, "..") + build_string(n.expr, builder, remove_pointers) + case ^Proc_Lit: + build_string(n.type, builder, remove_pointers) + build_string(n.body, builder, remove_pointers) + case ^Comp_Lit: + build_string(n.type, builder, remove_pointers) + strings.write_string(builder, "{") + for elem, i in n.elems { + build_string(elem, builder, remove_pointers) + if len(n.elems) - 1 != i { + strings.write_string(builder, ", ") + } + } + strings.write_string(builder, "}") + case ^Tag_Expr: + build_string(n.expr, builder, remove_pointers) + case ^Unary_Expr: + strings.write_string(builder, n.op.text) + build_string(n.expr, builder, remove_pointers) + case ^Binary_Expr: + build_string(n.left, builder, remove_pointers) + strings.write_string(builder, " ") + strings.write_string(builder, n.op.text) + strings.write_string(builder, " ") + build_string(n.right, builder, remove_pointers) + case ^Paren_Expr: + strings.write_string(builder, "(") + build_string(n.expr, builder, remove_pointers) + strings.write_string(builder, ")") + case ^Call_Expr: + build_string(n.expr, builder, remove_pointers) + strings.write_string(builder, "(") + for arg, i in n.args { + build_string(arg, builder, remove_pointers) + if len(n.args) - 1 != i { + strings.write_string(builder, ", ") + } + } + strings.write_string(builder, ")") + case ^Selector_Expr: + build_string(n.expr, builder, remove_pointers) + strings.write_string(builder, ".") + build_string(n.field, builder, remove_pointers) + case ^Index_Expr: + build_string(n.expr, builder, remove_pointers) + strings.write_string(builder, "[") + build_string(n.index, builder, remove_pointers) + strings.write_string(builder, "]") + case ^Deref_Expr: + build_string(n.expr, builder, remove_pointers) + case ^Slice_Expr: + build_string(n.expr, builder, remove_pointers) + build_string(n.low, builder, remove_pointers) + build_string(n.high, builder, remove_pointers) + case ^Field_Value: + build_string(n.field, builder, remove_pointers) + strings.write_string(builder, ": ") + build_string(n.value, builder, remove_pointers) + case ^Type_Cast: + build_string(n.type, builder, remove_pointers) + build_string(n.expr, builder, remove_pointers) + case ^Bad_Stmt: + case ^Bad_Decl: + case ^Attribute: + build_string(n.elems, builder, remove_pointers) + case ^Field: + for name, i in n.names { + build_string(name, builder, remove_pointers) + if len(n.names) - 1 != i { + strings.write_string(builder, ", ") + } + } + + if len(n.names) > 0 && n.type != nil { + strings.write_string(builder, ": ") + build_string(n.type, builder, remove_pointers) + + if n.default_value != nil && n.type != nil { + strings.write_string(builder, " = ") + } + + } else if len(n.names) > 0 && n.default_value != nil { + strings.write_string(builder, " := ") + } else { + build_string(n.type, builder, remove_pointers) + } + + build_string(n.default_value, builder, remove_pointers) + case ^Field_List: + for field, i in n.list { + build_string(field, builder, remove_pointers) + if len(n.list) - 1 != i { + strings.write_string(builder, ",") + } + } + case ^Typeid_Type: + strings.write_string(builder, "typeid") + build_string(n.specialization, builder, remove_pointers) + case ^Helper_Type: + build_string(n.type, builder, remove_pointers) + case ^Distinct_Type: + build_string(n.type, builder, remove_pointers) + case ^Poly_Type: + strings.write_string(builder, "$") + + build_string(n.type, builder, remove_pointers) + + if n.specialization != nil { + strings.write_string(builder, "/") + build_string(n.specialization, builder, remove_pointers) + } + case ^Proc_Type: + strings.write_string(builder, "proc(") + build_string(n.params, builder, remove_pointers) + strings.write_string(builder, ")") + if n.results != nil { + strings.write_string(builder, " -> ") + build_string(n.results, builder, remove_pointers) + } + case ^Pointer_Type: + if !remove_pointers { + strings.write_string(builder, "^") + } + build_string(n.elem, builder, remove_pointers) + case ^Array_Type: + strings.write_string(builder, "[") + build_string(n.len, builder, remove_pointers) + strings.write_string(builder, "]") + build_string(n.elem, builder, remove_pointers) + case ^Dynamic_Array_Type: + strings.write_string(builder, "[dynamic]") + build_string(n.elem, builder, remove_pointers) + case ^Struct_Type: + build_string(n.poly_params, builder, remove_pointers) + build_string(n.align, builder, remove_pointers) + build_string(n.fields, builder, remove_pointers) + case ^Union_Type: + build_string(n.poly_params, builder, remove_pointers) + build_string(n.align, builder, remove_pointers) + build_string(n.variants, builder, remove_pointers) + case ^Enum_Type: + build_string(n.base_type, builder, remove_pointers) + build_string(n.fields, builder, remove_pointers) + case ^Bit_Set_Type: + strings.write_string(builder, "bit_set") + strings.write_string(builder, "[") + build_string(n.elem, builder, remove_pointers) + strings.write_string(builder, "]") + build_string(n.underlying, builder, remove_pointers) + case ^Map_Type: + strings.write_string(builder, "map") + strings.write_string(builder, "[") + build_string(n.key, builder, remove_pointers) + strings.write_string(builder, "]") + build_string(n.value, builder, remove_pointers) + case ^ast.Multi_Pointer_Type: + strings.write_string(builder, "[^]") + build_string(n.elem, builder, remove_pointers) + case ^ast.Bit_Field_Type: + strings.write_string(builder, "bit_field") + build_string(n.backing_type, builder, remove_pointers) + for field, i in n.fields { + build_string(field, builder, remove_pointers) + if len(n.fields) - 1 != i { + strings.write_string(builder, ",") + } + } + case ^ast.Bit_Field_Field: + build_string(n.name, builder, remove_pointers) + strings.write_string(builder, ": ") + build_string(n.type, builder, remove_pointers) + strings.write_string(builder, " | ") + build_string(n.bit_size, builder, remove_pointers) + } +} diff --git a/src/server/hover.odin b/src/server/hover.odin index e69249b..79c97c1 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -34,13 +34,10 @@ write_hover_content :: proc(ast_context: ^AstContext, symbol: Symbol) -> MarkupC } } - build_procedure_symbol_signature(&symbol, false) - cat := concatenate_symbol_information(ast_context, symbol, false) - - if cat != "" { + if symbol.signature != "" { content.kind = "markdown" - content.value = fmt.tprintf("```odin\n%v\n```%v", cat, symbol.doc) + content.value = fmt.tprintf("```odin\n%v\n```%v", symbol.signature, symbol.doc) } else { content.kind = "plaintext" } 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..e076f0b 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 = get_short_signature(&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 = get_short_signature(&ast_context, symbol), documentation = symbol.doc, } |