Repository: nvim-lua/plenary.nvim
Pull request #610 (2024-07-31 10:10:48)

feat: add cairo language filetype
Added a new filetype for the cairo language: https://www.cairo-lang.org/

Telescope without filetype:
<img width="1601" alt="image" src="https://github.com/user-attachments/assets/db1825a1-6635-46ee-8837-ccdaef05a327">

Telescope with the filetype:
<img width="1601" alt="image" src="https://github.com/user-attachments/assets/b66725db-0b6d-47d1-97da-c6fc66c56e74">



Patch:
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index 302ab2b..cc227fa 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -11,7 +11,7 @@ jobs:
       matrix:
         include:
           - os: ubuntu-22.04
-            rev: nightly/nvim-linux64.tar.gz
+            rev: nightly/nvim-linux-x86_64.tar.gz
           - os: ubuntu-22.04
             rev: v0.7.2/nvim-linux64.tar.gz
           - os: ubuntu-22.04
@@ -19,12 +19,12 @@ jobs:
           - os: ubuntu-22.04
             rev: v0.9.5/nvim-linux64.tar.gz
           - os: ubuntu-22.04
-            rev: v0.10.0/nvim-linux64.tar.gz
+            rev: v0.10.4/nvim-linux-x86_64.tar.gz
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - run: date +%F > todays-date
       - name: Restore cache for today's nightly.
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: _neovim
           key: ${{ runner.os }}-${{ matrix.rev }}-${{ hashFiles('todays-date') }}
@@ -46,7 +46,7 @@ jobs:
     name: stylua
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - uses: JohnnyMorganz/stylua-action@v2
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -58,7 +58,7 @@ jobs:
     name: Luacheck
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
 
       - name: Prepare
         run: |
diff --git a/data/plenary/filetypes/builtin.lua b/data/plenary/filetypes/builtin.lua
index c4b6d50..6f341d6 100644
--- a/data/plenary/filetypes/builtin.lua
+++ b/data/plenary/filetypes/builtin.lua
@@ -21,6 +21,7 @@ return {
   extension = {
     ['_coffee'] = 'coffee',
     ['astro'] = 'astro',
+    ['cairo'] = 'cairo',
     ['cts'] = 'typescript',
     ['cljd'] = 'clojure',
     ['coffee'] = 'coffee',
@@ -51,6 +52,7 @@ return {
     ['resi'] = 'rescript',
     ['rkt'] = 'racket',
     ['svelte'] = 'svelte',
+    ['swift'] = 'swift',
     ['tres'] = 'gdresource',
     ['tscn'] = 'gdresource',
     ['tsx'] = 'typescriptreact',
diff --git a/lua/plenary/async/util.lua b/lua/plenary/async/util.lua
index c689ba3..b80f3fd 100644
--- a/lua/plenary/async/util.lua
+++ b/lua/plenary/async/util.lua
@@ -53,6 +53,10 @@ end
 M.join = function(async_fns)
   local len = #async_fns
   local results = {}
+  if len == 0 then
+    return results
+  end
+
   local done = 0
 
   local tx, rx = channel.oneshot()
diff --git a/lua/plenary/async/uv_async.lua b/lua/plenary/async/uv_async.lua
index 4ef0eba..427e1a5 100644
--- a/lua/plenary/async/uv_async.lua
+++ b/lua/plenary/async/uv_async.lua
@@ -3,8 +3,8 @@ local uv = vim.loop
 
 local M = {}
 
-local function add(name, argc)
-  local success, ret = pcall(a.wrap, uv[name], argc)
+local function add(name, argc, custom)
+  local success, ret = pcall(a.wrap, custom or uv[name], argc)
 
   if not success then
     error("Failed to add function with name " .. name)
@@ -48,7 +48,9 @@ add("fs_chown", 4)
 add("fs_fchown", 4)
 -- 'fs_lchown',
 add("fs_copyfile", 4)
--- add('fs_opendir', 3) -- TODO: fix this one
+add("fs_opendir", 3, function(path, entries, callback)
+  return uv.fs_opendir(path, callback, entries)
+end)
 add("fs_readdir", 2)
 add("fs_closedir", 2)
 -- 'fs_statfs',
diff --git a/lua/plenary/curl.lua b/lua/plenary/curl.lua
index b7fc997..20e8cc2 100644
--- a/lua/plenary/curl.lua
+++ b/lua/plenary/curl.lua
@@ -251,15 +251,25 @@ end
 ------------------------------------------------------------
 parse.response = function(lines, dump_path, code)
   local headers = P.readlines(dump_path)
-  local status = tonumber(string.match(headers[1], "([%w+]%d+)"))
-  local body = F.join(lines, "\n")
+  local status = nil
+  local processed_headers = {}
+
+  -- Process headers in a single pass
+  for _, line in ipairs(headers) do
+    local status_match = line:match "^HTTP/%S*%s+(%d+)"
+    if status_match then
+      status = tonumber(status_match)
+    elseif line ~= "" then
+      table.insert(processed_headers, line)
+    end
+  end
 
+  local body = F.join(lines, "\n")
   vim.loop.fs_unlink(dump_path)
-  table.remove(headers, 1)
 
   return {
-    status = status,
-    headers = headers,
+    status = status or 0,
+    headers = processed_headers,
     body = body,
     exit = code,
   }
diff --git a/lua/plenary/job.lua b/lua/plenary/job.lua
index c095c4c..7f54971 100644
--- a/lua/plenary/job.lua
+++ b/lua/plenary/job.lua
@@ -13,7 +13,7 @@ local F = require "plenary.functional"
 ---@field detached? boolean Spawn the child in a detached state making it a process group leader
 ---@field skip_validation? boolean Skip validating the arguments
 ---@field enable_handlers? boolean If set to false, disables all callbacks associated with output (default: true)
----@field enabled_recording? boolean
+---@field enable_recording? boolean
 ---@field on_start? fun()
 ---@field on_stdout? fun(error: string, data: string, self?: Job)
 ---@field on_stderr? fun(error: string, data: string, self?: Job)
@@ -361,7 +361,7 @@ local on_output = function(self, result_key, cb)
         cb(err, result_line, self)
       end
 
-      if data == nil or is_complete then
+      if is_complete then
         return
       end
 
@@ -457,12 +457,12 @@ function Job:sync(timeout, wait_interval)
 end
 
 function Job:result()
-  assert(self.enable_recording, "'enabled_recording' is not enabled for this job.")
+  assert(self.enable_recording, "'enable_recording' is not enabled for this job.")
   return self._stdout_results
 end
 
 function Job:stderr_result()
-  assert(self.enable_recording, "'enabled_recording' is not enabled for this job.")
+  assert(self.enable_recording, "'enable_recording' is not enabled for this job.")
   return self._stderr_results
 end
 
diff --git a/lua/plenary/json.lua b/lua/plenary/json.lua
index cf4358d..05581f7 100644
--- a/lua/plenary/json.lua
+++ b/lua/plenary/json.lua
@@ -33,20 +33,26 @@ local M = {}
 -- The resulting string can then be used by `vim.fn.json_decode`
 --
 ---@param jsonString string
----@param options table
+---@param options? table
 ---  * whitespace:
 ---     - defaults to true
 ---     - when true, comments will be replaced by whitespace
 ---     - when false, comments will be stripped
+---  * trailing_commas:
+---     - defaults to false
+---     - when true, trailing commas will be included
+---     - when false, trailing commas will be removed
 function M.json_strip_comments(jsonString, options)
   options = options or {}
   local strip = options.whitespace == false and stripWithoutWhitespace or stripWithWhitespace
+  local omitTrailingCommas = not options.trailing_commas
 
   local insideString = false
   local insideComment = false
   local offset = 1
   local result = ""
   local skip = false
+  local lastComma = 0
 
   for i = 1, #jsonString, 1 do
     if skip then
@@ -89,6 +95,16 @@ function M.json_strip_comments(jsonString, options)
           insideComment = false
           result = result .. strip(jsonString, offset, i)
           offset = i + 1
+        elseif omitTrailingCommas and not insideComment then
+          if currentCharacter == "," then
+            lastComma = i
+          elseif (currentCharacter == "]" or currentCharacter == "}") and lastComma > 0 then
+            result = result .. slice(jsonString, offset, lastComma - 1) .. slice(jsonString, lastComma + 1, i)
+            offset = i + 1
+            lastComma = 0
+          elseif currentCharacter:match "%S" then
+            lastComma = 0
+          end
         end
       end
     end
diff --git a/lua/plenary/path.lua b/lua/plenary/path.lua
index 51229f1..701679e 100644
--- a/lua/plenary/path.lua
+++ b/lua/plenary/path.lua
@@ -929,7 +929,7 @@ end
 
 function Path:find_upwards(filename)
   local folder = Path:new(self)
-  local root = path.root(folder)
+  local root = path.root(folder:absolute())
 
   while folder:absolute() ~= root do
     local p = folder:joinpath(filename)
@@ -938,7 +938,7 @@ function Path:find_upwards(filename)
     end
     folder = folder:parent()
   end
-  return ""
+  return nil
 end
 
 return Path


Test patch:
diff --git a/tests/plenary/job_spec.lua b/tests/plenary/job_spec.lua
index d9213be..c2edde0 100644
--- a/tests/plenary/job_spec.lua
+++ b/tests/plenary/job_spec.lua
@@ -99,7 +99,7 @@ describe("Job", function()
       assert.are.same(job:result(), results)
     end)
 
-    pending("should split stdin across newlines with no ending newline", function()
+    it("should split stdin across newlines with no ending newline", function()
       local results = {}
       local job = Job:new {
         -- writer = "hello\nword\nthis is\ntj",
@@ -113,6 +113,8 @@ describe("Job", function()
       job:start()
       job:send "hello\nwor"
       job:send "ld"
+
+      wait_for_result(job, { "hello", "world" })
       job:shutdown()
 
       assert.are.same(job:result(), { "hello", "world" })
@@ -137,7 +139,7 @@ describe("Job", function()
       assert.are.same(job:result(), results)
     end)
 
-    pending("should return last line when there is no ending newline", function()
+    it("should return last line when there is no ending newline", function()
       local results = {}
       local job = Job:new {
         command = "printf",
diff --git a/tests/plenary/json_spec.lua b/tests/plenary/json_spec.lua
index 955668a..88c00c2 100644
--- a/tests/plenary/json_spec.lua
+++ b/tests/plenary/json_spec.lua
@@ -67,4 +67,21 @@ describe("json", function()
       [[{"x":"x \"sed -e \\\"s/^.\\\\{46\\\\}T//\\\" -e \\\"s/#033/\\\\x1b/g\\\"\""}]]
     )
   end)
+
+  it("trailing commas", function()
+    eq(json.json_strip_comments '{"a":"b",}', '{"a":"b"}')
+    eq(json.json_strip_comments '{"a":{"b":"c",},}', '{"a":{"b":"c"}}')
+    eq(json.json_strip_comments '{"a":["b","c",],}', '{"a":["b","c"]}')
+  end)
+
+  it("trailing commas - ignored in strings and comments", function()
+    eq(json.json_strip_comments '{"a":"b,}"}', '{"a":"b,}"}')
+  end)
+
+  it("trailing commas - left when disabled in options", function()
+    local options = { trailing_commas = true }
+    eq(json.json_strip_comments('{"a":"b",}', options), '{"a":"b",}')
+    eq(json.json_strip_comments('{"a":{"b":"c",},}', options), '{"a":{"b":"c",},}')
+    eq(json.json_strip_comments('{"a":["b","c",],}', options), '{"a":["b","c",],}')
+  end)
 end)