Repository: nvim-lua/plenary.nvim
Pull request #292 (2021-12-19 14:11:15)

Add class annotation for Path


Patch:
diff --git a/stylua.toml b/.stylua.toml
similarity index 80%
rename from stylua.toml
rename to .stylua.toml
index df96b7b..ecb6dca 100644
--- a/stylua.toml
+++ b/.stylua.toml
@@ -3,4 +3,4 @@ line_endings = "Unix"
 indent_type = "Spaces"
 indent_width = 2
 quote_style = "AutoPreferDouble"
-no_call_parentheses = true
+call_parentheses = "None"
diff --git a/lua/plenary/job.lua b/lua/plenary/job.lua
index 56e2197..6cd3162 100644
--- a/lua/plenary/job.lua
+++ b/lua/plenary/job.lua
@@ -16,6 +16,7 @@ local F = require "plenary.functional"
 ---@field on_exit function        : (self, code: number, signal: number)
 ---@field maximum_results number  : stop processing results after this number
 ---@field writer Job|table|string : Job that writes to stdin of this job.
+---@field detached boolean        : Spawn the child in a detached state making it a process group leader
 ---@field enabled_recording boolean
 local Job = {}
 Job.__index = Job
@@ -131,6 +132,10 @@ function Job:new(o)
     obj.interactive = o.interactive
   end
 
+  if o.detached then
+    obj.detached = true
+  end
+
   -- enable_handlers: Do you want to do ANYTHING with the stdout/stderr of the proc
   obj.enable_handlers = F.if_nil(o.enable_handlers, true, o.enable_handlers)
 
@@ -272,6 +277,10 @@ function Job:_create_uv_options()
     options.env = self.env
   end
 
+  if self.detached then
+    options.detached = true
+  end
+
   return options
 end
 
@@ -418,7 +427,7 @@ function Job:_execute()
           self.stdin:write "\n"
         else
           self.stdin:write("\n", function()
-            self.stdin:close()
+            pcall(self.stdin.close, self.stdin)
           end)
         end
       end
diff --git a/lua/plenary/path.lua b/lua/plenary/path.lua
index a8152f1..7da6912 100644
--- a/lua/plenary/path.lua
+++ b/lua/plenary/path.lua
@@ -146,6 +146,7 @@ end
 -- S_IFLNK  = 0o120000  # symbolic link
 -- S_IFSOCK = 0o140000  # socket file
 
+---@class Path
 local Path = {
   path = path,
 }
@@ -397,7 +398,7 @@ local shorten = (function()
     local ffi = require "ffi"
     ffi.cdef [[
     typedef unsigned char char_u;
-    char_u *shorten_dir(char_u *str);
+    void shorten_dir(char_u *str);
     ]]
     return function(filename)
       if not filename or is_uri(filename) then
@@ -406,7 +407,8 @@ local shorten = (function()
 
       local c_str = ffi.new("char[?]", #filename + 1)
       ffi.copy(c_str, filename)
-      return ffi.string(ffi.C.shorten_dir(c_str))
+      ffi.C.shorten_dir(c_str)
+      return ffi.string(c_str)
     end
   end
   return function(filename)
diff --git a/lua/plenary/reload.lua b/lua/plenary/reload.lua
index b52a964..6189522 100644
--- a/lua/plenary/reload.lua
+++ b/lua/plenary/reload.lua
@@ -13,8 +13,9 @@ reload.reload_module = function(module_name, starts_with_only)
       return string.find(pack, module_name, 1, true)
     end
   else
+    local module_name_pattern = vim.pesc(module_name)
     matcher = function(pack)
-      return string.find(pack, "^" .. module_name)
+      return string.find(pack, "^" .. module_name_pattern)
     end
   end
 
diff --git a/lua/plenary/window/border.lua b/lua/plenary/window/border.lua
index 35448d0..afd0809 100644
--- a/lua/plenary/window/border.lua
+++ b/lua/plenary/window/border.lua
@@ -173,12 +173,19 @@ local set_title_highlights = function(bufnr, ranges, hl)
   end
 end
 
-function Border:change_title(new_title)
+function Border:change_title(new_title, pos)
   if self._border_win_options.title == new_title then
     return
   end
 
-  self._border_win_options.title = new_title
+  pos = pos
+    or (self._border_win_options.title and self._border_win_options.title[1] and self._border_win_options.title[1].pos)
+  if pos == nil then
+    self._border_win_options.title = new_title
+  else
+    self._border_win_options.title = { { text = new_title, pos = pos } }
+  end
+
   self.contents, self.title_ranges = Border._create_lines(
     self.content_win_id,
     self.content_win_options,


Test patch:
diff --git a/tests/plenary/strings_spec.lua b/tests/plenary/strings_spec.lua
index 4bf1039..fcf990e 100644
--- a/tests/plenary/strings_spec.lua
+++ b/tests/plenary/strings_spec.lua
@@ -3,14 +3,16 @@ local eq = assert.are.same
 
 describe("strings", function()
   describe("strdisplaywidth", function()
-    for _, case in ipairs {
-      { str = "abcde", expected = { single = 5, double = 5 } },
-      -- This space below is a tab (U+0009)
-      { str = "abc	de", expected = { single = 10, double = 10 } },
-      { str = "アイウエオ", expected = { single = 10, double = 10 } },
-      { str = "├─┤", expected = { single = 3, double = 6 } },
-      { str = 123, expected = { single = 3, double = 3 } },
-    } do
+    for _, case in
+      ipairs {
+        { str = "abcde", expected = { single = 5, double = 5 } },
+        -- This space below is a tab (U+0009)
+        { str = "abc	de", expected = { single = 10, double = 10 } },
+        { str = "アイウエオ", expected = { single = 10, double = 10 } },
+        { str = "├─┤", expected = { single = 3, double = 6 } },
+        { str = 123, expected = { single = 3, double = 3 } },
+      }
+    do
       for _, ambiwidth in ipairs { "single", "double" } do
         local item = type(case.str) == "string" and '"%s"' or "%s"
         local msg = ("ambiwidth = %s, " .. item .. " -> %d"):format(ambiwidth, case.str, case.expected[ambiwidth])
@@ -28,12 +30,14 @@ describe("strings", function()
   end)
 
   describe("strcharpart", function()
-    for _, case in ipairs {
-      { args = { "abcde", 2 }, expected = "cde" },
-      { args = { "abcde", 2, 2 }, expected = "cd" },
-      { args = { "アイウエオ", 2, 2 }, expected = "ウエ" },
-      { args = { "├───┤", 2, 2 }, expected = "──" },
-    } do
+    for _, case in
+      ipairs {
+        { args = { "abcde", 2 }, expected = "cde" },
+        { args = { "abcde", 2, 2 }, expected = "cd" },
+        { args = { "アイウエオ", 2, 2 }, expected = "ウエ" },
+        { args = { "├───┤", 2, 2 }, expected = "──" },
+      }
+    do
       local msg = ('("%s", %d, %s) -> "%s"'):format(case.args[1], case.args[2], tostring(case.args[3]), case.expected)
       it("lua: " .. msg, function()
         eq(case.expected, strings.strcharpart(unpack(case.args)))
@@ -45,74 +49,79 @@ describe("strings", function()
   end)
 
   describe("truncate", function()
-    for _, case in ipairs {
-      -- 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 = "…" } },
-      -- truncations from the middle
-      { args = { "abcde", 6, nil, 0 }, expected = { single = "abcde", double = "abcde" } },
-      { args = { "abcde", 5, nil, 0 }, expected = { single = "abcde", double = "abcde" } },
-      { args = { "abcde", 4, nil, 0 }, expected = { single = "a…de", double = "a…e" } },
-      {
-        args = { "アイウエオ", 11, nil, 0 },
-        expected = { single = "アイウエオ", double = "アイウエオ" },
-      },
-      {
-        args = { "アイウエオ", 10, nil, 0 },
-        expected = { single = "アイウエオ", double = "アイウエオ" },
-      },
-      {
-        args = { "アイウエオ", 9, nil, 0 },
-        expected = { single = "アイ…エオ", double = "ア…エオ" },
-      },
-      { args = { "アイウエオ", 8, nil, 0 }, expected = { single = "ア…エオ", double = "ア…エオ" } },
-      { args = { "├─┤", 7, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } },
-      { args = { "├─┤", 6, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } },
-      { args = { "├─┤", 5, nil, 0 }, expected = { single = "├─┤", double = "…┤" } },
-      { args = { "├─┤", 4, nil, 0 }, expected = { single = "├─┤", double = "…┤" } },
-      { args = { "├─┤", 3, nil, 0 }, expected = { single = "├─┤", double = "…" } },
-      { args = { "├─┤", 2, nil, 0 }, expected = { single = "…┤", double = "…" } },
-    } do
+    for _, case in
+      ipairs {
+        -- 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 = "…" } },
+        -- truncations from the middle
+        { args = { "abcde", 6, nil, 0 }, expected = { single = "abcde", double = "abcde" } },
+        { args = { "abcde", 5, nil, 0 }, expected = { single = "abcde", double = "abcde" } },
+        { args = { "abcde", 4, nil, 0 }, expected = { single = "a…de", double = "a…e" } },
+        {
+          args = { "アイウエオ", 11, nil, 0 },
+          expected = { single = "アイウエオ", double = "アイウエオ" },
+        },
+        {
+          args = { "アイウエオ", 10, nil, 0 },
+          expected = { single = "アイウエオ", double = "アイウエオ" },
+        },
+        {
+          args = { "アイウエオ", 9, nil, 0 },
+          expected = { single = "アイ…エオ", double = "ア…エオ" },
+        },
+        { args = { "アイウエオ", 8, nil, 0 }, expected = { single = "ア…エオ", double = "ア…エオ" } },
+        { args = { "├─┤", 7, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } },
+        { args = { "├─┤", 6, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } },
+        { args = { "├─┤", 5, nil, 0 }, expected = { single = "├─┤", double = "…┤" } },
+        { args = { "├─┤", 4, nil, 0 }, expected = { single = "├─┤", double = "…┤" } },
+        { args = { "├─┤", 3, nil, 0 }, expected = { single = "├─┤", double = "…" } },
+        { args = { "├─┤", 2, nil, 0 }, expected = { single = "…┤", double = "…" } },
+      }
+    do
       for _, ambiwidth in ipairs { "single", "double" } do
         local msg = ("ambiwidth = %s, direction = %s, [%s, %d] -> %s"):format(
           ambiwidth,
@@ -132,14 +141,16 @@ describe("strings", function()
   end)
 
   describe("align_str", function()
-    for _, case in ipairs {
-      { args = { "abcde", 8 }, expected = { single = "abcde   ", double = "abcde   " } },
-      { args = { "アイウ", 8 }, expected = { single = "アイウ  ", double = "アイウ  " } },
-      { args = { "├─┤", 8 }, expected = { single = "├─┤     ", double = "├─┤  " } },
-      { args = { "abcde", 8, true }, expected = { single = "   abcde", double = "   abcde" } },
-      { args = { "アイウ", 8, true }, expected = { single = "  アイウ", double = "  アイウ" } },
-      { args = { "├─┤", 8, true }, expected = { single = "     ├─┤", double = "  ├─┤" } },
-    } do
+    for _, case in
+      ipairs {
+        { args = { "abcde", 8 }, expected = { single = "abcde   ", double = "abcde   " } },
+        { args = { "アイウ", 8 }, expected = { single = "アイウ  ", double = "アイウ  " } },
+        { args = { "├─┤", 8 }, expected = { single = "├─┤     ", double = "├─┤  " } },
+        { args = { "abcde", 8, true }, expected = { single = "   abcde", double = "   abcde" } },
+        { args = { "アイウ", 8, true }, expected = { single = "  アイウ", double = "  アイウ" } },
+        { args = { "├─┤", 8, true }, expected = { single = "     ├─┤", double = "  ├─┤" } },
+      }
+    do
       for _, ambiwidth in ipairs { "single", "double" } do
         local msg = ('ambiwidth = %s, [%s, %d, %s] -> "%s"'):format(
           ambiwidth,
@@ -162,134 +173,136 @@ describe("strings", function()
     local function lines(t)
       return table.concat(t, "\n")
     end
-    for _, case in ipairs {
-      {
-        msg = "empty string",
-        tabstop = 8,
-        args = { "" },
-        expected = "",
-      },
-      {
-        msg = "in case tabs are longer than spaces",
-        tabstop = 8,
-        args = {
-          lines {
-            "		<Tab><Tab> -> 13 spaces",
-            "     5 spaces -> 0 space",
-          },
-        },
-        expected = lines {
-          "           <Tab><Tab> -> 13 spaces",
-          "5 spaces -> 0 space",
+    for _, case in
+      ipairs {
+        {
+          msg = "empty string",
+          tabstop = 8,
+          args = { "" },
+          expected = "",
         },
-      },
-      {
-        msg = "in case tabs are shorter than spaces",
-        tabstop = 2,
-        args = {
-          lines {
-            "		<Tab><Tab> -> 0 space",
-            "     5spaces -> 1 space",
+        {
+          msg = "in case tabs are longer than spaces",
+          tabstop = 8,
+          args = {
+            lines {
+              "		<Tab><Tab> -> 13 spaces",
+              "     5 spaces -> 0 space",
+            },
+          },
+          expected = lines {
+            "           <Tab><Tab> -> 13 spaces",
+            "5 spaces -> 0 space",
           },
         },
-        expected = lines {
-          "<Tab><Tab> -> 0 space",
-          " 5spaces -> 1 space",
+        {
+          msg = "in case tabs are shorter than spaces",
+          tabstop = 2,
+          args = {
+            lines {
+              "		<Tab><Tab> -> 0 space",
+              "     5spaces -> 1 space",
+            },
+          },
+          expected = lines {
+            "<Tab><Tab> -> 0 space",
+            " 5spaces -> 1 space",
+          },
         },
-      },
-      {
-        msg = "ignores empty lines",
-        tabstop = 2,
-        args = {
-          lines {
+        {
+          msg = "ignores empty lines",
+          tabstop = 2,
+          args = {
+            lines {
+              "",
+              "",
+              "",
+              "        8 spaces -> 3 spaces",
+              "",
+              "",
+              "     5 spaces -> 0 space",
+              "",
+              "",
+              "",
+            },
+          },
+          expected = lines {
             "",
             "",
             "",
-            "        8 spaces -> 3 spaces",
+            "   8 spaces -> 3 spaces",
             "",
             "",
-            "     5 spaces -> 0 space",
+            "5 spaces -> 0 space",
             "",
             "",
             "",
           },
         },
-        expected = lines {
-          "",
-          "",
-          "",
-          "   8 spaces -> 3 spaces",
-          "",
-          "",
-          "5 spaces -> 0 space",
-          "",
-          "",
-          "",
-        },
-      },
-      {
-        msg = "no indent",
-        tabstop = 2,
-        args = {
-          lines {
-            "	<Tab> -> 2 spaces",
+        {
+          msg = "no indent",
+          tabstop = 2,
+          args = {
+            lines {
+              "	<Tab> -> 2 spaces",
+              "Here is no indent.",
+              "    4 spaces will remain",
+            },
+          },
+          expected = lines {
+            "  <Tab> -> 2 spaces",
             "Here is no indent.",
             "    4 spaces will remain",
           },
         },
-        expected = lines {
-          "  <Tab> -> 2 spaces",
-          "Here is no indent.",
-          "    4 spaces will remain",
-        },
-      },
-      {
-        msg = "leave_indent = 4",
-        tabstop = 2,
-        args = {
-          lines {
-            "	<Tab> -> 6 spaces",
-            "0 indent -> 4 spaces",
-            "    4 spaces -> 8 spaces",
+        {
+          msg = "leave_indent = 4",
+          tabstop = 2,
+          args = {
+            lines {
+              "	<Tab> -> 6 spaces",
+              "0 indent -> 4 spaces",
+              "    4 spaces -> 8 spaces",
+            },
+            4,
+          },
+          expected = lines {
+            "      <Tab> -> 6 spaces",
+            "    0 indent -> 4 spaces",
+            "        4 spaces -> 8 spaces",
           },
-          4,
-        },
-        expected = lines {
-          "      <Tab> -> 6 spaces",
-          "    0 indent -> 4 spaces",
-          "        4 spaces -> 8 spaces",
         },
-      },
-      {
-        msg = "typical usecase: <Tab> to 5 spaces",
-        tabstop = 4,
-        args = {
-          lines {
+        {
+          msg = "typical usecase: <Tab> to 5 spaces",
+          tabstop = 4,
+          args = {
+            lines {
+              "",
+              "		Chapter 1",
+              "",
+              "	  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed",
+              "	do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
+              "",
+              "	  Ut enim ad minim veniam, quis nostrud exercitation ullamco",
+              "	laboris nisi ut aliquip ex ea commodo consequat.",
+              "",
+            },
+            5,
+          },
+          expected = lines {
             "",
-            "		Chapter 1",
+            "         Chapter 1",
             "",
-            "	  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed",
-            "	do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
+            "       Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed",
+            "     do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
             "",
-            "	  Ut enim ad minim veniam, quis nostrud exercitation ullamco",
-            "	laboris nisi ut aliquip ex ea commodo consequat.",
+            "       Ut enim ad minim veniam, quis nostrud exercitation ullamco",
+            "     laboris nisi ut aliquip ex ea commodo consequat.",
             "",
           },
-          5,
-        },
-        expected = lines {
-          "",
-          "         Chapter 1",
-          "",
-          "       Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed",
-          "     do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
-          "",
-          "       Ut enim ad minim veniam, quis nostrud exercitation ullamco",
-          "     laboris nisi ut aliquip ex ea commodo consequat.",
-          "",
         },
-      },
-    } do
+      }
+    do
       local msg = ("tabstop = %d, %s"):format(case.tabstop, case.msg)
       it(msg, function()
         local original = vim.bo.tabstop