diff options
| author | Daniel Gavin <danielgavin5@hotmail.com> | 2022-01-22 18:59:40 +0100 |
|---|---|---|
| committer | Daniel Gavin <danielgavin5@hotmail.com> | 2022-01-22 18:59:40 +0100 |
| commit | 165fdbc58c647f97dbdb3b5903b4826b022f02f9 (patch) | |
| tree | 89ff1ff9ec88f0d0c615067b5b32fb97ed39342b /src/server | |
| parent | 989345d0438429ae719287f9c7343f87b558f7e3 (diff) | |
Move the std in reading to be async with it's own seperate thread.
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/background.odin | 20 | ||||
| -rw-r--r-- | src/server/document_links.odin | 54 | ||||
| -rw-r--r-- | src/server/requests.odin | 212 | ||||
| -rw-r--r-- | src/server/semantic_tokens.odin | 27 | ||||
| -rw-r--r-- | src/server/types.odin | 20 |
5 files changed, 262 insertions, 71 deletions
diff --git a/src/server/background.odin b/src/server/background.odin deleted file mode 100644 index 6a91284..0000000 --- a/src/server/background.odin +++ /dev/null @@ -1,20 +0,0 @@ -package server - -/* - Background thread that runs and ensures that the dynamic indexer is not stale, and also calls the odin semantic check every now and then -*/ - -import "shared:index" -import "shared:common" - -background_main :: proc() { - - for common.config.running { - - - - - - } - -} diff --git a/src/server/document_links.odin b/src/server/document_links.odin new file mode 100644 index 0000000..205e9bc --- /dev/null +++ b/src/server/document_links.odin @@ -0,0 +1,54 @@ +package server + +import "core:odin/parser" +import "core:odin/ast" +import "core:odin/tokenizer" +import "core:fmt" +import "core:log" +import "core:strings" +import path "core:path/slashpath" +import "core:mem" +import "core:strconv" +import "core:path/filepath" +import "core:sort" +import "core:slice" +import "core:os" + + +import "shared:common" +import "shared:index" +import "shared:analysis" + +get_document_links :: proc(document: ^common.Document) -> ([]DocumentLink, bool) { + using analysis; + + links := make([dynamic]DocumentLink, 0, context.temp_allocator); + + for imp in document.ast.imports { + //Temporarly assuming non unicode + node := ast.Node { + pos = { + offset = imp.relpath.pos.offset, + column = imp.relpath.pos.column, + line = imp.relpath.pos.line, + }, + end = { + offset = imp.relpath.pos.offset + len(imp.relpath.text) - 1, + column = imp.relpath.pos.column + len(imp.relpath.text) - 1, + line = imp.relpath.pos.line, + }, + } + + range := common.get_token_range(node, string(document.text)); + + link := DocumentLink { + range = range, + target = "https://code.visualstudio.com/docs/extensions/overview#frag", + tooltip = "Documentation", + }; + + append(&links, link); + } + + return links[:], true; +} diff --git a/src/server/requests.odin b/src/server/requests.odin index 5ab8dde..dac74ba 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -75,8 +75,94 @@ make_response_message_error :: proc (id: RequestId, error: ResponseError) -> Res }; } -read_and_parse_header :: proc (reader: ^Reader) -> (Header, bool) { +RequestThreadData :: struct { + reader: ^Reader, + writer: ^Writer, +} + +Request :: struct { + id: RequestId, + value: json.Value, +} +RequestNotification :: struct { + index: int, + value: json.Value, +} + +requests_sempahore: sync.Semaphore; +requests_mutex: sync.Mutex; + +requests: [dynamic]Request; +notifications: [dynamic]RequestNotification; +deletings: [dynamic]Request; + +thread_request_main :: proc(data: rawptr) { + request_data := cast(^RequestThreadData)data; + + for common.config.running { + header, success := read_and_parse_header(request_data.reader); + + if (!success) { + log.error("Failed to read and parse header"); + return; + } + + value: json.Value; + value, success = read_and_parse_body(request_data.reader, header); + + if (!success) { + log.error("Failed to read and parse body"); + return; + } + + root, ok := value.(json.Object); + + if !ok { + log.error("No root object"); + return; + } + + id: RequestId; + id_value: json.Value; + id_value, ok = root["id"]; + + if ok { + #partial switch v in id_value { + case json.String: + id = v; + case json.Integer: + id = v; + case: + id = 0; + } + } + + sync.mutex_lock(&requests_mutex); + method := root["method"].(json.String); + + if method == "$/cancelRequest" { + + } else if strings.contains(method, "textDocument/did") || method == "exit" { + if len(requests) > 0 { + append(¬ifications, RequestNotification { index = len(requests)-1, value = root}); + sync.semaphore_post(&requests_sempahore); + } else { + append(¬ifications, RequestNotification { index = -1, value = root}); + sync.semaphore_post(&requests_sempahore); + } + } else { + append(&requests, Request { id = id, value = root}); + sync.semaphore_post(&requests_sempahore); + } + + sync.mutex_unlock(&requests_mutex); + + free_all(context.temp_allocator); + } +} + +read_and_parse_header :: proc (reader: ^Reader) -> (Header, bool) { header: Header; builder := strings.make_builder(context.temp_allocator); @@ -114,7 +200,6 @@ read_and_parse_header :: proc (reader: ^Reader) -> (Header, bool) { header_value := message[len(header_name) + 2:len(message) - 2]; if strings.compare(header_name, "Content-Length") == 0 { - if len(header_value) == 0 { log.error("Header value has no length"); return header, false; @@ -142,7 +227,6 @@ read_and_parse_header :: proc (reader: ^Reader) -> (Header, bool) { } read_and_parse_body :: proc (reader: ^Reader, header: Header) -> (json.Value, bool) { - value: json.Value; data := make([]u8, header.content_length, context.temp_allocator); @@ -154,7 +238,7 @@ read_and_parse_body :: proc (reader: ^Reader, header: Header) -> (json.Value, bo err: json.Error; - value, err = json.parse(data = data, allocator = context.temp_allocator, parse_integers = true); + value, err = json.parse(data = data, allocator = context.allocator, parse_integers = true); if (err != json.Error.None) { log.error("Failed to parse body"); @@ -182,59 +266,73 @@ call_map : map [string] proc(json.Value, RequestId, ^common.Config, ^Writer) -> "textDocument/semanticTokens/range" = request_semantic_token_range, "textDocument/hover" = request_hover, "textDocument/formatting" = request_format_document, + "odin/inlayHints" = request_inlay_hint, + "textDocument/documentLink" = request_document_links, }; -handle_request :: proc (request: json.Value, config: ^common.Config, writer: ^Writer) -> bool { - root, ok := request.(json.Object); +consume_requests :: proc (config: ^common.Config, writer: ^Writer) -> bool { + temp_requests := make([dynamic]Request, 0, context.temp_allocator); + temp_notifications := make([dynamic]RequestNotification, 0, context.temp_allocator); - if !ok { - log.error("No root object"); - return false; - } + sync.mutex_lock(&requests_mutex); - id: RequestId; - id_value: json.Value; - id_value, ok = root["id"]; + for request in requests { + append(&temp_requests, request); + } - if ok { + for notification in notifications { + append(&temp_notifications, notification); + } - #partial switch v in id_value { - case json.String: - id = v; - case json.Integer: - id = v; - case: - id = 0; + clear(&requests); + clear(¬ifications); + + sync.mutex_unlock(&requests_mutex); + + outer: for len(temp_notifications) > 0 || len(temp_requests) > 0 { + if len(temp_notifications) > 0 { + for notification in temp_notifications { + for request, i in temp_requests { + if notification.index >= i { + call(request.value, request.id, writer, config); + } else { + break; + } + } + call(notification.value, 0, writer, config); + } + } else { + for request, i in temp_requests { + call(request.value, request.id, writer, config); + } } } - method := root["method"].(json.String); + sync.semaphore_wait_for(&requests_sempahore); - fn: proc(json.Value, RequestId, ^common.Config, ^Writer) -> common.Error; - fn, ok = call_map[method]; + return true; +} - if !ok { - response := make_response_message_error( - id = id, - error = ResponseError {code = .MethodNotFound, message = ""}); +call :: proc(value: json.Value, id: RequestId, writer: ^Writer, config: ^common.Config) { + root := value.(json.Object); + method := root["method"].(json.String); + if fn, ok := call_map[method]; !ok { + response := make_response_message_error(id = id, error = ResponseError {code = .MethodNotFound, message = ""}); send_error(response, writer); } else { err := fn(root["params"], id, config, writer); - if err != .None { - response := make_response_message_error( + if err != .None { + response := make_response_message_error( id = id, - error = ResponseError {code = err, message = ""}, - ); - send_error(response, writer); - } + error = ResponseError {code = err, message = ""}, + ); + send_error(response, writer); + } } - - return true; } request_initialize :: proc (params: json.Value, id: RequestId, config: ^common.Config, writer: ^Writer) -> common.Error { - params_object, ok := params.(json.Object); if !ok { @@ -405,9 +503,11 @@ request_initialize :: proc (params: json.Value, id: RequestId, config: ^common.C documentSymbolProvider = config.enable_document_symbols, hoverProvider = config.enable_hover, documentFormattingProvider = config.enable_format, + documentLinkProvider = { + resolveProvider = false, + }, }, - }, - id = id); + }, id = id); send_response(response, writer); @@ -886,4 +986,38 @@ request_inlay_hint :: proc (params: json.Value, id: RequestId, config: ^common.C send_response(response, writer); return .None; +} + +request_document_links :: proc (params: json.Value, id: RequestId, config: ^common.Config, writer: ^Writer) -> common.Error { + params_object, ok := params.(json.Object); + + if !ok { + return .ParseError; + } + + link_params: DocumentLinkParams; + + if unmarshal(params, link_params, context.temp_allocator) != .None { + return .ParseError; + } + + document := document_get(link_params.textDocument.uri); + + if document == nil { + return .InternalError; + } + + links: []DocumentLink; + + links, ok = get_document_links(document); + + if !ok { + return .InternalError; + } + + response := make_response_message(params = links, id = id); + + send_response(response, writer); + + return .None; }
\ No newline at end of file diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin index 2977ddc..ac43b9d 100644 --- a/src/server/semantic_tokens.odin +++ b/src/server/semantic_tokens.odin @@ -175,8 +175,9 @@ visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context: if symbol, ok := builder.symbols[cast(uintptr)node]; ok { if symbol.type == .Variable { write_semantic_node(builder, node, ast_context.file.src, .Variable, .None); + return; } - + #partial switch v in symbol.value { case index.SymbolPackageValue: write_semantic_node(builder, node, ast_context.file.src, .Namespace, .None); @@ -190,6 +191,13 @@ visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context: write_semantic_node(builder, node, ast_context.file.src, .Function, .None); case index.SymbolProcedureGroupValue: write_semantic_node(builder, node, ast_context.file.src, .Function, .None); + case index.SymbolUntypedValue: + write_semantic_node(builder, node, ast_context.file.src, .Type, .None); + case index.SymbolBasicValue: + write_semantic_node(builder, node, ast_context.file.src, .Type, .None); + case: + //log.errorf("Unexpected symbol value: %v", symbol.value); + //panic(fmt.tprintf("Unexpected symbol value: %v", symbol.value)); } } case Selector_Expr: @@ -340,7 +348,7 @@ visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context: write_semantic_token(builder, n.relpath, ast_context.file.src, .String, .None); case: - log.errorf("unhandled semantic token node %v", n); + //log.errorf("unhandled semantic token node %v", n); //panic(fmt.tprintf("Missed semantic token handling %v", n)); } } @@ -377,6 +385,10 @@ visit_value_decl :: proc(value_decl: ast.Value_Decl, builder: ^SemanticTokenBuil if len(value_decl.values) == 1 { switch v in value_decl.values[0].derived { + case Union_Type: + write_semantic_node(builder, value_decl.names[0], ast_context.file.src, .Enum, .None); + write_semantic_string(builder, v.pos, "union", ast_context.file.src, .Keyword, .None); + visit(v.variants, builder, ast_context); case Struct_Type: write_semantic_node(builder, value_decl.names[0], ast_context.file.src, .Struct, .None); write_semantic_string(builder, v.pos, "struct", ast_context.file.src, .Keyword, .None); @@ -446,12 +458,7 @@ visit_proc_type :: proc(node: ^ast.Proc_Type, builder: ^SemanticTokenBuilder, as if node.results != nil { for result in node.results.list { - for name in result.names { - if ident, ok := name.derived.(Ident); ok { - //write_semantic_node(builder, name, ast_context.file.src, .Parameter, .None); - } - } - + visit(result.names, builder, ast_context); visit(result.type, builder, ast_context); } } @@ -496,7 +503,6 @@ visit_struct_fields :: proc(node: ast.Struct_Type, builder: ^SemanticTokenBuilde } visit_selector :: proc(selector: ^ast.Selector_Expr, builder: ^SemanticTokenBuilder, ast_context: ^analysis.AstContext) { - if _, ok := selector.expr.derived.(ast.Selector_Expr); ok { visit_selector(cast(^ast.Selector_Expr)selector.expr, builder, ast_context); } else { @@ -523,7 +529,4 @@ visit_selector :: proc(selector: ^ast.Selector_Expr, builder: ^SemanticTokenBuil write_semantic_node(builder, selector.field, ast_context.file.src, .Function, .None); } } - - - //((a.d).b).c }
\ No newline at end of file diff --git a/src/server/types.odin b/src/server/types.odin index bf0cff4..c3147e9 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -27,6 +27,7 @@ ResponseParams :: union { Hover, []TextEdit, []InlayHint, + []DocumentLink, } ResponseMessage :: struct { @@ -93,6 +94,7 @@ ServerCapabilities :: struct { hoverProvider: bool, documentFormattingProvider: bool, inlayHintsProvider: bool, + documentLinkProvider: DocumentLinkOptions, } CompletionOptions :: struct { @@ -373,4 +375,22 @@ InlayHint :: struct { range: common.Range, kind: string, label: string, +} + +DocumentLinkClientCapabilities :: struct { + tooltipSupport: bool, +} + +DocumentLinkParams :: struct { + textDocument: TextDocumentIdentifier, +} + +DocumentLink :: struct { + range: common.Range, + target: string, + tooltip: string, +} + +DocumentLinkOptions :: struct { + resolveProvider: bool, }
\ No newline at end of file |