summary refs log tree commit diff
path: root/.vim/lua/statusline.lua
blob: cb9d16668577e3be4ae0a454a4f6e7173b3379e0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
local spinner_ok, spinner = pcall(require, 'spinner')
if spinner_ok then
  spinner.setup {
    spinner = {'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'},
    interval = 120, -- spinner frame rate in ms
  }
end

function _G.statusline()
  local bufnr = vim.fn.bufnr() or 0
  local hi = statusline_color(vim.fn.mode())

  local sl = hi
  -- sl = sl .. statusline_append([[ ⢷]], '%#StatusLineIcon#', hi)

  sl = sl .. statusline_append(statusline_ts(),
    '%#StatusLineTreesitter#', hi)

  sl = sl .. statusline_append(statusline_lsp_diagnostics(bufnr, hi),
    '%#StatusLineDiagnostics#', hi)

  sl = sl .. statusline_append(vim.trim(require('lsp-status').status()),
    '%#StatusLineLsp#', hi)

  sl = sl .. statusline_append(statusline_dap(),
    '%#StatusLineDap#', hi)

  sl = sl .. [[ %= ]] -- spacer

  sl = sl .. statusline_append(statusline_notify(hi),
    '%#StatusLineNotify#', hi)

  if spinner_ok then
    sl = sl .. statusline_append(spinner.status(bufnr),
      '%#StatusLineJobs#', hi)
  end

  sl = sl .. statusline_append(require('nvim-lightbulb').get_status_text(),
    '%#StatusLineLightbulb#', hi)

  sl = sl .. statusline_append([[ %l:%v --%p%%-- %y]], hi, hi)

  return sl
end

function _G.statusline_color(mode)
  local hi = '%*'
  if mode == 'i' then
    -- hi = '%#StatusLineInsert#'
  elseif mode == 'c' then
    -- hi = '%#StatusLineCommand#'
  elseif mode == 'v' or mode == 'V' or mode == '' then
    hi = '%#StatusLineVisual#'
  elseif mode == 'R' or mode == 'Rv' then
    hi = '%#StatusLineReplace#'
  end
  -- return '%{g:actual_curwin==win_getid()?'.. hi ..':%#StatusLineNC#}'
  return hi
end

-- statusline_append
-- does item highlighting and conditionnal spacing
function _G.statusline_append(element, hi, default_hi, opts)
  if not element or element == '' then return '' end
  opts = opts or { append_space = true }

  if not (default_hi == '%*') then hi = default_hi end
  local el = hi .. element .. default_hi
  if opts.append_space then el = el .. ' ' end
  return el
end

function _G.statusline_lsp_diagnostics(bufnr, default_hi)
  local error_num = vim.diagnostic.get(bufnr, {severity=vim.diagnostic.severity.ERROR})
  local warn_num = vim.diagnostic.get(bufnr, {severity=vim.diagnostic.severity.WARN})

  local s = ''
  if #error_num > 0 then
    local errs = vim.fn.sign_getdefined('DiagnosticSignError')[1].text .. #error_num
    s = s .. statusline_append(errs, '%#'..statusline_combine_hi('StatusLineDiagnostics', 'DiagnosticError')..'#', default_hi)
  end
  if #warn_num > 0 then
    local warns = vim.fn.sign_getdefined('DiagnosticSignWarn')[1].text .. #warn_num
    s = s .. statusline_append(warns, '%#'..statusline_combine_hi('StatusLineDiagnostics', 'DiagnosticWarn')..'#', default_hi)
  end
  return vim.trim(s)
end

function _G.statusline_combine_hi(outer_name, inner_name)
  local outer = vim.api.nvim_get_hl_by_name(outer_name, true)
  local inner = vim.api.nvim_get_hl_by_name(inner_name, true)
  local hi = 'auto' .. outer_name .. inner_name
  if pcall(vim.api.nvim_get_hl_by_name, hi, true)  then return hi end
  local gui
  gui = inner.bold and 'bold'
  gui = inner.italic and 'italic'
  gui = inner.underline and 'underline'
  vim.api.nvim_set_hl(0, hi, {bg = outer.background, fg = inner.foreground, gui = gui})
  return hi
end
function _G.statusline_ts()
  local data = require('nvim-navic').get_data()
  if not data then return '' end
  local s = {}
  for i = 1, #data do
    table.insert(s, string.format('%%#CmpItemKind%s#%s%%* %%#StatusLine#%s%%*', data[i].type, data[i].icon, data[i].name))
  end
  return table.concat(s, ' > ')
end
function _G.statusline_dap()
  local dap_ok, dap = pcall(require, 'dap')
  if not dap_ok then return '' end

  local s = dap.status()
  if s == '' then return s end
  return '[DAP: ' .. s .. ']'
end

-- notifications cache
_G.statusline_notifications = {}
_G.statusline_notifications_archive = {}

function _G.statusline_notify(default_hi)
  if #_G.statusline_notifications == 0 then return '' end
  local n = _G.statusline_notifications[1]
  local timeout = (n.options and n.options.timeout) or 2000
  -- pop message after <timeout> and move to archive
  vim.defer_fn(function()
    local n = table.remove(_G.statusline_notifications, 1)
    table.insert(_G.statusline_notifications_archive, n)
    -- make sure archive is never longer than 20 notifications
    if #_G.statusline_notifications_archive > 20 then
      table.remove(_G.statusline_notifications_archive, 1)
    end
  end, timeout)

  local s = ''
  if n.hi then
    s = s .. statusline_append(n.lvl_string .. ' ', '%#'..statusline_combine_hi('StatusLineNotify', n.hi)..'#', default_hi, { append_space = false })
  end
  s = s .. statusline_append(n.message, '%#StatusLineNotify#', default_hi)
  return vim.trim(s)
end

-- out vim.notify implementation
local notify = function(m, l, o)
  local lvl_string = l
  local hi = nil
  if l and type(l) == 'number' then
    if l == vim.log.levels.TRACE then
      hi = 'deEmph'
    elseif l == vim.log.levels.DEBUG then
      hi = 'DiagnosticHint'
    elseif l == vim.log.levels.INFO then
      hi = 'DiagnosticInfo'
    elseif l == vim.log.levels.WARN then
      hi = 'DiagnosticWarn'
    elseif l == vim.log.levels.ERROR then
      hi = 'DiagnosticError'
    end
    -- get level string
    lvl_string = vim.lsp.log_levels[l]
  end
  local display = m
  if lvl_string then display = lvl_string .. ' ' .. display end
  table.insert(_G.statusline_notifications, {
    message = m,
    level = l,
    options = o,
    lvl_string = lvl_string,
    hi = hi,
    display = display,
  })

  if _G.statusline_enable_notifysend then
    local args = {}
    if lvl_string then
      if lvl_string == 'ERROR' then
        table.insert(args, '--category=error')
      end
      if lvl_string == 'WARN' then
        table.insert(args, '--category=warning')
      end
    end
    table.insert(args, 'nvim')
    table.insert(args, display)
    require('job').jobstart { command = 'notify-send', args = args }
  end
end

-- overwrite vim.notify
vim.notify = function( m, l, o)
  if vim.in_fast_event() then
    vim.schedule(function()
      notify(m, l, o)
    end)
  else
    return notify(m, l, o)
  end
end

vim.keymap.set('n', '<leader>n', function()
  local pickers = require('telescope.pickers')
  local finders = require('telescope.finders')
  local conf = require('telescope.config').values
  local actions = require('telescope.actions')
  -- local action_state = require('telescope.actions.state')
  -- local previewers = require('telescope.previewers')

  local opts = {}
  pickers.new(opts, {
    prompt_title = "Notifications",
    finder = finders.new_dynamic {
      fn = function()
        return _G.statusline_notifications_archive
      end,
      entry_maker = function(n)
        return {
          value = n,
          display = n.display,
          ordinal = n.message,
        }
      end,
    },
    sorter = conf.generic_sorter(opts),
    attach_mappings = function(prompt_bufnr, _)
      actions.select_default:replace(function()
        actions.close(prompt_bufnr)
        -- noop
        -- local sel = action_state.get_selected_entry()
        -- vim.notify(sel.value.message, sel.value.level, sel.value.options)
      end)
      return true
    end,
    -- previewer = previewers.new_buffer_previewer {
    --   define_preview = function(self, entry)
    --     vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, {entry.value.display})
    --   end,
    -- },
  }):find()
end)
wk_register { ['<leader>n'] = {'notifications'} }

-- vim.opt.statusline = '%!luaeval("statusline()")'
vim.opt.statusline = '%{%v:lua.statusline()%}'

-- WINBAR --
vim.opt.winbar = [[%h%w%q%f %-m %-r %= %#WinBarCwd#%{%getcwd()%}%*]] -- keep it simple
-- function _G.winbar()
--   return [[%<%F %-m %-r]]
-- end
-- vim.opt.winbar = '%{%v:lua.winbar()%}'