diff options
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | build.bat | 8 | ||||
| -rw-r--r-- | src/documents.odin | 108 | ||||
| -rw-r--r-- | src/position.odin | 54 | ||||
| -rw-r--r-- | src/requests.odin | 26 | ||||
| -rw-r--r-- | src/types.odin | 29 | ||||
| -rw-r--r-- | src/unmarshal.odin | 2 | ||||
| -rw-r--r-- | tests/tests.odin | 58 |
8 files changed, 195 insertions, 95 deletions
@@ -1 +1,4 @@ -*.exe
\ No newline at end of file +*.exe +*.pdb +*.rdbg +*.obj
\ No newline at end of file @@ -1,8 +1,12 @@ @echo off -odin run tests\ -llvm-api -show-timings -microarch:native -collection:shared=src -out:ols +odin run tests\ -show-timings -llvm-api -collection:shared=src -microarch:native -out:test -odin build src\ -llvm-api -show-timings -microarch:native -collection:shared=src -out:ols + +rem odin build tests\ -show-timings -collection:shared=src -microarch:native -out:test -debug + + +odin build src\ -llvm-api -show-timings -microarch:native -collection:shared=src -out:ols -debug diff --git a/src/documents.odin b/src/documents.odin index 0618f0f..0be8cae 100644 --- a/src/documents.odin +++ b/src/documents.odin @@ -5,15 +5,15 @@ import "core:fmt" import "core:log" Package :: struct { - documents: [dynamic]^Document, + documents: [dynamic]^Document, }; Document :: struct { uri: string, path: string, - text: string, + text: [] u8, //transmuted version of text plus potential unused space + used_text: int, //allow for the text to be reallocated with more data than needed client_owned: bool, - lines: [dynamic] int, }; DocumentStorage :: struct { @@ -25,26 +25,27 @@ document_storage: DocumentStorage; document_open :: proc(uri_string: string, text: string) -> Error { - uri, parsed_ok := parse_uri(uri_string, context.temp_allocator); + uri, parsed_ok := parse_uri(uri_string); if !parsed_ok { return .ParseError; } if document := &document_storage.documents[uri.path]; document != nil { - + //According to the specification you can't call open more than once without closing it. if document.client_owned { log.errorf("Client called open on an already open document: %v ", document.path); return .InvalidRequest; } - if document.text != "" { + if document.text != nil { delete(document.text); } document.client_owned = true; - document.text = text; + document.text = transmute([] u8)text; + document.used_text = len(document.text); if err := document_refresh(document); err != .None { return err; @@ -58,8 +59,9 @@ document_open :: proc(uri_string: string, text: string) -> Error { document := Document { uri = uri.full, path = uri.path, - text = text, + text = transmute([] u8)text, client_owned = true, + used_text = len(text), }; if err := document_refresh(&document); err != .None { @@ -69,25 +71,93 @@ document_open :: proc(uri_string: string, text: string) -> Error { document_storage.documents[uri.path] = document; } - - //hmm feels like odin needs some ownership semantic + + //hmm feels like odin needs some ownership semantic delete(uri_string); return .None; -} +} document_apply_changes :: proc(uri_string: string, changes: [dynamic] TextDocumentContentChangeEvent) -> Error { - - + uri, parsed_ok := parse_uri(uri_string, context.temp_allocator); + + if !parsed_ok { + return .ParseError; + } + + document := &document_storage.documents[uri.path]; + + if !document.client_owned { + log.errorf("Client called change on an document not opened: %v ", document.path); + return .InvalidRequest; + } + + for change in changes { + + absolute_range, ok := get_absolute_range(change.range, document.text); + + if !ok { + return .ParseError; + } + + //lower bound is before the change + lower := document.text[:absolute_range.start]; + + //new change between lower and upper + middle := change.text; + + //upper bound is after the change + upper := document.text[min(len(document.text), absolute_range.end+1):]; + + //total new size needed + document.used_text = len(lower) + len(change.text) + len(upper); + + if document.used_text > len(document.text) { + new_text := make([]u8, document.used_text * 2); + + //join the 3 splices into the text + copy(new_text, lower); + copy(new_text[len(lower):], middle); + copy(new_text[len(lower)+len(middle):], upper); + + delete(document.text); + + document.text = new_text; + } + + else { + //no need to copy the lower since it is already in the document. + copy(document.text[len(lower):], middle); + copy(document.text[len(lower)+len(middle):], upper); + } + + + /* + fmt.println(string(document.text[:document.used_text])); + + fmt.println("LOWER"); + fmt.println(string(lower)); + + fmt.println("CHANGE"); + fmt.println(change.text); + fmt.println(len(change.text)); + + fmt.println("UPPER"); + fmt.println(string(upper)); + */ + + } + + return .None; } -document_close :: proc(uri_string: string, text: string) -> Error { +document_close :: proc(uri_string: string) -> Error { uri, parsed_ok := parse_uri(uri_string, context.temp_allocator); @@ -104,16 +174,6 @@ document_close :: proc(uri_string: string, text: string) -> Error { document.client_owned = false; - if document.text != "" { - delete(document.text); - } - - document.text = text; - - if err := document_refresh(document); err != .None { - return err; - } - return .None; } diff --git a/src/position.odin b/src/position.odin index bcc095f..a0856fb 100644 --- a/src/position.odin +++ b/src/position.odin @@ -2,22 +2,23 @@ package main import "core:strings" import "core:unicode/utf8" +import "core:fmt" /* - This file handles the conversion from utf-16 to utf-8 offsets in the text document + This file handles the conversion between utf-16 and utf-8 offsets in the text document */ AbsoluteRange :: struct { - begin: int, + start: int, end: int, }; -get_absolute_range :: proc(range: Range, document_text: string) -> (AbsoluteRange, bool) { +get_absolute_range :: proc(range: Range, document_text: [] u8) -> (AbsoluteRange, bool) { absolute: AbsoluteRange; - if len(document_text) >= 2 { + if len(document_text) <= 2 { return absolute, false; } @@ -25,16 +26,27 @@ get_absolute_range :: proc(range: Range, document_text: string) -> (AbsoluteRang index := 1; last := document_text[0]; - get_index_at_line(&index, &index, &last, document_text, range.start.line); + if !get_index_at_line(&index, &line_count, &last, document_text, range.start.line) { + return absolute, false; + } + + absolute.start = index + get_character_offset_u16_to_u8(range.start.character, document_text[index:]); + + if !get_index_at_line(&index, &line_count, &last, document_text, range.end.line) { + return absolute, false; + } - - + absolute.end = index + get_character_offset_u16_to_u8(range.end.character, document_text[index:]); return absolute, true; } -get_index_at_line :: proc(current_index: ^int, current_line: ^int, last: ^u8, document_text: string, end_line: int) -> bool { +get_index_at_line :: proc(current_index: ^int, current_line: ^int, last: ^u8, document_text: []u8, end_line: int) -> bool { + + if current_line^ == end_line { + return true; + } for ; current_index^ < len(document_text); current_index^ += 1 { @@ -67,4 +79,30 @@ get_index_at_line :: proc(current_index: ^int, current_line: ^int, last: ^u8, do return false; +} + +get_character_offset_u16_to_u8 :: proc(character_offset: int, document_text: [] u8) -> int { + + utf8_idx := 0; + utf16_idx := 0; + + fmt.println(character_offset); + + for utf16_idx < character_offset { + + r, w := utf8.decode_rune(document_text[utf8_idx:]); + + if r < 0x10000 { + utf16_idx += 1; + } + + else { + utf16_idx += 2; + } + + utf8_idx += w; + + } + + return utf8_idx; }
\ No newline at end of file diff --git a/src/requests.odin b/src/requests.odin index 61f4f6a..50d6dd7 100644 --- a/src/requests.odin +++ b/src/requests.odin @@ -1,4 +1,4 @@ -package main +package main import "core:fmt" import "core:log" @@ -99,7 +99,7 @@ read_and_parse_header :: proc(reader: ^Reader) -> (Header, bool) { return header, false; } } - + } return header, found_content_length; @@ -126,12 +126,12 @@ read_and_parse_body :: proc(reader: ^Reader, header: Header) -> (json.Value, boo } return value, true; -} +} handle_request :: proc(request: json.Value, config: ^Config, writer: ^Writer) -> bool { log.info("Handling request"); - + root, ok := request.value.(json.Object); if !ok { @@ -151,13 +151,13 @@ handle_request :: proc(request: json.Value, config: ^Config, writer: ^Writer) -> case json.Integer: id = v; case: - id = 0; + id = 0; } } method := root["method"].value.(json.String); - call_map : map [string] proc(json.Value, RequestId, ^Config, ^Writer) -> Error = + call_map : map [string] proc(json.Value, RequestId, ^Config, ^Writer) -> Error = {"initialize" = request_initialize, "initialized" = request_initialized, "shutdown" = request_shutdown, @@ -196,7 +196,7 @@ handle_request :: proc(request: json.Value, config: ^Config, writer: ^Writer) -> return true; } -request_initialize :: proc(params: json.Value, id: RequestId, config: ^Config, writer: ^Writer) -> Error { +request_initialize :: proc(params: json.Value, id: RequestId, config: ^Config, writer: ^Writer) -> Error { params_object, ok := params.value.(json.Object); @@ -221,8 +221,8 @@ request_initialize :: proc(params: json.Value, id: RequestId, config: ^Config, w config.hover_support_md = true; } } - - response := make_response_message( + + response := make_response_message( params = ResponseInitializeParams { capabilities = ServerCapabilities { textDocumentSync = 2, //incremental @@ -242,7 +242,7 @@ request_initialized :: proc(params: json.Value, id: RequestId, config: ^Config, request_shutdown :: proc(params: json.Value, id: RequestId, config: ^Config, writer: ^Writer) -> Error { - response := make_response_message( + response := make_response_message( params = nil, id = id, ); @@ -270,7 +270,7 @@ notification_did_open :: proc(params: json.Value, id: RequestId, config: ^Config if unmarshal(params, open_params, context.allocator) != .None { return .ParseError; } - + return document_open(open_params.textDocument.uri, open_params.textDocument.text); } @@ -288,7 +288,7 @@ notification_did_change :: proc(params: json.Value, id: RequestId, config: ^Conf return .ParseError; } - fmt.println(change_params); + document_apply_changes(change_params.textDocument.uri, change_params.contentChanges); return .None; } @@ -307,6 +307,6 @@ notification_did_close :: proc(params: json.Value, id: RequestId, config: ^Confi return .ParseError; } - return document_close(close_params.textDocument.uri, close_params.textDocument.text); + return document_close(close_params.textDocument.uri); } diff --git a/src/types.odin b/src/types.odin index 8ef22be..2edd135 100644 --- a/src/types.odin +++ b/src/types.odin @@ -49,7 +49,7 @@ ResponseError :: struct { }; NotificationLoggingParams :: struct { - type: int, + type: int, message: string, }; @@ -58,8 +58,8 @@ NotificationParams :: union { }; Notification :: struct { - jsonrpc: string, - method: string, + jsonrpc: string, + method: string, params: NotificationParams }; @@ -106,10 +106,6 @@ ClientCapabilities :: struct { textDocument: TextDocumentClientCapabilities, }; -DidOpenTextDocumentParams :: struct { - textDocument: TextDocumentItem, -}; - Position :: struct { line: int, character: int, @@ -134,16 +130,25 @@ VersionedTextDocumentIdentifier :: struct { uri: string, }; +TextDocumentIdentifier :: struct { + uri: string, +}; + +TextDocumentItem :: struct { + uri: string, + text: string, +}; + +DidOpenTextDocumentParams :: struct { + textDocument: TextDocumentItem, +}; + DidChangeTextDocumentParams :: struct { textDocument: VersionedTextDocumentIdentifier, contentChanges: [dynamic] TextDocumentContentChangeEvent, }; DidCloseTextDocumentParams :: struct{ - textDocument: TextDocumentItem, + textDocument: TextDocumentIdentifier, }; -TextDocumentItem :: struct { - uri: string, - text: string, -};
\ No newline at end of file diff --git a/src/unmarshal.odin b/src/unmarshal.odin index 07498c2..297bd89 100644 --- a/src/unmarshal.odin +++ b/src/unmarshal.odin @@ -78,7 +78,7 @@ unmarshal :: proc(json_value: json.Value, v: any, allocator := context.allocator case json.Integer: #partial switch variant in &type_info.variant { case Type_Info_Integer: - switch type_info.size {< + switch type_info.size { case 8: tmp := i64(j); mem.copy(v.data, &tmp, type_info.size); diff --git a/tests/tests.odin b/tests/tests.odin index 773eeff..e538c16 100644 --- a/tests/tests.odin +++ b/tests/tests.odin @@ -12,7 +12,7 @@ import src "../src" initialize_request := ` { "jsonrpc":"2.0", "id":0, - "method":"initialize", + "method":"initialize", "params": { "processId": 39964, "clientInfo": { @@ -341,7 +341,7 @@ test_init_check_shutdown :: proc() -> bool { writer := src.make_writer(src.os_write, cast(rawptr)os.stdout); src.run(&reader, &writer); - + delete(buffer.data); return true; @@ -359,7 +359,7 @@ test_open_and_change_notification :: proc() -> bool { "uri": "file:///c%3A/Users/danie/OneDrive/Desktop/Computer_Science/ols/tests/test_project/src/main.odin", "languageId": "odin", "version": 1, - "text": "package main\r\n\r\nimport \"core:fmt\"\r\nimport \"core:log\"\r\n\r\n\r\n\r\nmain :: proc() {\r\n\r\n}" + "text": "package main\r\n\r\nimport \"core:fmt\"\r\nimport \"core:log\"\r\n\r\n\r\n\r\nmain :: proc() {\r\n\r\n}import \"core:fmt\"\r\nimport \"core:log\"\r\n" } } }`; @@ -372,37 +372,37 @@ test_open_and_change_notification :: proc() -> bool { "textDocument": { "uri": "file:///c%3A/Users/danie/OneDrive/Desktop/Computer_Science/ols/tests/test_project/src/main.odin", "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { - "line": 8, - "character": 1 - }, - "end": { - "line": 8, - "character": 1 - } + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 9, + "character": 1 }, - "rangeLength": 0, - "text": "h" - } - ] + "end": { + "line": 10, + "character": 17 + } + }, + "rangeLength": 36, + "text": "" + } + ] } }`; buffer := TestReadBuffer { - data = transmute([]byte) strings.join({make_request(initialize_request), make_request(open_notification), - make_request(change_notification), make_request(shutdown_request), + data = transmute([]byte) strings.join({make_request(initialize_request), make_request(open_notification), + make_request(change_notification), make_request(shutdown_request), make_request(exit_notification)}, "", context.temp_allocator), }; - + reader := src.make_reader(test_read, &buffer); writer := src.make_writer(src.os_write, cast(rawptr)os.stdout); src.run(&reader, &writer); - + delete(buffer.data); return true; @@ -414,19 +414,9 @@ main :: proc() { context.logger = log.create_console_logger(); test_init_check_shutdown(); - - test_open_and_change_notification(); - - /* - - - reader := src.make_reader(test_read, &TestReadBuffer, context.temp_allocator); - - header, success := src.read_and_parse_header(&reader); + test_open_and_change_notification(); - log.info(header); - */ } |