Authentication-Results: mail-b.sr.ht; dkim=pass header.d=hmachet.com header.i=@hmachet.com Received: from out1.migadu.com (out1.migadu.com [91.121.223.63]) by mail-b.sr.ht (Postfix) with ESMTPS id 8B80C11EF57 for <~leon_plickat/nfm@lists.sr.ht>; Sun, 22 May 2022 09:06:41 +0000 (UTC) X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hmachet.com; s=key1; t=1653210399; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=BZdgS/sCM/sHyp93xrPRIfxNPj3JLHb5K90Vs+xwxRI=; b=McZ/O9AQxSCHzEwzPr7Rrg1n0FR8kZYUlUgqtF8Gi0UneI0Wxgaid8FNFPNgpxU6WkTFwh xbkQO82ctcNtRd36bDn5KlKuwWmkXU+auhk0tenKl8pMYqLZocc0AKIJWeR8J6Yc2FaqSR Tt0g4PcgQQ+08+oM18dRz2F8pqa1kn4= From: Hugo Machet To: ~leon_plickat/nfm@lists.sr.ht Cc: Hugo Machet Subject: [PATCH nfm] Add tab completion for files Date: Sun, 22 May 2022 11:06:36 +0200 Message-Id: <20220522090636.6910-1-mail@hmachet.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT X-Migadu-Auth-User: hmachet.com Implements: https://todo.sr.ht/~leon_plickat/nfm/17 --- src/nfm.zig | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/nfm.zig b/src/nfm.zig index d978210d4f9f..1414a5f81407 100644 --- a/src/nfm.zig +++ b/src/nfm.zig @@ -546,6 +546,19 @@ fn handleInputUserInput(in: spoon.Input) !void { try handleReturnUserInput(); return; }, + '\t' => { + var current = try util.codepointSliceToUtf8SlizeZAlloc(context.gpa, buffer.buffer.items); + defer context.gpa.free(current); + + var it = mem.tokenize(u8, current, " "); + var buf: []const u8 = undefined; + while (it.next()) |tok| buf = tok; + + if (buf.len < 2) return; + var comp = try completion(buf); + if (mem.eql(u8, comp, buf)) return; + try buffer.insertFormat(comp); + }, 127 => { // Backspace. if (context.mode.user_input.history_index != null) return; buffer.delete(.left, 1); @@ -653,6 +666,48 @@ fn handleReturnUserInput() !void { } } +/// Compare current input with files/directories names. Complete the name if +/// there is only one match else only complete the common part. +/// Case insensitive. +fn completion(input: []const u8) ![]const u8 { + var found: std.ArrayListUnmanaged([:0]const u8) = .{}; + defer found.deinit(context.gpa); + + for (context.cwd.files.items) |file| { + if (!context.show_hidden and file.isHidden()) continue; + if (file.name.len <= input.len) continue; + if (ascii.startsWithIgnoreCase(file.name, input)) { + try found.append(context.gpa, file.name); + } + } + + if (found.items.len == 0) { + return input; + } else if (found.items.len == 1) { + return found.items[0][input.len..]; + } else { + var common: []const u8 = found.items[0]; + var i: usize = 1; + while (i < found.items.len) : (i += 1) { + common = commonStart(common, found.items[i]); + } + return common[input.len..]; + } +} + +/// Compare two strings and return the common beginning part. +/// Case insensitive. +fn commonStart(a: []const u8, b: []const u8) []const u8 { + const len: usize = if (a.len < b.len) a.len else b.len; + + var offset: usize = 0; + while (offset < len) : (offset += 1) { + if (ascii.toLower(a[offset]) != ascii.toLower(b[offset])) break; + } + + return a[0..offset]; +} + test { _ = @import("ini.zig"); _ = @import("CommandTokenizer.zig"); -- 2.36.1