Repository: nvim-lualine/lualine.nvim (lua)

Issue:
Bug: lualine doesn't show filetype icon for files without extension
The 'filetype' component doesn't find the filetype icon sometimes:

<img width="1025" alt="_HOME_src_gdk_gitlab_a_rb" src="https://user-images.githubusercontent.com/120483/153911616-140a0326-e565-462c-9b6e-f3cb57da5f11.png">

<details>
<summary>My config</summary>

```lua
lualine.setup({
  extensions = {
    require('plugins.statusline.extensions.filetree'),
    require('plugins.statusline.extensions.neoterm'),
  },
  options = {
    theme = theme.colorscheme,
    icons_enabled = true,
    section_separators = '',
    component_separators = '',
  },
  sections = {
    lualine_a = { utils.bufnr() },
    lualine_b = {
      {
        'filetype',
        colored = true,
        icon_only = true,
        padding = 1,
      },
      {
        'filename',
        file_status = true,
        path = 1,
        shorting_target = 30,
        symbols = { modified = ' ', readonly = ' ', unnamed = ' [No Name] ' },
        padding = 0,
      },
    },
    lualine_c = {
      { vim.my.treesitter.gps.location },
    },
    lualine_x = {
      {
        'diagnostics',
        sources = { 'nvim_diagnostic' },
        symbols = {
          error = theme.signs.error,
          warn = theme.signs.warn,
          hint = theme.signs.hint,
          info = theme.signs.info,
        },
        color_error = theme.colors.error,
        color_warn = theme.colors.warn,
        color_hint = theme.colors.hint,
        color_info = theme.colors.info,
      },
    },
    lualine_y = { 'diff' },
    lualine_z = {
      { '[[%3l:%-3c]]' },
      utils.mode(),
    },
  },
  inactive_sections = {
    lualine_a = { utils.bufnr() },
    lualine_b = utils.filename({ icon_color = false }),
    lualine_c = {},
    lualine_x = {},
    lualine_y = {},
    lualine_z = {},
  },
})
```

</details>

### Self Checks

<!-- Check these boxes by placeing an X in the middle -->
- [x] I'm using the latest lualine.
- [x] I didn't find the issue in exsisting issues or prs.

### How to reproduce the problem

I'm not sure, but it seems related to how the filetype is set, when it's set in `ftdetect` with `BufReadPos` it doesn't set the icon.

### Expected behaviour


### Actual behaviour


### Minimal config to reproduce the issue

<details>
<summary>Config</summary>
<!--
  =================
  ### IMPORTENT ###
  =================

  You need to provide a way to reproduce the issue without influence
  of other plugins.

  Issues without proper format and a proper way to
  reproduce might be ignored.

  This script can help you with that.
  https://github.com/nvim-lualine/lualine.nvim/blob/master/scripts/nvim_isolated_conf.sh

  Create an isolated config directory with:

    `nvim_isolated_conf.sh -c DirectoryName`

  Then modify the DirectoryName/.config/nvim/init.vim
  so you can reproduce the issue .

  You can load the config to see if the issue is occuring with

    `nvim_isolated_conf.sh -l DirectoryName`

  Paste the DirectoryName/.config/init.vim without comments below
-->

</details>

### Aditional information

Pull request:
With https://github.com/kyazdani42/nvim-web-devicons/pull/125 a filetype based lookup was implemented upstream which can be used to get an icon when a file doesn't have an extension.

fixes #578

Patch:
diff --git a/lua/lualine/components/filetype.lua b/lua/lualine/components/filetype.lua
index cd98e55cd..673379dd2 100644
--- a/lua/lualine/components/filetype.lua
+++ b/lua/lualine/components/filetype.lua
@@ -31,9 +31,10 @@ function M:apply_icon()
   local icon, icon_highlight_group
   local ok, devicons = pcall(require, 'nvim-web-devicons')
   if ok then
-    local f_name, f_extension = vim.fn.expand('%:t'), vim.fn.expand('%:e')
-    f_extension = f_extension ~= '' and f_extension or vim.bo.filetype
-    icon, icon_highlight_group = devicons.get_icon(f_name, f_extension)
+    icon, icon_highlight_group = devicons.get_icon(vim.fn.expand('%:t'))
+    if icon == nil then
+      icon, icon_highlight_group = devicons.get_icon_by_filetype(vim.bo.filetype)
+    end
 
     if icon == nil and icon_highlight_group == nil then
       icon = ''


Test patch:
diff --git a/tests/spec/component_spec.lua b/tests/spec/component_spec.lua
index 48c1990ac..b5dad639f 100644
--- a/tests/spec/component_spec.lua
+++ b/tests/spec/component_spec.lua
@@ -285,21 +285,24 @@ describe('Filetype component', function()
   end)
 
   it('colors nvim-web-devicons icons', function()
-    package.loaded['nvim-web-devicons'] = {
-      get_icon = function()
-        return '*', 'test_highlight_group'
-      end,
-    }
     vim.g.actual_curwin = tostring(vim.api.nvim_get_current_win())
+    stub(vim.fn, 'expand')
+    vim.fn.expand.on_call_with('%:t').returns('test.lua')
+
     local hl = require('lualine.highlight')
-    local utils = require('lualine.utils.utils')
     stub(hl, 'create_component_highlight_group')
-    stub(hl, 'format_highlight')
-    stub(utils, 'extract_highlight_colors')
     hl.create_component_highlight_group.returns { name = 'MyCompHl', no_mode = false, section = 'a' }
+    stub(hl, 'format_highlight')
     hl.format_highlight.returns('%#lualine_c_normal#')
+
+    local utils = require('lualine.utils.utils')
+    stub(utils, 'extract_highlight_colors')
     utils.extract_highlight_colors.returns('#000')
 
+    local devicons = require('nvim-web-devicons')
+    stub(devicons, 'get_icon')
+    devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group')
+
     local opts = build_component_opts {
       component_separators = { left = '', right = '' },
       hl = '%#lualine_c_normal#',
@@ -308,6 +311,7 @@ describe('Filetype component', function()
       icon_only = false,
     }
     assert_component('filetype', opts, '%#MyCompHl_normal#*%#lualine_c_normal# lua%#lualine_c_normal#')
+    assert.stub(devicons.get_icon).was_called_with('test.lua')
     assert.stub(utils.extract_highlight_colors).was_called_with('test_highlight_group', 'fg')
     assert.stub(hl.create_component_highlight_group).was_called_with(
       { fg = '#000' },
@@ -315,42 +319,60 @@ describe('Filetype component', function()
       opts,
       false
     )
+    assert.stub(vim.fn.expand).was_called_with('%:t')
+
+    devicons.get_icon:revert()
+    utils.extract_highlight_colors:revert()
     hl.create_component_highlight_group:revert()
     hl.format_highlight:revert()
-    utils.extract_highlight_colors:revert()
-    package.loaded['nvim-web-devicons'] = nil
+    vim.fn.expand:revert()
     vim.g.actual_curwin = nil
   end)
 
-  it("Doesn't color when colored is false", function()
-    package.loaded['nvim-web-devicons'] = {
-      get_icon = function()
-        return '*', 'test_highlight_group'
-      end,
-    }
+  it("doesn't color when colored is false", function()
+    stub(vim.fn, 'expand')
+    vim.fn.expand.on_call_with('%:t').returns('test.lua')
+
     local hl = require('lualine.highlight')
-    local utils = require('lualine.utils.utils')
     stub(hl, 'create_component_highlight_group')
+
+    local utils = require('lualine.utils.utils')
     stub(utils, 'extract_highlight_colors')
-    hl.create_component_highlight_group.returns('MyCompHl')
-    utils.extract_highlight_colors.returns('#000')
+
+    local devicons = require('nvim-web-devicons')
+    stub(devicons, 'get_icon')
+    devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group')
+
     local opts = build_component_opts {
       component_separators = { left = '', right = '' },
       padding = 0,
       colored = false,
     }
     assert_component('filetype', opts, '* lua')
-    hl.create_component_highlight_group:revert()
+    assert.stub(devicons.get_icon).was_called_with('test.lua')
+    assert.stub(utils.extract_highlight_colors).was_not_called()
+    assert.stub(hl.create_component_highlight_group).was_not_called()
+    assert.stub(vim.fn.expand).was_called_with('%:t')
+
+    devicons.get_icon:revert()
     utils.extract_highlight_colors:revert()
-    package.loaded['nvim-web-devicons'] = nil
+    hl.create_component_highlight_group:revert()
+    vim.fn.expand:revert()
   end)
 
   it('displays only icon when icon_only is true', function()
-    package.loaded['nvim-web-devicons'] = {
-      get_icon = function()
-        return '*', 'test_highlight_group'
-      end,
-    }
+    stub(vim.fn, 'expand')
+    vim.fn.expand.on_call_with('%:t').returns('test.lua')
+
+    local hl = require('lualine.highlight')
+    stub(hl, 'create_component_highlight_group')
+
+    local utils = require('lualine.utils.utils')
+    stub(utils, 'extract_highlight_colors')
+
+    local devicons = require('nvim-web-devicons')
+    stub(devicons, 'get_icon')
+    devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group')
 
     local opts = build_component_opts {
       component_separators = { left = '', right = '' },
@@ -359,15 +381,30 @@ describe('Filetype component', function()
       icon_only = true,
     }
     assert_component('filetype', opts, '*')
-    package.loaded['nvim-web-devicons'] = nil
+    assert.stub(devicons.get_icon).was_called_with('test.lua')
+    assert.stub(utils.extract_highlight_colors).was_not_called()
+    assert.stub(hl.create_component_highlight_group).was_not_called()
+    assert.stub(vim.fn.expand).was_called_with('%:t')
+
+    devicons.get_icon:revert()
+    utils.extract_highlight_colors:revert()
+    hl.create_component_highlight_group:revert()
+    vim.fn.expand:revert()
   end)
 
   it('displays right aligned icon when icon.align is "right"', function()
-    package.loaded['nvim-web-devicons'] = {
-      get_icon = function()
-        return '*', 'test_highlight_group'
-      end,
-    }
+    stub(vim.fn, 'expand')
+    vim.fn.expand.on_call_with('%:t').returns('test.lua')
+
+    local hl = require('lualine.highlight')
+    stub(hl, 'create_component_highlight_group')
+
+    local utils = require('lualine.utils.utils')
+    stub(utils, 'extract_highlight_colors')
+
+    local devicons = require('nvim-web-devicons')
+    stub(devicons, 'get_icon')
+    devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group')
 
     local opts = build_component_opts {
       component_separators = { left = '', right = '' },
@@ -377,7 +414,51 @@ describe('Filetype component', function()
       icon = { align = 'right' }
     }
     assert_component('filetype', opts, 'lua *')
-    package.loaded['nvim-web-devicons'] = nil
+    assert.stub(devicons.get_icon).was_called_with('test.lua')
+    assert.stub(utils.extract_highlight_colors).was_not_called()
+    assert.stub(hl.create_component_highlight_group).was_not_called()
+    assert.stub(vim.fn.expand).was_called_with('%:t')
+
+    devicons.get_icon:revert()
+    utils.extract_highlight_colors:revert()
+    hl.create_component_highlight_group:revert()
+    vim.fn.expand:revert()
+  end)
+
+  it('uses filetype lookup when file has no extension', function()
+    stub(vim.fn, 'expand')
+    vim.fn.expand.on_call_with('%:t').returns('test')
+
+    local hl = require('lualine.highlight')
+    stub(hl, 'create_component_highlight_group')
+
+    local utils = require('lualine.utils.utils')
+    stub(utils, 'extract_highlight_colors')
+
+    local devicons = require('nvim-web-devicons')
+    stub(devicons, 'get_icon')
+    devicons.get_icon.on_call_with('test').returns(nil)
+    stub(devicons, 'get_icon_by_filetype')
+    devicons.get_icon_by_filetype.on_call_with('lua').returns('*', 'test_highlight_group')
+
+    local opts = build_component_opts {
+      component_separators = { left = '', right = '' },
+      padding = 0,
+      colored = false,
+      icon_only = false,
+    }
+    assert_component('filetype', opts, '* lua')
+    assert.stub(devicons.get_icon).was_called_with('test')
+    assert.stub(devicons.get_icon_by_filetype).was_called_with('lua')
+    assert.stub(utils.extract_highlight_colors).was_not_called()
+    assert.stub(hl.create_component_highlight_group).was_not_called()
+    assert.stub(vim.fn.expand).was_called_with('%:t')
+
+    devicons.get_icon_by_filetype:revert()
+    devicons.get_icon:revert()
+    utils.extract_highlight_colors:revert()
+    hl.create_component_highlight_group:revert()
+    vim.fn.expand:revert()
   end)
 end)