Skip to content

Commit

Permalink
use std.Uri instead of our own code
Browse files Browse the repository at this point in the history
`std.Uri` provides everything we need to deal with URIs we
don't to write everything our self.
A follow-up can then remove the entire uri.zig file.
  • Loading branch information
Techatrix committed Dec 12, 2023
1 parent d1ad449 commit c5b3b1f
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 196 deletions.
17 changes: 10 additions & 7 deletions src/DocumentStore.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1435,16 +1435,19 @@ 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,

Check warning on line 1439 in src/DocumentStore.zig

View check run for this annotation

Codecov / codecov/patch

src/DocumentStore.zig#L1439

Added line #L1439 was not covered by tests
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);
}
}

Expand Down
9 changes: 3 additions & 6 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1443,12 +1443,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);

Check warning on line 1448 in src/analysis.zig

View check run for this annotation

Codecov / codecov/patch

src/analysis.zig#L1446-L1448

Added lines #L1446 - L1448 were not covered by tests

const new_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null;
const new_handle_document_scope = try new_handle.getDocumentScope();
Expand Down
141 changes: 18 additions & 123 deletions src/uri.zig
Original file line number Diff line number Diff line change
@@ -1,132 +1,27 @@
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 "";
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]);
} else {
try buf.append(allocator, char);
}
}

// On windows, we need to lowercase the drive name.
if (builtin.os.tag == .windows) {
if (buf.items.len > prefix.len + 1 and
std.ascii.isAlphanumeric(buf.items[prefix.len]) and
std.mem.startsWith(u8, buf.items[prefix.len + 1 ..], "%3A"))
{
buf.items[prefix.len] = std.ascii.toLower(buf.items[prefix.len]);
}
}

return buf.toOwnedSlice(allocator);
}

/// 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.tokenize(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);
}

// 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,
/// Returns a URI from a path.
/// Caller should free memory
pub fn fromPath(allocator: std.mem.Allocator, path: []const u8) error{OutOfMemory}![]const u8 {
const uri = std.Uri{
.scheme = "file",
.user = null,
.password = null,
.host = null,
.port = null,
.path = path,
.query = null,
.fragment = null,
};
return std.fmt.allocPrint(allocator, "{}", .{uri});
}

/// parses a Uri and return the unescaped path
/// 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;
}
}
pub fn parse(allocator: std.mem.Allocator, str: []const u8) (std.Uri.ParseError || error{OutOfMemory})![]u8 {
const uri = try std.Uri.parse(str);

// Remove trailing separator
if (i > 0 and uri[i - 1] == std.fs.path.sep) {
i -= 1;
}
if (!std.mem.eql(u8, uri.scheme, "file")) return error.InvalidFormat;

return allocator.realloc(uri, i);
return try std.Uri.unescapeString(allocator, uri.path);
}
1 change: 0 additions & 1 deletion tests/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ comptime {
_ = @import("utility/ast.zig");
_ = @import("utility/offsets.zig");
_ = @import("utility/position_context.zig");
_ = @import("utility/uri.zig");
_ = @import("utility/diff.zig");

_ = @import("lifecycle.zig");
Expand Down
59 changes: 0 additions & 59 deletions tests/utility/uri.zig

This file was deleted.

0 comments on commit c5b3b1f

Please sign in to comment.