From 0519e3ae81cf0198007d325a60f6d0a750a6d1ed Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 3 Oct 2023 15:23:34 +0200 Subject: [PATCH] lazily compute resources of DocumentStore handles Instead creating the DocumentStore (and ZIR if needed) whenever the document gets modified, it is more efficient to compute them lazily. This will mean that series of text edits without any requests on them which often occur while typing will be processed quicker. I've also tested lazily computing the Ast but it turned out that the Ast was (almost) always needed which made this unnecessary. --- src/ComptimeInterpreter.zig | 26 +- src/DocumentStore.zig | 490 ++++++++++++++++++---------- src/Server.zig | 30 +- src/analysis.zig | 243 ++++++++------ src/features/code_actions.zig | 33 +- src/features/completions.zig | 16 +- src/features/diagnostics.zig | 57 ++-- src/features/folding_range.zig | 2 +- src/features/goto.zig | 45 +-- src/features/hover.zig | 34 +- src/features/inlay_hints.zig | 6 +- src/features/references.zig | 14 +- src/features/selection_range.zig | 6 +- src/features/semantic_tokens.zig | 11 +- src/features/signature_help.zig | 11 +- tests/lsp_features/code_actions.zig | 2 +- 16 files changed, 614 insertions(+), 412 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index a198de9d3e..ffa0e73e85 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -25,7 +25,7 @@ document_store: *DocumentStore, uri: DocumentStore.Uri, namespaces: std.MultiArrayList(Namespace) = .{}, -pub fn getHandle(interpreter: *ComptimeInterpreter) *const DocumentStore.Handle { +pub fn getHandle(interpreter: *ComptimeInterpreter) *DocumentStore.Handle { // This interpreter is loaded from a known-valid handle so a valid handle must exist return interpreter.document_store.getHandle(interpreter.uri).?; } @@ -39,7 +39,7 @@ pub fn recordError( ) error{OutOfMemory}!void { const message = try std.fmt.allocPrint(interpreter.allocator, fmt, args); errdefer interpreter.allocator.free(message); - const handle = interpreter.document_store.handles.get(interpreter.uri).?; + const handle = interpreter.getHandle(); try handle.analysis_errors.append(interpreter.document_store.allocator, .{ .loc = offsets.nodeToLoc(handle.tree, node_idx), .code = code, @@ -886,15 +886,21 @@ pub fn interpret( var import_uri = (try interpreter.document_store.uriFromImportStr(interpreter.allocator, interpreter.getHandle().*, import_str[1 .. import_str.len - 1])) orelse return error.ImportFailure; defer interpreter.allocator.free(import_uri); - var handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure; - _ = try interpreter.document_store.ensureInterpreterExists(handle.uri, interpreter.ip); + const import_handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure; + const import_interpreter = try import_handle.getComptimeInterpreter(interpreter.document_store, interpreter.ip); - return InterpretResult{ - .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .index = try interpreter.ip.get(interpreter.allocator, .{ .unknown_value = .{ .ty = .type_type } }), - }, + return import_interpreter.interpret(0, .none, options) catch |err| { + log.err("Failed to interpret node: {s}", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + return InterpretResult{ + .value = Value{ + .interpreter = import_interpreter, + .node_idx = 0, + .index = .unknown_type, + }, + }; }; } diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 0dd1cafb93..5e80229749 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -50,23 +50,8 @@ const BuildFile = struct { }; pub const Handle = struct { - /// `true` if the document has been directly opened by the client i.e. with `textDocument/didOpen` - /// `false` indicates the document only exists because it is a dependency of another document - /// or has been closed with `textDocument/didClose` and is awaiting cleanup through `garbageCollection` - open: std.atomic.Atomic(bool), uri: Uri, - text: [:0]const u8, tree: Ast, - /// do not access unless `zir_status != .none` - zir: Zir = undefined, - zir_status: enum { - none, - outdated, - done, - } = .none, - /// Not null if a ComptimeInterpreter is actually used - interpreter: ?*ComptimeInterpreter = null, - document_scope: analysis.DocumentScope, /// Contains one entry for every import in the document import_uris: std.ArrayListUnmanaged(Uri) = .{}, /// Contains one entry for every cimport in the document @@ -79,31 +64,314 @@ pub const Handle = struct { /// uri memory managed by its build_file associated_build_file: ?Uri = null, - pub fn deinit(self: *Handle, allocator: std.mem.Allocator) void { - if (self.interpreter) |interpreter| { - interpreter.deinit(); - allocator.destroy(interpreter); + /// private field + impl: struct { + /// @bitCast from/to `Status` + status: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(@bitCast(Status{})), + /// TODO can we avoid storing one allocator per Handle? + allocator: std.mem.Allocator, + + lock: std.Thread.Mutex = .{}, + condition: std.Thread.Condition = .{}, + + document_scope: analysis.DocumentScope = undefined, + zir: Zir = undefined, + comptime_interpreter: *ComptimeInterpreter = undefined, + }, + + const Status = packed struct(u32) { + /// `true` if the document has been directly opened by the client i.e. with `textDocument/didOpen` + /// `false` indicates the document only exists because it is a dependency of another document + /// or has been closed with `textDocument/didClose` and is awaiting cleanup through `garbageCollection` + open: bool = false, + /// true if a thread has acquired the permission to compute the `DocumentScope` + /// all other threads will wait until the given thread has computed the `DocumentScope` before reading it. + has_document_scope_lock: bool = false, + /// true if `handle.impl.document_scope` has been set + has_document_scope: bool = false, + /// true if a thread has acquired the permission to compute the `ZIR` + has_zir_lock: bool = false, + /// all other threads will wait until the given thread has computed the `ZIR` before reading it. + /// true if `handle.impl.zir` has been set + has_zir: bool = false, + zir_outdated: bool = undefined, + /// true if `handle.impl.comptime_interpreter` has been set + has_comptime_interpreter: bool = false, + _: u25 = undefined, + }; + + pub const ZirStatus = enum { + none, + outdated, + done, + }; + + /// takes ownership of `text` + pub fn init(allocator: std.mem.Allocator, uri: Uri, text: [:0]const u8) error{OutOfMemory}!Handle { + const duped_uri = try allocator.dupe(u8, uri); + errdefer allocator.free(duped_uri); + + const tree = try parseTree(allocator, text); + errdefer tree.deinit(allocator); + + return .{ + .uri = duped_uri, + .tree = tree, + .impl = .{ + .allocator = allocator, + }, + }; + } + + pub fn getDocumentScope(self: *Handle) error{OutOfMemory}!analysis.DocumentScope { + if (self.getStatus().has_document_scope) return self.impl.document_scope; + return try self.getDocumentScopeCold(); + } + + pub fn getZir(self: *Handle) error{OutOfMemory}!Zir { + if (self.getStatus().has_zir) return self.impl.zir; + return try self.getZirCold(); + } + + pub fn getZirStatus(self: Handle) ZirStatus { + const status = self.getStatus(); + if (!status.has_zir) return .none; + return if (status.zir_outdated) .outdated else .done; + } + + pub fn getComptimeInterpreter(self: *Handle, document_store: *DocumentStore, ip: *InternPool) error{OutOfMemory}!*ComptimeInterpreter { + if (self.getStatus().has_comptime_interpreter) return self.impl.comptime_interpreter; + return try self.getComptimeInterpreterCold(document_store, ip); + } + + fn getDocumentScopeCold(self: *Handle) error{OutOfMemory}!analysis.DocumentScope { + @setCold(true); + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + + self.impl.lock.lock(); + defer self.impl.lock.unlock(); + while (true) { + const status = self.getStatus(); + if (status.has_document_scope) break; + if (status.has_document_scope_lock) { + // another thread is currently computing the document scope + self.impl.condition.wait(&self.impl.lock); + continue; + } else if (self.impl.status.bitSet(@bitOffsetOf(Status, "has_document_scope_lock"), .Release) != 0) { + // another thread is currently computing the document scope + self.impl.condition.wait(&self.impl.lock); + continue; + } + + self.impl.document_scope = blk: { + const tracy_zone_inner = tracy.traceNamed(@src(), "analysis.makeDocumentScope"); + defer tracy_zone_inner.end(); + + var document_scope = try analysis.makeDocumentScope(self.impl.allocator, self.tree); + errdefer document_scope.deinit(self.impl.allocator); + + // remove unused capacity + try document_scope.scopes.setCapacity(self.impl.allocator, document_scope.scopes.len); + + break :blk document_scope; + }; + const old_has_document_scope = self.impl.status.bitSet(@bitOffsetOf(Status, "has_document_scope"), .Release); // atomically set has_document_scope + std.debug.assert(old_has_document_scope == 0); // race condition: another thread set `has_document_scope` even though we hold the lock + + self.impl.condition.broadcast(); } - self.document_scope.deinit(allocator); - if (self.zir_status != .none) self.zir.deinit(allocator); - self.tree.deinit(allocator); - allocator.free(self.text); - allocator.free(self.uri); + return self.impl.document_scope; + } + + fn getZirCold(self: *Handle) error{OutOfMemory}!Zir { + @setCold(true); + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + + self.impl.lock.lock(); + defer self.impl.lock.unlock(); + while (true) { + const status = self.getStatus(); + if (status.has_zir) break; + if (status.has_zir_lock) { + // another thread is currently computing the ZIR + self.impl.condition.wait(&self.impl.lock); + continue; + } else if (self.impl.status.bitSet(@bitOffsetOf(Status, "has_zir_lock"), .Release) != 0) { + // another thread is currently computing the ZIR + self.impl.condition.wait(&self.impl.lock); + continue; + } + + self.impl.zir = blk: { + const tracy_zone_inner = tracy.traceNamed(@src(), "AstGen.generate"); + defer tracy_zone_inner.end(); + + var zir = try AstGen.generate(self.impl.allocator, self.tree); + errdefer zir.deinit(self.impl.allocator); + + // remove unused capacity + var instructions = zir.instructions.toMultiArrayList(); + try instructions.setCapacity(self.impl.allocator, instructions.len); + zir.instructions = instructions.slice(); + + break :blk zir; + }; + _ = self.impl.status.bitReset(@bitOffsetOf(Status, "zir_outdated"), .Release); // atomically set zir_outdated + const old_has_zir = self.impl.status.bitSet(@bitOffsetOf(Status, "has_zir"), .Release); // atomically set has_zir + std.debug.assert(old_has_zir == 0); // race condition: another thread set `has_zir` even though we hold the lock - for (self.import_uris.items) |import_uri| { - allocator.free(import_uri); + self.impl.condition.broadcast(); } - self.import_uris.deinit(allocator); + return self.impl.zir; + } - for (self.cimports.items(.source)) |source| { - allocator.free(source); + fn getComptimeInterpreterCold(self: *Handle, document_store: *DocumentStore, ip: *InternPool) error{OutOfMemory}!*ComptimeInterpreter { + @setCold(true); + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + + const comptime_interpreter = try self.impl.allocator.create(ComptimeInterpreter); + errdefer self.impl.allocator.destroy(comptime_interpreter); + + comptime_interpreter.* = ComptimeInterpreter{ + .allocator = self.impl.allocator, + .ip = ip, + .document_store = document_store, + .uri = self.uri, + }; + + { + self.impl.lock.lock(); + errdefer @compileError(""); + + if (self.getStatus().has_comptime_interpreter) { // another thread outpaced us + self.impl.lock.unlock(); + self.impl.allocator.destroy(comptime_interpreter); + return self.impl.comptime_interpreter; + } + self.impl.comptime_interpreter = comptime_interpreter; + const old = self.impl.status.bitSet(@bitOffsetOf(Status, "has_comptime_interpreter"), .Release); // atomically set has_imports + std.debug.assert(old == 0); // race condition: another thread set the resource even though we hold the lock + self.impl.lock.unlock(); } - self.cimports.deinit(allocator); - for (self.analysis_errors.items) |err| { - allocator.free(err.message); + return self.impl.comptime_interpreter; + } + + fn getStatus(self: Handle) Status { + return @bitCast(self.impl.status.load(.Acquire)); + } + + /// returns the previous value + fn setOpen(self: *Handle, open: bool) bool { + if (open) { + return self.impl.status.bitSet(@offsetOf(Handle.Status, "open"), .Release) == 1; + } else { + return self.impl.status.bitReset(@offsetOf(Handle.Status, "open"), .Release) == 1; + } + } + + fn parseTree(allocator: std.mem.Allocator, new_text: [:0]const u8) error{OutOfMemory}!Ast { + const tracy_zone_inner = tracy.traceNamed(@src(), "Ast.parse"); + defer tracy_zone_inner.end(); + + var tree = try Ast.parse(allocator, new_text, .zig); + errdefer tree.deinit(allocator); + + // remove unused capacity + var nodes = tree.nodes.toMultiArrayList(); + try nodes.setCapacity(allocator, nodes.len); + tree.nodes = nodes.slice(); + + // remove unused capacity + var tokens = tree.tokens.toMultiArrayList(); + try tokens.setCapacity(allocator, tokens.len); + tree.tokens = tokens.slice(); + return tree; + } + + fn setSource(self: *Handle, new_text: [:0]const u8) error{OutOfMemory}!void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + + const new_status = Handle.Status{ + .open = self.getStatus().open, + }; + + const new_tree = try parseTree(self.impl.allocator, new_text); + + self.impl.lock.lock(); + errdefer @compileError(""); + + const old_status: Handle.Status = @bitCast(self.impl.status.swap(@bitCast(new_status), .AcqRel)); + + var old_tree = self.tree; + var old_import_uris = self.import_uris; + var old_cimports = self.cimports; + var old_analysis_errors = self.analysis_errors; + var old_document_scope = if (old_status.has_document_scope) self.impl.document_scope else null; + var old_zir = if (old_status.has_zir) self.impl.zir else null; + var old_comptime_interpreter = if (old_status.has_comptime_interpreter) self.impl.comptime_interpreter else null; + + self.tree = new_tree; + self.import_uris = .{}; + self.cimports = .{}; + self.analysis_errors = .{}; + self.impl.document_scope = undefined; + self.impl.zir = undefined; + self.impl.comptime_interpreter = undefined; + + self.impl.lock.unlock(); + + self.impl.allocator.free(old_tree.source); + old_tree.deinit(self.impl.allocator); + + for (old_import_uris.items) |uri| self.impl.allocator.free(uri); + old_import_uris.deinit(self.impl.allocator); + + for (old_analysis_errors.items) |err| self.impl.allocator.free(err.message); + old_analysis_errors.deinit(self.impl.allocator); + + for (old_cimports.items(.source)) |source| self.impl.allocator.free(source); + old_cimports.deinit(self.impl.allocator); + + if (old_document_scope) |*document_scope| document_scope.deinit(self.impl.allocator); + if (old_zir) |*zir| zir.deinit(self.impl.allocator); + if (old_comptime_interpreter) |comptime_interpreter| { + comptime_interpreter.deinit(); + self.impl.allocator.destroy(comptime_interpreter); } + } + + fn deinit(self: *Handle) void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + + const status = self.getStatus(); + + const allocator = self.impl.allocator; + + if (status.has_comptime_interpreter) { + self.impl.comptime_interpreter.deinit(); + allocator.destroy(self.impl.comptime_interpreter); + } + if (status.has_zir) self.impl.zir.deinit(allocator); + if (status.has_document_scope) self.impl.document_scope.deinit(allocator); + allocator.free(self.tree.source); + self.tree.deinit(allocator); + allocator.free(self.uri); + + for (self.import_uris.items) |uri| allocator.free(uri); + self.import_uris.deinit(allocator); + + for (self.analysis_errors.items) |err| allocator.free(err.message); self.analysis_errors.deinit(allocator); + + for (self.cimports.items(.source)) |source| allocator.free(source); + self.cimports.deinit(allocator); + self.* = undefined; } }; @@ -124,7 +392,7 @@ cimports: std.AutoArrayHashMapUnmanaged(Hash, translate_c.Result) = .{}, pub fn deinit(self: *DocumentStore) void { for (self.handles.values()) |handle| { - handle.deinit(self.allocator); + handle.deinit(); self.allocator.destroy(handle); } self.handles.deinit(self.allocator); @@ -143,7 +411,7 @@ pub fn deinit(self: *DocumentStore) void { /// Returns a handle to the given document /// **Thread safe** takes a shared lock -pub fn getHandle(self: *DocumentStore, uri: Uri) ?*const Handle { +pub fn getHandle(self: *DocumentStore, uri: Uri) ?*Handle { self.lock.lockShared(); defer self.lock.unlockShared(); return self.handles.get(uri); @@ -152,7 +420,7 @@ pub fn getHandle(self: *DocumentStore, uri: Uri) ?*const Handle { /// Returns a handle to the given document /// Will load the document from disk if it hasn't been already /// **Thread safe** takes an exclusive lock -pub fn getOrLoadHandle(self: *DocumentStore, uri: Uri) ?*const Handle { +pub fn getOrLoadHandle(self: *DocumentStore, uri: Uri) ?*Handle { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -225,7 +493,7 @@ pub fn openDocument(self: *DocumentStore, uri: Uri, text: []const u8) error{OutO defer self.lock.unlockShared(); if (self.handles.get(uri)) |handle| { - if (!handle.open.swap(true, .Acquire)) { + if (!handle.setOpen(true)) { log.warn("Document already open: {s}", .{uri}); } return; @@ -251,7 +519,7 @@ pub fn closeDocument(self: *DocumentStore, uri: Uri) void { }; // instead of destroying the handle here we just mark it not open // and let it be destroy by the garbage collection code - if (!handle.open.swap(false, .Acquire)) { + if (!handle.setOpen(false)) { log.warn("Document already closed: {s}", .{uri}); } } @@ -266,48 +534,16 @@ pub fn closeDocument(self: *DocumentStore, uri: Uri) void { /// Takes ownership of `new_text` which has to be allocated /// with this DocumentStore's allocator -/// **Thread safe** takes an exclusive lock +/// **Thread safe** takes a shared lock pub fn refreshDocument(self: *DocumentStore, uri: Uri, new_text: [:0]const u8) !void { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const old_handle = self.getHandle(uri).?; - const old_import_count = old_handle.import_uris.items.len; - const old_cimport_count = old_handle.cimports.len; - - var new_handle = try self.createDocument(uri, new_text, old_handle.open.load(.Acquire)); - - self.lock.lock(); - defer self.lock.unlock(); - - const handle: *Handle = self.handles.get(uri).?; - - // keep the old memory address of the handle uri - self.allocator.free(new_handle.uri); - new_handle.uri = handle.uri; - handle.uri = ""; - - // if the new document failed to generate ZIR, reuse - // the outdated ZIR of the old document - if (new_handle.zir_status == .none and handle.zir_status != .none) { - new_handle.zir = handle.zir; - new_handle.zir_status = .outdated; - handle.zir_status = .none; - handle.zir = undefined; - } - - handle.deinit(self.allocator); - handle.* = new_handle; - - const new_import_count = handle.import_uris.items.len; - const new_cimport_count = handle.cimports.len; - - if (old_import_count != new_import_count or - old_cimport_count != new_cimport_count) - { - self.garbageCollectionImports() catch {}; - self.garbageCollectionCImports() catch {}; + const handle = self.getHandle(uri).?; + if (!handle.getStatus().open) { + log.warn("Document modified without being opened: {s}", .{uri}); } + try handle.setSource(new_text); } /// Invalidates a build files. @@ -355,8 +591,7 @@ fn garbageCollectionImports(self: *DocumentStore) error{OutOfMemory}!void { var queue = std.ArrayListUnmanaged(Uri){}; for (self.handles.values(), 0..) |handle, handle_index| { - if (!handle.open.load(.Acquire)) continue; - + if (!handle.getStatus().open) continue; reachable.set(handle_index); try self.collectDependenciesInternal(arena.allocator(), handle.*, &queue, false); @@ -381,7 +616,7 @@ fn garbageCollectionImports(self: *DocumentStore) error{OutOfMemory}!void { const handle = self.handles.values()[handle_index]; log.debug("Closing document {s}", .{handle.uri}); self.handles.swapRemoveAt(handle_index); - handle.deinit(self.allocator); + handle.deinit(); self.allocator.destroy(handle); } } @@ -758,56 +993,10 @@ fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - var handle: Handle = blk: { - errdefer self.allocator.free(text); - - var duped_uri = try self.allocator.dupe(u8, uri); - errdefer self.allocator.free(duped_uri); - - var tree = try Ast.parse(self.allocator, text, .zig); - errdefer tree.deinit(self.allocator); - - // remove unused capacity - var nodes = tree.nodes.toMultiArrayList(); - try nodes.setCapacity(self.allocator, nodes.len); - tree.nodes = nodes.slice(); - - // remove unused capacity - var tokens = tree.tokens.toMultiArrayList(); - try tokens.setCapacity(self.allocator, tokens.len); - tree.tokens = tokens.slice(); + var handle = try Handle.init(self.allocator, uri, text); + errdefer handle.deinit(); - const generate_zir = self.wantZir() and open and tree.errors.len == 0; - var zir: ?Zir = if (generate_zir) try AstGen.generate(self.allocator, tree) else null; - errdefer if (zir) |*code| code.deinit(self.allocator); - - // remove unused capacity - if (zir) |*code| { - var instructions = code.instructions.toMultiArrayList(); - try instructions.setCapacity(self.allocator, instructions.len); - code.instructions = instructions.slice(); - } - - var document_scope = try analysis.makeDocumentScope(self.allocator, tree); - errdefer document_scope.deinit(self.allocator); - - // remove unused capacity - try document_scope.scopes.setCapacity(self.allocator, document_scope.scopes.len); - - break :blk Handle{ - .open = std.atomic.Atomic(bool).init(open), - .uri = duped_uri, - .text = text, - .tree = tree, - .zir = if (zir) |code| code else undefined, - .zir_status = if (zir != null) .done else .none, - .document_scope = document_scope, - }; - }; - errdefer handle.deinit(self.allocator); - - handle.import_uris = try self.collectImportUris(handle); - handle.cimports = try collectCIncludes(self.allocator, handle.tree); + _ = handle.setOpen(open); if (isBuildFile(handle.uri) and !isInStd(handle.uri)) { _ = self.getOrLoadBuildFile(handle.uri); @@ -840,6 +1029,9 @@ fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool } } + handle.import_uris = try self.collectImportUris(handle); + handle.cimports = try collectCIncludes(self.allocator, handle.tree); + return handle; } @@ -847,20 +1039,20 @@ fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool /// invalidates any pointers into `DocumentStore.build_files` /// **Thread safe** takes an exclusive lock fn createAndStoreDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool) error{OutOfMemory}!*Handle { - var handle = try self.createDocument(uri, text, open); - - const handle_ptr = try self.allocator.create(Handle); + const handle_ptr: *Handle = try self.allocator.create(Handle); errdefer self.allocator.destroy(handle_ptr); - handle_ptr.* = handle; + + handle_ptr.* = try self.createDocument(uri, text, open); + errdefer handle_ptr.deinit(); const gop = blk: { self.lock.lock(); defer self.lock.unlock(); - break :blk try self.handles.getOrPutValue(self.allocator, handle.uri, handle_ptr); + break :blk try self.handles.getOrPutValue(self.allocator, handle_ptr.uri, handle_ptr); }; if (gop.found_existing) { - handle.deinit(self.allocator); + handle_ptr.deinit(); self.allocator.destroy(handle_ptr); } @@ -1164,7 +1356,8 @@ fn tagStoreCompletionItems(self: *DocumentStore, arena: std.mem.Allocator, handl for (dependencies.items) |uri| { // not every dependency is loaded which results in incomplete completion const hdl = self.getHandle(uri) orelse continue; // takes a shared lock - const curr_set = @field(hdl.document_scope, name); + const document_scope = try hdl.getDocumentScope(); + const curr_set = @field(document_scope, name); try result_set.ensureUnusedCapacity(arena, curr_set.count()); for (curr_set.keys()) |completion| { result_set.putAssumeCapacity(completion, {}); @@ -1183,34 +1376,3 @@ pub fn errorCompletionItems(self: *DocumentStore, arena: std.mem.Allocator, hand pub fn enumCompletionItems(self: *DocumentStore, arena: std.mem.Allocator, handle: Handle) error{OutOfMemory}![]types.CompletionItem { return try self.tagStoreCompletionItems(arena, handle, "enum_completions"); } - -pub fn wantZir(self: DocumentStore) bool { - if (!self.config.enable_ast_check_diagnostics) return false; - const can_run_ast_check = std.process.can_spawn and self.config.zig_exe_path != null and self.config.prefer_ast_check_as_child_process; - return !can_run_ast_check; -} - -/// **Thread safe** takes an exclusive lock -pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri, ip: *InternPool) !*ComptimeInterpreter { - if (self.getHandle(uri).?.interpreter) |interpreter| return interpreter; - - self.lock.lock(); - defer self.lock.unlock(); - - const handle = self.handles.get(uri).?; - { - var interpreter = try self.allocator.create(ComptimeInterpreter); - errdefer self.allocator.destroy(interpreter); - - interpreter.* = ComptimeInterpreter{ - .allocator = self.allocator, - .ip = ip, - .document_store = self, - .uri = uri, - }; - handle.interpreter = interpreter; - } - - _ = try handle.interpreter.?.interpret(0, .none, .{}); - return handle.interpreter.?; -} diff --git a/src/Server.zig b/src/Server.zig index 01acbd75d4..2d0297b595 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -326,12 +326,12 @@ fn getAutofixMode(server: *Server) enum { } /// caller owns returned memory. -fn autofix(server: *Server, arena: std.mem.Allocator, handle: *const DocumentStore.Handle) error{OutOfMemory}!std.ArrayListUnmanaged(types.TextEdit) { +fn autofix(server: *Server, arena: std.mem.Allocator, handle: *DocumentStore.Handle) error{OutOfMemory}!std.ArrayListUnmanaged(types.TextEdit) { if (!server.config.enable_ast_check_diagnostics) return .{}; if (handle.tree.errors.len != 0) return .{}; var diagnostics = std.ArrayListUnmanaged(types.Diagnostic){}; - try diagnostics_gen.getAstCheckDiagnostics(server, arena, handle.*, &diagnostics); + try diagnostics_gen.getAstCheckDiagnostics(server, arena, handle, &diagnostics); if (diagnostics.items.len == 0) return .{}; var analyser = Analyser.init(server.allocator, &server.document_store, &server.ip); @@ -1147,7 +1147,7 @@ fn openDocumentHandler(server: *Server, _: std.mem.Allocator, notification: type fn changeDocumentHandler(server: *Server, _: std.mem.Allocator, notification: types.DidChangeTextDocumentParams) Error!void { const handle = server.document_store.getHandle(notification.textDocument.uri) orelse return; - const new_text = try diff.applyContentChanges(server.allocator, handle.text, notification.contentChanges, server.offset_encoding); + const new_text = try diff.applyContentChanges(server.allocator, handle.tree.source, notification.contentChanges, server.offset_encoding); try server.document_store.refreshDocument(handle.uri, new_text); @@ -1244,7 +1244,7 @@ fn semanticTokensRangeHandler(server: *Server, arena: std.mem.Allocator, request fn completionHandler(server: *Server, arena: std.mem.Allocator, request: types.CompletionParams) Error!ResultType("textDocument/completion") { const handle = server.document_store.getHandle(request.textDocument.uri) orelse return null; - const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); + const source_index = offsets.positionToIndex(handle.tree.source, request.position, server.offset_encoding); var analyser = Analyser.init(server.allocator, &server.document_store, &server.ip); defer analyser.deinit(); @@ -1259,7 +1259,7 @@ fn signatureHelpHandler(server: *Server, arena: std.mem.Allocator, request: type if (request.position.character == 0) return null; - const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); + const source_index = offsets.positionToIndex(handle.tree.source, request.position, server.offset_encoding); var analyser = Analyser.init(server.allocator, &server.document_store, &server.ip); defer analyser.deinit(); @@ -1298,7 +1298,7 @@ fn gotoHandler( if (request.position.character == 0) return null; const handle = server.document_store.getHandle(request.textDocument.uri) orelse return null; - const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); + const source_index = offsets.positionToIndex(handle.tree.source, request.position, server.offset_encoding); var analyser = Analyser.init(server.allocator, &server.document_store, &server.ip); defer analyser.deinit(); @@ -1363,7 +1363,7 @@ fn hoverHandler(server: *Server, arena: std.mem.Allocator, request: types.HoverP if (request.position.character == 0) return null; const handle = server.document_store.getHandle(request.textDocument.uri) orelse return null; - const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); + const source_index = offsets.positionToIndex(handle.tree.source, request.position, server.offset_encoding); const markup_kind: types.MarkupKind = if (server.client_capabilities.hover_supports_md) .markdown else .plaintext; @@ -1398,9 +1398,9 @@ fn formattingHandler(server: *Server, arena: std.mem.Allocator, request: types.D const formatted = try handle.tree.render(arena); - if (std.mem.eql(u8, handle.text, formatted)) return null; + if (std.mem.eql(u8, handle.tree.source, formatted)) return null; - return if (diff.edits(arena, handle.text, formatted, server.offset_encoding)) |text_edits| text_edits.items else |_| null; + return if (diff.edits(arena, handle.tree.source, formatted, server.offset_encoding)) |text_edits| text_edits.items else |_| null; } fn renameHandler(server: *Server, arena: std.mem.Allocator, request: types.RenameParams) Error!?types.WorkspaceEdit { @@ -1455,10 +1455,10 @@ fn generalReferencesHandler(server: *Server, arena: std.mem.Allocator, request: if (request.position().character <= 0) return null; - const source_index = offsets.positionToIndex(handle.text, request.position(), server.offset_encoding); + const source_index = offsets.positionToIndex(handle.tree.source, request.position(), server.offset_encoding); const name_loc = Analyser.identifierLocFromPosition(source_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); - const pos_context = try Analyser.getPositionContext(server.allocator, handle.text, source_index, true); + const name = offsets.locToSlice(handle.tree.source, name_loc); + const pos_context = try Analyser.getPositionContext(server.allocator, handle.tree.source, source_index, true); var analyser = Analyser.init(server.allocator, &server.document_store, &server.ip); defer analyser.deinit(); @@ -1542,7 +1542,7 @@ fn inlayHintHandler(server: *Server, arena: std.mem.Allocator, request: types.In const handle = server.document_store.getHandle(request.textDocument.uri) orelse return null; const hover_kind: types.MarkupKind = if (server.client_capabilities.hover_supports_md) .markdown else .plaintext; - const loc = offsets.rangeToLoc(handle.text, request.range, server.offset_encoding); + const loc = offsets.rangeToLoc(handle.tree.source, request.range, server.offset_encoding); var analyser = Analyser.init(server.allocator, &server.document_store, &server.ip); defer analyser.deinit(); @@ -1574,7 +1574,7 @@ fn codeActionHandler(server: *Server, arena: std.mem.Allocator, request: types.C // as of right now, only ast-check errors may get a code action var diagnostics = std.ArrayListUnmanaged(types.Diagnostic){}; if (server.config.enable_ast_check_diagnostics and handle.tree.errors.len == 0) { - try diagnostics_gen.getAstCheckDiagnostics(server, arena, handle.*, &diagnostics); + try diagnostics_gen.getAstCheckDiagnostics(server, arena, handle, &diagnostics); } var actions = std.ArrayListUnmanaged(types.CodeAction){}; @@ -2133,7 +2133,7 @@ fn processJob(server: *Server, job: Job, wait_group: ?*std.Thread.WaitGroup) voi const handle = server.document_store.getHandle(uri) orelse return; var arena_allocator = std.heap.ArenaAllocator.init(server.allocator); defer arena_allocator.deinit(); - const diagnostics = diagnostics_gen.generateDiagnostics(server, arena_allocator.allocator(), handle.*) catch return; + const diagnostics = diagnostics_gen.generateDiagnostics(server, arena_allocator.allocator(), handle) catch return; const json_message = server.sendToClientNotification("textDocument/publishDiagnostics", diagnostics) catch return; server.allocator.free(json_message); }, diff --git a/src/analysis.zig b/src/analysis.zig index 9c8c15beb2..d0da68e070 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -213,16 +213,17 @@ pub fn getFunctionSnippet( pub fn isInstanceCall( analyser: *Analyser, - call_handle: *const DocumentStore.Handle, + call_handle: *DocumentStore.Handle, call: Ast.full.Call, - func_handle: *const DocumentStore.Handle, + func_handle: *DocumentStore.Handle, func: Ast.full.FnProto, ) !bool { - return call_handle.tree.tokens.items(.tag)[call.ast.lparen - 2] == .period and + const tree = call_handle.tree; + return tree.tokens.items(.tag)[call.ast.lparen - 2] == .period and try analyser.hasSelfParam(func_handle, func); } -pub fn hasSelfParam(analyser: *Analyser, handle: *const DocumentStore.Handle, func: Ast.full.FnProto) !bool { +pub fn hasSelfParam(analyser: *Analyser, handle: *DocumentStore.Handle, func: Ast.full.FnProto) !bool { // Non-decl prototypes cannot have a self parameter. if (func.name_token == null) return false; if (func.ast.params.len == 0) return false; @@ -233,7 +234,7 @@ pub fn hasSelfParam(analyser: *Analyser, handle: *const DocumentStore.Handle, fu if (param.type_expr == 0) return false; const token_starts = tree.tokens.items(.start); - const in_container = innermostContainer(handle, token_starts[func.ast.fn_token]); + const in_container = try innermostContainer(handle, token_starts[func.ast.fn_token]); if (try analyser.resolveTypeOfNodeInternal(.{ .node = param.type_expr, @@ -486,7 +487,7 @@ fn resolveVarDeclAliasUncached(analyser: *Analyser, node_handle: NodeWithHandle, .aligned_var_decl, .simple_var_decl, => { - const var_decl = handle.tree.fullVarDecl(node_handle.node).?; + const var_decl = tree.fullVarDecl(node_handle.node).?; if (var_decl.ast.init_node == 0) return null; const base_exp = var_decl.ast.init_node; @@ -507,7 +508,8 @@ fn resolveVarDeclAliasUncached(analyser: *Analyser, node_handle: NodeWithHandle, const inner_node = (try analyser.resolveTypeOfNode(.{ .node = lhs, .handle = handle })) orelse return null; // assert root node std.debug.assert(inner_node.type.data.other == 0); - const root_decl = &inner_node.handle.document_scope.decls.items[0]; + const document_scope = try inner_node.handle.getDocumentScope(); + const root_decl = &document_scope.decls.items[0]; break :blk DeclWithHandle{ .decl = root_decl, .handle = inner_node.handle }; }, else => return null, @@ -584,7 +586,7 @@ fn findReturnStatement(tree: Ast, fn_decl: Ast.full.FnProto, body: Ast.Node.Inde return findReturnStatementInternal(tree, fn_decl, body, &already_found); } -fn resolveReturnType(analyser: *Analyser, fn_decl: Ast.full.FnProto, handle: *const DocumentStore.Handle, fn_body: ?Ast.Node.Index) !?TypeWithHandle { +fn resolveReturnType(analyser: *Analyser, fn_decl: Ast.full.FnProto, handle: *DocumentStore.Handle, fn_body: ?Ast.Node.Index) !?TypeWithHandle { const tree = handle.tree; if (isTypeFunction(tree, fn_decl) and fn_body != null) { // If this is a type function and it only contains a single return statement that returns @@ -621,9 +623,10 @@ fn resolveUnwrapOptionalType(analyser: *Analyser, opt: TypeWithHandle) !?TypeWit else => return null, }; - if (opt.handle.tree.nodes.items(.tag)[opt_node] == .optional_type) { + const tree = opt.handle.tree; + if (tree.nodes.items(.tag)[opt_node] == .optional_type) { return ((try analyser.resolveTypeOfNodeInternal(.{ - .node = opt.handle.tree.nodes.items(.data)[opt_node].lhs, + .node = tree.nodes.items(.data)[opt_node].lhs, .handle = opt.handle, })) orelse return null).instanceTypeVal(); } @@ -641,8 +644,9 @@ fn resolveUnwrapErrorUnionType(analyser: *Analyser, rhs: TypeWithHandle, side: E else => return null, }; - if (rhs.handle.tree.nodes.items(.tag)[rhs_node] == .error_union) { - const data = rhs.handle.tree.nodes.items(.data)[rhs_node]; + const tree = rhs.handle.tree; + if (tree.nodes.items(.tag)[rhs_node] == .error_union) { + const data = tree.nodes.items(.data)[rhs_node]; return ((try analyser.resolveTypeOfNodeInternal(.{ .node = switch (side) { .left => data.lhs, @@ -1112,7 +1116,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e switch (child.decl.*) { .ast_node => |n| { if (n == node) return null; - if (child.handle.tree.fullVarDecl(n)) |var_decl| { + const child_decl_tree = child.handle.tree; + if (child_decl_tree.fullVarDecl(n)) |var_decl| { if (var_decl.ast.init_node == node) return null; } @@ -1189,7 +1194,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e log.info("Invoking interpreter!", .{}); - const interpreter = analyser.store.ensureInterpreterExists(handle.uri, analyser.ip.?) catch |err| { + const interpreter = try handle.getComptimeInterpreter(analyser.store, analyser.ip.?); + _ = interpreter.interpret(0, .none, .{}) catch |err| { log.err("Failed to interpret file: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -1254,7 +1260,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e .address_of, => { if (node_tags[node].isContainerField()) { - const container_type = innermostContainer(handle, offsets.tokenToIndex(tree, tree.firstToken(node))); + const container_type = try innermostContainer(handle, offsets.tokenToIndex(tree, tree.firstToken(node))); if (container_type.isEnumType()) return container_type.instanceTypeVal(); @@ -1356,7 +1362,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e const call_name = tree.tokenSlice(main_tokens[node]); if (std.mem.eql(u8, call_name, "@This")) { if (params.len != 0) return null; - return innermostContainer(handle, starts[tree.firstToken(node)]); + return try innermostContainer(handle, starts[tree.firstToken(node)]); } const cast_map = std.ComptimeStringMap(void, .{ @@ -1399,10 +1405,12 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e }; const new_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null; - const root_scope_decls = new_handle.document_scope.scopes.items(.decls)[0]; + const new_handle_document_scope = try new_handle.getDocumentScope(); + + const root_scope_decls = new_handle_document_scope.scopes.items(.decls)[0]; const decl_key = Declaration.Key{ .kind = .variable, .name = "Type" }; const decl_index = root_scope_decls.get(decl_key) orelse return null; - const decl = new_handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const decl = new_handle_document_scope.decls.items[@intFromEnum(decl_index)]; if (decl != .ast_node) return null; const var_decl = new_handle.tree.fullVarDecl(decl.ast_node) orelse return null; @@ -1475,9 +1483,9 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e // HACK: resolve std.ArrayList(T).Slice if (std.mem.endsWith(u8, node_handle.handle.uri, "array_list.zig") and if_node.payload_token != null and - std.mem.eql(u8, offsets.tokenToSlice(node_handle.handle.tree, if_node.payload_token.?), "a") and + std.mem.eql(u8, offsets.tokenToSlice(tree, if_node.payload_token.?), "a") and node_tags[if_node.ast.cond_expr] == .identifier and - std.mem.eql(u8, offsets.tokenToSlice(node_handle.handle.tree, main_tokens[if_node.ast.cond_expr]), "alignment")) + std.mem.eql(u8, offsets.tokenToSlice(tree, main_tokens[if_node.ast.cond_expr]), "alignment")) blk: { return (try analyser.resolveTypeOfNodeInternal(.{ .handle = handle, .node = if_node.ast.then_expr })) orelse break :blk; } @@ -1784,7 +1792,7 @@ pub const Type = struct { pub const TypeWithHandle = struct { type: Type, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, const Context = struct { // Note that we don't hash/equate descriptors to remove @@ -1878,7 +1886,7 @@ pub const TypeWithHandle = struct { pub const Deduplicator = std.HashMapUnmanaged(TypeWithHandle, void, TypeWithHandle.Context, std.hash_map.default_max_load_percentage); - pub fn fromEither(allocator: std.mem.Allocator, entries: []const Type.EitherEntry, handle: *const DocumentStore.Handle) error{OutOfMemory}!?TypeWithHandle { + pub fn fromEither(allocator: std.mem.Allocator, entries: []const Type.EitherEntry, handle: *DocumentStore.Handle) error{OutOfMemory}!?TypeWithHandle { if (entries.len == 0) return null; @@ -2026,6 +2034,13 @@ pub const TypeWithHandle = struct { }; } + pub fn isContainer(self: TypeWithHandle) bool { + return switch (self.type.data) { + .other => |node| ast.isContainer(self.handle.tree, node), + else => false, + }; + } + pub fn typeDefinitionToken(self: TypeWithHandle) ?TokenWithHandle { return switch (self.type.data) { .other => |n| .{ @@ -2036,10 +2051,10 @@ pub const TypeWithHandle = struct { }; } - pub fn docComments(self: TypeWithHandle, allocator: std.mem.Allocator) !?[]const u8 { + pub fn docComments(self: TypeWithHandle, allocator: std.mem.Allocator) error{OutOfMemory}!?[]const u8 { if (self.type.is_type_val) { switch (self.type.data) { - .other => |n| return getDocComments(allocator, self.handle.tree, n), + .other => |n| return try getDocComments(allocator, self.handle.tree, n), else => {}, } } @@ -2163,7 +2178,7 @@ pub const NodeWithUri = struct { pub const NodeWithHandle = struct { node: Ast.Node.Index, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pub fn eql(a: NodeWithHandle, b: NodeWithHandle) bool { if (a.node != b.node) return false; @@ -2176,7 +2191,7 @@ pub const FieldAccessReturn = struct { unwrapped: ?TypeWithHandle = null, }; -pub fn getFieldAccessType(analyser: *Analyser, handle: *const DocumentStore.Handle, source_index: usize, tokenizer: *std.zig.Tokenizer) !?FieldAccessReturn { +pub fn getFieldAccessType(analyser: *Analyser, handle: *DocumentStore.Handle, source_index: usize, tokenizer: *std.zig.Tokenizer) !?FieldAccessReturn { analyser.bound_type_params.clearRetainingCapacity(); var current_type: ?TypeWithHandle = null; @@ -2678,7 +2693,7 @@ pub fn getPositionContext( pub const TokenWithHandle = struct { token: Ast.TokenIndex, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, }; pub const ErrorUnionSide = enum { left, right }; @@ -2780,7 +2795,7 @@ pub const Declaration = union(enum) { pub const DeclWithHandle = struct { decl: *Declaration, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pub fn eql(a: DeclWithHandle, b: DeclWithHandle) bool { return a.decl.eql(b.decl.*) and std.mem.eql(u8, a.handle.uri, b.handle.uri); @@ -2811,7 +2826,7 @@ pub const DeclWithHandle = struct { }; } - pub fn definitionToken(self: DeclWithHandle, analyser: *Analyser, resolve_alias: bool) !TokenWithHandle { + pub fn definitionToken(self: DeclWithHandle, analyser: *Analyser, resolve_alias: bool) error{OutOfMemory}!TokenWithHandle { if (resolve_alias) { switch (self.decl.*) { .ast_node => |node| { @@ -2832,7 +2847,7 @@ pub const DeclWithHandle = struct { return .{ .token = self.nameToken(), .handle = self.handle }; } - pub fn docComments(self: DeclWithHandle, allocator: std.mem.Allocator) !?[]const u8 { + pub fn docComments(self: DeclWithHandle, allocator: std.mem.Allocator) error{OutOfMemory}!?[]const u8 { const tree = self.handle.tree; return switch (self.decl.*) { // TODO: delete redundant `Analyser.` @@ -2854,7 +2869,7 @@ pub const DeclWithHandle = struct { }; } - pub fn resolveType(self: DeclWithHandle, analyser: *Analyser) !?TypeWithHandle { + pub fn resolveType(self: DeclWithHandle, analyser: *Analyser) error{OutOfMemory}!?TypeWithHandle { const tree = self.handle.tree; const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -2900,7 +2915,7 @@ pub const DeclWithHandle = struct { var handle = analyser.store.getOrLoadHandle(ref.uri).?; var call_buf: [1]Ast.Node.Index = undefined; - var call = handle.tree.fullCall(&call_buf, ref.call_node).?; + var call = tree.fullCall(&call_buf, ref.call_node).?; const real_param_idx = if (func_params_len != 0 and pay.param_index != 0 and call.ast.params.len == func_params_len - 1) pay.param_index - 1 @@ -2920,7 +2935,7 @@ pub const DeclWithHandle = struct { var gop = try deduplicator.getOrPut(analyser.gpa, ty); if (gop.found_existing) continue; - var loc = offsets.tokenToPosition(handle.tree, main_tokens[call.ast.params[real_param_idx]], .@"utf-8"); + var loc = offsets.tokenToPosition(tree, main_tokens[call.ast.params[real_param_idx]], .@"utf-8"); try possible.append(analyser.arena.allocator(), .{ // TODO: Dedup .type_with_handle = ty, .descriptor = try std.fmt.allocPrint(analyser.arena.allocator(), "{s}:{d}:{d}", .{ handle.uri, loc.line + 1, loc.character + 1 }), @@ -2933,7 +2948,7 @@ pub const DeclWithHandle = struct { return maybe_type_handle; } - if (isMetaType(self.handle.tree, param.type_expr)) { + if (isMetaType(tree, param.type_expr)) { if (analyser.bound_type_params.get(.{ .func = pay.func, .param_index = pay.param_index })) |resolved_type| { return resolved_type; } @@ -3007,13 +3022,16 @@ pub const DeclWithHandle = struct { } }; -fn findContainerScopeIndex(container_handle: NodeWithHandle) ?usize { +fn findContainerScopeIndex(container_handle: NodeWithHandle) !?usize { const container = container_handle.node; const handle = container_handle.handle; - if (!ast.isContainer(handle.tree, container)) return null; + const tree = handle.tree; + const document_scope = try handle.getDocumentScope(); + + if (!ast.isContainer(tree, container)) return null; - return for (handle.document_scope.scopes.items(.data), 0..) |data, scope_index| { + return for (document_scope.scopes.items(.data), 0..) |data, scope_index| { switch (data) { .container => |node| if (node == container) { break scope_index; @@ -3026,7 +3044,7 @@ fn findContainerScopeIndex(container_handle: NodeWithHandle) ?usize { fn iterateSymbolsContainerInternal( analyser: *Analyser, container_handle: NodeWithHandle, - orig_handle: *const DocumentStore.Handle, + orig_handle: *DocumentStore.Handle, comptime callback: anytype, context: anytype, instance_access: bool, @@ -3035,18 +3053,19 @@ fn iterateSymbolsContainerInternal( const handle = container_handle.handle; const tree = handle.tree; + const document_scope = try handle.getDocumentScope(); const node_tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); const main_token = tree.nodes.items(.main_token)[container]; const is_enum = token_tags[main_token] == .keyword_enum; - const scope_decls = handle.document_scope.scopes.items(.decls); - const scope_uses = handle.document_scope.scopes.items(.uses); - const container_scope_index = findContainerScopeIndex(container_handle) orelse return; + const scope_decls = document_scope.scopes.items(.decls); + const scope_uses = document_scope.scopes.items(.uses); + const container_scope_index = try findContainerScopeIndex(container_handle) orelse return; for (scope_decls[container_scope_index].values()) |decl_index| { - const decl = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const decl = &document_scope.decls.items[@intFromEnum(decl_index)]; switch (decl.*) { .ast_node => |node| switch (node_tags[node]) { .container_field_init, @@ -3093,7 +3112,7 @@ fn iterateSymbolsContainerInternal( fn iterateUsingnamespaceContainerSymbols( analyser: *Analyser, usingnamespace_node: NodeWithHandle, - orig_handle: *const DocumentStore.Handle, + orig_handle: *DocumentStore.Handle, comptime callback: anytype, context: anytype, instance_access: bool, @@ -3102,13 +3121,14 @@ fn iterateUsingnamespaceContainerSymbols( if (gop.found_existing) return; const handle = usingnamespace_node.handle; + const tree = handle.tree; - const use_token = handle.tree.nodes.items(.main_token)[usingnamespace_node.node]; - const is_pub = use_token > 0 and handle.tree.tokens.items(.tag)[use_token - 1] == .keyword_pub; + const use_token = tree.nodes.items(.main_token)[usingnamespace_node.node]; + const is_pub = use_token > 0 and tree.tokens.items(.tag)[use_token - 1] == .keyword_pub; if (handle != orig_handle and !is_pub) return; const use_expr = (try analyser.resolveTypeOfNode(.{ - .node = handle.tree.nodes.items(.data)[usingnamespace_node.node].lhs, + .node = tree.nodes.items(.data)[usingnamespace_node.node].lhs, .handle = handle, })) orelse return; @@ -3175,7 +3195,7 @@ fn iterateEnclosingScopes(document_scope: DocumentScope, source_index: usize) En pub fn iterateSymbolsContainer( analyser: *Analyser, container_handle: NodeWithHandle, - orig_handle: *const DocumentStore.Handle, + orig_handle: *DocumentStore.Handle, comptime callback: anytype, context: anytype, instance_access: bool, @@ -3184,13 +3204,14 @@ pub fn iterateSymbolsContainer( return try analyser.iterateSymbolsContainerInternal(container_handle, orig_handle, callback, context, instance_access); } -pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype) error{OutOfMemory}!void { - const scope_decls = handle.document_scope.scopes.items(.decls); +pub fn iterateLabels(handle: *DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype) error{OutOfMemory}!void { + const document_scope = try handle.getDocumentScope(); + const scope_decls = document_scope.scopes.items(.decls); - var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); + var scope_iterator = iterateEnclosingScopes(document_scope, source_index); while (scope_iterator.next()) |scope_index| { for (scope_decls[@intFromEnum(scope_index)].values()) |decl_index| { - const decl = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const decl = &document_scope.decls.items[@intFromEnum(decl_index)]; if (decl.* != .label_decl) continue; try callback(context, DeclWithHandle{ .decl = decl, .handle = handle }); } @@ -3199,19 +3220,21 @@ pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, c fn iterateSymbolsGlobalInternal( analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype, ) error{OutOfMemory}!void { - const scope_decls = handle.document_scope.scopes.items(.decls); - const scope_uses = handle.document_scope.scopes.items(.uses); + const document_scope = try handle.getDocumentScope(); + const scope_decls = document_scope.scopes.items(.decls); + const scope_uses = document_scope.scopes.items(.uses); - var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); + var scope_iterator = iterateEnclosingScopes(document_scope, source_index); while (scope_iterator.next()) |scope_index| { + const tree = handle.tree; for (scope_decls[@intFromEnum(scope_index)].values()) |decl_index| { - const decl = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; - if (decl.* == .ast_node and handle.tree.nodes.items(.tag)[decl.ast_node].isContainerField()) continue; + const decl = &document_scope.decls.items[@intFromEnum(decl_index)]; + if (decl.* == .ast_node and tree.nodes.items(.tag)[decl.ast_node].isContainerField()) continue; if (decl.* == .label_decl) continue; try callback(context, DeclWithHandle{ .decl = decl, .handle = handle }); } @@ -3230,7 +3253,7 @@ fn iterateSymbolsGlobalInternal( pub fn iterateSymbolsGlobal( analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype, @@ -3239,8 +3262,8 @@ pub fn iterateSymbolsGlobal( return try analyser.iterateSymbolsGlobalInternal(handle, source_index, callback, context); } -pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usize) Scope.Index { - var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); +pub fn innermostBlockScopeIndex(document_scope: DocumentScope, source_index: usize) Scope.Index { + var scope_iterator = iterateEnclosingScopes(document_scope, source_index); var scope_index: Scope.Index = .none; while (scope_iterator.next()) |inner_scope| { scope_index = inner_scope; @@ -3248,15 +3271,15 @@ pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usiz return scope_index; } -pub fn innermostBlockScope(handle: DocumentStore.Handle, source_index: usize) Ast.Node.Index { - return innermostBlockScopeInternal(handle, source_index, false); +pub fn innermostBlockScope(document_scope: DocumentScope, source_index: usize) Ast.Node.Index { + return innermostBlockScopeInternal(document_scope, source_index, false); } -fn innermostBlockScopeInternal(handle: DocumentStore.Handle, source_index: usize, skip_block: bool) Ast.Node.Index { - const scope_datas = handle.document_scope.scopes.items(.data); - const scope_parents = handle.document_scope.scopes.items(.parent); +fn innermostBlockScopeInternal(document_scope: DocumentScope, source_index: usize, skip_block: bool) Ast.Node.Index { + const scope_datas = document_scope.scopes.items(.data); + const scope_parents = document_scope.scopes.items(.parent); - var scope_index = innermostBlockScopeIndex(handle, source_index); + var scope_index = innermostBlockScopeIndex(document_scope, source_index); while (true) { defer scope_index = scope_parents[@intFromEnum(scope_index)]; const data = scope_datas[@intFromEnum(scope_index)]; @@ -3271,13 +3294,14 @@ fn innermostBlockScopeInternal(handle: DocumentStore.Handle, source_index: usize } } -pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usize) TypeWithHandle { - const scope_datas = handle.document_scope.scopes.items(.data); +pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) error{OutOfMemory}!TypeWithHandle { + const document_scope = try handle.getDocumentScope(); + const scope_datas = document_scope.scopes.items(.data); var current = scope_datas[0].container; - if (handle.document_scope.scopes.len == 1) return TypeWithHandle.typeVal(.{ .node = current, .handle = handle }); + if (document_scope.scopes.len == 1) return TypeWithHandle.typeVal(.{ .node = current, .handle = handle }); - var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); + var scope_iterator = iterateEnclosingScopes(document_scope, source_index); while (scope_iterator.next()) |scope_index| { switch (scope_datas[@intFromEnum(scope_index)]) { .container => |node| current = node, @@ -3287,15 +3311,16 @@ pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usi return TypeWithHandle.typeVal(.{ .node = current, .handle = handle }); } -fn resolveUse(analyser: *Analyser, uses: []const Ast.Node.Index, symbol: []const u8, handle: *const DocumentStore.Handle) error{OutOfMemory}!?DeclWithHandle { +fn resolveUse(analyser: *Analyser, uses: []const Ast.Node.Index, symbol: []const u8, handle: *DocumentStore.Handle) error{OutOfMemory}!?DeclWithHandle { analyser.use_trail.clearRetainingCapacity(); for (uses) |index| { const gop = try analyser.use_trail.getOrPut(analyser.gpa, .{ .node = index, .uri = handle.uri }); if (gop.found_existing) continue; - if (handle.tree.nodes.items(.data).len <= index) continue; + const tree = handle.tree; + if (tree.nodes.items(.data).len <= index) continue; - const expr = .{ .node = handle.tree.nodes.items(.data)[index].lhs, .handle = handle }; + const expr = .{ .node = tree.nodes.items(.data)[index].lhs, .handle = handle }; const expr_type = (try analyser.resolveTypeOfNode(expr)) orelse continue; @@ -3314,17 +3339,18 @@ fn resolveUse(analyser: *Analyser, uses: []const Ast.Node.Index, symbol: []const } pub fn lookupLabel( - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, symbol: []const u8, source_index: usize, ) error{OutOfMemory}!?DeclWithHandle { - const scope_decls = handle.document_scope.scopes.items(.decls); + const document_scope = try handle.getDocumentScope(); + const scope_decls = document_scope.scopes.items(.decls); - var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); + var scope_iterator = iterateEnclosingScopes(document_scope, source_index); while (scope_iterator.next()) |scope_index| { const decl_key = Declaration.Key{ .kind = .variable, .name = symbol }; const decl_index = scope_decls[@intFromEnum(scope_index)].get(decl_key) orelse continue; - const decl = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const decl = &document_scope.decls.items[@intFromEnum(decl_index)]; if (decl.* != .label_decl) continue; @@ -3335,7 +3361,7 @@ pub fn lookupLabel( pub fn lookupSymbolGlobal( analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, symbol: []const u8, source_index: usize, ) error{OutOfMemory}!?DeclWithHandle { @@ -3343,17 +3369,18 @@ pub fn lookupSymbolGlobal( const var_decl_key = Declaration.Key{ .kind = .variable, .name = symbol }; const tree = handle.tree; - const scope_parents = handle.document_scope.scopes.items(.parent); - const scope_decls = handle.document_scope.scopes.items(.decls); - const scope_uses = handle.document_scope.scopes.items(.uses); + const document_scope = try handle.getDocumentScope(); + const scope_parents = document_scope.scopes.items(.parent); + const scope_decls = document_scope.scopes.items(.decls); + const scope_uses = document_scope.scopes.items(.uses); - var current_scope = innermostBlockScopeIndex(handle.*, source_index); + var current_scope = innermostBlockScopeIndex(document_scope, source_index); while (current_scope != .none) { const scope_index = @intFromEnum(current_scope); defer current_scope = scope_parents[scope_index]; if (scope_decls[scope_index].get(field_decl_key)) |decl_index| { - const field_decl = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const field_decl = &document_scope.decls.items[@intFromEnum(decl_index)]; std.debug.assert(field_decl.* == .ast_node); var field = tree.fullContainerField(field_decl.ast_node).?; @@ -3364,7 +3391,7 @@ pub fn lookupSymbolGlobal( return DeclWithHandle{ .decl = field_decl, .handle = handle }; } if (scope_decls[scope_index].get(var_decl_key)) |decl_index| { - const candidate = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const candidate = &document_scope.decls.items[@intFromEnum(decl_index)]; return DeclWithHandle{ .decl = candidate, .handle = handle }; } if (try analyser.resolveUse(scope_uses[scope_index], symbol, handle)) |result| return result; @@ -3380,13 +3407,14 @@ pub fn lookupSymbolContainer( kind: Declaration.Kind, ) error{OutOfMemory}!?DeclWithHandle { const handle = container_handle.handle; - const scope_decls = handle.document_scope.scopes.items(.decls); - const scope_uses = handle.document_scope.scopes.items(.uses); + const document_scope = try handle.getDocumentScope(); + const scope_decls = document_scope.scopes.items(.decls); + const scope_uses = document_scope.scopes.items(.uses); const decl_key = Declaration.Key{ .kind = kind, .name = symbol }; - if (findContainerScopeIndex(container_handle)) |container_scope_index| { + if (try findContainerScopeIndex(container_handle)) |container_scope_index| { if (scope_decls[container_scope_index].get(decl_key)) |decl_index| { - const decl = &handle.document_scope.decls.items[@intFromEnum(decl_index)]; + const decl = &document_scope.decls.items[@intFromEnum(decl_index)]; return DeclWithHandle{ .decl = decl, .handle = handle }; } @@ -3398,7 +3426,7 @@ pub fn lookupSymbolContainer( pub fn lookupSymbolFieldInit( analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, field_name: []const u8, nodes: []Ast.Node.Index, ) error{OutOfMemory}!?DeclWithHandle { @@ -3430,7 +3458,7 @@ pub fn lookupSymbolFieldInit( pub fn resolveExpressionType( analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, node: Ast.Node.Index, ancestors: []Ast.Node.Index, ) error{OutOfMemory}!?TypeWithHandle { @@ -3446,7 +3474,7 @@ pub fn resolveExpressionType( pub fn resolveExpressionTypeFromAncestors( analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, node: Ast.Node.Index, ancestors: []Ast.Node.Index, ) error{OutOfMemory}!?TypeWithHandle { @@ -3714,20 +3742,21 @@ pub fn resolveExpressionTypeFromAncestors( return null; } -pub fn identifierLocFromPosition(pos_index: usize, handle: *const DocumentStore.Handle) ?std.zig.Token.Loc { - if (pos_index + 1 >= handle.text.len) return null; +pub fn identifierLocFromPosition(pos_index: usize, handle: *DocumentStore.Handle) ?std.zig.Token.Loc { + if (pos_index + 1 >= handle.tree.source.len) return null; var start_idx = pos_index; - while (start_idx > 0 and Analyser.isSymbolChar(handle.text[start_idx - 1])) { + while (start_idx > 0 and Analyser.isSymbolChar(handle.tree.source[start_idx - 1])) { start_idx -= 1; } - const token_index = offsets.sourceIndexToTokenIndex(handle.tree, start_idx); - if (handle.tree.tokens.items(.tag)[token_index] == .identifier) - return offsets.tokenToLoc(handle.tree, token_index); + const tree = handle.tree; + const token_index = offsets.sourceIndexToTokenIndex(tree, start_idx); + if (tree.tokens.items(.tag)[token_index] == .identifier) + return offsets.tokenToLoc(tree, token_index); var end_idx = pos_index; - while (end_idx < handle.text.len and Analyser.isSymbolChar(handle.text[end_idx])) { + while (end_idx < handle.tree.source.len and Analyser.isSymbolChar(handle.tree.source[end_idx])) { end_idx += 1; } @@ -3737,7 +3766,7 @@ pub fn identifierLocFromPosition(pos_index: usize, handle: *const DocumentStore. pub fn getLabelGlobal( pos_index: usize, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, name: []const u8, ) error{OutOfMemory}!?DeclWithHandle { const tracy_zone = tracy.trace(@src()); @@ -3749,7 +3778,7 @@ pub fn getLabelGlobal( pub fn getSymbolGlobal( analyser: *Analyser, pos_index: usize, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, name: []const u8, ) error{OutOfMemory}!?DeclWithHandle { const tracy_zone = tracy.trace(@src()); @@ -3761,14 +3790,15 @@ pub fn getSymbolGlobal( pub fn getSymbolEnumLiteral( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, name: []const u8, ) error{OutOfMemory}!?DeclWithHandle { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const nodes = try ast.nodesOverlappingIndex(arena, handle.tree, source_index); + const tree = handle.tree; + const nodes = try ast.nodesOverlappingIndex(arena, tree, source_index); if (nodes.len == 0) return null; return analyser.lookupSymbolFieldInit(handle, name, nodes); } @@ -3777,7 +3807,7 @@ pub fn getSymbolEnumLiteral( pub fn getSymbolFieldAccesses( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, held_loc: offsets.Loc, name: []const u8, @@ -3785,7 +3815,7 @@ pub fn getSymbolFieldAccesses( const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const held_range = try arena.dupeZ(u8, offsets.locToSlice(handle.text, held_loc)); + const held_range = try arena.dupeZ(u8, offsets.locToSlice(handle.tree.source, held_loc)); var tokenizer = std.zig.Tokenizer.init(held_range); var decls_with_handles = std.ArrayListUnmanaged(DeclWithHandle){}; @@ -4454,7 +4484,7 @@ fn makeScopeAt( pub const ReferencedType = struct { str: []const u8, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, token: Ast.TokenIndex, pub const Collector = struct { @@ -4663,7 +4693,8 @@ fn addReferencedTypes( return str; } if (token >= 1 and token_tags[token - 1] == .keyword_return) { - const func_node = innermostBlockScopeInternal(handle.*, token_starts[token - 1], true); + const document_scope = try handle.getDocumentScope(); + const func_node = innermostBlockScopeInternal(document_scope, token_starts[token - 1], true); var buf: [1]Ast.Node.Index = undefined; const func = tree.fullFnProto(&buf, func_node) orelse return null; const func_name_token = func.name_token orelse return null; diff --git a/src/features/code_actions.zig b/src/features/code_actions.zig index fbee044d62..74dd0ad645 100644 --- a/src/features/code_actions.zig +++ b/src/features/code_actions.zig @@ -11,7 +11,7 @@ const tracy = @import("../tracy.zig"); pub const Builder = struct { arena: std.mem.Allocator, analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, offset_encoding: offsets.Encoding, pub fn generateCodeAction( @@ -22,7 +22,7 @@ pub const Builder = struct { ) error{OutOfMemory}!void { const kind = DiagnosticKind.parse(diagnostic.message) orelse return; - const loc = offsets.rangeToLoc(builder.handle.text, diagnostic.range, builder.offset_encoding); + const loc = offsets.rangeToLoc(builder.handle.tree.source, diagnostic.range, builder.offset_encoding); switch (kind) { .unused => |id| switch (id) { @@ -47,12 +47,12 @@ pub const Builder = struct { } pub fn createTextEditLoc(self: *Builder, loc: offsets.Loc, new_text: []const u8) types.TextEdit { - const range = offsets.locToRange(self.handle.text, loc, self.offset_encoding); + const range = offsets.locToRange(self.handle.tree.source, loc, self.offset_encoding); return types.TextEdit{ .range = range, .newText = new_text }; } pub fn createTextEditPos(self: *Builder, index: usize, new_text: []const u8) types.TextEdit { - const position = offsets.indexToPosition(self.handle.text, index, self.offset_encoding); + const position = offsets.indexToPosition(self.handle.tree.source, index, self.offset_encoding); return types.TextEdit{ .range = .{ .start = position, .end = position }, .newText = new_text }; } @@ -65,7 +65,7 @@ pub const Builder = struct { }; fn handleNonCamelcaseFunction(builder: *Builder, actions: *std.ArrayListUnmanaged(types.CodeAction), loc: offsets.Loc) !void { - const identifier_name = offsets.locToSlice(builder.handle.text, loc); + const identifier_name = offsets.locToSlice(builder.handle.tree.source, loc); if (std.mem.allEqual(u8, identifier_name, '_')) return; @@ -85,7 +85,7 @@ fn handleUnusedFunctionParameter(builder: *Builder, actions: *std.ArrayListUnman const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const identifier_name = offsets.locToSlice(builder.handle.text, loc); + const identifier_name = offsets.locToSlice(builder.handle.tree.source, loc); const tree = builder.handle.tree; const node_tags = tree.nodes.items(.tag); @@ -135,7 +135,7 @@ fn handleUnusedVariableOrConstant(builder: *Builder, actions: *std.ArrayListUnma const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const identifier_name = offsets.locToSlice(builder.handle.text, loc); + const identifier_name = offsets.locToSlice(builder.handle.tree.source, loc); const tree = builder.handle.tree; const token_tags = tree.tokens.items(.tag); @@ -180,15 +180,16 @@ fn handleUnusedCapture( const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const capture_loc = getCaptureLoc(builder.handle.text, loc) orelse return; + const source = builder.handle.tree.source; + const capture_loc = getCaptureLoc(source, loc) orelse return; // look for next non-whitespace after last '|'. if its a '{' we can insert discards. // this means bare loop/switch captures (w/out curlies) aren't supported. var block_start = capture_loc.end + 1; var is_comment = false; - while (block_start < builder.handle.text.len) : (block_start += 1) { - switch (builder.handle.text[block_start]) { - '/' => if (block_start + 1 < builder.handle.text.len and builder.handle.text[block_start + 1] == '/') { + while (block_start < source.len) : (block_start += 1) { + switch (source[block_start]) { + '/' => if (block_start + 1 < source.len and source[block_start + 1] == '/') { is_comment = true; // we already know the next character is a `/` so lets skip that iteration block_start += 1; @@ -201,12 +202,12 @@ fn handleUnusedCapture( else => |c| if (!std.ascii.isWhitespace(c) and !is_comment) break, } } - if (builder.handle.text[block_start] != '{') { + if (source[block_start] != '{') { return; } const block_start_loc = offsets.Loc{ .start = block_start + 1, .end = block_start + 1 }; - const identifier_name = builder.handle.text[loc.start..loc.end]; + const identifier_name = source[loc.start..loc.end]; const new_text = try createDiscardText(builder, identifier_name, block_start, true); const action1 = .{ .title = "discard capture", @@ -242,7 +243,7 @@ fn handlePointlessDiscard(builder: *Builder, actions: *std.ArrayListUnmanaged(ty const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const edit_loc = getDiscardLoc(builder.handle.text, loc) orelse return; + const edit_loc = getDiscardLoc(builder.handle.tree.source, loc) orelse return; try actions.append(builder.arena, .{ .title = "remove pointless discard", @@ -311,7 +312,7 @@ fn createCamelcaseText(allocator: std.mem.Allocator, identifier: []const u8) ![] // returns a discard string `\n{indent}_ = identifier_name;` fn createDiscardText(builder: *Builder, identifier_name: []const u8, declaration_start: usize, add_block_indentation: bool) ![]const u8 { const indent = find_indent: { - const line = offsets.lineSliceUntilIndex(builder.handle.text, declaration_start); + const line = offsets.lineSliceUntilIndex(builder.handle.tree.source, declaration_start); for (line, 0..) |char, i| { if (!std.ascii.isWhitespace(char)) { break :find_indent line[0..i]; @@ -319,7 +320,7 @@ fn createDiscardText(builder: *Builder, identifier_name: []const u8, declaration } break :find_indent line; }; - const additional_indent = if (add_block_indentation) detectIndentation(builder.handle.text) else ""; + const additional_indent = if (add_block_indentation) detectIndentation(builder.handle.tree.source) else ""; const new_text_len = 1 + indent.len + additional_indent.len + "_ = ;".len + identifier_name.len; var new_text = try std.ArrayListUnmanaged(u8).initCapacity(builder.arena, new_text_len); diff --git a/src/features/completions.zig b/src/features/completions.zig index a4a89015bd..e96b15481c 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -22,7 +22,7 @@ fn typeToCompletion( arena: std.mem.Allocator, list: *std.ArrayListUnmanaged(types.CompletionItem), field_access: Analyser.FieldAccessReturn, - orig_handle: *const DocumentStore.Handle, + orig_handle: *DocumentStore.Handle, either_descriptor: ?[]const u8, ) error{OutOfMemory}!void { const tracy_zone = tracy.trace(@src()); @@ -120,7 +120,7 @@ fn nodeToCompletion( list: *std.ArrayListUnmanaged(types.CompletionItem), node_handle: Analyser.NodeWithHandle, unwrapped: ?Analyser.TypeWithHandle, - orig_handle: *const DocumentStore.Handle, + orig_handle: *DocumentStore.Handle, orig_name: ?[]const u8, orig_doc: ?[]const u8, is_type_val: bool, @@ -371,7 +371,7 @@ const DeclToCompletionContext = struct { analyser: *Analyser, arena: std.mem.Allocator, completions: *std.ArrayListUnmanaged(types.CompletionItem), - orig_handle: *const DocumentStore.Handle, + orig_handle: *DocumentStore.Handle, orig_name: ?[]const u8 = null, orig_doc: ?[]const u8 = null, parent_is_type_val: ?bool = null, @@ -477,7 +477,7 @@ fn completeLabel( server: *Server, analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pos_index: usize, ) error{OutOfMemory}![]types.CompletionItem { const tracy_zone = tracy.trace(@src()); @@ -547,7 +547,7 @@ fn completeBuiltin(server: *Server, arena: std.mem.Allocator) error{OutOfMemory} return completions; } -fn completeGlobal(server: *Server, analyser: *Analyser, arena: std.mem.Allocator, handle: *const DocumentStore.Handle, pos_index: usize) error{OutOfMemory}![]types.CompletionItem { +fn completeGlobal(server: *Server, analyser: *Analyser, arena: std.mem.Allocator, handle: *DocumentStore.Handle, pos_index: usize) error{OutOfMemory}![]types.CompletionItem { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -567,7 +567,7 @@ fn completeGlobal(server: *Server, analyser: *Analyser, arena: std.mem.Allocator return completions.toOwnedSlice(arena); } -fn completeFieldAccess(server: *Server, analyser: *Analyser, arena: std.mem.Allocator, handle: *const DocumentStore.Handle, source_index: usize, loc: offsets.Loc) error{OutOfMemory}!?[]types.CompletionItem { +fn completeFieldAccess(server: *Server, analyser: *Analyser, arena: std.mem.Allocator, handle: *DocumentStore.Handle, source_index: usize, loc: offsets.Loc) error{OutOfMemory}!?[]types.CompletionItem { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -761,7 +761,7 @@ fn formatDetailedLabel(item: *types.CompletionItem, arena: std.mem.Allocator) er // logger.info("labelDetails: {s} :: {s}", .{item.labelDetails.?.detail, item.labelDetails.?.description}); } -fn completeError(server: *Server, arena: std.mem.Allocator, handle: *const DocumentStore.Handle) error{OutOfMemory}![]types.CompletionItem { +fn completeError(server: *Server, arena: std.mem.Allocator, handle: *DocumentStore.Handle) error{OutOfMemory}![]types.CompletionItem { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -922,7 +922,7 @@ fn completeFileSystemStringLiteral( return completions.keys(); } -pub fn completionAtIndex(server: *Server, analyser: *Analyser, arena: std.mem.Allocator, handle: *const DocumentStore.Handle, source_index: usize) error{OutOfMemory}!?types.CompletionList { +pub fn completionAtIndex(server: *Server, analyser: *Analyser, arena: std.mem.Allocator, handle: *DocumentStore.Handle, source_index: usize) error{OutOfMemory}!?types.CompletionList { const source = handle.tree.source; // Provide `top_level_decl_data` only if `offsets.lineSliceUntilIndex(handle.tree.source, source_index).len` is diff --git a/src/features/diagnostics.zig b/src/features/diagnostics.zig index b437a8954e..b6caa159c4 100644 --- a/src/features/diagnostics.zig +++ b/src/features/diagnostics.zig @@ -16,7 +16,7 @@ const tracy = @import("../tracy.zig"); const Module = @import("../stage2/Module.zig"); const Zir = @import("../stage2/Zir.zig"); -pub fn generateDiagnostics(server: *Server, arena: std.mem.Allocator, handle: DocumentStore.Handle) error{OutOfMemory}!types.PublishDiagnosticsParams { +pub fn generateDiagnostics(server: *Server, arena: std.mem.Allocator, handle: *DocumentStore.Handle) error{OutOfMemory}!types.PublishDiagnosticsParams { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -128,7 +128,7 @@ pub fn generateDiagnostics(server: *Server, arena: std.mem.Allocator, handle: Do const err_msg = error_bundle.getErrorMessage(err_msg_index); diagnostics.appendAssumeCapacity(.{ - .range = offsets.nodeToRange(handle.tree, node, server.offset_encoding), + .range = offsets.nodeToRange(tree, node, server.offset_encoding), .severity = .Error, .code = .{ .string = "cImport" }, .source = "zls", @@ -169,7 +169,7 @@ pub fn generateDiagnostics(server: *Server, arena: std.mem.Allocator, handle: Do try diagnostics.ensureUnusedCapacity(arena, handle.analysis_errors.items.len); for (handle.analysis_errors.items) |err| { diagnostics.appendAssumeCapacity(.{ - .range = offsets.locToRange(handle.tree.source, err.loc, server.offset_encoding), + .range = offsets.locToRange(tree.source, err.loc, server.offset_encoding), .severity = .Error, .code = .{ .string = err.code }, .source = "zls", @@ -354,7 +354,7 @@ pub fn generateBuildOnSaveDiagnostics( pub fn getAstCheckDiagnostics( server: *Server, arena: std.mem.Allocator, - handle: DocumentStore.Handle, + handle: *DocumentStore.Handle, diagnostics: *std.ArrayListUnmanaged(types.Diagnostic), ) error{OutOfMemory}!void { std.debug.assert(server.config.enable_ast_check_diagnostics); @@ -368,18 +368,14 @@ pub fn getAstCheckDiagnostics( log.err("failed to run ast-check: {}", .{err}); }; } else { - std.debug.assert(server.document_store.wantZir()); - switch (handle.zir_status) { - .none, .outdated => {}, - .done => try getDiagnosticsFromZir(server, arena, handle, diagnostics), - } + try getDiagnosticsFromZir(server, arena, handle, diagnostics); } } fn getDiagnosticsFromAstCheck( server: *Server, arena: std.mem.Allocator, - handle: DocumentStore.Handle, + handle: *DocumentStore.Handle, diagnostics: *std.ArrayListUnmanaged(types.Diagnostic), ) !void { comptime std.debug.assert(std.process.can_spawn); @@ -399,7 +395,7 @@ fn getDiagnosticsFromAstCheck( log.warn("Failed to spawn zig ast-check process, error: {}", .{err}); return; }; - try process.stdin.?.writeAll(handle.text); + try process.stdin.?.writeAll(handle.tree.source); process.stdin.?.close(); process.stdin = null; @@ -440,8 +436,8 @@ fn getDiagnosticsFromAstCheck( }; // zig uses utf-8 encoding for character offsets - const position = offsets.convertPositionEncoding(handle.text, utf8_position, .@"utf-8", server.offset_encoding); - const range = offsets.tokenPositionToRange(handle.text, position, server.offset_encoding); + const position = offsets.convertPositionEncoding(handle.tree.source, utf8_position, .@"utf-8", server.offset_encoding); + const range = offsets.tokenPositionToRange(handle.tree.source, position, server.offset_encoding); const msg = pos_and_diag_iterator.rest()[1..]; @@ -491,28 +487,31 @@ fn getDiagnosticsFromAstCheck( fn getDiagnosticsFromZir( server: *const Server, arena: std.mem.Allocator, - handle: DocumentStore.Handle, + handle: *DocumentStore.Handle, diagnostics: *std.ArrayListUnmanaged(types.Diagnostic), ) error{OutOfMemory}!void { - std.debug.assert(handle.zir_status != .none); + const tree = handle.tree; + std.debug.assert(tree.errors.len == 0); + const zir = try handle.getZir(); + std.debug.assert(handle.getZirStatus() == .done); - const payload_index = handle.zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)]; + const payload_index = zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)]; if (payload_index == 0) return; - const header = handle.zir.extraData(Zir.Inst.CompileErrors, payload_index); + const header = zir.extraData(Zir.Inst.CompileErrors, payload_index); const items_len = header.data.items_len; try diagnostics.ensureUnusedCapacity(arena, items_len); var extra_index = header.end; for (0..items_len) |_| { - const item = handle.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); + const item = zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); extra_index = item.end; const err_loc = blk: { if (item.data.node != 0) { - break :blk offsets.nodeToLoc(handle.tree, item.data.node); + break :blk offsets.nodeToLoc(tree, item.data.node); } - const loc = offsets.tokenToLoc(handle.tree, item.data.token); + const loc = offsets.tokenToLoc(tree, item.data.token); break :blk offsets.Loc{ .start = loc.start + item.data.byte_offset, .end = loc.end, @@ -521,18 +520,18 @@ fn getDiagnosticsFromZir( var notes: []types.DiagnosticRelatedInformation = &.{}; if (item.data.notes != 0) { - const block = handle.zir.extraData(Zir.Inst.Block, item.data.notes); - const body = handle.zir.extra[block.end..][0..block.data.body_len]; + const block = zir.extraData(Zir.Inst.Block, item.data.notes); + const body = zir.extra[block.end..][0..block.data.body_len]; notes = try arena.alloc(types.DiagnosticRelatedInformation, body.len); for (notes, body) |*note, note_index| { - const note_item = handle.zir.extraData(Zir.Inst.CompileErrors.Item, note_index); - const msg = handle.zir.nullTerminatedString(note_item.data.msg); + const note_item = zir.extraData(Zir.Inst.CompileErrors.Item, note_index); + const msg = zir.nullTerminatedString(note_item.data.msg); const loc = blk: { if (note_item.data.node != 0) { - break :blk offsets.nodeToLoc(handle.tree, note_item.data.node); + break :blk offsets.nodeToLoc(tree, note_item.data.node); } - const loc = offsets.tokenToLoc(handle.tree, note_item.data.token); + const loc = offsets.tokenToLoc(tree, note_item.data.token); break :blk offsets.Loc{ .start = loc.start + note_item.data.byte_offset, .end = loc.end, @@ -542,16 +541,16 @@ fn getDiagnosticsFromZir( note.* = .{ .location = .{ .uri = handle.uri, - .range = offsets.locToRange(handle.text, loc, server.offset_encoding), + .range = offsets.locToRange(handle.tree.source, loc, server.offset_encoding), }, .message = msg, }; } } - const msg = handle.zir.nullTerminatedString(item.data.msg); + const msg = zir.nullTerminatedString(item.data.msg); diagnostics.appendAssumeCapacity(.{ - .range = offsets.locToRange(handle.text, err_loc, server.offset_encoding), + .range = offsets.locToRange(handle.tree.source, err_loc, server.offset_encoding), .severity = .Error, .code = .{ .string = "ast_check" }, .source = "zls", diff --git a/src/features/folding_range.zig b/src/features/folding_range.zig index a28ebb6f88..906fdcfd4e 100644 --- a/src/features/folding_range.zig +++ b/src/features/folding_range.zig @@ -246,7 +246,7 @@ pub fn generateFoldingRanges(allocator: std.mem.Allocator, tree: Ast, encoding: try builder.add(null, start_tok, end_tok, .exclusive, .exclusive); } else { // no members (yet), ie `const T = type {};` var start_tok = tree.firstToken(node); - while(token_tags[start_tok] != .l_brace) start_tok += 1; + while (token_tags[start_tok] != .l_brace) start_tok += 1; const end_tok = ast.lastToken(tree, node); try builder.add(null, start_tok, end_tok, .exclusive, .exclusive); } diff --git a/src/features/goto.zig b/src/features/goto.zig index ac98177ffe..3512f4dbfc 100644 --- a/src/features/goto.zig +++ b/src/features/goto.zig @@ -51,7 +51,7 @@ pub fn gotoDefinitionSymbol( pub fn gotoDefinitionLabel( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pos_index: usize, kind: GotoKind, offset_encoding: offsets.Encoding, @@ -61,15 +61,15 @@ pub fn gotoDefinitionLabel( _ = arena; const name_loc = Analyser.identifierLocFromPosition(pos_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const decl = (try Analyser.getLabelGlobal(pos_index, handle, name)) orelse return null; - return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.text, name_loc, offset_encoding), decl, kind, offset_encoding); + return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding); } pub fn gotoDefinitionGlobal( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pos_index: usize, kind: GotoKind, offset_encoding: offsets.Encoding, @@ -79,15 +79,15 @@ pub fn gotoDefinitionGlobal( _ = arena; const name_loc = Analyser.identifierLocFromPosition(pos_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const decl = (try analyser.getSymbolGlobal(pos_index, handle, name)) orelse return null; - return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.text, name_loc, offset_encoding), decl, kind, offset_encoding); + return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding); } pub fn gotoDefinitionEnumLiteral( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, kind: GotoKind, offset_encoding: offsets.Encoding, @@ -96,26 +96,27 @@ pub fn gotoDefinitionEnumLiteral( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(source_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const decl = (try analyser.getSymbolEnumLiteral(arena, handle, source_index, name)) orelse return null; - return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.text, name_loc, offset_encoding), decl, kind, offset_encoding); + return try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), decl, kind, offset_encoding); } pub fn gotoDefinitionBuiltin( document_store: *DocumentStore, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, loc: offsets.Loc, offset_encoding: offsets.Encoding, ) error{OutOfMemory}!?types.DefinitionLink { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const name_loc = offsets.tokenIndexToLoc(handle.text, loc.start); - const name = offsets.locToSlice(handle.text, name_loc); + const name_loc = offsets.tokenIndexToLoc(handle.tree.source, loc.start); + const name = offsets.locToSlice(handle.tree.source, name_loc); if (std.mem.eql(u8, name, "@cImport")) { + const tree = handle.tree; const index = for (handle.cimports.items(.node), 0..) |cimport_node, index| { - const main_token = handle.tree.nodes.items(.main_token)[cimport_node]; - if (loc.start == offsets.tokenToIndex(handle.tree, main_token)) break index; + const main_token = tree.nodes.items(.main_token)[cimport_node]; + if (loc.start == offsets.tokenToIndex(tree, main_token)) break index; } else return null; const hash = handle.cimports.items(.hash)[index]; @@ -127,7 +128,7 @@ pub fn gotoDefinitionBuiltin( switch (result) { .failure => return null, .success => |uri| return types.DefinitionLink{ - .originSelectionRange = offsets.locToRange(handle.text, name_loc, offset_encoding), + .originSelectionRange = offsets.locToRange(handle.tree.source, name_loc, offset_encoding), .targetUri = uri, .targetRange = target_range, .targetSelectionRange = target_range, @@ -141,7 +142,7 @@ pub fn gotoDefinitionBuiltin( pub fn gotoDefinitionFieldAccess( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, loc: offsets.Loc, kind: GotoKind, @@ -151,13 +152,13 @@ pub fn gotoDefinitionFieldAccess( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(source_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const held_loc = offsets.locMerge(loc, name_loc); const accesses = (try analyser.getSymbolFieldAccesses(arena, handle, source_index, held_loc, name)) orelse return null; var locs = std.ArrayListUnmanaged(types.DefinitionLink){}; for (accesses) |access| { - if (try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.text, name_loc, offset_encoding), access, kind, offset_encoding)) |l| + if (try gotoDefinitionSymbol(analyser, offsets.locToRange(handle.tree.source, name_loc, offset_encoding), access, kind, offset_encoding)) |l| try locs.append(arena, l); } @@ -171,7 +172,7 @@ pub fn gotoDefinitionString( document_store: *DocumentStore, arena: std.mem.Allocator, pos_context: Analyser.PositionContext, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, offset_encoding: offsets.Encoding, ) error{OutOfMemory}!?types.DefinitionLink { const tracy_zone = tracy.trace(@src()); @@ -215,7 +216,7 @@ pub fn gotoDefinitionString( .end = .{ .line = 0, .character = 0 }, }; return types.DefinitionLink{ - .originSelectionRange = offsets.locToRange(handle.text, import_str_loc, offset_encoding), + .originSelectionRange = offsets.locToRange(handle.tree.source, import_str_loc, offset_encoding), .targetUri = uri orelse return null, .targetRange = target_range, .targetSelectionRange = target_range, @@ -226,12 +227,12 @@ pub fn goto( analyser: *Analyser, document_store: *DocumentStore, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, kind: GotoKind, offset_encoding: offsets.Encoding, ) !?[]const types.DefinitionLink { - const pos_context = try Analyser.getPositionContext(arena, handle.text, source_index, true); + const pos_context = try Analyser.getPositionContext(arena, handle.tree.source, source_index, true); var links = std.ArrayListUnmanaged(types.DefinitionLink){}; try links.append(arena, switch (pos_context) { diff --git a/src/features/hover.zig b/src/features/hover.zig index 72029b6ec9..082df269a5 100644 --- a/src/features/hover.zig +++ b/src/features/hover.zig @@ -145,7 +145,7 @@ pub fn hoverSymbol( pub fn hoverDefinitionLabel( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pos_index: usize, markup_kind: types.MarkupKind, offset_encoding: offsets.Encoding, @@ -154,7 +154,7 @@ pub fn hoverDefinitionLabel( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(pos_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const decl = (try Analyser.getLabelGlobal(pos_index, handle, name)) orelse return null; return .{ @@ -164,14 +164,14 @@ pub fn hoverDefinitionLabel( .value = (try hoverSymbol(analyser, arena, decl, markup_kind, null)) orelse return null, }, }, - .range = offsets.locToRange(handle.text, name_loc, offset_encoding), + .range = offsets.locToRange(handle.tree.source, name_loc, offset_encoding), }; } pub fn hoverDefinitionBuiltin( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pos_index: usize, markup_kind: types.MarkupKind, offset_encoding: offsets.Encoding, @@ -182,7 +182,7 @@ pub fn hoverDefinitionBuiltin( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(pos_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const builtin = for (data.builtins) |builtin| { if (std.mem.eql(u8, builtin.name[1..], name)) { @@ -224,14 +224,14 @@ pub fn hoverDefinitionBuiltin( .value = contents.items, }, }, - .range = offsets.locToRange(handle.text, name_loc, offset_encoding), + .range = offsets.locToRange(handle.tree.source, name_loc, offset_encoding), }; } pub fn hoverDefinitionGlobal( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, pos_index: usize, markup_kind: types.MarkupKind, offset_encoding: offsets.Encoding, @@ -240,7 +240,7 @@ pub fn hoverDefinitionGlobal( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(pos_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const decl = (try analyser.getSymbolGlobal(pos_index, handle, name)) orelse return null; return .{ @@ -250,14 +250,14 @@ pub fn hoverDefinitionGlobal( .value = (try hoverSymbol(analyser, arena, decl, markup_kind, null)) orelse return null, }, }, - .range = offsets.locToRange(handle.text, name_loc, offset_encoding), + .range = offsets.locToRange(handle.tree.source, name_loc, offset_encoding), }; } pub fn hoverDefinitionEnumLiteral( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, markup_kind: types.MarkupKind, offset_encoding: offsets.Encoding, @@ -266,7 +266,7 @@ pub fn hoverDefinitionEnumLiteral( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(source_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const decl = (try analyser.getSymbolEnumLiteral(arena, handle, source_index, name)) orelse return null; return .{ @@ -276,14 +276,14 @@ pub fn hoverDefinitionEnumLiteral( .value = (try hoverSymbol(analyser, arena, decl, markup_kind, null)) orelse return null, }, }, - .range = offsets.locToRange(handle.text, name_loc, offset_encoding), + .range = offsets.locToRange(handle.tree.source, name_loc, offset_encoding), }; } pub fn hoverDefinitionFieldAccess( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, loc: offsets.Loc, markup_kind: types.MarkupKind, @@ -293,7 +293,7 @@ pub fn hoverDefinitionFieldAccess( defer tracy_zone.end(); const name_loc = Analyser.identifierLocFromPosition(source_index, handle) orelse return null; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const held_loc = offsets.locMerge(loc, name_loc); const decls = (try analyser.getSymbolFieldAccesses(arena, handle, source_index, held_loc, name)) orelse return null; @@ -316,19 +316,19 @@ pub fn hoverDefinitionFieldAccess( } }, else => .{ .array_of_MarkedString = try content.toOwnedSlice(arena) }, }, - .range = offsets.locToRange(handle.text, name_loc, offset_encoding), + .range = offsets.locToRange(handle.tree.source, name_loc, offset_encoding), }; } pub fn hover( analyser: *Analyser, arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, source_index: usize, markup_kind: types.MarkupKind, offset_encoding: offsets.Encoding, ) !?types.Hover { - const pos_context = try Analyser.getPositionContext(arena, handle.text, source_index, true); + const pos_context = try Analyser.getPositionContext(arena, handle.tree.source, source_index, true); const response = switch (pos_context) { .builtin => try hoverDefinitionBuiltin(analyser, arena, handle, source_index, markup_kind, offset_encoding), diff --git a/src/features/inlay_hints.zig b/src/features/inlay_hints.zig index 54b6006249..9a3eae86e9 100644 --- a/src/features/inlay_hints.zig +++ b/src/features/inlay_hints.zig @@ -33,7 +33,7 @@ const Builder = struct { arena: std.mem.Allocator, analyser: *Analyser, config: *const Config, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, hints: std.ArrayListUnmanaged(InlayHint) = .{}, hover_kind: types.MarkupKind, @@ -302,7 +302,7 @@ fn writeCallNodeHint(builder: *Builder, call: Ast.full.Call) !void { const start = offsets.tokenToIndex(tree, lhsToken); const rhs_loc = offsets.tokenToLoc(tree, rhsToken); - var held_range = try builder.arena.dupeZ(u8, handle.text[start..rhs_loc.end]); + var held_range = try builder.arena.dupeZ(u8, handle.tree.source[start..rhs_loc.end]); var tokenizer = std.zig.Tokenizer.init(held_range); // note: we have the ast node, traversing it would probably yield better results @@ -390,7 +390,7 @@ pub fn writeRangeInlayHint( arena: std.mem.Allocator, config: Config, analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, loc: offsets.Loc, hover_kind: types.MarkupKind, offset_encoding: offsets.Encoding, diff --git a/src/features/references.zig b/src/features/references.zig index 02722524f6..e636c918e1 100644 --- a/src/features/references.zig +++ b/src/features/references.zig @@ -68,21 +68,21 @@ const Builder = struct { const Context = struct { builder: *Builder, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, }; pub fn deinit(self: *Builder) void { self.locations.deinit(self.allocator); } - pub fn add(self: *Builder, handle: *const DocumentStore.Handle, token_index: Ast.TokenIndex) error{OutOfMemory}!void { + pub fn add(self: *Builder, handle: *DocumentStore.Handle, token_index: Ast.TokenIndex) error{OutOfMemory}!void { try self.locations.append(self.allocator, .{ .uri = handle.uri, .range = offsets.tokenToRange(handle.tree, token_index, self.encoding), }); } - fn collectReferences(self: *Builder, handle: *const DocumentStore.Handle, node: Ast.Node.Index) error{OutOfMemory}!void { + fn collectReferences(self: *Builder, handle: *DocumentStore.Handle, node: Ast.Node.Index) error{OutOfMemory}!void { const context = Context{ .builder = self, .handle = handle, @@ -137,7 +137,7 @@ const Builder = struct { fn gatherReferences( allocator: std.mem.Allocator, analyser: *Analyser, - curr_handle: *const DocumentStore.Handle, + curr_handle: *DocumentStore.Handle, skip_std_references: bool, include_decl: bool, builder: anytype, @@ -247,21 +247,21 @@ const CallBuilder = struct { const Context = struct { builder: *CallBuilder, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, }; pub fn deinit(self: *CallBuilder) void { self.callsites.deinit(self.allocator); } - pub fn add(self: *CallBuilder, handle: *const DocumentStore.Handle, call_node: Ast.Node.Index) error{OutOfMemory}!void { + pub fn add(self: *CallBuilder, handle: *DocumentStore.Handle, call_node: Ast.Node.Index) error{OutOfMemory}!void { try self.callsites.append(self.allocator, .{ .uri = handle.uri, .call_node = call_node, }); } - fn collectReferences(self: *CallBuilder, handle: *const DocumentStore.Handle, node: Ast.Node.Index) error{OutOfMemory}!void { + fn collectReferences(self: *CallBuilder, handle: *DocumentStore.Handle, node: Ast.Node.Index) error{OutOfMemory}!void { const context = Context{ .builder = self, .handle = handle, diff --git a/src/features/selection_range.zig b/src/features/selection_range.zig index c75be48cb7..a3c89dc1a3 100644 --- a/src/features/selection_range.zig +++ b/src/features/selection_range.zig @@ -7,7 +7,7 @@ const offsets = @import("../offsets.zig"); pub fn generateSelectionRanges( arena: std.mem.Allocator, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, positions: []const types.Position, offset_encoding: offsets.Encoding, ) error{OutOfMemory}!?[]types.SelectionRange { @@ -21,7 +21,7 @@ pub fn generateSelectionRanges( var result = try arena.alloc(types.SelectionRange, positions.len); var locs = try std.ArrayListUnmanaged(offsets.Loc).initCapacity(arena, 32); for (positions, result) |position, *out| { - const index = offsets.positionToIndex(handle.text, position, offset_encoding); + const index = offsets.positionToIndex(handle.tree.source, position, offset_encoding); locs.clearRetainingCapacity(); for (0..handle.tree.nodes.len) |i| { @@ -46,7 +46,7 @@ pub fn generateSelectionRanges( var selection_ranges = try arena.alloc(types.SelectionRange, locs.items.len); for (selection_ranges, 0..) |*range, i| { - range.range = offsets.locToRange(handle.text, locs.items[i], offset_encoding); + range.range = offsets.locToRange(handle.tree.source, locs.items[i], offset_encoding); range.parent = if (i + 1 < selection_ranges.len) &selection_ranges[i + 1] else null; } out.* = selection_ranges[0]; diff --git a/src/features/semantic_tokens.zig b/src/features/semantic_tokens.zig index ddf09d1ca3..e3ffdf9aa8 100644 --- a/src/features/semantic_tokens.zig +++ b/src/features/semantic_tokens.zig @@ -60,7 +60,7 @@ pub const TokenModifiers = packed struct(u16) { const Builder = struct { arena: std.mem.Allocator, analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, previous_source_index: usize = 0, token_buffer: std.ArrayListUnmanaged(u32) = .{}, encoding: offsets.Encoding, @@ -166,9 +166,10 @@ const Builder = struct { => if (self.limited) return, } - const delta_text = self.handle.tree.source[self.previous_source_index..loc.start]; + const source = self.handle.tree.source; + const delta_text = source[self.previous_source_index..loc.start]; const delta = offsets.indexToPosition(delta_text, delta_text.len, self.encoding); - const length = offsets.locLength(self.handle.tree.source, loc, self.encoding); + const length = offsets.locLength(source, loc, self.encoding); try self.token_buffer.appendSlice(self.arena, &.{ @as(u32, @truncate(delta.line)), @@ -203,7 +204,7 @@ fn writeDocComments(builder: *Builder, tree: Ast, doc: Ast.TokenIndex) !void { fn fieldTokenType( container_decl: Ast.Node.Index, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, is_static_access: bool, ) ?TokenType { if (!ast.isContainer(handle.tree, container_decl)) @@ -1035,7 +1036,7 @@ fn writeIdentifier(builder: *Builder, name_token: Ast.Node.Index) error{OutOfMem pub fn writeSemanticTokens( arena: std.mem.Allocator, analyser: *Analyser, - handle: *const DocumentStore.Handle, + handle: *DocumentStore.Handle, loc: ?offsets.Loc, encoding: offsets.Encoding, limited: bool, diff --git a/src/features/signature_help.zig b/src/features/signature_help.zig index 55a55bee31..fd23321823 100644 --- a/src/features/signature_help.zig +++ b/src/features/signature_help.zig @@ -10,7 +10,7 @@ const offsets = @import("../offsets.zig"); const data = @import("version_data"); -fn fnProtoToSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, commas: u32, skip_self_param: bool, handle: *const DocumentStore.Handle, fn_node: Ast.Node.Index, proto: Ast.full.FnProto) !types.SignatureInformation { +fn fnProtoToSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, commas: u32, skip_self_param: bool, handle: *DocumentStore.Handle, fn_node: Ast.Node.Index, proto: Ast.full.FnProto) !types.SignatureInformation { const tree = handle.tree; const label = Analyser.getFunctionSignature(tree, proto); const proto_comments = (try Analyser.getDocComments(arena, tree, fn_node)) orelse ""; @@ -47,8 +47,9 @@ fn fnProtoToSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, commas: }; } -pub fn getSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, handle: *const DocumentStore.Handle, absolute_index: usize) !?types.SignatureInformation { - const innermost_block = Analyser.innermostBlockScope(handle.*, absolute_index); +pub fn getSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, handle: *DocumentStore.Handle, absolute_index: usize) !?types.SignatureInformation { + const document_scope = try handle.getDocumentScope(); + const innermost_block = Analyser.innermostBlockScope(document_scope, absolute_index); const tree = handle.tree; const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); @@ -229,7 +230,7 @@ pub fn getSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, handle: * const last_token_slice = tree.tokenSlice(expr_last_token); const expr_end = token_starts[expr_last_token] + last_token_slice.len; - var held_expr = try arena.dupeZ(u8, handle.text[expr_start..expr_end]); + var held_expr = try arena.dupeZ(u8, handle.tree.source[expr_start..expr_end]); // Resolve the expression. var tokenizer = std.zig.Tokenizer.init(held_expr); @@ -264,7 +265,7 @@ pub fn getSignatureInfo(analyser: *Analyser, arena: std.mem.Allocator, handle: * try symbol_stack.append(arena, .l_paren); continue; }; - const name = offsets.locToSlice(handle.text, name_loc); + const name = offsets.locToSlice(handle.tree.source, name_loc); const skip_self_param = !type_handle.type.is_type_val; const decl_handle = (try type_handle.lookupSymbol(analyser, name)) orelse { diff --git a/tests/lsp_features/code_actions.zig b/tests/lsp_features/code_actions.zig index a080cdb632..ac4993fac0 100644 --- a/tests/lsp_features/code_actions.zig +++ b/tests/lsp_features/code_actions.zig @@ -139,7 +139,7 @@ fn testAutofix(before: []const u8, after: []const u8) !void { const handle = ctx.server.document_store.getHandle(uri).?; var diagnostics: std.ArrayListUnmanaged(types.Diagnostic) = .{}; - try zls.diagnostics.getAstCheckDiagnostics(ctx.server, ctx.arena.allocator(), handle.*, &diagnostics); + try zls.diagnostics.getAstCheckDiagnostics(ctx.server, ctx.arena.allocator(), handle, &diagnostics); const params = types.CodeActionParams{ .textDocument = .{ .uri = uri },