aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2020-11-04 20:26:46 +0100
committerDanielGavin <danielgavin5@hotmail.com>2020-11-04 20:26:46 +0100
commit877a696d876cc64fb7c17aad3725f125da58e1eb (patch)
treeadd4ecdc46288eccd320a5ce62231823150a54d0 /src
parent35ca720dd6fa200c70a5727507fc6e432bd1a44b (diff)
fixed incremental sync + added diagnostic notification
Diffstat (limited to 'src')
-rw-r--r--src/analysis.odin92
-rw-r--r--src/documents.odin121
-rw-r--r--src/log.odin8
-rw-r--r--src/main.odin10
-rw-r--r--src/position.odin55
-rw-r--r--src/requests.odin16
-rw-r--r--src/types.odin30
7 files changed, 279 insertions, 53 deletions
diff --git a/src/analysis.odin b/src/analysis.odin
index 85f0393..8cba000 100644
--- a/src/analysis.odin
+++ b/src/analysis.odin
@@ -1 +1,91 @@
-package main \ No newline at end of file
+package main
+
+import "core:odin/parser"
+import "core:odin/ast"
+import "core:odin/tokenizer"
+import "core:fmt"
+import "core:log"
+
+ParserError :: struct {
+ message: string,
+ line: int,
+ column: int,
+ file: string,
+ offset: int,
+};
+
+StructSymbol :: struct {
+
+};
+
+ProcedureSymbol :: struct {
+
+};
+
+Symbol :: union {
+ StructSymbol,
+ ProcedureSymbol,
+};
+
+DocumentSymbols :: struct {
+ globals: map [string] Symbol,
+};
+
+DocumentPositionContext :: struct {
+ symbol: Symbol,
+};
+
+current_errors: [dynamic] ParserError;
+
+parser_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) {
+ error := ParserError { line = pos.line, column = pos.column, file = pos.file,
+ offset = pos.offset, message = fmt.tprintf(msg, ..args) };
+ append(&current_errors, error);
+}
+
+parser_warning_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) {
+
+}
+
+
+/*
+ Parses and walks through the ast saving all the global symbols for the document. Local symbols are not saved
+ because they are determined by the position.
+
+ Document is responsible in freeing the DocumentSymbols
+*/
+
+parse_document_symbols :: proc(document: ^Document) -> (DocumentSymbols, [dynamic] ParserError, bool) {
+
+ p := parser.Parser {
+ err = parser_error_handler,
+ warn = parser_warning_handler,
+ };
+
+ current_errors = make([dynamic] ParserError, context.temp_allocator);
+
+
+ ast := ast.File {
+ fullpath = document.path,
+ src = document.text[:document.used_text],
+ };
+
+ parser.parse_file(&p, &ast);
+
+ return DocumentSymbols {}, current_errors, true;
+}
+
+
+
+/*
+ Figure out what exactly is at the given position and whether it is in a function, struct, etc.
+*/
+get_document_position_context :: proc(document: ^Document, position: Position) -> DocumentPositionContext {
+
+ position_context: DocumentPositionContext;
+
+ return position_context;
+}
+
+
+
diff --git a/src/documents.odin b/src/documents.odin
index 0be8cae..ecab990 100644
--- a/src/documents.odin
+++ b/src/documents.odin
@@ -11,9 +11,10 @@ Package :: struct {
Document :: struct {
uri: string,
path: string,
- text: [] u8, //transmuted version of text plus potential unused space
+ text: [] u8,
used_text: int, //allow for the text to be reallocated with more data than needed
client_owned: bool,
+ diagnosed_errors: bool,
};
DocumentStorage :: struct {
@@ -23,7 +24,7 @@ DocumentStorage :: struct {
document_storage: DocumentStorage;
-document_open :: proc(uri_string: string, text: string) -> Error {
+document_open :: proc(uri_string: string, text: string, writer: ^Writer) -> Error {
uri, parsed_ok := parse_uri(uri_string);
@@ -33,7 +34,6 @@ document_open :: proc(uri_string: string, text: string) -> Error {
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;
@@ -47,11 +47,10 @@ document_open :: proc(uri_string: string, text: string) -> Error {
document.text = transmute([] u8)text;
document.used_text = len(document.text);
- if err := document_refresh(document); err != .None {
+ if err := document_refresh(document, writer); err != .None {
return err;
}
- document_refresh(document);
}
else {
@@ -64,7 +63,7 @@ document_open :: proc(uri_string: string, text: string) -> Error {
used_text = len(text),
};
- if err := document_refresh(&document); err != .None {
+ if err := document_refresh(&document, writer); err != .None {
return err;
}
@@ -79,7 +78,10 @@ document_open :: proc(uri_string: string, text: string) -> Error {
return .None;
}
-document_apply_changes :: proc(uri_string: string, changes: [dynamic] TextDocumentContentChangeEvent) -> Error {
+/*
+ Function that applies changes to the given document through incremental syncronization
+ */
+document_apply_changes :: proc(uri_string: string, changes: [dynamic] TextDocumentContentChangeEvent, writer: ^Writer) -> Error {
uri, parsed_ok := parse_uri(uri_string, context.temp_allocator);
@@ -96,24 +98,25 @@ document_apply_changes :: proc(uri_string: string, changes: [dynamic] TextDocume
for change in changes {
- absolute_range, ok := get_absolute_range(change.range, document.text);
+ absolute_range, ok := get_absolute_range(change.range, document.text[:document.used_text]);
if !ok {
return .ParseError;
}
//lower bound is before the change
- lower := document.text[:absolute_range.start];
+ 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):];
+ upper := document.text[absolute_range.end:document.used_text];
//total new size needed
document.used_text = len(lower) + len(change.text) + len(upper);
+ //Reduce the amount of allocation by allocating more memory than needed
if document.used_text > len(document.text) {
new_text := make([]u8, document.used_text * 2);
@@ -128,33 +131,15 @@ document_apply_changes :: proc(uri_string: string, changes: [dynamic] TextDocume
}
else {
- //no need to copy the lower since it is already in the document.
- copy(document.text[len(lower):], middle);
+ //order matters here, we need to make sure we swap the data already in the text before the middle
+ copy(document.text, lower);
copy(document.text[len(lower)+len(middle):], upper);
+ copy(document.text[len(lower):], middle);
}
-
- /*
- 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;
+ return document_refresh(document, writer);
}
document_close :: proc(uri_string: string) -> Error {
@@ -177,7 +162,77 @@ document_close :: proc(uri_string: string) -> Error {
return .None;
}
-document_refresh :: proc(document: ^Document) -> Error {
+
+
+document_refresh :: proc(document: ^Document, writer: ^Writer) -> Error {
+
+
+ document_symbols, errors, ok := parse_document_symbols(document);
+
+ if !ok {
+ return .ParseError;
+ }
+
+ if len(errors) > 0 {
+ document.diagnosed_errors = true;
+
+ params := NotificationPublishDiagnosticsParams {
+ uri = document.uri,
+ diagnostics = make([] Diagnostic, len(errors), context.temp_allocator),
+ };
+
+ for error, i in errors {
+
+ params.diagnostics[i] = Diagnostic {
+ range = Range {
+ start = Position {
+ line = error.line - 1,
+ character = 0,
+ },
+ end = Position {
+ line = error.line,
+ character = 0,
+ },
+ },
+ severity = DiagnosticSeverity.Error,
+ code = "test",
+ message = error.message,
+ };
+
+ }
+
+ notifaction := Notification {
+ jsonrpc = "2.0",
+ method = "textDocument/publishDiagnostics",
+ params = params,
+ };
+
+ send_notification(notifaction, writer);
+
+ }
+
+ if len(errors) == 0 {
+
+ //send empty diagnosis to remove the clients errors
+ if document.diagnosed_errors {
+
+ notifaction := Notification {
+ jsonrpc = "2.0",
+ method = "textDocument/publishDiagnostics",
+
+ params = NotificationPublishDiagnosticsParams {
+ uri = document.uri,
+ diagnostics = make([] Diagnostic, len(errors), context.temp_allocator),
+ },
+ };
+
+ document.diagnosed_errors = false;
+
+ send_notification(notifaction, writer);
+ }
+
+ }
+
return .None;
}
diff --git a/src/log.odin b/src/log.odin
index fd370c9..82a52a4 100644
--- a/src/log.odin
+++ b/src/log.odin
@@ -48,13 +48,13 @@ lsp_logger_proc :: proc(logger_data: rawptr, level: log.Level, text: string, opt
}
}
- message := fmt.tprintf("%s %s", buf, text);
+ message := fmt.tprintf("%s", text);
notification := Notification {
- jsonrpc = "2.0",
- method = "window/logMessage",
+ jsonrpc = "2.0",
+ method = "window/logMessage",
params = NotificationLoggingParams {
- type = 1,
+ type = 1,
message = message,
}
};
diff --git a/src/main.odin b/src/main.odin
index 896f275..e388be5 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -40,7 +40,7 @@ run :: proc(reader: ^Reader, writer: ^Writer) {
return;
}
-
+
value: json.Value;
value, success = read_and_parse_body(reader, header);
@@ -56,12 +56,14 @@ run :: proc(reader: ^Reader, writer: ^Writer) {
return;
}
+ free_all(context.temp_allocator);
+
}
}
end :: proc() {
-
+
}
@@ -69,7 +71,9 @@ main :: proc() {
reader := make_reader(os_read, cast(rawptr)os.stdin);
writer := make_writer(os_write, cast(rawptr)os.stdout);
-
+
+ context.logger = create_lsp_logger(&writer);
+
run(&reader, &writer);
}
diff --git a/src/position.odin b/src/position.odin
index a0856fb..18cb547 100644
--- a/src/position.odin
+++ b/src/position.odin
@@ -8,7 +8,6 @@ import "core:fmt"
This file handles the conversion between utf-16 and utf-8 offsets in the text document
*/
-
AbsoluteRange :: struct {
start: int,
end: int,
@@ -18,8 +17,10 @@ get_absolute_range :: proc(range: Range, document_text: [] u8) -> (AbsoluteRange
absolute: AbsoluteRange;
- if len(document_text) <= 2 {
- return absolute, false;
+ if len(document_text) == 0 {
+ absolute.start = 0;
+ absolute.end = 0;
+ return absolute, true;
}
line_count := 0;
@@ -32,6 +33,12 @@ get_absolute_range :: proc(range: Range, document_text: [] u8) -> (AbsoluteRange
absolute.start = index + get_character_offset_u16_to_u8(range.start.character, document_text[index:]);
+ //if the last line was indexed at zero we have to move it back to index 1.
+ //This happens when line = 0
+ if index == 0 {
+ index = 1;
+ }
+
if !get_index_at_line(&index, &line_count, &last, document_text, range.end.line) {
return absolute, false;
}
@@ -44,10 +51,16 @@ get_absolute_range :: proc(range: Range, document_text: [] u8) -> (AbsoluteRange
get_index_at_line :: proc(current_index: ^int, current_line: ^int, last: ^u8, document_text: []u8, end_line: int) -> bool {
+ if end_line == 0 {
+ current_index^ = 0;
+ return true;
+ }
+
if current_line^ == end_line {
return true;
}
+
for ; current_index^ < len(document_text); current_index^ += 1 {
current := document_text[current_index^];
@@ -86,13 +99,15 @@ get_character_offset_u16_to_u8 :: proc(character_offset: int, document_text: []
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 {
+ if r == '\n' {
+ return utf8_idx;
+ }
+
+ else if r < 0x10000 {
utf16_idx += 1;
}
@@ -105,4 +120,32 @@ get_character_offset_u16_to_u8 :: proc(character_offset: int, document_text: []
}
return utf8_idx;
+}
+
+
+get_end_line_u16 :: proc(document_text: [] u8) -> int {
+
+ utf8_idx := 0;
+ utf16_idx := 0;
+
+ for utf8_idx < len(document_text) {
+ r, w := utf8.decode_rune(document_text[utf8_idx:]);
+
+ if r == '\n' {
+ return utf16_idx;
+ }
+
+ else if r < 0x10000 {
+ utf16_idx += 1;
+ }
+
+ else {
+ utf16_idx += 2;
+ }
+
+ utf8_idx += w;
+
+ }
+
+ return utf16_idx;
} \ No newline at end of file
diff --git a/src/requests.odin b/src/requests.odin
index 50d6dd7..84d131c 100644
--- a/src/requests.odin
+++ b/src/requests.odin
@@ -130,7 +130,6 @@ read_and_parse_body :: proc(reader: ^Reader, header: Header) -> (json.Value, boo
handle_request :: proc(request: json.Value, config: ^Config, writer: ^Writer) -> bool {
- log.info("Handling request");
root, ok := request.value.(json.Object);
@@ -164,7 +163,8 @@ handle_request :: proc(request: json.Value, config: ^Config, writer: ^Writer) ->
"exit" = notification_exit,
"textDocument/didOpen" = notification_did_open,
"textDocument/didChange" = notification_did_change,
- "textDocument/didClose" = notification_did_close};
+ "textDocument/didClose" = notification_did_close,
+ "textDocument/didSave" = notification_did_save };
fn: proc(json.Value, RequestId, ^Config, ^Writer) -> Error;
fn, ok = call_map[method];
@@ -226,6 +226,7 @@ request_initialize :: proc(params: json.Value, id: RequestId, config: ^Config, w
params = ResponseInitializeParams {
capabilities = ServerCapabilities {
textDocumentSync = 2, //incremental
+ definitionProvider = true,
},
},
id = id,
@@ -271,7 +272,7 @@ notification_did_open :: proc(params: json.Value, id: RequestId, config: ^Config
return .ParseError;
}
- return document_open(open_params.textDocument.uri, open_params.textDocument.text);
+ return document_open(open_params.textDocument.uri, open_params.textDocument.text, writer);
}
notification_did_change :: proc(params: json.Value, id: RequestId, config: ^Config, writer: ^Writer) -> Error {
@@ -288,7 +289,7 @@ notification_did_change :: proc(params: json.Value, id: RequestId, config: ^Conf
return .ParseError;
}
- document_apply_changes(change_params.textDocument.uri, change_params.contentChanges);
+ document_apply_changes(change_params.textDocument.uri, change_params.contentChanges, writer);
return .None;
}
@@ -310,3 +311,10 @@ notification_did_close :: proc(params: json.Value, id: RequestId, config: ^Confi
return document_close(close_params.textDocument.uri);
}
+notification_did_save :: proc(params: json.Value, id: RequestId, config: ^Config, writer: ^Writer) -> Error {
+
+
+
+ return .None;
+}
+
diff --git a/src/types.odin b/src/types.odin
index 2edd135..274caae 100644
--- a/src/types.odin
+++ b/src/types.odin
@@ -2,6 +2,12 @@ package main
import "core:encoding/json"
+/*
+ General types
+*/
+
+//TODO(Daniel, move some of the more specific structs to their appropriate place)
+
RequestId :: union {
string,
i64,
@@ -53,8 +59,14 @@ NotificationLoggingParams :: struct {
message: string,
};
+NotificationPublishDiagnosticsParams :: struct {
+ uri: string,
+ diagnostics: [] Diagnostic,
+};
+
NotificationParams :: union {
NotificationLoggingParams,
+ NotificationPublishDiagnosticsParams,
};
Notification :: struct {
@@ -86,6 +98,7 @@ MarkupKind :: enum {
ServerCapabilities :: struct {
textDocumentSync: int,
+ definitionProvider: bool,
};
CompletionClientCapabilities :: struct {
@@ -139,6 +152,20 @@ TextDocumentItem :: struct {
text: string,
};
+DiagnosticSeverity :: enum {
+ Error = 1,
+ Warning = 2,
+ Information = 3,
+ Hint = 4,
+};
+
+Diagnostic :: struct {
+ range: Range,
+ severity: DiagnosticSeverity,
+ code: string,
+ message: string,
+};
+
DidOpenTextDocumentParams :: struct {
textDocument: TextDocumentItem,
};
@@ -148,7 +175,6 @@ DidChangeTextDocumentParams :: struct {
contentChanges: [dynamic] TextDocumentContentChangeEvent,
};
-DidCloseTextDocumentParams :: struct{
+DidCloseTextDocumentParams :: struct {
textDocument: TextDocumentIdentifier,
};
-