simplify completionHandler, gotoHandler, hoverHandler and their callees

This commit is contained in:
Techatrix 2022-09-22 20:09:16 +02:00
parent 87bfa683bd
commit 869d27c75d

View File

@ -647,11 +647,9 @@ fn isSymbolChar(char: u8) bool {
fn gotoDefinitionSymbol( fn gotoDefinitionSymbol(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
decl_handle: analysis.DeclWithHandle, decl_handle: analysis.DeclWithHandle,
resolve_alias: bool, resolve_alias: bool,
) !void { ) error{OutOfMemory}!?types.Location {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -667,28 +665,18 @@ fn gotoDefinitionSymbol(
} }
} }
break :block analysis.getDeclNameToken(handle.tree, node) orelse return try respondGeneric(writer, id, null_result_response); break :block analysis.getDeclNameToken(handle.tree, node) orelse return null;
}, },
else => decl_handle.nameToken(), else => decl_handle.nameToken(),
}; };
try send(writer, server.arena.allocator(), types.Response{ return types.Location{
.id = id,
.result = .{
.Location = .{
.uri = handle.document.uri, .uri = handle.document.uri,
.range = offsets.tokenToRange(handle.tree, name_token, server.offset_encoding), .range = offsets.tokenToRange(handle.tree, name_token, server.offset_encoding),
}, };
},
});
} }
fn hoverSymbol( fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutOfMemory}!?types.Hover {
server: *Server,
writer: anytype,
id: types.RequestId,
decl_handle: analysis.DeclWithHandle,
) (std.os.WriteError || error{OutOfMemory})!void {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -701,7 +689,7 @@ fn hoverSymbol(
const def_str = switch (decl_handle.decl.*) { const def_str = switch (decl_handle.decl.*) {
.ast_node => |node| def: { .ast_node => |node| def: {
if (try analysis.resolveVarDeclAlias(&server.document_store, &server.arena, .{ .node = node, .handle = handle })) |result| { if (try analysis.resolveVarDeclAlias(&server.document_store, &server.arena, .{ .node = node, .handle = handle })) |result| {
return try server.hoverSymbol(writer, id, result); return try server.hoverSymbol(result);
} }
doc_str = try analysis.getDocComments(server.arena.allocator(), tree, node, hover_kind); doc_str = try analysis.getDocComments(server.arena.allocator(), tree, node, hover_kind);
@ -714,8 +702,7 @@ fn hoverSymbol(
} else if (ast.containerField(tree, node)) |field| { } else if (ast.containerField(tree, node)) |field| {
break :def analysis.getContainerFieldSignature(tree, field); break :def analysis.getContainerFieldSignature(tree, field);
} else { } else {
break :def analysis.nodeToString(tree, node) orelse break :def analysis.nodeToString(tree, node) orelse return null;
return try respondGeneric(writer, id, null_result_response);
} }
}, },
.param_decl => |param| def: { .param_decl => |param| def: {
@ -799,17 +786,12 @@ fn hoverSymbol(
try std.fmt.allocPrint(server.arena.allocator(), "{s} ({s})", .{ def_str, resolved_type_str }); try std.fmt.allocPrint(server.arena.allocator(), "{s} ({s})", .{ def_str, resolved_type_str });
} }
try send(writer, server.arena.allocator(), types.Response{ return types.Hover{
.id = id,
.result = .{
.Hover = .{
.contents = .{ .value = hover_text }, .contents = .{ .value = hover_text },
}, };
},
});
} }
fn getLabelGlobal(pos_index: usize, handle: *DocumentStore.Handle) !?analysis.DeclWithHandle { fn getLabelGlobal(pos_index: usize, handle: *DocumentStore.Handle) error{OutOfMemory}!?analysis.DeclWithHandle {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -823,7 +805,7 @@ fn getSymbolGlobal(
server: *Server, server: *Server,
pos_index: usize, pos_index: usize,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
) !?analysis.DeclWithHandle { ) error{OutOfMemory}!?analysis.DeclWithHandle {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -835,54 +817,47 @@ fn getSymbolGlobal(
fn gotoDefinitionLabel( fn gotoDefinitionLabel(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
pos_index: usize, pos_index: usize,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
) !void { ) error{OutOfMemory}!?types.Location {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response); const decl = (try getLabelGlobal(pos_index, handle)) orelse return null;
return try server.gotoDefinitionSymbol(writer, id, decl, false); return try server.gotoDefinitionSymbol(decl, false);
} }
fn gotoDefinitionGlobal( fn gotoDefinitionGlobal(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
pos_index: usize, pos_index: usize,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
resolve_alias: bool, resolve_alias: bool,
) !void { ) error{OutOfMemory}!?types.Location {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response); const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return null;
return try server.gotoDefinitionSymbol(writer, id, decl, resolve_alias); return try server.gotoDefinitionSymbol(decl, resolve_alias);
} }
fn hoverDefinitionLabel(server: *Server, writer: anytype, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle) !void { fn hoverDefinitionLabel(server: *Server, pos_index: usize, handle: *DocumentStore.Handle) error{OutOfMemory}!?types.Hover {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response); const decl = (try getLabelGlobal(pos_index, handle)) orelse return null;
return try server.hoverSymbol(writer, id, decl); return try server.hoverSymbol(decl);
} }
fn hoverDefinitionBuiltin(server: *Server, writer: anytype, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle) !void { fn hoverDefinitionBuiltin(server: *Server, pos_index: usize, handle: *DocumentStore.Handle) error{OutOfMemory}!?types.Hover {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const name = identifierFromPosition(pos_index, handle.*); const name = identifierFromPosition(pos_index, handle.*);
if (name.len == 0) return try respondGeneric(writer, id, null_result_response); if (name.len == 0) return null;
for (data.builtins) |builtin| { for (data.builtins) |builtin| {
if (std.mem.eql(u8, builtin.name[1..], name)) { if (std.mem.eql(u8, builtin.name[1..], name)) {
return try send(writer, server.arena.allocator(), types.Response{ return types.Hover{
.id = id,
.result = .{
.Hover = .{
.contents = .{ .contents = .{
.value = try std.fmt.allocPrint( .value = try std.fmt.allocPrint(
server.arena.allocator(), server.arena.allocator(),
@ -890,21 +865,19 @@ fn hoverDefinitionBuiltin(server: *Server, writer: anytype, id: types.RequestId,
.{ builtin.signature, builtin.documentation }, .{ builtin.signature, builtin.documentation },
), ),
}, },
}, };
},
});
} }
} }
return try respondGeneric(writer, id, null_result_response); return null;
} }
fn hoverDefinitionGlobal(server: *Server, writer: anytype, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle) !void { fn hoverDefinitionGlobal(server: *Server, pos_index: usize, handle: *DocumentStore.Handle) error{OutOfMemory}!?types.Hover {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response); const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return null;
return try server.hoverSymbol(writer, id, decl); return try server.hoverSymbol(decl);
} }
fn getSymbolFieldAccess( fn getSymbolFieldAccess(
@ -943,78 +916,52 @@ fn getSymbolFieldAccess(
fn gotoDefinitionFieldAccess( fn gotoDefinitionFieldAccess(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
source_index: usize, source_index: usize,
loc: offsets.Loc, loc: offsets.Loc,
resolve_alias: bool, resolve_alias: bool,
) !void { ) error{OutOfMemory}!?types.Location {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return try respondGeneric(writer, id, null_result_response); const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return null;
return try server.gotoDefinitionSymbol(writer, id, decl, resolve_alias); return try server.gotoDefinitionSymbol(decl, resolve_alias);
} }
fn hoverDefinitionFieldAccess( fn hoverDefinitionFieldAccess(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
source_index: usize, source_index: usize,
loc: offsets.Loc, loc: offsets.Loc,
) !void { ) error{OutOfMemory}!?types.Hover {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return try respondGeneric(writer, id, null_result_response); const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return null;
return try server.hoverSymbol(writer, id, decl); return try server.hoverSymbol(decl);
} }
fn gotoDefinitionString( fn gotoDefinitionString(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
pos_index: usize, pos_index: usize,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
) !void { ) error{OutOfMemory}!?types.Location {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const tree = handle.tree; const import_str = analysis.getImportStr(handle.tree, 0, pos_index) orelse return null;
const uri = server.document_store.uriFromImportStr(server.arena.allocator(), handle.*, import_str) catch |err| switch (err) {
error.UriBadScheme => return null,
error.OutOfMemory => |e| return e,
};
const import_str = analysis.getImportStr(tree, 0, pos_index) orelse return try respondGeneric(writer, id, null_result_response); return types.Location{
const uri = (try server.document_store.uriFromImportStr( .uri = uri orelse return null,
server.arena.allocator(),
handle.*,
import_str,
)) orelse return try respondGeneric(writer, id, null_result_response);
try send(writer, server.arena.allocator(), types.Response{
.id = id,
.result = .{
.Location = .{
.uri = uri,
.range = .{ .range = .{
.start = .{ .line = 0, .character = 0 }, .start = .{ .line = 0, .character = 0 },
.end = .{ .line = 0, .character = 0 }, .end = .{ .line = 0, .character = 0 },
}, },
}, };
},
});
}
fn hasComment(tree: Ast.Tree, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
const token_starts = tree.tokens.items(.start);
const start = token_starts[start_token];
const end = token_starts[end_token];
return std.mem.indexOf(u8, tree.source[start..end], "//") != null;
} }
const DeclToCompletionContext = struct { const DeclToCompletionContext = struct {
@ -1110,11 +1057,9 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl
fn completeLabel( fn completeLabel(
server: *Server, server: *Server,
writer: anytype,
id: types.RequestId,
pos_index: usize, pos_index: usize,
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
) !void { ) ![]types.CompletionItem {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -1126,30 +1071,28 @@ fn completeLabel(
.orig_handle = handle, .orig_handle = handle,
}; };
try analysis.iterateLabels(handle, pos_index, declToCompletion, context); try analysis.iterateLabels(handle, pos_index, declToCompletion, context);
sortCompletionItems(completions.items, server.arena.allocator());
truncateCompletions(completions.items, server.config.max_detail_length);
try send(writer, server.arena.allocator(), types.Response{ return completions.toOwnedSlice(server.arena.allocator());
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = completions.items,
},
},
});
} }
fn populateBuiltinCompletions(builtin_completions: *std.ArrayListUnmanaged(types.CompletionItem), config: Config) !void { fn completeBuiltin(server: *Server) ![]types.CompletionItem {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
if (server.builtin_completions) |completions| return completions.items;
var completions = try std.ArrayListUnmanaged(types.CompletionItem).initCapacity(server.allocator, data.builtins.len);
errdefer completions.deinit();
for (data.builtins) |builtin| { for (data.builtins) |builtin| {
const insert_text = if (config.enable_snippets) builtin.snippet else builtin.name; const insert_text = if (server.config.enable_snippets) builtin.snippet else builtin.name;
builtin_completions.appendAssumeCapacity(.{ completions.appendAssumeCapacity(.{
.label = builtin.name, .label = builtin.name,
.kind = .Function, .kind = .Function,
.filterText = builtin.name[1..], .filterText = builtin.name[1..],
.detail = builtin.signature, .detail = builtin.signature,
.insertText = if (config.include_at_in_builtins) insert_text else insert_text[1..], .insertText = if (server.config.include_at_in_builtins) insert_text else insert_text[1..],
.insertTextFormat = if (config.enable_snippets) .Snippet else .PlainText, .insertTextFormat = if (server.config.enable_snippets) .Snippet else .PlainText,
.documentation = .{ .documentation = .{
.kind = .Markdown, .kind = .Markdown,
.value = builtin.documentation, .value = builtin.documentation,
@ -1157,30 +1100,11 @@ fn populateBuiltinCompletions(builtin_completions: *std.ArrayListUnmanaged(types
}); });
} }
truncateCompletions(builtin_completions.items, config.max_detail_length); server.builtin_completions = completions;
return completions.items;
} }
fn completeBuiltin(server: *Server, writer: anytype, id: types.RequestId) !void { fn completeGlobal(server: *Server, pos_index: usize, handle: *DocumentStore.Handle) ![]types.CompletionItem {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
if (server.builtin_completions == null) {
server.builtin_completions = try std.ArrayListUnmanaged(types.CompletionItem).initCapacity(server.allocator, data.builtins.len);
try populateBuiltinCompletions(&server.builtin_completions.?, server.config.*);
}
try send(writer, server.arena.allocator(), types.Response{
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = server.builtin_completions.?.items,
},
},
});
}
fn completeGlobal(server: *Server, writer: anytype, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle) !void {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -1192,8 +1116,6 @@ fn completeGlobal(server: *Server, writer: anytype, id: types.RequestId, pos_ind
.orig_handle = handle, .orig_handle = handle,
}; };
try analysis.iterateSymbolsGlobal(&server.document_store, &server.arena, handle, pos_index, declToCompletion, context); try analysis.iterateSymbolsGlobal(&server.document_store, &server.arena, handle, pos_index, declToCompletion, context);
sortCompletionItems(completions.items, server.arena.allocator());
truncateCompletions(completions.items, server.config.max_detail_length);
if (server.client_capabilities.label_details_support) { if (server.client_capabilities.label_details_support) {
for (completions.items) |*item| { for (completions.items) |*item| {
@ -1201,18 +1123,10 @@ fn completeGlobal(server: *Server, writer: anytype, id: types.RequestId, pos_ind
} }
} }
try send(writer, server.arena.allocator(), types.Response{ return completions.toOwnedSlice(server.arena.allocator());
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = completions.items,
},
},
});
} }
fn completeFieldAccess(server: *Server, writer: anytype, id: types.RequestId, handle: *DocumentStore.Handle, source_index: usize, loc: offsets.Loc) !void { fn completeFieldAccess(server: *Server, handle: *DocumentStore.Handle, source_index: usize, loc: offsets.Loc) !?[]types.CompletionItem {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -1222,26 +1136,15 @@ fn completeFieldAccess(server: *Server, writer: anytype, id: types.RequestId, ha
defer held_range.release(); defer held_range.release();
var tokenizer = std.zig.Tokenizer.init(held_range.data()); var tokenizer = std.zig.Tokenizer.init(held_range.data());
if (try analysis.getFieldAccessType(&server.document_store, &server.arena, handle, source_index, &tokenizer)) |result| { const result = (try analysis.getFieldAccessType(&server.document_store, &server.arena, handle, source_index, &tokenizer)) orelse return null;
try server.typeToCompletion(&completions, result, handle); try server.typeToCompletion(&completions, result, handle);
sortCompletionItems(completions.items, server.arena.allocator());
truncateCompletions(completions.items, server.config.max_detail_length);
if (server.client_capabilities.label_details_support) { if (server.client_capabilities.label_details_support) {
for (completions.items) |*item| { for (completions.items) |*item| {
try formatDetailledLabel(item, server.arena.allocator()); try formatDetailledLabel(item, server.arena.allocator());
} }
} }
}
try send(writer, server.arena.allocator(), types.Response{ return completions.toOwnedSlice(server.arena.allocator());
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = completions.items,
},
},
});
} }
fn formatDetailledLabel(item: *types.CompletionItem, alloc: std.mem.Allocator) !void { fn formatDetailledLabel(item: *types.CompletionItem, alloc: std.mem.Allocator) !void {
@ -1409,28 +1312,19 @@ fn formatDetailledLabel(item: *types.CompletionItem, alloc: std.mem.Allocator) !
// logger.info("labelDetails: {s} :: {s}", .{item.labelDetails.?.detail, item.labelDetails.?.description}); // logger.info("labelDetails: {s} :: {s}", .{item.labelDetails.?.detail, item.labelDetails.?.description});
} }
fn completeError(server: *Server, writer: anytype, id: types.RequestId, handle: *DocumentStore.Handle) !void { fn completeError(server: *Server, handle: *DocumentStore.Handle) ![]types.CompletionItem {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
var completions = try server.document_store.errorCompletionItems(&server.arena, handle); return try server.document_store.errorCompletionItems(&server.arena, handle);
truncateCompletions(completions, server.config.max_detail_length);
log.debug("Completing error:", .{});
try send(writer, server.arena.allocator(), types.Response{
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = completions,
},
},
});
} }
fn kindToSortScore(kind: types.CompletionItem.Kind) []const u8 { fn kindToSortScore(kind: types.CompletionItem.Kind) ?[]const u8 {
return switch (kind) { return switch (kind) {
.Module => "1_", // use for packages
.Folder => "2_",
.File => "3_",
.Constant => "1_", .Constant => "1_",
.Variable => "2_", .Variable => "2_",
@ -1446,40 +1340,84 @@ fn kindToSortScore(kind: types.CompletionItem.Kind) []const u8 {
.TypeParameter, .TypeParameter,
=> "6_", => "6_",
else => "9_", else => {
std.log.debug(@typeName(types.CompletionItem.Kind) ++ "{s} has no sort score specified!", .{@tagName(kind)});
return null;
},
}; };
} }
fn sortCompletionItems(completions: []types.CompletionItem, alloc: std.mem.Allocator) void { fn sortCompletionItems(completions: []types.CompletionItem, allocator: std.mem.Allocator) error{OutOfMemory}!void {
// TODO: config for sorting rule? // TODO: config for sorting rule?
for (completions) |*c| { for (completions) |*c| {
c.sortText = kindToSortScore(c.kind); const prefix = kindToSortScore(c.kind) orelse continue;
if (alloc.alloc(u8, 2 + c.label.len)) |it| { c.sortText = try std.fmt.allocPrint(allocator, "{s}{s}", .{ prefix, c.label });
std.mem.copy(u8, it, c.sortText.?);
std.mem.copy(u8, it[2..], c.label);
c.sortText = it;
} else |_| {}
} }
} }
fn completeDot(server: *Server, writer: anytype, id: types.RequestId, handle: *DocumentStore.Handle) !void { fn completeDot(server: *Server, handle: *DocumentStore.Handle) ![]types.CompletionItem {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
var completions = try server.document_store.enumCompletionItems(&server.arena, handle); var completions = try server.document_store.enumCompletionItems(&server.arena, handle);
sortCompletionItems(completions, server.arena.allocator());
truncateCompletions(completions, server.config.max_detail_length);
try send(writer, server.arena.allocator(), types.Response{ return completions;
.id = id, }
.result = .{
.CompletionList = .{ fn completeFileSystemStringLiteral(allocator: std.mem.Allocator, handle: *DocumentStore.Handle, completing: []const u8, is_import: bool) ![]types.CompletionItem {
.isIncomplete = false, var subpath_present = false;
.items = completions, var completions = std.ArrayListUnmanaged(types.CompletionItem){};
},
}, fsc: {
var document_path = try uri_utils.parse(allocator, handle.uri());
var document_dir_path = std.fs.openIterableDirAbsolute(std.fs.path.dirname(document_path) orelse break :fsc, .{}) catch break :fsc;
defer document_dir_path.close();
if (std.mem.lastIndexOfScalar(u8, completing, '/')) |subpath_index| {
var subpath = completing[0..subpath_index];
if (std.mem.startsWith(u8, subpath, "./") and subpath_index > 2) {
subpath = completing[2..subpath_index];
} else if (std.mem.startsWith(u8, subpath, ".") and subpath_index > 1) {
subpath = completing[1..subpath_index];
}
var old = document_dir_path;
document_dir_path = document_dir_path.dir.openIterableDir(subpath, .{}) catch break :fsc // NOTE: Is this even safe lol?
old.close();
subpath_present = true;
}
var dir_iterator = document_dir_path.iterate();
while (try dir_iterator.next()) |entry| {
if (std.mem.startsWith(u8, entry.name, ".")) continue;
if (entry.kind == .File and is_import and !std.mem.endsWith(u8, entry.name, ".zig")) continue;
const l = try allocator.dupe(u8, entry.name);
try completions.append(allocator, types.CompletionItem{
.label = l,
.insertText = l,
.kind = if (entry.kind == .File) .File else .Folder,
}); });
}
}
if (!subpath_present and is_import) {
if (handle.associated_build_file) |bf| {
try completions.ensureUnusedCapacity(allocator, bf.config.packages.len);
for (bf.config.packages) |pkg| {
completions.appendAssumeCapacity(.{
.label = pkg.name,
.kind = .Module,
});
}
}
}
return completions.toOwnedSlice(allocator);
} }
fn documentSymbol(server: *Server, writer: anytype, id: types.RequestId, handle: *DocumentStore.Handle) !void { fn documentSymbol(server: *Server, writer: anytype, id: types.RequestId, handle: *DocumentStore.Handle) !void {
@ -1496,23 +1434,23 @@ fn initializeHandler(server: *Server, writer: anytype, id: types.RequestId, req:
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
if(req.params.capabilities.general) |general| { if (req.params.capabilities.general) |general| {
var supports_utf8 = false; var supports_utf8 = false;
var supports_utf16 = false; var supports_utf16 = false;
var supports_utf32 = false; var supports_utf32 = false;
for(general.positionEncodings.value) |encoding| { for (general.positionEncodings.value) |encoding| {
if (std.mem.eql(u8, encoding, "utf-8")) { if (std.mem.eql(u8, encoding, "utf-8")) {
supports_utf8 = true; supports_utf8 = true;
} else if(std.mem.eql(u8, encoding, "utf-16")) { } else if (std.mem.eql(u8, encoding, "utf-16")) {
supports_utf16 = true; supports_utf16 = true;
} else if(std.mem.eql(u8, encoding, "utf-32")) { } else if (std.mem.eql(u8, encoding, "utf-32")) {
supports_utf32 = true; supports_utf32 = true;
} }
} }
if(supports_utf8) { if (supports_utf8) {
server.offset_encoding = .utf8; server.offset_encoding = .utf8;
} else if(supports_utf32) { } else if (supports_utf32) {
server.offset_encoding = .utf32; server.offset_encoding = .utf32;
} else { } else {
server.offset_encoding = .utf16; server.offset_encoding = .utf16;
@ -1776,84 +1714,36 @@ fn completionHandler(server: *Server, writer: anytype, id: types.RequestId, req:
const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding); const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding);
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index); const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
switch (pos_context) { const maybe_completions = switch (pos_context) {
.builtin => try server.completeBuiltin(writer, id), .builtin => try server.completeBuiltin(),
.var_access, .empty => try server.completeGlobal(writer, id, source_index, handle), .var_access, .empty => try server.completeGlobal(source_index, handle),
.field_access => |loc| try server.completeFieldAccess(writer, id, handle, source_index, loc), .field_access => |loc| try server.completeFieldAccess(handle, source_index, loc),
.global_error_set => try server.completeError(writer, id, handle), .global_error_set => try server.completeError(handle),
.enum_literal => try server.completeDot(writer, id, handle), .enum_literal => try server.completeDot(handle),
.label => try server.completeLabel(writer, id, source_index, handle), .label => try server.completeLabel(source_index, handle),
.import_string_literal, .embedfile_string_literal => |loc| { .import_string_literal, .embedfile_string_literal => |loc| blk: {
if (!server.config.enable_import_embedfile_argument_completions) if (!server.config.enable_import_embedfile_argument_completions) break :blk null;
return try respondGeneric(writer, id, no_completions_response);
const completing = offsets.locToSlice(handle.tree.source, loc); const completing = offsets.locToSlice(handle.tree.source, loc);
const is_import = pos_context == .import_string_literal;
break :blk try completeFileSystemStringLiteral(server.arena.allocator(), handle, completing, is_import);
},
else => null,
};
var subpath_present = false; const completions = maybe_completions orelse return try respondGeneric(writer, id, no_completions_response);
var fsl_completions = std.ArrayListUnmanaged(types.CompletionItem){}; truncateCompletions(completions, server.config.max_detail_length);
try sortCompletionItems(completions, server.arena.allocator());
fsc: {
var document_path = try uri_utils.parse(server.arena.allocator(), handle.uri());
var document_dir_path = std.fs.openIterableDirAbsolute(std.fs.path.dirname(document_path) orelse break :fsc, .{}) catch break :fsc;
defer document_dir_path.close();
if (std.mem.lastIndexOfScalar(u8, completing, '/')) |subpath_index| {
var subpath = completing[0..subpath_index];
if (std.mem.startsWith(u8, subpath, "./") and subpath_index > 2) {
subpath = completing[2..subpath_index];
} else if (std.mem.startsWith(u8, subpath, ".") and subpath_index > 1) {
subpath = completing[1..subpath_index];
}
var old = document_dir_path;
document_dir_path = document_dir_path.dir.openIterableDir(subpath, .{}) catch break :fsc // NOTE: Is this even safe lol?
old.close();
subpath_present = true;
}
var dir_iterator = document_dir_path.iterate();
while (try dir_iterator.next()) |entry| {
if (std.mem.startsWith(u8, entry.name, ".")) continue;
if (entry.kind == .File and pos_context == .import_string_literal and !std.mem.endsWith(u8, entry.name, ".zig")) continue;
const l = try server.arena.allocator().dupe(u8, entry.name);
try fsl_completions.append(server.arena.allocator(), types.CompletionItem{
.label = l,
.insertText = l,
.kind = if (entry.kind == .File) .File else .Folder,
});
}
}
if (!subpath_present and pos_context == .import_string_literal) {
if (handle.associated_build_file) |bf| {
try fsl_completions.ensureUnusedCapacity(server.arena.allocator(), bf.config.packages.len);
for (bf.config.packages) |pkg| {
try fsl_completions.append(server.arena.allocator(), .{
.label = pkg.name,
.kind = .Module,
});
}
}
}
truncateCompletions(fsl_completions.items, server.config.max_detail_length);
try send(writer, server.arena.allocator(), types.Response{ try send(writer, server.arena.allocator(), types.Response{
.id = id, .id = id,
.result = .{ .result = .{
.CompletionList = .{ .CompletionList = .{
.isIncomplete = false, .isIncomplete = false,
.items = fsl_completions.items, .items = completions,
}, },
}, },
}); });
},
else => try respondGeneric(writer, id, no_completions_response),
}
} }
fn signatureHelpHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.SignatureHelp) !void { fn signatureHelpHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.SignatureHelp) !void {
@ -1900,20 +1790,25 @@ fn gotoHandler(server: *Server, writer: anytype, id: types.RequestId, req: reque
return try respondGeneric(writer, id, null_result_response); return try respondGeneric(writer, id, null_result_response);
}; };
if (req.params.position.character >= 0) { if (req.params.position.character == 0) return try respondGeneric(writer, id, null_result_response);
const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding); const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding);
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index); const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
switch (pos_context) { const maybe_location = switch (pos_context) {
.var_access => try server.gotoDefinitionGlobal(writer, id, source_index, handle, resolve_alias), .var_access => try server.gotoDefinitionGlobal(source_index, handle, resolve_alias),
.field_access => |loc| try server.gotoDefinitionFieldAccess(writer, id, handle, source_index, loc, resolve_alias), .field_access => |loc| try server.gotoDefinitionFieldAccess(handle, source_index, loc, resolve_alias),
.import_string_literal => try server.gotoDefinitionString(writer, id, source_index, handle), .import_string_literal => try server.gotoDefinitionString(source_index, handle),
.label => try server.gotoDefinitionLabel(writer, id, source_index, handle), .label => try server.gotoDefinitionLabel(source_index, handle),
else => try respondGeneric(writer, id, null_result_response), else => null,
} };
} else {
try respondGeneric(writer, id, null_result_response); const location = maybe_location orelse return try respondGeneric(writer, id, null_result_response);
}
try send(writer, server.arena.allocator(), types.Response{
.id = id,
.result = .{ .Location = location },
});
} }
fn gotoDefinitionHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.GotoDefinition) !void { fn gotoDefinitionHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.GotoDefinition) !void {
@ -1939,19 +1834,25 @@ fn hoverHandler(server: *Server, writer: anytype, id: types.RequestId, req: requ
return try respondGeneric(writer, id, null_result_response); return try respondGeneric(writer, id, null_result_response);
}; };
if (req.params.position.character >= 0) { if (req.params.position.character == 0) return try respondGeneric(writer, id, null_result_response);
const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding); const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding);
const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index); const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
switch (pos_context) {
.builtin => try server.hoverDefinitionBuiltin(writer, id, source_index, handle), const maybe_hover = switch (pos_context) {
.var_access => try server.hoverDefinitionGlobal(writer, id, source_index, handle), .builtin => try server.hoverDefinitionBuiltin(source_index, handle),
.field_access => |loc| try server.hoverDefinitionFieldAccess(writer, id, handle, source_index, loc), .var_access => try server.hoverDefinitionGlobal(source_index, handle),
.label => try server.hoverDefinitionLabel(writer, id, source_index, handle), .field_access => |loc| try server.hoverDefinitionFieldAccess(handle, source_index, loc),
else => try respondGeneric(writer, id, null_result_response), .label => try server.hoverDefinitionLabel(source_index, handle),
} else => null,
} else { };
try respondGeneric(writer, id, null_result_response);
} const hover = maybe_hover orelse return try respondGeneric(writer, id, null_result_response);
try send(writer, server.arena.allocator(), types.Response{
.id = id,
.result = .{ .Hover = hover },
});
} }
fn documentSymbolsHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.DocumentSymbols) !void { fn documentSymbolsHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.DocumentSymbols) !void {