Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use std.Uri instead of our own code #1671

Merged
merged 1 commit into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions src/DocumentStore.zig
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ fn loadBuildAssociatedConfiguration(allocator: std.mem.Allocator, build_file: Bu

const build_file_path = try URI.parse(allocator, build_file.uri);
defer allocator.free(build_file_path);
const config_file_path = try std.fs.path.resolve(allocator, &.{ build_file_path, "../zls.build.json" });
const config_file_path = try std.fs.path.resolve(allocator, &.{ build_file_path, "..", "zls.build.json" });
defer allocator.free(config_file_path);

var config_file = try std.fs.cwd().openFile(config_file_path, .{});
Expand Down Expand Up @@ -1173,7 +1173,7 @@ fn createBuildFile(self: *DocumentStore, uri: Uri) error{OutOfMemory}!BuildFile

if (cfg.value.relative_builtin_path) |relative_builtin_path| blk: {
const build_file_path = URI.parse(self.allocator, build_file.uri) catch break :blk;
const absolute_builtin_path = std.fs.path.resolve(self.allocator, &.{ build_file_path, "../", relative_builtin_path }) catch break :blk;
const absolute_builtin_path = std.fs.path.resolve(self.allocator, &.{ build_file_path, "..", relative_builtin_path }) catch break :blk;
defer self.allocator.free(absolute_builtin_path);
build_file.builtin_uri = try URI.fromPath(self.allocator, absolute_builtin_path);
}
Expand Down Expand Up @@ -1583,12 +1583,9 @@ pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, hand
if (std.mem.eql(u8, import_str, "std")) {
const zig_lib_path = self.config.zig_lib_path orelse return null;

const std_path = std.fs.path.resolve(allocator, &[_][]const u8{ zig_lib_path, "./std/std.zig" }) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return null,
};

const std_path = try std.fs.path.join(allocator, &.{ zig_lib_path, "std", "std.zig" });
defer allocator.free(std_path);

return try URI.fromPath(allocator, std_path);
} else if (std.mem.eql(u8, import_str, "builtin")) {
if (try handle.getAssociatedBuildFileUri(self)) |build_file_uri| {
Expand Down Expand Up @@ -1625,15 +1622,18 @@ pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, hand
}
return null;
} else {
var separator_index = handle.uri.len;
while (separator_index > 0) : (separator_index -= 1) {
if (std.fs.path.isSep(handle.uri[separator_index - 1])) break;
}
const base = handle.uri[0 .. separator_index - 1];
const base_path = URI.parse(allocator, handle.uri) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return null,
};
defer allocator.free(base_path);

return URI.pathRelative(allocator, base, import_str) catch |err| switch (err) {
const joined_path = std.fs.path.resolve(allocator, &.{ base_path, "..", import_str }) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.UriBadScheme => return null,
else => return null,
};
defer allocator.free(joined_path);

return try URI.fromPath(allocator, joined_path);
}
}
9 changes: 3 additions & 6 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1765,12 +1765,9 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
}

if (std.mem.eql(u8, call_name, "@typeInfo")) {
const zig_lib_path = try URI.fromPath(analyser.arena.allocator(), analyser.store.config.zig_lib_path orelse return null);

const builtin_uri = URI.pathRelative(analyser.arena.allocator(), zig_lib_path, "/std/builtin.zig") catch |err| switch (err) {
error.OutOfMemory => |e| return e,
else => return null,
};
const zig_lib_path = analyser.store.config.zig_lib_path orelse return null;
const builtin_path = try std.fs.path.join(analyser.arena.allocator(), &.{ zig_lib_path, "std", "builtin.zig" });
const builtin_uri = try URI.fromPath(analyser.arena.allocator(), builtin_path);

const new_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null;
const new_handle_document_scope = try new_handle.getDocumentScope();
Expand Down
177 changes: 37 additions & 140 deletions src/uri.zig
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
const std = @import("std");
const builtin = @import("builtin");

// http://tools.ietf.org/html/rfc3986#section-2.2
const reserved_chars = &[_]u8{
'!', '#', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', ':',
';', '=', '?', '@', '[', ']',
};

const reserved_escapes = blk: {
var escapes: [reserved_chars.len][3]u8 = [_][3]u8{[_]u8{undefined} ** 3} ** reserved_chars.len;

for (reserved_chars, 0..) |c, i| {
escapes[i][0] = '%';
_ = std.fmt.bufPrint(escapes[i][1..], "{X}", .{c}) catch unreachable;
}
break :blk escapes;
};

/// Returns a URI from a path, caller owns the memory allocated with `allocator`
pub fn fromPath(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
if (path.len == 0) return "";
/// Returns a file URI from a path.
/// Caller owns the returned memory
pub fn fromPath(allocator: std.mem.Allocator, path: []const u8) error{OutOfMemory}![]u8 {
if (path.len == 0) return try allocator.dupe(u8, "/");
const prefix = if (builtin.os.tag == .windows) "file:///" else "file://";

var buf = try std.ArrayListUnmanaged(u8).initCapacity(allocator, prefix.len + path.len);
errdefer buf.deinit(allocator);

buf.appendSliceAssumeCapacity(prefix);

for (path) |char| {
if (char == std.fs.path.sep) {
try buf.append(allocator, '/');
} else if (std.mem.indexOfScalar(u8, reserved_chars, char)) |reserved| {
try buf.appendSlice(allocator, &reserved_escapes[reserved]);
const writer = buf.writer(allocator);

var start: usize = 0;
for (path, 0..) |char, index| {
switch (char) {
// zig fmt: off
'A'...'Z',
'a'...'z',
'0'...'9',
'-', '.', '_', '~', '!',
'$', '&', '\'','(', ')',
'+', ',', ';', '=', '@',
// zig fmt: on
=> continue,
':', '*' => if (builtin.os.tag != .windows) continue,
else => {},
}

try writer.writeAll(path[start..index]);
if (std.fs.path.isSep(char)) {
try writer.writeByte('/');
} else {
try buf.append(allocator, char);
try writer.print("%{X:0>2}", .{char});
}
start = index + 1;
}
try writer.writeAll(path[start..]);

// On windows, we need to lowercase the drive name.
if (builtin.os.tag == .windows) {
Expand All @@ -53,7 +55,7 @@ pub fn fromPath(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {

test fromPath {
if (builtin.os.tag == .windows) {
const fromPathWin = try fromPath(std.testing.allocator, "c:\\main.zig");
const fromPathWin = try fromPath(std.testing.allocator, "C:\\main.zig");
defer std.testing.allocator.free(fromPathWin);
try std.testing.expectEqualStrings("file:///c%3A/main.zig", fromPathWin);
}
Expand All @@ -65,118 +67,13 @@ test fromPath {
}
}

/// Move along `rel` from `base` with a single allocation.
/// `base` is a URI of a folder, `rel` is a raw relative path.
pub fn pathRelative(allocator: std.mem.Allocator, base: []const u8, rel: []const u8) error{ OutOfMemory, UriBadScheme }![]const u8 {
const max_size = base.len + rel.len * 3 + 1;

var result = try std.ArrayListUnmanaged(u8).initCapacity(allocator, max_size);
errdefer result.deinit(allocator);

result.appendSliceAssumeCapacity(base);

var it = std.mem.tokenizeScalar(u8, rel, '/');
while (it.next()) |component| {
if (std.mem.eql(u8, component, ".")) {
continue;
} else if (std.mem.eql(u8, component, "..")) {
while ((result.getLastOrNull() orelse return error.UriBadScheme) == '/') {
_ = result.pop();
}
while (true) {
const char = result.popOrNull() orelse return error.UriBadScheme;
if (char == '/') break;
}
} else {
result.appendAssumeCapacity('/');
for (component) |char| {
if (std.mem.indexOfScalar(u8, reserved_chars, char)) |reserved| {
const escape = &reserved_escapes[reserved];
result.appendSliceAssumeCapacity(escape);
} else {
result.appendAssumeCapacity(char);
}
}
}
}

return result.toOwnedSlice(allocator);
}

test pathRelative {
const join1 = try pathRelative(std.testing.allocator, "file:///project/zig", "/src/main+.zig");
defer std.testing.allocator.free(join1);
try std.testing.expectEqualStrings("file:///project/zig/src/main%2B.zig", join1);

const join2 = try pathRelative(std.testing.allocator, "file:///project/zig/wow", "../]src]/]main.zig");
defer std.testing.allocator.free(join2);
try std.testing.expectEqualStrings("file:///project/zig/%5Dsrc%5D/%5Dmain.zig", join2);

const join3 = try pathRelative(std.testing.allocator, "file:///project/zig/wow//", "../src/main.zig");
defer std.testing.allocator.free(join3);
try std.testing.expectEqualStrings("file:///project/zig/src/main.zig", join3);
}

// Original code: https://github.com/andersfr/zig-lsp/blob/master/uri.zig
fn parseHex(c: u8) !u8 {
return switch (c) {
'0'...'9' => c - '0',
'a'...'f' => c - 'a' + 10,
'A'...'F' => c - 'A' + 10,
else => return error.UriBadHexChar,
};
}

/// Caller should free memory
pub fn parse(allocator: std.mem.Allocator, str: []const u8) ![]u8 {
if (str.len < 7 or !std.mem.eql(u8, "file://", str[0..7])) return error.UriBadScheme;

var uri = try allocator.alloc(u8, str.len - (if (std.fs.path.sep == '\\') 8 else 7));
errdefer allocator.free(uri);

const path = if (std.fs.path.sep == '\\') str[8..] else str[7..];

var i: usize = 0;
var j: usize = 0;
while (j < path.len) : (i += 1) {
if (path[j] == '%') {
if (j + 2 >= path.len) return error.UriBadEscape;
const upper = try parseHex(path[j + 1]);
const lower = try parseHex(path[j + 2]);
uri[i] = (upper << 4) + lower;
j += 3;
} else {
uri[i] = if (path[j] == '/') std.fs.path.sep else path[j];
j += 1;
}
}

// Remove trailing separator
if (i > 0 and uri[i - 1] == std.fs.path.sep) {
i -= 1;
}

return allocator.realloc(uri, i);
}

test parse {
if (builtin.os.tag == .windows) {
const parseWin = try parse(std.testing.allocator, "file:///c%3A/main.zig");
defer std.testing.allocator.free(parseWin);
try std.testing.expectEqualStrings("c:\\main.zig", parseWin);

const parseWin2 = try parse(std.testing.allocator, "file:///c%3A/main%2B.zig");
defer std.testing.allocator.free(parseWin2);
try std.testing.expectEqualStrings("c:\\main+.zig", parseWin2);
}

if (builtin.os.tag != .windows) {
const parseUnix = try parse(std.testing.allocator, "file:///home/main.zig");
defer std.testing.allocator.free(parseUnix);
try std.testing.expectEqualStrings("/home/main.zig", parseUnix);

const parseUnix2 = try parse(std.testing.allocator, "file:///home/main%2B.zig");
defer std.testing.allocator.free(parseUnix2);
try std.testing.expectEqualStrings("/home/main+.zig", parseUnix2);
/// Parses a Uri and returns the unescaped path
/// Caller owns the returned memory
pub fn parse(allocator: std.mem.Allocator, str: []const u8) (std.Uri.ParseError || error{OutOfMemory})![]u8 {
var uri = try std.Uri.parse(str);
if (!std.mem.eql(u8, uri.scheme, "file")) return error.InvalidFormat;
if (builtin.os.tag == .windows and uri.path.percent_encoded[0] == '/') {
uri.path.percent_encoded = uri.path.percent_encoded[1..];
}
return try std.fmt.allocPrint(allocator, "{raw}", .{uri.path});
}
3 changes: 2 additions & 1 deletion tests/language_features/cimport.zig
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ fn testTranslate(c_source: []const u8) !translate_c.Result {
var ctx = try Context.init();
defer ctx.deinit();

const result = (try translate_c.translate(allocator, zls.DocumentStore.Config.fromMainConfig(ctx.server.config), &.{}, c_source)).?;
var result = (try translate_c.translate(allocator, zls.DocumentStore.Config.fromMainConfig(ctx.server.config), &.{}, c_source)).?;
errdefer result.deinit(allocator);

switch (result) {
.success => |uri| {
Expand Down