aboutsummaryrefslogtreecommitdiff
path: root/plugins/gitstatus.lua
blob: 04f6b15c7bc147c583b7e60e0ccf64d2bb0ab589 (plain)
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
-- mod-version:3
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local style = require "core.style"
local StatusView = require "core.statusview"
local TreeView = require "plugins.treeview"

config.plugins.gitstatus = common.merge({
  color_icons = true,
  recurse_submodules = true,
  -- The config specification used by the settings gui
  config_spec = {
    name = "Git Status",
    {
      label = "Colorize icons",
      description = "Colorize the icons as well",
      path = "color_icons",
      type = "toggle",
      default = true
    },
    {
      label = "Recurse Submodules",
      description = "Also retrieve git stats from submodules.",
      path = "recurse_submodules",
      type = "toggle",
      default = true
    }
  }
}, config.plugins.gitstatus)

style.gitstatus_addition = {common.color "#587c0c"}
style.gitstatus_modification = {common.color "#0c7d9d"}
style.gitstatus_deletion = {common.color "#94151b"}

local scan_rate = config.project_scan_rate or 5
local cached_color_for_item = {}


-- Override TreeView's get_item_text to add modification color
local treeview_get_item_text = TreeView.get_item_text
function TreeView:get_item_text(item, active, hovered)
  local text, font, color = treeview_get_item_text(self, item, active, hovered)
  if cached_color_for_item[item.abs_filename] then
    color = cached_color_for_item[item.abs_filename]
  end
  return text, font, color
end

-- Override TreeView's get_item_icon to add modification color
local treeview_get_item_icon = TreeView.get_item_icon
function TreeView:get_item_icon(item, active, hovered)
  local character, font, color = treeview_get_item_icon(self, item, active, hovered)
  if config.plugins.gitstatus and config.plugins.gitstatus.color_icons and cached_color_for_item[item.abs_filename] then
    color = cached_color_for_item[item.abs_filename]
  end
  return character, font, color
end

local git = {
  branch = nil,
  inserts = 0,
  deletes = 0,
}

local function exec(cmd)
  local proc = process.start(cmd)
  -- Don't use proc:wait() here - that will freeze the app.
  -- Instead, rely on the fact that this is only called within
  -- a coroutine, and yield for a fraction of a second, allowing
  -- other stuff to happen while we wait for the process to complete.
  while proc:running() do
    coroutine.yield(0.1)
  end
  return proc:read_stdout() or ""
end


core.add_thread(function()
  while true do
    if system.get_file_info(".git") then
      -- get branch name
      git.branch = exec({"git", "rev-parse", "--abbrev-ref", "HEAD"}):match("[^\n]*")

      local inserts = 0
      local deletes = 0

      -- get diff
      local diff = exec({"git", "diff", "--numstat"})
      if
        config.plugins.gitstatus.recurse_submodules
        and
        system.get_file_info(".gitmodules")
      then
        local diff2 = exec({"git", "submodule", "foreach", "git diff --numstat"})
        diff = diff .. diff2
      end

      -- forget the old state
      cached_color_for_item = {}

      local folder = core.project_dir
      for line in string.gmatch(diff, "[^\n]+") do
        local submodule = line:match("^Entering '(.+)'$")
        if submodule then
          folder = core.project_dir .. PATHSEP .. submodule
        else
          local ins, dels, path = line:match("(%d+)%s+(%d+)%s+(.+)")
          if path then
            inserts = inserts + (tonumber(ins) or 0)
            deletes = deletes + (tonumber(dels) or 0)
            local abs_path = folder .. PATHSEP .. path
            -- Color this file, and each parent folder,
            -- so you can see at a glance which folders
            -- have modified files in them.
            while abs_path do
              cached_color_for_item[abs_path] = style.gitstatus_modification
              abs_path = common.dirname(abs_path)
            end
          end
        end
      end

      git.inserts = inserts
      git.deletes = deletes

    else
      git.branch = nil
    end

    coroutine.yield(scan_rate)
  end
end)


core.status_view:add_item({
  name = "status:git",
  alignment = StatusView.Item.RIGHT,
  get_item = function()
    if not git.branch then
      return {}
    end
    return {
      (git.inserts ~= 0 or git.deletes ~= 0) and style.accent or style.text,
      git.branch,
      style.dim, "  ",
      git.inserts ~= 0 and style.accent or style.text, "+", git.inserts,
      style.dim, " / ",
      git.deletes ~= 0 and style.accent or style.text, "-", git.deletes,
    }
  end,
  position = -1,
  tooltip = "branch and changes",
  separator = core.status_view.separator2
})