Correctly support utf16 offsets
This commit is contained in:
parent
8c154c2a60
commit
d8aba7da0b
@ -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 line = try document.getLine(@intCast(usize, position.line));
|
||||
const pos_char = @intCast(usize, position.character);
|
||||
const idx = if (pos_char > line.len) line.len else pos_char;
|
||||
var tokenizer = std.zig.Tokenizer.init(line[0..idx]);
|
||||
const DocumentPosition = @import("offsets.zig").DocumentPosition;
|
||||
|
||||
pub fn documentPositionContext(arena: *std.heap.ArenaAllocator, document: types.TextDocument, doc_position: DocumentPosition) !PositionContext {
|
||||
const line = doc_position.line;
|
||||
var tokenizer = std.zig.Tokenizer.init(line[0..doc_position.line_index]);
|
||||
var stack = try std.ArrayList(StackState).initCapacity(&arena.allocator, 8);
|
||||
|
||||
while (true) {
|
||||
@ -1262,11 +1262,11 @@ pub fn documentPositionContext(arena: *std.heap.ArenaAllocator, document: types.
|
||||
switch (tok.id) {
|
||||
.Invalid, .Invalid_ampersands => {
|
||||
// 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{
|
||||
.builtin = .{
|
||||
.start = idx - 1,
|
||||
.end = idx,
|
||||
.start = doc_position.line_index - 1,
|
||||
.end = doc_position.line_index,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
const URI = @import("uri.zig");
|
||||
const analysis = @import("analysis.zig");
|
||||
const offsets = @import("offsets.zig");
|
||||
|
||||
const DocumentStore = @This();
|
||||
|
||||
@ -471,8 +472,9 @@ pub fn applyChanges(
|
||||
};
|
||||
|
||||
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 new_len = old_len + change_text.len;
|
||||
|
84
src/main.zig
84
src/main.zig
@ -11,9 +11,7 @@ const types = @import("types.zig");
|
||||
const analysis = @import("analysis.zig");
|
||||
const URI = @import("uri.zig");
|
||||
const rename = @import("rename.zig");
|
||||
|
||||
// TODO Fix LSP -> byte and byte -> LSP offsets
|
||||
// Implement clangd extension for utf8 offsets as well.
|
||||
const offsets = @import("offsets.zig");
|
||||
|
||||
pub const log_level: std.log.Level = switch (std.builtin.mode) {
|
||||
.Debug => .debug,
|
||||
@ -619,18 +617,15 @@ fn hoverDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, p
|
||||
fn getSymbolFieldAccess(
|
||||
handle: *DocumentStore.Handle,
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
position: types.Position,
|
||||
position: offsets.DocumentPosition,
|
||||
range: analysis.SourceRange,
|
||||
config: Config,
|
||||
) !?analysis.DeclWithHandle {
|
||||
const pos_index = try handle.document.positionToIndex(position);
|
||||
const name = identifierFromPosition(pos_index, handle.*);
|
||||
const name = identifierFromPosition(position.absolute_index, handle.*);
|
||||
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));
|
||||
var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]);
|
||||
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, pos_index, &tokenizer)) |container_handle| {
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |container_handle| {
|
||||
const container_handle_node = switch (container_handle.type.data) {
|
||||
.other => |n| n,
|
||||
else => return null,
|
||||
@ -650,7 +645,7 @@ fn gotoDefinitionFieldAccess(
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
id: types.RequestId,
|
||||
handle: *DocumentStore.Handle,
|
||||
position: types.Position,
|
||||
position: offsets.DocumentPosition,
|
||||
range: analysis.SourceRange,
|
||||
config: Config,
|
||||
resolve_alias: bool,
|
||||
@ -663,7 +658,7 @@ fn hoverDefinitionFieldAccess(
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
id: types.RequestId,
|
||||
handle: *DocumentStore.Handle,
|
||||
position: types.Position,
|
||||
position: offsets.DocumentPosition,
|
||||
range: analysis.SourceRange,
|
||||
config: Config,
|
||||
) !void {
|
||||
@ -712,7 +707,7 @@ fn renameDefinitionFieldAccess(
|
||||
arena: *std.heap.ArenaAllocator,
|
||||
id: types.RequestId,
|
||||
handle: *DocumentStore.Handle,
|
||||
position: types.Position,
|
||||
position: offsets.DocumentPosition,
|
||||
range: analysis.SourceRange,
|
||||
new_name: []const u8,
|
||||
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);
|
||||
|
||||
const line = try handle.document.getLine(@intCast(usize, position.line));
|
||||
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| {
|
||||
var tokenizer = std.zig.Tokenizer.init(position.line[range.start..range.end]);
|
||||
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |node| {
|
||||
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) {
|
||||
const pos_index = try handle.document.positionToIndex(req.params.position);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position);
|
||||
const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
|
||||
|
||||
const this_config = configFromUriOr(req.params.textDocument.uri, config);
|
||||
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),
|
||||
.field_access => |range| try completeFieldAccess(arena, id, handle, req.params.position, range, this_config),
|
||||
.var_access, .empty => try completeGlobal(arena, id, doc_position.absolute_index, handle, this_config),
|
||||
.field_access => |range| try completeFieldAccess(arena, id, handle, doc_position, range, this_config),
|
||||
.global_error_set => try send(arena, types.Response{
|
||||
.id = id,
|
||||
.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 {
|
||||
@ -1144,15 +1142,15 @@ fn gotoHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reques
|
||||
};
|
||||
|
||||
if (req.params.position.character >= 0) {
|
||||
const pos_index = try handle.document.positionToIndex(req.params.position);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position);
|
||||
const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
|
||||
|
||||
const this_config = configFromUriOr(req.params.textDocument.uri, config);
|
||||
switch (pos_context) {
|
||||
.var_access => try gotoDefinitionGlobal(arena, id, pos_index, handle, this_config, resolve_alias),
|
||||
.field_access => |range| try gotoDefinitionFieldAccess(arena, id, handle, req.params.position, range, this_config, resolve_alias),
|
||||
.string_literal => try gotoDefinitionString(arena, id, pos_index, handle, config),
|
||||
.label => try gotoDefinitionLabel(arena, id, pos_index, handle, this_config),
|
||||
.var_access => try gotoDefinitionGlobal(arena, id, doc_position.absolute_index, handle, 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, doc_position.absolute_index, handle, config),
|
||||
.label => try gotoDefinitionLabel(arena, id, doc_position.absolute_index, handle, this_config),
|
||||
else => try respondGeneric(id, null_result_response),
|
||||
}
|
||||
} else {
|
||||
@ -1175,14 +1173,14 @@ fn hoverHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reque
|
||||
};
|
||||
|
||||
if (req.params.position.character >= 0) {
|
||||
const pos_index = try handle.document.positionToIndex(req.params.position);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position);
|
||||
const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
|
||||
|
||||
const this_config = configFromUriOr(req.params.textDocument.uri, config);
|
||||
switch (pos_context) {
|
||||
.var_access => try hoverDefinitionGlobal(arena, id, pos_index, handle, this_config),
|
||||
.field_access => |range| try hoverDefinitionFieldAccess(arena, id, handle, req.params.position, range, this_config),
|
||||
.label => try hoverDefinitionLabel(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, doc_position, range, this_config),
|
||||
.label => try hoverDefinitionLabel(arena, id, doc_position.absolute_index, handle, this_config),
|
||||
else => try respondGeneric(id, null_result_response),
|
||||
}
|
||||
} else {
|
||||
@ -1248,14 +1246,14 @@ fn renameHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requ
|
||||
};
|
||||
|
||||
if (req.params.position.character >= 0) {
|
||||
const pos_index = try handle.document.positionToIndex(req.params.position);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, req.params.position);
|
||||
const doc_position = try offsets.documentPosition(handle.document, req.params.position, .utf16);
|
||||
const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
|
||||
|
||||
const this_config = configFromUriOr(req.params.textDocument.uri, config);
|
||||
switch (pos_context) {
|
||||
.var_access => try renameDefinitionGlobal(arena, id, handle, pos_index, req.params.newName),
|
||||
.field_access => |range| try renameDefinitionFieldAccess(arena, id, handle, req.params.position, range, req.params.newName, this_config),
|
||||
.label => try renameDefinitionLabel(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, doc_position, range, req.params.newName, this_config),
|
||||
.label => try renameDefinitionLabel(arena, id, handle, doc_position.absolute_index, req.params.newName),
|
||||
else => try respondGeneric(id, null_result_response),
|
||||
}
|
||||
} else {
|
||||
@ -1331,7 +1329,9 @@ fn processJsonRpc(arena: *std.heap.ArenaAllocator, parser: *std.json.Parser, jso
|
||||
}
|
||||
} else {
|
||||
done = error.HackDone;
|
||||
(method_info[2])(arena, id, config) catch |err| { done = err; };
|
||||
(method_info[2])(arena, id, config) catch |err| {
|
||||
done = err;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,35 +139,6 @@ pub const TextDocument = struct {
|
||||
// This holds the memory that we have actually allocated.
|
||||
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 {
|
||||
var line_idx: i64 = 0;
|
||||
var curr_line: []const u8 = self.text;
|
||||
|
Loading…
Reference in New Issue
Block a user