From 982a39f868bd569a9e99c99b25e7f4355098b7e1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 8 May 2020 13:50:21 +0300 Subject: [PATCH 1/4] Added context detection from document and position for builtins, variable accesses, field accesses, string literals and comments. --- src/main.zig | 117 +++++++++++++++++++++++++++++++++++++++++++++----- src/types.zig | 2 +- 2 files changed, 107 insertions(+), 12 deletions(-) diff --git a/src/main.zig b/src/main.zig index 5c13999..a09574f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -301,6 +301,109 @@ const builtin_completions = block: { break :block temp; }; +const Context = enum { + builtin, + comment, + string_literal, + field_access, + var_access, + other, +}; + +fn documentContext(doc: types.TextDocument, pos_index: usize) Context { + // First extract the whole current line up to the cursor. + var curr_position = pos_index; + while (curr_position > 0) : (curr_position -= 1) { + if (doc.text[curr_position - 1] == '\n') break; + } + + var line = doc.text[curr_position .. pos_index + 1]; + // Strip any leading whitespace. + curr_position = 0; + while (line[curr_position] == ' ' or line[curr_position] == '\t') : (curr_position += 1) {} + line = line[curr_position .. ]; + + // Quick exit for whole-comment lines. + if (line.len > 2 and line[0] == '/' and line[1] == '/') + return .comment; + + // TODO: This does not detect if we are in a string literal over multiple lines. + // Find out what context we are in. + // Go over the current line character by character + // and determine the context. + curr_position = 0; + var new_token = true; + var context: Context = .other; + var string_pop_ctx: Context = .other; + while (curr_position < line.len) : (curr_position += 1) { + const c = line[curr_position]; + const next_char = if (curr_position < line.len - 1) line[curr_position + 1] else null; + + if (context != .string_literal and c == '"') { + context = .string_literal; + continue; + } + + if (context == .string_literal) { + // Skip over escaped quotes + if (c == '\\' and next_char != null and next_char.? == '"') { + curr_position += 1; + } else if (c == '"') { + context = string_pop_ctx; + string_pop_ctx = .other; + new_token = true; + } + + continue; + } + + if (c == '/' and next_char != null and next_char.? == '/') { + context = .comment; + break; + } + + if (c == ' ' or c == '\t') { + new_token = true; + context = .other; + continue; + } + + if (c == '.' and context != .builtin) { + new_token = true; + context = .field_access; + continue; + } + + if (new_token) { + const access_ctx: Context = if (context == .field_access) .field_access else .var_access; + new_token = false; + + if (c == '_' or std.ascii.isAlpha(c)) { + context = access_ctx; + } else if (c == '@') { + // This checks for @"..." identifiers by controlling + // the context the string will set after it is over. + if (next_char != null and next_char.? == '"') { + string_pop_ctx = access_ctx; + } + context = .builtin; + } + continue; + } + + if (context == .field_access or context == .var_access or context == .builtin) { + if (c != '_' and !std.ascii.isAlNum(c)) { + context = .other; + } + continue; + } + + context = .other; + } + + return context; +} + fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void { var tree = try parser.parse(json); defer tree.deinit(); @@ -409,17 +512,9 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void { }; if (pos.character >= 0) { const pos_index = try document.positionToIndex(pos); - const char = document.text[pos_index]; + const context = documentContext(document.*, pos_index); - var check_for_builtin_pos = pos_index; - var is_builtin = false; - - while (check_for_builtin_pos > 0) : (check_for_builtin_pos -= 1) { - if (document.text[check_for_builtin_pos] == '@') {is_builtin = true; break;} - if (!std.ascii.isAlpha(document.text[check_for_builtin_pos])) break; - } - - if (is_builtin) { + if (context == .builtin) { try send(types.Response{ .id = .{.Integer = id}, .result = .{ @@ -429,7 +524,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void { }, }, }); - } else if (char != '.') { + } else if (context == .var_access) { try completeGlobal(id, document); } else { try respondGeneric(id, no_completions_response); diff --git a/src/types.zig b/src/types.zig index d46cb2b..856fc8c 100644 --- a/src/types.zig +++ b/src/types.zig @@ -142,7 +142,7 @@ pub const TextDocument = struct { mem: []u8, sane_text: ?String = null, - pub fn positionToIndex(self: *const TextDocument, position: Position) !usize { + pub fn positionToIndex(self: TextDocument, position: Position) !usize { var split_iterator = std.mem.split(self.text, "\n"); var line: i64 = 0; From 0149f11c7ce46e692a67c681250f7a6e216cbd73 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 8 May 2020 14:36:54 +0300 Subject: [PATCH 2/4] Switched Context to PositionContext --- src/main.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.zig b/src/main.zig index a09574f..4376151 100644 --- a/src/main.zig +++ b/src/main.zig @@ -301,7 +301,7 @@ const builtin_completions = block: { break :block temp; }; -const Context = enum { +const PositionContext = enum { builtin, comment, string_literal, @@ -310,7 +310,7 @@ const Context = enum { other, }; -fn documentContext(doc: types.TextDocument, pos_index: usize) Context { +fn documentContext(doc: types.TextDocument, pos_index: usize) PositionContext { // First extract the whole current line up to the cursor. var curr_position = pos_index; while (curr_position > 0) : (curr_position -= 1) { @@ -333,8 +333,8 @@ fn documentContext(doc: types.TextDocument, pos_index: usize) Context { // and determine the context. curr_position = 0; var new_token = true; - var context: Context = .other; - var string_pop_ctx: Context = .other; + var context: PositionContext = .other; + var string_pop_ctx: PositionContext = .other; while (curr_position < line.len) : (curr_position += 1) { const c = line[curr_position]; const next_char = if (curr_position < line.len - 1) line[curr_position + 1] else null; @@ -375,7 +375,7 @@ fn documentContext(doc: types.TextDocument, pos_index: usize) Context { } if (new_token) { - const access_ctx: Context = if (context == .field_access) .field_access else .var_access; + const access_ctx: PositionContext = if (context == .field_access) .field_access else .var_access; new_token = false; if (c == '_' or std.ascii.isAlpha(c)) { @@ -512,9 +512,9 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void { }; if (pos.character >= 0) { const pos_index = try document.positionToIndex(pos); - const context = documentContext(document.*, pos_index); + const pos_context = documentContext(document.*, pos_index); - if (context == .builtin) { + if (pos_context == .builtin) { try send(types.Response{ .id = .{.Integer = id}, .result = .{ @@ -524,7 +524,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void { }, }, }); - } else if (context == .var_access) { + } else if (pos_context == .var_access) { try completeGlobal(id, document); } else { try respondGeneric(id, no_completions_response); From 0b5eb4b9d2516cc115c16ea53b6f18424d322e4b Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 8 May 2020 15:07:14 +0300 Subject: [PATCH 3/4] Fixed field_access check --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 4376151..038a495 100644 --- a/src/main.zig +++ b/src/main.zig @@ -368,7 +368,7 @@ fn documentContext(doc: types.TextDocument, pos_index: usize) PositionContext { continue; } - if (c == '.' and context != .builtin) { + if (c == '.' and (!new_token or context == .string_literal)) { new_token = true; context = .field_access; continue; From aca7db3e8837a17d1e3fe278a1597dc1c5466536 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 8 May 2020 15:09:31 +0300 Subject: [PATCH 4/4] Reset context to other when trying to find field/var access and don't get alphanumerical or @"..." --- src/main.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.zig b/src/main.zig index 038a495..a632530 100644 --- a/src/main.zig +++ b/src/main.zig @@ -387,6 +387,8 @@ fn documentContext(doc: types.TextDocument, pos_index: usize) PositionContext { string_pop_ctx = access_ctx; } context = .builtin; + } else { + context = .other; } continue; }