Repository: nvim-lua/plenary.nvim
Pull request #236 (2021-09-15 19:51:35)

feat: allow truncation of strings from left
Allows optional `direction` for `truncate`, where >0 truncates from the right and <0 truncates from the left.

Could be used for truncation of paths in telescope, as requested [here](https://github.com/nvim-telescope/telescope.nvim/issues/1167#issuecomment-918298987).

Conni2461: nice. I like it. Thanks :)

But i hate to be the guy. Can we get one or two tests here https://github.com/nvim-lua/plenary.nvim/blob/master/tests/plenary/strings_spec.lua#L47
l-kershaw: No worries, was planning to do that later today 👍 
l-kershaw: @Conni2461 tests are now added
Conni2461: thanks :)

Patch:
diff --git a/lua/plenary/strings.lua b/lua/plenary/strings.lua
index 9ee5bd4..1a4df46 100644
--- a/lua/plenary/strings.lua
+++ b/lua/plenary/strings.lua
@@ -92,25 +92,33 @@ M.strcharpart = (function()
   end
 end)()
 
-M.truncate = function(str, len, dots)
+M.truncate = function(str, len, dots, direction)
   str = tostring(str) -- We need to make sure its an actually a string and not a number
   dots = dots or "…"
+  direction = direction or 1
   if M.strdisplaywidth(str) <= len then
     return str
   end
-  local start = 0
+  local start = direction > 0 and 0 or str:len()
   local current = 0
   local result = ""
   local len_of_dots = M.strdisplaywidth(dots)
+  local concat = function(a, b, dir)
+    if dir > 0 then
+      return a .. b
+    else
+      return b .. a
+    end
+  end
   while true do
     local part = M.strcharpart(str, start, 1)
     current = current + M.strdisplaywidth(part)
     if (current + len_of_dots) > len then
-      result = result .. dots
+      result = concat(result, dots, direction)
       break
     end
-    result = result .. part
-    start = start + 1
+    result = concat(result, part, direction)
+    start = start + direction
   end
   return result
 end


Test patch:
diff --git a/tests/plenary/strings_spec.lua b/tests/plenary/strings_spec.lua
index 0085d3e..2ce3d9e 100644
--- a/tests/plenary/strings_spec.lua
+++ b/tests/plenary/strings_spec.lua
@@ -46,23 +46,39 @@ describe("strings", function()
 
   describe("truncate", function()
     for _, case in ipairs {
-      { args = { "abcde", 6 }, expected = { single = "abcde", double = "abcde" } },
-      { args = { "abcde", 5 }, expected = { single = "abcde", double = "abcde" } },
-      { args = { "abcde", 4 }, expected = { single = "abc…", double = "ab…" } },
-      { args = { "アイウエオ", 11 }, expected = { single = "アイウエオ", double = "アイウエオ" } },
-      { args = { "アイウエオ", 10 }, expected = { single = "アイウエオ", double = "アイウエオ" } },
-      { args = { "アイウエオ", 9 }, expected = { single = "アイウエ…", double = "アイウ…" } },
-      { args = { "アイウエオ", 8 }, expected = { single = "アイウ…", double = "アイウ…" } },
-      { args = { "├─┤", 7 }, expected = { single = "├─┤", double = "├─┤" } },
-      { args = { "├─┤", 6 }, expected = { single = "├─┤", double = "├─┤" } },
-      { args = { "├─┤", 5 }, expected = { single = "├─┤", double = "├…" } },
-      { args = { "├─┤", 4 }, expected = { single = "├─┤", double = "├…" } },
-      { args = { "├─┤", 3 }, expected = { single = "├─┤", double = "…" } },
-      { args = { "├─┤", 2 }, expected = { single = "├…", double = "…" } },
+      -- truncations from the right
+      { args = { "abcde", 6, nil, 1 }, expected = { single = "abcde", double = "abcde" } },
+      { args = { "abcde", 5, nil, 1 }, expected = { single = "abcde", double = "abcde" } },
+      { args = { "abcde", 4, nil, 1 }, expected = { single = "abc…", double = "ab…" } },
+      { args = { "アイウエオ", 11, nil, 1 }, expected = { single = "アイウエオ", double = "アイウエオ" } },
+      { args = { "アイウエオ", 10, nil, 1 }, expected = { single = "アイウエオ", double = "アイウエオ" } },
+      { args = { "アイウエオ", 9, nil, 1 }, expected = { single = "アイウエ…", double = "アイウ…" } },
+      { args = { "アイウエオ", 8, nil, 1 }, expected = { single = "アイウ…", double = "アイウ…" } },
+      { args = { "├─┤", 7, nil, 1 }, expected = { single = "├─┤", double = "├─┤" } },
+      { args = { "├─┤", 6, nil, 1 }, expected = { single = "├─┤", double = "├─┤" } },
+      { args = { "├─┤", 5, nil, 1 }, expected = { single = "├─┤", double = "├…" } },
+      { args = { "├─┤", 4, nil, 1 }, expected = { single = "├─┤", double = "├…" } },
+      { args = { "├─┤", 3, nil, 1 }, expected = { single = "├─┤", double = "…" } },
+      { args = { "├─┤", 2, nil, 1 }, expected = { single = "├…", double = "…" } },
+      -- truncations from the left
+      { args = { "abcde", 6, nil, -1 }, expected = { single = "abcde", double = "abcde" } },
+      { args = { "abcde", 5, nil, -1 }, expected = { single = "abcde", double = "abcde" } },
+      { args = { "abcde", 4, nil, -1 }, expected = { single = "…cde", double = "…de" } },
+      { args = { "アイウエオ", 11, nil, -1 }, expected = { single = "アイウエオ", double = "アイウエオ" } },
+      { args = { "アイウエオ", 10, nil, -1 }, expected = { single = "アイウエオ", double = "アイウエオ" } },
+      { args = { "アイウエオ", 9, nil, -1 }, expected = { single = "…イウエオ", double = "…ウエオ" } },
+      { args = { "アイウエオ", 8, nil, -1 }, expected = { single = "…ウエオ", double = "…ウエオ" } },
+      { args = { "├─┤", 7, nil, -1 }, expected = { single = "├─┤", double = "├─┤" } },
+      { args = { "├─┤", 6, nil, -1 }, expected = { single = "├─┤", double = "├─┤" } },
+      { args = { "├─┤", 5, nil, -1 }, expected = { single = "├─┤", double = "…┤" } },
+      { args = { "├─┤", 4, nil, -1 }, expected = { single = "├─┤", double = "…┤" } },
+      { args = { "├─┤", 3, nil, -1 }, expected = { single = "├─┤", double = "…" } },
+      { args = { "├─┤", 2, nil, -1 }, expected = { single = "…┤", double = "…" } },
     } do
       for _, ambiwidth in ipairs { "single", "double" } do
-        local msg = ("ambiwidth = %s, [%s, %d] -> %s"):format(
+        local msg = ("ambiwidth = %s, direction = %s, [%s, %d] -> %s"):format(
           ambiwidth,
+          (case.args[4] > 0) and "right" or "left",
           case.args[1],
           case.args[2],
           case.expected[ambiwidth]