aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorDaniel Gavin <danielgavin5@hotmail.com>2022-01-22 18:59:40 +0100
committerDaniel Gavin <danielgavin5@hotmail.com>2022-01-22 18:59:40 +0100
commit165fdbc58c647f97dbdb3b5903b4826b022f02f9 (patch)
tree89ff1ff9ec88f0d0c615067b5b32fb97ed39342b /src/server
parent989345d0438429ae719287f9c7343f87b558f7e3 (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.odin20
-rw-r--r--src/server/document_links.odin54
-rw-r--r--src/server/requests.odin212
-rw-r--r--src/server/semantic_tokens.odin27
-rw-r--r--src/server/types.odin20
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(&notifications, RequestNotification { index = len(requests)-1, value = root});
+ sync.semaphore_post(&requests_sempahore);
+ } else {
+ append(&notifications, 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(&notifications);
+
+ 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