Correctly support utf16 offsets

This commit is contained in:
Alexandros Naskos 2020-07-03 02:31:28 +03:00
parent 8c154c2a60
commit d8aba7da0b
4 changed files with 54 additions and 81 deletions

View File

@ -1249,11 +1249,11 @@ fn tokenRangeAppend(prev: SourceRange, token: std.zig.Token) SourceRange {
}; };
} }
pub fn documentPositionContext(arena: *std.heap.ArenaAllocator, document: types.TextDocument, position: types.Position) !PositionContext { const DocumentPosition = @import("offsets.zig").DocumentPosition;
const line = try document.getLine(@intCast(usize, position.line));
const pos_char = @intCast(usize, position.character); pub fn documentPositionContext(arena: *std.heap.ArenaAllocator, document: types.TextDocument, doc_position: DocumentPosition) !PositionContext {
const idx = if (pos_char > line.len) line.len else pos_char; const line = doc_position.line;
var tokenizer = std.zig.Tokenizer.init(line[0..idx]); var tokenizer = std.zig.Tokenizer.init(line[0..doc_position.line_index]);
var stack = try std.ArrayList(StackState).initCapacity(&arena.allocator, 8); var stack = try std.ArrayList(StackState).initCapacity(&arena.allocator, 8);
while (true) { while (true) {
@ -1262,11 +1262,11 @@ pub fn documentPositionContext(arena: *std.heap.ArenaAllocator, document: types.
switch (tok.id) { switch (tok.id) {
.Invalid, .Invalid_ampersands => { .Invalid, .Invalid_ampersands => {
// Single '@' do not return a builtin token so we check this on our own. // Single '@' do not return a builtin token so we check this on our own.
if (line[idx - 1] == '@') { if (line[doc_position.line_index - 1] == '@') {
return PositionContext{ return PositionContext{
.builtin = .{ .builtin = .{
.start = idx - 1, .start = doc_position.line_index - 1,
.end = idx, .end = doc_position.line_index,
}, },
}; };
} }

View File

@ -2,6 +2,7 @@ const std = @import("std");
const types = @import("types.zig"); const types = @import("types.zig");
const URI = @import("uri.zig"); const URI = @import("uri.zig");
const analysis = @import("analysis.zig"); const analysis = @import("analysis.zig");
const offsets = @import("offsets.zig");
const DocumentStore = @This(); const DocumentStore = @This();
@ -471,8 +472,9 @@ pub fn applyChanges(
}; };
const change_text = change.Object.getValue("text").?.String; const change_text = change.Object.getValue("text").?.String;
const start_index = try document.positionToIndex(start_pos);
const end_index = try document.positionToIndex(end_pos); const start_index = (try offsets.documentPosition(document.*, start_pos, .utf16)).absolute_index;
const end_index = (try offsets.documentPosition(document.*, end_pos, .utf16)).absolute_index;
const old_len = document.text.len; const old_len = document.text.len;
const new_len = old_len + change_text.len; const new_len = old_len + change_text.len;

View File

@ -11,9 +11,7 @@ const types = @import("types.zig");
const analysis = @import("analysis.zig"); const analysis = @import("analysis.zig");
const URI = @import("uri.zig"); const URI = @import("uri.zig");
const rename = @import("rename.zig"); const rename = @import("rename.zig");
const offsets = @import("offsets.zig");
// TODO Fix LSP -> byte and byte -> LSP offsets
// Implement clangd extension for utf8 offsets as well.
pub const log_level: std.log.Level = switch (std.builtin.mode) { pub const log_level: std.log.Level = switch (std.builtin.mode) {
.Debug => .debug, .Debug => .debug,
@ -619,18 +617,15 @@ fn hoverDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, p
fn getSymbolFieldAccess( fn getSymbolFieldAccess(
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
arena: *std.heap.ArenaAllocator, arena: *std.heap.ArenaAllocator,
position: types.Position, position: offsets.DocumentPosition,
range: analysis.SourceRange, range: analysis.SourceRange,
config: Config, config: Config,
) !?analysis.DeclWithHandle { ) !?analysis.DeclWithHandle {
const pos_index = try handle.document.positionToIndex(position); const name = identifierFromPosition(position.absolute_index, handle.*);
const name = identifierFromPosition(pos_index, handle.*);
if (name.len == 0) return null; if (name.len == 0) return null;
var tokenizer = std.zig.Tokenizer.init(position.line[range.start..range.end]);
const line = try handle.document.getLine(@intCast(usize, position.line)); if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |container_handle| {
var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]);
if (try analysis.getFieldAccessType(&document_store, arena, handle, pos_index, &tokenizer)) |container_handle| {
const container_handle_node = switch (container_handle.type.data) { const container_handle_node = switch (container_handle.type.data) {
.other => |n| n, .other => |n| n,
else => return null, else => return null,
@ -650,7 +645,7 @@ fn gotoDefinitionFieldAccess(
arena: *std.heap.ArenaAllocator, arena: *std.heap.ArenaAllocator,
id: types.RequestId, id: types.RequestId,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
position: types.Position, position: offsets.DocumentPosition,
range: analysis.SourceRange, range: analysis.SourceRange,
config: Config, config: Config,
resolve_alias: bool, resolve_alias: bool,
@ -663,7 +658,7 @@ fn hoverDefinitionFieldAccess(
arena: *std.heap.ArenaAllocator, arena: *std.heap.ArenaAllocator,
id: types.RequestId, id: types.RequestId,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
position: types.Position, position: offsets.DocumentPosition,
range: analysis.SourceRange, range: analysis.SourceRange,
config: Config, config: Config,
) !void { ) !void {
@ -712,7 +707,7 @@ fn renameDefinitionFieldAccess(
arena: *std.heap.ArenaAllocator, arena: *std.heap.ArenaAllocator,
id: types.RequestId, id: types.RequestId,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
position: types.Position, position: offsets.DocumentPosition,
range: analysis.SourceRange, range: analysis.SourceRange,
new_name: []const u8, new_name: []const u8,
config: Config, config: Config,
@ -842,14 +837,17 @@ fn completeGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_inde
}); });
} }
fn completeFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, position: types.Position, range: analysis.SourceRange, config: Config) !void { fn completeFieldAccess(
arena: *std.heap.ArenaAllocator,
id: types.RequestId,
handle: *DocumentStore.Handle,
position: offsets.DocumentPosition,
range: analysis.SourceRange,
config: Config,
) !void {
var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator); var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator);
var tokenizer = std.zig.Tokenizer.init(position.line[range.start..range.end]);
const line = try handle.document.getLine(@intCast(usize, position.line)); if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |node| {
var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]);
const pos_index = try handle.document.positionToIndex(position);
if (try analysis.getFieldAccessType(&document_store, arena, handle, pos_index, &tokenizer)) |node| {
try typeToCompletion(arena, &completions, node, handle, config); try typeToCompletion(arena, &completions, node, handle, config);
} }
@ -1087,8 +1085,8 @@ fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
}; };
if (req.params.position.character >= 0) { if (req.params.position.character >= 0) {
const pos_index = try handle.document.positionToIndex(req.params.position); const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position); const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
const this_config = configFromUriOr(req.params.textDocument.uri, config); const this_config = configFromUriOr(req.params.textDocument.uri, config);
const use_snippets = this_config.enable_snippets and client_capabilities.supports_snippets; const use_snippets = this_config.enable_snippets and client_capabilities.supports_snippets;
@ -1102,8 +1100,8 @@ fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
}, },
}, },
}), }),
.var_access, .empty => try completeGlobal(arena, id, pos_index, handle, this_config), .var_access, .empty => try completeGlobal(arena, id, doc_position.absolute_index, handle, this_config),
.field_access => |range| try completeFieldAccess(arena, id, handle, req.params.position, range, this_config), .field_access => |range| try completeFieldAccess(arena, id, handle, doc_position, range, this_config),
.global_error_set => try send(arena, types.Response{ .global_error_set => try send(arena, types.Response{
.id = id, .id = id,
.result = .{ .result = .{
@ -1122,7 +1120,7 @@ fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
}, },
}, },
}), }),
.label => try completeLabel(arena, id, pos_index, handle, this_config), .label => try completeLabel(arena, id, doc_position.absolute_index, handle, this_config),
else => try respondGeneric(id, no_completions_response), else => try respondGeneric(id, no_completions_response),
} }
} else { } else {
@ -1144,15 +1142,15 @@ fn gotoHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reques
}; };
if (req.params.position.character >= 0) { if (req.params.position.character >= 0) {
const pos_index = try handle.document.positionToIndex(req.params.position); const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position); const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
const this_config = configFromUriOr(req.params.textDocument.uri, config); const this_config = configFromUriOr(req.params.textDocument.uri, config);
switch (pos_context) { switch (pos_context) {
.var_access => try gotoDefinitionGlobal(arena, id, pos_index, handle, this_config, resolve_alias), .var_access => try gotoDefinitionGlobal(arena, id, doc_position.absolute_index, handle, this_config, resolve_alias),
.field_access => |range| try gotoDefinitionFieldAccess(arena, id, handle, req.params.position, range, this_config, resolve_alias), .field_access => |range| try gotoDefinitionFieldAccess(arena, id, handle, doc_position, range, this_config, resolve_alias),
.string_literal => try gotoDefinitionString(arena, id, pos_index, handle, config), .string_literal => try gotoDefinitionString(arena, id, doc_position.absolute_index, handle, config),
.label => try gotoDefinitionLabel(arena, id, pos_index, handle, this_config), .label => try gotoDefinitionLabel(arena, id, doc_position.absolute_index, handle, this_config),
else => try respondGeneric(id, null_result_response), else => try respondGeneric(id, null_result_response),
} }
} else { } else {
@ -1175,14 +1173,14 @@ fn hoverHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reque
}; };
if (req.params.position.character >= 0) { if (req.params.position.character >= 0) {
const pos_index = try handle.document.positionToIndex(req.params.position); const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position); const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
const this_config = configFromUriOr(req.params.textDocument.uri, config); const this_config = configFromUriOr(req.params.textDocument.uri, config);
switch (pos_context) { switch (pos_context) {
.var_access => try hoverDefinitionGlobal(arena, id, pos_index, handle, this_config), .var_access => try hoverDefinitionGlobal(arena, id, doc_position.absolute_index, handle, this_config),
.field_access => |range| try hoverDefinitionFieldAccess(arena, id, handle, req.params.position, range, this_config), .field_access => |range| try hoverDefinitionFieldAccess(arena, id, handle, doc_position, range, this_config),
.label => try hoverDefinitionLabel(arena, id, pos_index, handle, this_config), .label => try hoverDefinitionLabel(arena, id, doc_position.absolute_index, handle, this_config),
else => try respondGeneric(id, null_result_response), else => try respondGeneric(id, null_result_response),
} }
} else { } else {
@ -1248,14 +1246,14 @@ fn renameHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requ
}; };
if (req.params.position.character >= 0) { if (req.params.position.character >= 0) {
const pos_index = try handle.document.positionToIndex(req.params.position); const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position); const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
const this_config = configFromUriOr(req.params.textDocument.uri, config); const this_config = configFromUriOr(req.params.textDocument.uri, config);
switch (pos_context) { switch (pos_context) {
.var_access => try renameDefinitionGlobal(arena, id, handle, pos_index, req.params.newName), .var_access => try renameDefinitionGlobal(arena, id, handle, doc_position.absolute_index, req.params.newName),
.field_access => |range| try renameDefinitionFieldAccess(arena, id, handle, req.params.position, range, req.params.newName, this_config), .field_access => |range| try renameDefinitionFieldAccess(arena, id, handle, doc_position, range, req.params.newName, this_config),
.label => try renameDefinitionLabel(arena, id, handle, pos_index, req.params.newName), .label => try renameDefinitionLabel(arena, id, handle, doc_position.absolute_index, req.params.newName),
else => try respondGeneric(id, null_result_response), else => try respondGeneric(id, null_result_response),
} }
} else { } else {
@ -1331,7 +1329,9 @@ fn processJsonRpc(arena: *std.heap.ArenaAllocator, parser: *std.json.Parser, jso
} }
} else { } else {
done = error.HackDone; done = error.HackDone;
(method_info[2])(arena, id, config) catch |err| { done = err; }; (method_info[2])(arena, id, config) catch |err| {
done = err;
};
} }
} }
} }

View File

@ -139,35 +139,6 @@ pub const TextDocument = struct {
// This holds the memory that we have actually allocated. // This holds the memory that we have actually allocated.
mem: []u8, mem: []u8,
pub fn positionToIndex(self: TextDocument, position: Position) !usize {
var split_iterator = std.mem.split(self.text, "\n");
var line: i64 = 0;
while (line < position.line) : (line += 1) {
_ = split_iterator.next() orelse return error.InvalidParams;
}
var index = @intCast(i64, split_iterator.index.?) + position.character;
if (index < 0 or index > @intCast(i64, self.text.len)) {
return error.InvalidParams;
}
return @intCast(usize, index);
}
pub fn getLine(self: TextDocument, target_line: usize) ![]const u8 {
var split_iterator = std.mem.split(self.text, "\n");
var line: i64 = 0;
while (line < target_line) : (line += 1) {
_ = split_iterator.next() orelse return error.InvalidParams;
}
if (split_iterator.next()) |next| {
return next;
} else return error.InvalidParams;
}
pub fn range(self: TextDocument) Range { pub fn range(self: TextDocument) Range {
var line_idx: i64 = 0; var line_idx: i64 = 0;
var curr_line: []const u8 = self.text; var curr_line: []const u8 = self.text;