diff options
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/analysis.odin | 8 | ||||
| -rw-r--r-- | src/server/ast.odin | 101 | ||||
| -rw-r--r-- | src/server/collector.odin | 4 | ||||
| -rw-r--r-- | src/server/documentation.odin | 16 | ||||
| -rw-r--r-- | src/server/hover.odin | 6 | ||||
| -rw-r--r-- | src/server/signature.odin | 6 | ||||
| -rw-r--r-- | src/server/symbol.odin | 12 |
7 files changed, 93 insertions, 60 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin index a4227dc..cec34e3 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -1981,8 +1981,8 @@ resolve_local_identifier :: proc(ast_context: ^AstContext, node: ast.Ident, loca return_symbol.flags |= {.Local} return_symbol.value_expr = local.value_expr return_symbol.type_expr = local.type_expr - return_symbol.doc = get_doc(local.docs, ast_context.allocator) - return_symbol.comment = get_comment(local.comment) + return_symbol.doc = get_comment(local.docs, ast_context.allocator) + return_symbol.comment = get_comment(local.comment, ast_context.allocator) return return_symbol, ok } @@ -2071,11 +2071,11 @@ resolve_global_identifier :: proc(ast_context: ^AstContext, node: ast.Ident, glo } if global.docs != nil { - return_symbol.doc = get_doc(global.docs, ast_context.allocator) + return_symbol.doc = get_comment(global.docs, ast_context.allocator) } if global.comment != nil { - return_symbol.comment = get_comment(global.comment) + return_symbol.comment = get_comment(global.comment, ast_context.allocator) } return_symbol.type_expr = global.type_expr diff --git a/src/server/ast.odin b/src/server/ast.odin index d1846f7..0547b83 100644 --- a/src/server/ast.odin +++ b/src/server/ast.odin @@ -7,6 +7,7 @@ import "core:odin/ast" import "core:odin/parser" import path "core:path/slashpath" import "core:strings" +import "core:log" keyword_map: map[string]struct{} = { "typeid" = {}, @@ -563,51 +564,79 @@ get_ast_node_string :: proc(node: ^ast.Node, src: string) -> string { return string(src[node.pos.offset:node.end.offset]) } -get_doc :: proc(comment: ^ast.Comment_Group, allocator: mem.Allocator) -> string { - if comment == nil { - return "" +COMMENT_DELIMITER_LENGTH :: len("//") +#assert(COMMENT_DELIMITER_LENGTH == len("/*")) +#assert(COMMENT_DELIMITER_LENGTH == len("*/")) + +// Returns the minimum indentation across all non-empty lines +get_min_indent :: proc(lines: []string) -> int { + min_indent := max(int) + for line in lines { + if strings.trim_space(line) == "" do continue + for c, i in line { + if !strings.is_space(c) { + min_indent = min(min_indent, i) + break + } + } } + return 0 if min_indent == max(int) else min_indent +} - tmp: string - - for doc in comment.list { - if strings.starts_with(doc.text, "/*") && doc.pos.column != 1 { - lines := strings.split(doc.text, "\n", context.temp_allocator) - for line, i in lines { - if i != 0 && len(line) > 0 { - column := 0 - for column < doc.pos.column - 1 { - if line[column] == '\t' || line[column] == ' ' { - column += 1 - } else { - break - } - } - tmp = strings.concatenate({tmp, "\n", line[column:]}, context.temp_allocator) - } else { - tmp = strings.concatenate({tmp, "\n", line}, context.temp_allocator) - } - } +// Strips min_indent characters from each line and joins with newlines +strip_indent_and_join :: proc(lines: []string, min_indent: int, allocator: mem.Allocator) -> string { + result := make([dynamic]string, context.temp_allocator) + for line in lines { + if len(line) >= min_indent { + append(&result, line[min_indent:]) } else { - tmp = strings.concatenate({tmp, "\n", doc.text}, context.temp_allocator) + append(&result, strings.trim_left_space(line)) } } + return strings.join(result[:], "\n", allocator) +} - if tmp != "" { - no_lines, _ := strings.replace_all(tmp, "//", "", context.temp_allocator) - no_begin_comments, _ := strings.replace_all(no_lines, "/*", "", context.temp_allocator) - no_end_comments, _ := strings.replace_all(no_begin_comments, "*/", "", context.temp_allocator) - return strings.clone(no_end_comments, allocator) - } +// Aggregates the content from the provided comment group, +// omitting extraneous spaces and delimiters. +get_comment :: proc(comment: ^ast.Comment_Group, allocator := context.allocator) -> string { + if comment == nil do return "" - return "" -} + lines := make([dynamic]string, context.temp_allocator) + + for token in comment.list { + if len(token.text) < COMMENT_DELIMITER_LENGTH do continue + delimiter := token.text[:COMMENT_DELIMITER_LENGTH] + + switch delimiter { + case "/*": + if len(token.text) <= COMMENT_DELIMITER_LENGTH * 2 do continue + content := token.text[COMMENT_DELIMITER_LENGTH:len(token.text) - COMMENT_DELIMITER_LENGTH] -get_comment :: proc(comment: ^ast.Comment_Group) -> string { - if comment != nil && len(comment.list) > 0 { - return comment.list[0].text + // Check if this is a single-line block comment (no newlines) + if !strings.contains(content, "\n") { + text := strings.trim_space(content) + if text != "" do append(&lines, text) + } else { + // Multi-line block comment: strip leading/trailing newlines + content = strings.trim(content, "\r\n") + for line in strings.split_lines(content, context.temp_allocator) { + append(&lines, line) + } + } + + case "//": + text := token.text[COMMENT_DELIMITER_LENGTH:] + append(&lines, text) + + case: + log.error("unsupported comment delimiter") + } } - return "" + + if len(lines) == 0 do return "" + + min_indent := get_min_indent(lines[:]) + return strip_indent_and_join(lines[:], min_indent, allocator) } free_ast :: proc { diff --git a/src/server/collector.odin b/src/server/collector.odin index d2b8445..d8a4e65 100644 --- a/src/server/collector.odin +++ b/src/server/collector.odin @@ -705,12 +705,12 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri symbol.range = common.get_token_range(expr.name_expr, file.src) symbol.name = get_index_unique_string(collection, name) symbol.type = token_type - symbol.doc = get_doc(expr.docs, collection.allocator) + symbol.doc = get_comment(expr.docs, collection.allocator) symbol.uri = get_index_unique_string(collection, uri) symbol.type_expr = clone_type(expr.type_expr, collection.allocator, &collection.unique_strings) symbol.value_expr = clone_type(expr.value_expr, collection.allocator, &collection.unique_strings) comment, _ := get_file_comment(file, symbol.range.start.line + 1) - symbol.comment = strings.clone(get_comment(comment), collection.allocator) + symbol.comment = get_comment(comment, collection.allocator) if expr.builtin || strings.contains(uri, "builtin.odin") { symbol.pkg = "$builtin" diff --git a/src/server/documentation.odin b/src/server/documentation.odin index 2fe8842..25e4140 100644 --- a/src/server/documentation.odin +++ b/src/server/documentation.odin @@ -6,6 +6,10 @@ import "core:odin/ast" import path "core:path/slashpath" import "core:strings" +DOC_SECTION_DELIMITER :: "\n---\n" // The string separating each section of documentation +DOC_FMT_ODIN :: "```odin\n%v\n```" // The format for wrapping odin code in a markdown codeblock +DOC_FMT_MARKDOWN :: DOC_FMT_ODIN + DOC_SECTION_DELIMITER + "%v" // The format for presenting documentation on hover + // Adds signature and docs information to the provided symbol // This should only be used for a symbol created with the temp allocator build_documentation :: proc(ast_context: ^AstContext, symbol: ^Symbol, short_signature := true) { @@ -20,21 +24,17 @@ build_documentation :: proc(ast_context: ^AstContext, symbol: ^Symbol, short_sig } } -construct_symbol_docs :: proc(symbol: Symbol, markdown := true, allocator := context.temp_allocator) -> string { +construct_symbol_docs :: proc(symbol: Symbol, allocator := context.temp_allocator) -> string { sb := strings.builder_make(allocator = allocator) if symbol.doc != "" { strings.write_string(&sb, symbol.doc) - if symbol.comment != "" { - strings.write_string(&sb, "\n") - } } if symbol.comment != "" { - if markdown { - fmt.sbprintf(&sb, "\n```odin\n%s\n```", symbol.comment) - } else { - fmt.sbprintf(&sb, "\n%s", symbol.comment) + if symbol.doc != "" { + strings.write_string(&sb, DOC_SECTION_DELIMITER) } + strings.write_string(&sb, symbol.comment) } return strings.to_string(sb) diff --git a/src/server/hover.odin b/src/server/hover.odin index ca791ef..9b42f7e 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -16,7 +16,11 @@ write_hover_content :: proc(ast_context: ^AstContext, symbol: Symbol) -> MarkupC if cat != "" { content.kind = "markdown" - content.value = fmt.tprintf("```odin\n%v\n```%v", cat, doc) + if doc != "" { + content.value = fmt.tprintf(DOC_FMT_MARKDOWN, cat, doc) + } else { + content.value = fmt.tprintf(DOC_FMT_ODIN, cat) + } } else { content.kind = "plaintext" } diff --git a/src/server/signature.odin b/src/server/signature.odin index 3148951..f6c79fc 100644 --- a/src/server/signature.odin +++ b/src/server/signature.odin @@ -110,7 +110,7 @@ get_signature_information :: proc( &signature_information, SignatureInformation { label = get_signature(symbol), - documentation = construct_symbol_docs(symbol, markdown = false), + documentation = construct_symbol_docs(symbol), }, ) } @@ -176,7 +176,7 @@ add_proc_signature :: proc( info := SignatureInformation { label = get_signature(call), - documentation = construct_symbol_docs(call, markdown = false), + documentation = construct_symbol_docs(call), parameters = parameters, } append(signature_information, info) @@ -204,7 +204,7 @@ add_proc_signature :: proc( info := SignatureInformation { label = get_signature(symbol), - documentation = construct_symbol_docs(symbol, markdown = false), + documentation = construct_symbol_docs(symbol), parameters = parameters, } diff --git a/src/server/symbol.odin b/src/server/symbol.odin index 956eaa9..96c83ed 100644 --- a/src/server/symbol.odin +++ b/src/server/symbol.odin @@ -922,8 +922,8 @@ construct_struct_field_symbol :: proc(symbol: ^Symbol, parent_name: string, valu symbol.name = value.names[index] symbol.type = .Field symbol.parent_name = parent_name - symbol.doc = get_doc(value.docs[index], context.temp_allocator) - symbol.comment = get_comment(value.comments[index]) + symbol.doc = get_comment(value.docs[index], context.temp_allocator) + symbol.comment = get_comment(value.comments[index], context.temp_allocator) symbol.range = value.ranges[index] } @@ -936,16 +936,16 @@ construct_bit_field_field_symbol :: proc( symbol.name = value.names[index] symbol.parent_name = parent_name symbol.type = .Field - symbol.doc = get_doc(value.docs[index], context.temp_allocator) - symbol.comment = get_comment(value.comments[index]) + symbol.doc = get_comment(value.docs[index], context.temp_allocator) + symbol.comment = get_comment(value.comments[index], context.temp_allocator) symbol.signature = get_bit_field_field_signature(value, index) symbol.range = value.ranges[index] } construct_enum_field_symbol :: proc(symbol: ^Symbol, value: SymbolEnumValue, index: int) { symbol.type = .Field - symbol.doc = get_doc(value.docs[index], context.temp_allocator) - symbol.comment = get_comment(value.comments[index]) + symbol.doc = get_comment(value.docs[index], context.temp_allocator) + symbol.comment = get_comment(value.comments[index], context.temp_allocator) symbol.signature = get_enum_field_signature(value, index) symbol.range = value.ranges[index] } |