mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	Merge pull request #15017 from donbex/local-file-uri
fix(lsp): accept file URIs without a hostname
This commit is contained in:
		@@ -52,7 +52,7 @@ end
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
--@private
 | 
					--@private
 | 
				
			||||||
local function is_windows_file_uri(uri)
 | 
					local function is_windows_file_uri(uri)
 | 
				
			||||||
  return uri:match('^file:///[a-zA-Z]:') ~= nil
 | 
					  return uri:match('^file:/+[a-zA-Z]:') ~= nil
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Get a URI from a file path.
 | 
					--- Get a URI from a file path.
 | 
				
			||||||
@@ -74,7 +74,7 @@ local function uri_from_fname(path)
 | 
				
			|||||||
  return table.concat(uri_parts)
 | 
					  return table.concat(uri_parts)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*)://.*'
 | 
					local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):.*'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- Get a URI from a bufnr
 | 
					--- Get a URI from a bufnr
 | 
				
			||||||
--@param bufnr (number): Buffer number
 | 
					--@param bufnr (number): Buffer number
 | 
				
			||||||
@@ -100,10 +100,10 @@ local function uri_to_fname(uri)
 | 
				
			|||||||
  uri = uri_decode(uri)
 | 
					  uri = uri_decode(uri)
 | 
				
			||||||
  -- TODO improve this.
 | 
					  -- TODO improve this.
 | 
				
			||||||
  if is_windows_file_uri(uri) then
 | 
					  if is_windows_file_uri(uri) then
 | 
				
			||||||
    uri = uri:gsub('^file:///', '')
 | 
					    uri = uri:gsub('^file:/+', '')
 | 
				
			||||||
    uri = uri:gsub('/', '\\')
 | 
					    uri = uri:gsub('/', '\\')
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    uri = uri:gsub('^file://', '')
 | 
					    uri = uri:gsub('^file:/+', '/')
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  return uri
 | 
					  return uri
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,12 +53,18 @@ describe('URI methods', function()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  describe('uri to filepath', function()
 | 
					  describe('uri to filepath', function()
 | 
				
			||||||
    describe('decode Unix file path', function()
 | 
					    describe('decode Unix file path', function()
 | 
				
			||||||
      it('file path includes only ascii charactors', function()
 | 
					      it('file path includes only ascii characters', function()
 | 
				
			||||||
        exec_lua("uri = 'file:///Foo/Bar/Baz.txt'")
 | 
					        exec_lua("uri = 'file:///Foo/Bar/Baz.txt'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)"))
 | 
					        eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)"))
 | 
				
			||||||
      end)
 | 
					      end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('local file path without hostname', function()
 | 
				
			||||||
 | 
					        exec_lua("uri = 'file:/Foo/Bar/Baz.txt'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)"))
 | 
				
			||||||
 | 
					      end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('file path including white space', function()
 | 
					      it('file path including white space', function()
 | 
				
			||||||
        exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'")
 | 
					        exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,6 +91,15 @@ describe('URI methods', function()
 | 
				
			|||||||
        eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
 | 
					        eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
 | 
				
			||||||
      end)
 | 
					      end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('local file path without hostname', function()
 | 
				
			||||||
 | 
					        local test_case = [[
 | 
				
			||||||
 | 
					        local uri = 'file:/C:/Foo/Bar/Baz.txt'
 | 
				
			||||||
 | 
					        return vim.uri_to_fname(uri)
 | 
				
			||||||
 | 
					        ]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
 | 
				
			||||||
 | 
					      end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('file path includes only ascii charactors with encoded colon character', function()
 | 
					      it('file path includes only ascii charactors with encoded colon character', function()
 | 
				
			||||||
        local test_case = [[
 | 
					        local test_case = [[
 | 
				
			||||||
        local uri = 'file:///C%3A/Foo/Bar/Baz.txt'
 | 
					        local uri = 'file:///C%3A/Foo/Bar/Baz.txt'
 | 
				
			||||||
@@ -125,6 +140,12 @@ describe('URI methods', function()
 | 
				
			|||||||
          return vim.uri_to_fname('JDT://content/%5C/')
 | 
					          return vim.uri_to_fname('JDT://content/%5C/')
 | 
				
			||||||
        ]])
 | 
					        ]])
 | 
				
			||||||
      end)
 | 
					      end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it('uri_to_fname returns non-file scheme URI without authority unchanged', function()
 | 
				
			||||||
 | 
					        eq('zipfile:/path/to/archive.zip%3A%3Afilename.txt', exec_lua [[
 | 
				
			||||||
 | 
					          return vim.uri_to_fname('zipfile:/path/to/archive.zip%3A%3Afilename.txt')
 | 
				
			||||||
 | 
					        ]])
 | 
				
			||||||
 | 
					      end)
 | 
				
			||||||
    end)
 | 
					    end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe('decode URI without scheme', function()
 | 
					    describe('decode URI without scheme', function()
 | 
				
			||||||
@@ -146,5 +167,14 @@ describe('URI methods', function()
 | 
				
			|||||||
      ]], uri)
 | 
					      ]], uri)
 | 
				
			||||||
      eq(uri, exec_lua(test_case))
 | 
					      eq(uri, exec_lua(test_case))
 | 
				
			||||||
    end)
 | 
					    end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris without authority', function()
 | 
				
			||||||
 | 
					      local uri = 'zipfile:/path/to/archive.zip%3A%3Afilename.txt'
 | 
				
			||||||
 | 
					      local test_case = string.format([[
 | 
				
			||||||
 | 
					        local uri = '%s'
 | 
				
			||||||
 | 
					        return vim.uri_from_bufnr(vim.uri_to_bufnr(uri))
 | 
				
			||||||
 | 
					      ]], uri)
 | 
				
			||||||
 | 
					      eq(uri, exec_lua(test_case))
 | 
				
			||||||
 | 
					    end)
 | 
				
			||||||
  end)
 | 
					  end)
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ describe('vim.lsp.codelens', function()
 | 
				
			|||||||
  after_each(helpers.clear)
 | 
					  after_each(helpers.clear)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('on_codelens_stores_and_displays_lenses', function()
 | 
					  it('on_codelens_stores_and_displays_lenses', function()
 | 
				
			||||||
    local fake_uri = "file://fake/uri"
 | 
					    local fake_uri = "file:///fake/uri"
 | 
				
			||||||
    local bufnr = exec_lua([[
 | 
					    local bufnr = exec_lua([[
 | 
				
			||||||
      fake_uri = ...
 | 
					      fake_uri = ...
 | 
				
			||||||
      local bufnr = vim.uri_to_bufnr(fake_uri)
 | 
					      local bufnr = vim.uri_to_bufnr(fake_uri)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ describe('vim.lsp.diagnostic', function()
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
    ]]
 | 
					    ]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fake_uri = "file://fake/uri"
 | 
					    fake_uri = "file:///fake/uri"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    exec_lua([[
 | 
					    exec_lua([[
 | 
				
			||||||
      fake_uri = ...
 | 
					      fake_uri = ...
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1147,14 +1147,14 @@ describe('LSP', function()
 | 
				
			|||||||
          make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄")
 | 
					          make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄")
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        textDocument = {
 | 
					        textDocument = {
 | 
				
			||||||
          uri = "file://fake/uri";
 | 
					          uri = "file:///fake/uri";
 | 
				
			||||||
          version = editVersion
 | 
					          version = editVersion
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    before_each(function()
 | 
					    before_each(function()
 | 
				
			||||||
      target_bufnr = exec_lua [[
 | 
					      target_bufnr = exec_lua [[
 | 
				
			||||||
        local bufnr = vim.uri_to_bufnr("file://fake/uri")
 | 
					        local bufnr = vim.uri_to_bufnr("file:///fake/uri")
 | 
				
			||||||
        local lines = {"1st line of text", "2nd line of 语text"}
 | 
					        local lines = {"1st line of text", "2nd line of 语text"}
 | 
				
			||||||
        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
					        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
				
			||||||
        return bufnr
 | 
					        return bufnr
 | 
				
			||||||
@@ -1234,7 +1234,7 @@ describe('LSP', function()
 | 
				
			|||||||
          make_edit(row, 0, row, 1000, new_line)
 | 
					          make_edit(row, 0, row, 1000, new_line)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        textDocument = {
 | 
					        textDocument = {
 | 
				
			||||||
          uri = "file://fake/uri";
 | 
					          uri = "file:///fake/uri";
 | 
				
			||||||
          version = editVersion
 | 
					          version = editVersion
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -1252,7 +1252,7 @@ describe('LSP', function()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    before_each(function()
 | 
					    before_each(function()
 | 
				
			||||||
      local ret = exec_lua [[
 | 
					      local ret = exec_lua [[
 | 
				
			||||||
        local bufnr = vim.uri_to_bufnr("file://fake/uri")
 | 
					        local bufnr = vim.uri_to_bufnr("file:///fake/uri")
 | 
				
			||||||
        local lines = {
 | 
					        local lines = {
 | 
				
			||||||
          "Original Line #1",
 | 
					          "Original Line #1",
 | 
				
			||||||
          "Original Line #2"
 | 
					          "Original Line #2"
 | 
				
			||||||
@@ -1532,19 +1532,19 @@ describe('LSP', function()
 | 
				
			|||||||
    it('Convert Location[] to items', function()
 | 
					    it('Convert Location[] to items', function()
 | 
				
			||||||
      local expected = {
 | 
					      local expected = {
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          filename = 'fake/uri',
 | 
					          filename = '/fake/uri',
 | 
				
			||||||
          lnum = 1,
 | 
					          lnum = 1,
 | 
				
			||||||
          col = 3,
 | 
					          col = 3,
 | 
				
			||||||
          text = 'testing'
 | 
					          text = 'testing'
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      local actual = exec_lua [[
 | 
					      local actual = exec_lua [[
 | 
				
			||||||
        local bufnr = vim.uri_to_bufnr("file://fake/uri")
 | 
					        local bufnr = vim.uri_to_bufnr("file:///fake/uri")
 | 
				
			||||||
        local lines = {"testing", "123"}
 | 
					        local lines = {"testing", "123"}
 | 
				
			||||||
        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
					        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
				
			||||||
        local locations = {
 | 
					        local locations = {
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            uri = 'file://fake/uri',
 | 
					            uri = 'file:///fake/uri',
 | 
				
			||||||
            range = {
 | 
					            range = {
 | 
				
			||||||
              start = { line = 0, character = 2 },
 | 
					              start = { line = 0, character = 2 },
 | 
				
			||||||
              ['end'] = { line = 0, character = 3 },
 | 
					              ['end'] = { line = 0, character = 3 },
 | 
				
			||||||
@@ -1558,14 +1558,14 @@ describe('LSP', function()
 | 
				
			|||||||
    it('Convert LocationLink[] to items', function()
 | 
					    it('Convert LocationLink[] to items', function()
 | 
				
			||||||
      local expected = {
 | 
					      local expected = {
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          filename = 'fake/uri',
 | 
					          filename = '/fake/uri',
 | 
				
			||||||
          lnum = 1,
 | 
					          lnum = 1,
 | 
				
			||||||
          col = 3,
 | 
					          col = 3,
 | 
				
			||||||
          text = 'testing'
 | 
					          text = 'testing'
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      local actual = exec_lua [[
 | 
					      local actual = exec_lua [[
 | 
				
			||||||
        local bufnr = vim.uri_to_bufnr("file://fake/uri")
 | 
					        local bufnr = vim.uri_to_bufnr("file:///fake/uri")
 | 
				
			||||||
        local lines = {"testing", "123"}
 | 
					        local lines = {"testing", "123"}
 | 
				
			||||||
        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
					        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
				
			||||||
        local locations = {
 | 
					        local locations = {
 | 
				
			||||||
@@ -1779,14 +1779,14 @@ describe('LSP', function()
 | 
				
			|||||||
        local expected = {
 | 
					        local expected = {
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            col = 1,
 | 
					            col = 1,
 | 
				
			||||||
            filename = 'test_a',
 | 
					            filename = '/test_a',
 | 
				
			||||||
            kind = 'File',
 | 
					            kind = 'File',
 | 
				
			||||||
            lnum = 2,
 | 
					            lnum = 2,
 | 
				
			||||||
            text = '[File] TestA'
 | 
					            text = '[File] TestA'
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            col = 1,
 | 
					            col = 1,
 | 
				
			||||||
            filename = 'test_b',
 | 
					            filename = '/test_b',
 | 
				
			||||||
            kind = 'Module',
 | 
					            kind = 'Module',
 | 
				
			||||||
            lnum = 4,
 | 
					            lnum = 4,
 | 
				
			||||||
            text = '[Module] TestB'
 | 
					            text = '[Module] TestB'
 | 
				
			||||||
@@ -1809,7 +1809,7 @@ describe('LSP', function()
 | 
				
			|||||||
                    line = 2
 | 
					                    line = 2
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                uri = "file://test_a"
 | 
					                uri = "file:///test_a"
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
              contanerName = "TestAContainer"
 | 
					              contanerName = "TestAContainer"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@@ -1828,7 +1828,7 @@ describe('LSP', function()
 | 
				
			|||||||
                    line = 4
 | 
					                    line = 4
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                uri = "file://test_b"
 | 
					                uri = "file:///test_b"
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
              contanerName = "TestBContainer"
 | 
					              contanerName = "TestBContainer"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -1867,7 +1867,7 @@ describe('LSP', function()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    before_each(function()
 | 
					    before_each(function()
 | 
				
			||||||
      target_bufnr = exec_lua [[
 | 
					      target_bufnr = exec_lua [[
 | 
				
			||||||
        local bufnr = vim.uri_to_bufnr("file://fake/uri")
 | 
					        local bufnr = vim.uri_to_bufnr("file:///fake/uri")
 | 
				
			||||||
        local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
 | 
					        local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
 | 
				
			||||||
        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
					        vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
 | 
				
			||||||
        return bufnr
 | 
					        return bufnr
 | 
				
			||||||
@@ -1876,7 +1876,7 @@ describe('LSP', function()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    local location = function(start_line, start_char, end_line, end_char)
 | 
					    local location = function(start_line, start_char, end_line, end_char)
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        uri = "file://fake/uri",
 | 
					        uri = "file:///fake/uri",
 | 
				
			||||||
        range = {
 | 
					        range = {
 | 
				
			||||||
          start = { line = start_line, character = start_char },
 | 
					          start = { line = start_line, character = start_char },
 | 
				
			||||||
          ["end"] = { line = end_line, character = end_char },
 | 
					          ["end"] = { line = end_line, character = end_char },
 | 
				
			||||||
@@ -1901,7 +1901,7 @@ describe('LSP', function()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    it('jumps to a LocationLink', function()
 | 
					    it('jumps to a LocationLink', function()
 | 
				
			||||||
      local pos = jump({
 | 
					      local pos = jump({
 | 
				
			||||||
          targetUri = "file://fake/uri",
 | 
					          targetUri = "file:///fake/uri",
 | 
				
			||||||
          targetSelectionRange = {
 | 
					          targetSelectionRange = {
 | 
				
			||||||
            start = { line = 0, character = 4 },
 | 
					            start = { line = 0, character = 4 },
 | 
				
			||||||
            ["end"] = { line = 0, character = 4 },
 | 
					            ["end"] = { line = 0, character = 4 },
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user