Merge pull request #9 from alexnask/master

Added position context detection
This commit is contained in:
Auguste Rame 2020-05-08 09:09:57 -04:00 committed by GitHub
commit d368c84aa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 109 additions and 12 deletions

View File

@ -301,6 +301,111 @@ const builtin_completions = block: {
break :block temp; break :block temp;
}; };
const PositionContext = enum {
builtin,
comment,
string_literal,
field_access,
var_access,
other,
};
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) {
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: 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;
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 (!new_token or context == .string_literal)) {
new_token = true;
context = .field_access;
continue;
}
if (new_token) {
const access_ctx: PositionContext = 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;
} else {
context = .other;
}
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 { fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void {
var tree = try parser.parse(json); var tree = try parser.parse(json);
defer tree.deinit(); defer tree.deinit();
@ -409,17 +514,9 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void {
}; };
if (pos.character >= 0) { if (pos.character >= 0) {
const pos_index = try document.positionToIndex(pos); const pos_index = try document.positionToIndex(pos);
const char = document.text[pos_index]; const pos_context = documentContext(document.*, pos_index);
var check_for_builtin_pos = pos_index; if (pos_context == .builtin) {
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) {
try send(types.Response{ try send(types.Response{
.id = .{.Integer = id}, .id = .{.Integer = id},
.result = .{ .result = .{
@ -429,7 +526,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8) !void {
}, },
}, },
}); });
} else if (char != '.') { } else if (pos_context == .var_access) {
try completeGlobal(id, document); try completeGlobal(id, document);
} else { } else {
try respondGeneric(id, no_completions_response); try respondGeneric(id, no_completions_response);

View File

@ -142,7 +142,7 @@ pub const TextDocument = struct {
mem: []u8, mem: []u8,
sane_text: ?String = null, 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 split_iterator = std.mem.split(self.text, "\n");
var line: i64 = 0; var line: i64 = 0;