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 | |
| parent | 989345d0438429ae719287f9c7343f87b558f7e3 (diff) | |
Move the std in reading to be async with it's own seperate thread.
Diffstat (limited to 'src')
| -rw-r--r-- | src/analysis/analysis.odin | 63 | ||||
| -rw-r--r-- | src/main.odin | 47 | ||||
| -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 |
7 files changed, 311 insertions, 132 deletions
diff --git a/src/analysis/analysis.odin b/src/analysis/analysis.odin index 68d5fc1..b5716e1 100644 --- a/src/analysis/analysis.odin +++ b/src/analysis/analysis.odin @@ -165,11 +165,6 @@ get_poly_node_to_expr :: proc(node: ^ast.Node) -> ^ast.Expr { } resolve_poly_spec_node :: proc(ast_context: ^AstContext, call_node: ^ast.Node, spec_node: ^ast.Node, poly_map: ^map[string]^ast.Expr) { - - /* - Note(Daniel, uncertain about the switch cases being enough or too little) - */ - using ast; if call_node == nil || spec_node == nil { @@ -667,7 +662,6 @@ is_symbol_same_typed :: proc(ast_context: ^AstContext, a, b: index.Symbol, flags } get_field_list_name_index :: proc(name: string, field_list: []^ast.Field) -> (int, bool) { - for field, i in field_list { for field_name in field.names { if ident, ok := field_name.derived.(ast.Ident); ok { @@ -685,7 +679,6 @@ get_field_list_name_index :: proc(name: string, field_list: []^ast.Field) -> (in Figure out which function the call expression is using out of the list from proc group */ resolve_function_overload :: proc(ast_context: ^AstContext, group: ast.Proc_Group) -> (index.Symbol, bool) { - using ast; call_expr := ast_context.call; @@ -832,6 +825,12 @@ resolve_type_expression :: proc(ast_context: ^AstContext, node: ^ast.Expr) -> (i return {}, false; } + saved_package := ast_context.current_package; + + defer { + ast_context.current_package = saved_package; + } + if ast_context.recursion_counter > 15 { log.error("Recursion passed 15 attempts - giving up"); return {}, false; @@ -1119,6 +1118,12 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i return {}, false; } + saved_package := ast_context.current_package; + + defer { + ast_context.current_package = saved_package; + } + ast_context.recursion_counter += 1; defer { @@ -2414,23 +2419,20 @@ resolve_entire_file :: proc(document: ^common.Document, allocator := context.all symbols := make(map[uintptr]index.Symbol, 100, allocator); for k, v in ast_context.globals { - switch n in v.expr.derived { - case ast.Proc_Lit: - resolve_entire_procedure(&ast_context, n, &symbols, allocator); - clear_local_group(&ast_context, 0); - add_local_group(&ast_context, 0); - } + resolve_entire_decl(&ast_context, v.expr, &symbols, allocator); + clear_local_group(&ast_context, 0); + add_local_group(&ast_context, 0); } return symbols; } -resolve_entire_procedure :: proc(ast_context: ^AstContext, procedure: ast.Proc_Lit, symbols: ^map[uintptr]index.Symbol, allocator := context.allocator) { +resolve_entire_decl :: proc(ast_context: ^AstContext, decl: ^ast.Expr, symbols: ^map[uintptr]index.Symbol, allocator := context.allocator) { Scope :: struct { offset: int, id: int, } - + Visit_Data :: struct { ast_context: ^AstContext, symbols: ^map[uintptr]index.Symbol, @@ -2472,7 +2474,7 @@ resolve_entire_procedure :: proc(ast_context: ^AstContext, procedure: ast.Proc_L } switch v in &node.derived { - case ast.If_Stmt, ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt: + case ast.If_Stmt, ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt, ast.Proc_Lit: scope: Scope; scope.id = data.id_counter; scope.offset = node.end.offset; @@ -2485,9 +2487,6 @@ resolve_entire_procedure :: proc(ast_context: ^AstContext, procedure: ast.Proc_L position_context: DocumentPositionContext; position_context.position = node.end.offset; get_locals_stmt(ast_context.file, cast(^ast.Stmt)node, ast_context, &position_context); - } - - switch v in &node.derived { case ast.Ident: if symbol, ok := resolve_type_identifier(ast_context, v); ok { data.symbols[cast(uintptr)node] = symbol; @@ -2502,6 +2501,17 @@ resolve_entire_procedure :: proc(ast_context: ^AstContext, procedure: ast.Proc_L } } + switch v in &node.derived { + case ast.Proc_Lit: + if v.body == nil { + break; + } + + type_position_context: DocumentPositionContext; + type_position_context.position = v.end.offset; + get_locals_proc_param_and_results(ast_context.file, v, ast_context, &type_position_context); + } + return visitor; } @@ -2510,20 +2520,7 @@ resolve_entire_procedure :: proc(ast_context: ^AstContext, procedure: ast.Proc_L visit = visit, } - if procedure.body == nil { - return; - } - - type_position_context: DocumentPositionContext; - type_position_context.position = procedure.end.offset; - get_locals_proc_param_and_results(ast_context.file, procedure, ast_context, &type_position_context); - - body_position_context: DocumentPositionContext; - body_position_context.position = procedure.body.end.offset; - - get_locals_stmt(ast_context.file, cast(^ast.Stmt)procedure.body, ast_context, &body_position_context); - - ast.walk(&visitor, procedure.body); + ast.walk(&visitor, decl); } concatenate_symbol_information :: proc { diff --git a/src/main.odin b/src/main.odin index 6ac8f2e..7966c1f 100644 --- a/src/main.odin +++ b/src/main.odin @@ -10,6 +10,7 @@ import "core:strconv" import "core:thread" import "core:encoding/json" import "core:reflect" +import "core:sync" import "core:intrinsics" @@ -17,8 +18,6 @@ import "shared:index" import "shared:server" import "shared:common" - - os_read :: proc(handle: rawptr, data: []byte) -> (int, int) { ptr := cast(^os.Handle)handle; a, b := os.read(ptr^, data); @@ -36,6 +35,7 @@ os_write :: proc(handle: rawptr, data: []byte) -> (int, int) { verbose_logger: log.Logger; file_logger: log.Logger; file_logger_init: bool; +request_thread: ^thread.Thread; run :: proc(reader: ^server.Reader, writer: ^server.Writer) { common.config.collections = make(map[string]string); @@ -44,8 +44,23 @@ run :: proc(reader: ^server.Reader, writer: ^server.Writer) { common.config.running = true; - for common.config.running { + request_thread_data := server.RequestThreadData { + reader = reader, + writer = writer, + }; + + sync.mutex_init(&server.requests_mutex); + sync.semaphore_init(&server.requests_sempahore); + + server.requests = make([dynamic]server.Request, context.allocator); + server.notifications = make([dynamic]server.RequestNotification, context.allocator); + server.deletings = make([dynamic]server.Request, context.allocator); + + + request_thread = thread.create_and_start_with_data(cast(rawptr)&request_thread_data, server.thread_request_main); + + for common.config.running { if common.config.file_log { if !file_logger_init { if fh, err := os.open("log.txt"); err == 0 { @@ -58,32 +73,8 @@ run :: proc(reader: ^server.Reader, writer: ^server.Writer) { } else { context.logger = log.Logger {nil, nil, log.Level.Debug, nil}; } - a: int; - b: int; - - header, success := server.read_and_parse_header(reader); - - if (!success) { - log.error("Failed to read and parse header"); - return; - } - - value: json.Value; - value, success = server.read_and_parse_body(reader, header); - - if (!success) { - log.error("Failed to read and parse body"); - return; - } - - success = server.handle_request(value, &common.config, writer); - - if (!success) { - log.error("Unrecoverable handle request"); - return; - } - free_all(context.temp_allocator); + server.consume_requests(&common.config, writer); } for k, v in common.config.collections { 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 |