chore: format with stylua

This commit is contained in:
smjonas 2022-07-09 15:51:06 +02:00
parent 6863ccf6be
commit f7ec0ac130
62 changed files with 2017 additions and 1790 deletions

6
.stylua.toml Normal file
View file

@ -0,0 +1,6 @@
column_width = 110
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses = "NoSingleTable"

View file

@ -5,10 +5,12 @@
-- --
-- The module returns true if everything went well, or false if any part of -- The module returns true if everything went well, or false if any part of
-- the initialization failed. -- the initialization failed.
local res, err = pcall(require, 'plenary') local res, err = pcall(require, "plenary")
if not res then if not res then
print("WARNING: Neogit depends on `nvim-lua/plenary.nvim` to work, but loading the plugin failed!") print("WARNING: Neogit depends on `nvim-lua/plenary.nvim` to work, but loading the plugin failed!")
print("Make sure you add `nvim-lua/plenary.nvim` to your plugin manager BEFORE neogit for everything to work") print(
"Make sure you add `nvim-lua/plenary.nvim` to your plugin manager BEFORE neogit for everything to work"
)
print(err) -- TODO: find out how to print the error without raising it AND properly print tabs print(err) -- TODO: find out how to print the error without raising it AND properly print tabs
return false return false
end end

View file

@ -1,5 +1,5 @@
local Buffer = require("neogit.lib.buffer") local Buffer = require("neogit.lib.buffer")
local ui = require 'neogit.buffers.branch_select_view.ui' local ui = require("neogit.buffers.branch_select_view.ui")
local M = {} local M = {}
@ -17,7 +17,7 @@ function M.new(branches, action)
local instance = { local instance = {
action = action, action = action,
branches = branches, branches = branches,
buffer = nil buffer = nil,
} }
setmetatable(instance, { __index = M }) setmetatable(instance, { __index = M })
@ -45,11 +45,11 @@ function M:open()
end end
self:close() self:close()
end, end,
} },
}, },
render = function() render = function()
return ui.View(self.branches) return ui.View(self.branches)
end end,
} }
end end

View file

@ -1,5 +1,5 @@
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local row = Ui.row local row = Ui.row
local text = Ui.text local text = Ui.text
@ -9,8 +9,8 @@ local M = {}
function M.View(branches) function M.View(branches)
return map(branches, function(branch_name) return map(branches, function(branch_name)
return row{ return row {
text(branch_name) text(branch_name),
} }
end) end)
end end

View file

@ -1,10 +1,10 @@
local Buffer = require("neogit.lib.buffer") local Buffer = require("neogit.lib.buffer")
local cli = require 'neogit.lib.git.cli' local cli = require("neogit.lib.git.cli")
local parser = require 'neogit.buffers.commit_view.parsing' local parser = require("neogit.buffers.commit_view.parsing")
local ui = require 'neogit.buffers.commit_view.ui' local ui = require("neogit.buffers.commit_view.ui")
local M = { local M = {
instance = nil instance = nil,
} }
-- @class CommitViewBuffer -- @class CommitViewBuffer
@ -22,15 +22,15 @@ local M = {
function M.new(commit_id, notify) function M.new(commit_id, notify)
local notification local notification
if notify then if notify then
local notif = require 'neogit.lib.notification' local notif = require("neogit.lib.notification")
notification = notif.create "Parsing commit..." notification = notif.create("Parsing commit...")
end end
local instance = { local instance = {
is_open = false, is_open = false,
commit_info = parser.parse_commit_info(cli.show.format("fuller").args(commit_id).call_sync()), commit_info = parser.parse_commit_info(cli.show.format("fuller").args(commit_id).call_sync()),
commit_overview = parser.parse_commit_overview(cli.show.stat.oneline.args(commit_id).call_sync()), commit_overview = parser.parse_commit_overview(cli.show.stat.oneline.args(commit_id).call_sync()),
buffer = nil buffer = nil,
} }
if notification then if notification then
@ -42,15 +42,12 @@ function M.new(commit_id, notify)
return instance return instance
end end
function M:close() function M:close()
self.is_open = false self.is_open = false
self.buffer:close() self.buffer:close()
self.buffer = nil self.buffer = nil
end end
function M:open() function M:open()
if M.instance and M.instance.is_open then if M.instance and M.instance.is_open then
M.instance:close() M.instance:close()
@ -83,7 +80,7 @@ function M:open()
end, end,
["BufUnload"] = function() ["BufUnload"] = function()
M.instance.is_open = false M.instance.is_open = false
end end,
}, },
mappings = { mappings = {
n = { n = {
@ -106,12 +103,12 @@ function M:open()
self.buffer.ui:update() self.buffer.ui:update()
end end
end end
end end,
} },
}, },
render = function() render = function()
return ui.CommitView(self.commit_info, self.commit_overview) return ui.CommitView(self.commit_info, self.commit_overview)
end end,
} }
end end

View file

@ -1,7 +1,7 @@
local M = {} local M = {}
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local diff_lib = require('neogit.lib.git.diff') local diff_lib = require("neogit.lib.git.diff")
-- @class CommitOverviewFile -- @class CommitOverviewFile
-- @field path the path to the file relative to the git root -- @field path the path to the file relative to the git root
@ -10,18 +10,18 @@ local diff_lib = require('neogit.lib.git.diff')
-- @field deletions deletion count visualized as list of `-` -- @field deletions deletion count visualized as list of `-`
-- @class CommitOverview -- @class CommitOverview
-- @field summary a short summary about what happened -- @field summary a short summary about what happened
-- @field files a list of CommitOverviewFile -- @field files a list of CommitOverviewFile
-- @see CommitOverviewFile -- @see CommitOverviewFile
local CommitOverview = {} local CommitOverview = {}
function M.parse_commit_overview(raw) function M.parse_commit_overview(raw)
local overview = { local overview = {
summary = util.trim(raw[#raw]), summary = util.trim(raw[#raw]),
files = {} files = {},
} }
for i=2,#raw-1 do for i = 2, #raw - 1 do
local file = {} local file = {}
if raw[i] ~= "" then if raw[i] ~= "" then
file.path, file.changes, file.insertions, file.deletions = raw[i]:match(" (.*)%s+|%s+(%d+) (%+*)(%-*)") file.path, file.changes, file.insertions, file.deletions = raw[i]:match(" (.*)%s+|%s+(%d+) (%+*)(%-*)")
@ -75,7 +75,7 @@ function M.parse_commit_info(raw_info)
info.committer_date = advance():match("CommitDate:%s*(.+)") info.committer_date = advance():match("CommitDate:%s*(.+)")
info.description = {} info.description = {}
info.diffs = {} info.diffs = {}
-- skip empty line -- skip empty line
advance() advance()

View file

@ -1,8 +1,8 @@
local M = {} local M = {}
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local common_ui = require 'neogit.buffers.common' local common_ui = require("neogit.buffers.common")
local Diff = common_ui.Diff local Diff = common_ui.Diff
local text = Ui.text local text = Ui.text
@ -14,9 +14,9 @@ local intersperse = util.intersperse
function M.OverviewFile(file) function M.OverviewFile(file)
return row.tag("OverviewFile") { return row.tag("OverviewFile") {
text.highlight("NeogitFilePath")(file.path), text.highlight("NeogitFilePath")(file.path),
text " | " , text(" | "),
text.highlight("Number")(file.changes), text.highlight("Number")(file.changes),
text " " , text(" "),
text.highlight("NeogitDiffAdd")(file.insertions), text.highlight("NeogitDiffAdd")(file.insertions),
text.highlight("NeogitDiffDelete")(file.deletions), text.highlight("NeogitDiffDelete")(file.deletions),
} }
@ -35,13 +35,13 @@ end
function M.CommitView(info, overview) function M.CommitView(info, overview)
return { return {
M.CommitHeader(info), M.CommitHeader(info),
text "" , text(""),
col(map(info.description, text), { sign = "NeogitCommitViewDescription", tag = "Description" }), col(map(info.description, text), { sign = "NeogitCommitViewDescription", tag = "Description" }),
text "", text(""),
text(overview.summary), text(overview.summary),
col(map(overview.files, M.OverviewFile), { tag = "OverviewFileList" }), col(map(overview.files, M.OverviewFile), { tag = "OverviewFileList" }),
text "", text(""),
col(intersperse(map(info.diffs, Diff), text("")), { tag = "DiffList" }) col(intersperse(map(info.diffs, Diff), text("")), { tag = "DiffList" }),
} }
end end

View file

@ -1,6 +1,6 @@
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local Component = require 'neogit.lib.ui.component' local Component = require("neogit.lib.ui.component")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local text = Ui.text local text = Ui.text
local col = Ui.col local col = Ui.col
@ -12,11 +12,11 @@ local range = util.range
local M = {} local M = {}
local diff_add_matcher = vim.regex('^+') local diff_add_matcher = vim.regex("^+")
local diff_delete_matcher = vim.regex('^-') local diff_delete_matcher = vim.regex("^-")
M.Diff = Component.new(function(diff) M.Diff = Component.new(function(diff)
local hunk_props = map(diff.hunks, function(hunk) local hunk_props = map(diff.hunks, function(hunk)
local header = diff.lines[hunk.diff_from] local header = diff.lines[hunk.diff_from]
local content = map(range(hunk.diff_from + 1, hunk.diff_to), function(i) local content = map(range(hunk.diff_from + 1, hunk.diff_to), function(i)
@ -25,13 +25,13 @@ M.Diff = Component.new(function(diff)
return { return {
header = header, header = header,
content = content content = content,
} }
end) end)
return col.tag("Diff") { return col.tag("Diff") {
text(diff.kind .. " " .. diff.file), text(diff.kind .. " " .. diff.file),
col.tag("HunkList")(map(hunk_props, M.Hunk)) col.tag("HunkList")(map(hunk_props, M.Hunk)),
} }
end) end)
@ -39,9 +39,9 @@ local HunkLine = Component.new(function(line)
local sign local sign
if diff_add_matcher:match_str(line) then if diff_add_matcher:match_str(line) then
sign = 'NeogitDiffAdd' sign = "NeogitDiffAdd"
elseif diff_delete_matcher:match_str(line) then elseif diff_delete_matcher:match_str(line) then
sign = 'NeogitDiffDelete' sign = "NeogitDiffDelete"
end end
return text(line, { sign = sign }) return text(line, { sign = sign })
@ -50,13 +50,13 @@ end)
M.Hunk = Component.new(function(props) M.Hunk = Component.new(function(props)
return col.tag("Hunk") { return col.tag("Hunk") {
text.sign("NeogitHunkHeader")(props.header), text.sign("NeogitHunkHeader")(props.header),
col.tag("HunkContent")(map(props.content, HunkLine)) col.tag("HunkContent")(map(props.content, HunkLine)),
} }
end) end)
M.List = Component.new(function(props) M.List = Component.new(function(props)
local children = filter(props.items, function(x) local children = filter(props.items, function(x)
return type(x) == "table" return type(x) == "table"
end) end)
if props.separator then if props.separator then
@ -76,25 +76,25 @@ M.Grid = Component.new(function(props)
props = vim.tbl_extend("force", { props = vim.tbl_extend("force", {
gap = 0, gap = 0,
columns = true, -- whether the items represents a list of columns instead of a list of row columns = true, -- whether the items represents a list of columns instead of a list of row
items = {} items = {},
}, props) }, props)
if props.columns then if props.columns then
local new_items = {} local new_items = {}
local row_count = 0 local row_count = 0
for i=1,#props.items do for i = 1, #props.items do
local l = #props.items[i] local l = #props.items[i]
if l > row_count then if l > row_count then
row_count = l row_count = l
end end
end end
for _=1,row_count do for _ = 1, row_count do
table.insert(new_items, {}) table.insert(new_items, {})
end end
for i=1,#props.items do for i = 1, #props.items do
for j=1,row_count do for j = 1, row_count do
local x = props.items[i][j] or text "" local x = props.items[i][j] or text("")
table.insert(new_items[j], x) table.insert(new_items[j], x)
end end
end end
@ -104,18 +104,18 @@ M.Grid = Component.new(function(props)
local rendered = {} local rendered = {}
local column_widths = {} local column_widths = {}
for i=1,#props.items do for i = 1, #props.items do
local children = {} local children = {}
if i ~= 1 then if i ~= 1 then
children = map(range(props.gap), function() children = map(range(props.gap), function()
return text "" return text("")
end) end)
end end
-- current row -- current row
local r = props.items[i] local r = props.items[i]
for j=1,#r do for j = 1, #r do
local item = r[j] local item = r[j]
local c = props.render_item(item) local c = props.render_item(item)
@ -134,11 +134,11 @@ M.Grid = Component.new(function(props)
rendered[i] = row(children) rendered[i] = row(children)
end end
for i=1,#rendered do for i = 1, #rendered do
-- current row -- current row
local r = rendered[i] local r = rendered[i]
for j=1,#r.children do for j = 1, #r.children do
local item = r.children[j] local item = r.children[j]
local gap_str = "" local gap_str = ""
local column_width = column_widths[j] local column_width = column_widths[j]
@ -163,5 +163,4 @@ M.Grid = Component.new(function(props)
return col(rendered) return col(rendered)
end) end)
return M return M

View file

@ -1,7 +1,7 @@
local Buffer = require("neogit.lib.buffer") local Buffer = require("neogit.lib.buffer")
local Git = require("neogit.lib.git") local Git = require("neogit.lib.git")
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local map = util.map local map = util.map
@ -16,7 +16,7 @@ function GitCommandHistory:new(state)
local this = { local this = {
buffer = nil, buffer = nil,
state = state or Git.cli.history, state = state or Git.cli.history,
open = false open = false,
} }
setmetatable(this, self) setmetatable(this, self)
@ -43,8 +43,8 @@ function GitCommandHistory:show()
c.children[2]:toggle_hidden() c.children[2]:toggle_hidden()
self.buffer.ui:update() self.buffer.ui:update()
end end
end end,
} },
}, },
render = function() render = function()
return map(self.state, function(item) return map(self.state, function(item)
@ -54,19 +54,25 @@ function GitCommandHistory:show()
highlight_code = "NeogitCommandCodeError" highlight_code = "NeogitCommandCodeError"
end end
return col { return col {
row { row {
text.highlight(highlight_code)(string.format("%3d", item.code)), text.highlight(highlight_code)(string.format("%3d", item.code)),
text " ", text(" "),
text(item.cmd), text(item.cmd),
text " ", text(" "),
text.highlight("NeogitCommandTime")(string.format("(%3.3f ms)", item.time)), text.highlight("NeogitCommandTime")(string.format("(%3.3f ms)", item.time)),
text " ", text(" "),
text.highlight("NeogitCommandTime")(string.format("[%s %d]", is_err and "stderr" or "stdout", is_err and #item.stderr or #item.stdout)), text.highlight("NeogitCommandTime")(
string.format(
"[%s %d]",
is_err and "stderr" or "stdout",
is_err and #item.stderr or #item.stdout
)
),
}, },
col col
.hidden(true) .hidden(true)
.padding_left(" | ") .padding_left(" | ")
.highlight("NeogitCommandText")(map(is_err and item.stderr or item.stdout, text)) .highlight("NeogitCommandText")(map(is_err and item.stderr or item.stdout, text)),
} }
end) end)
end, end,

View file

@ -1,6 +1,6 @@
local Buffer = require("neogit.lib.buffer") local Buffer = require("neogit.lib.buffer")
local CommitViewBuffer = require 'neogit.buffers.commit_view' local CommitViewBuffer = require("neogit.buffers.commit_view")
local ui = require 'neogit.buffers.log_view.ui' local ui = require("neogit.buffers.log_view.ui")
local M = {} local M = {}
@ -20,7 +20,7 @@ function M.new(data, show_graph)
is_open = false, is_open = false,
data = data, data = data,
show_graph = show_graph, show_graph = show_graph,
buffer = nil buffer = nil,
} }
setmetatable(instance, { __index = M }) setmetatable(instance, { __index = M })
@ -81,21 +81,21 @@ function M:open()
buffer.ui:update() buffer.ui:update()
buffer:move_cursor(target.position.row_start) buffer:move_cursor(target.position.row_start)
vim.fn.feedkeys "zz" vim.fn.feedkeys("zz")
end, end,
["<tab>"] = function() ["<tab>"] = function()
local stack = self.buffer.ui:get_component_stack_under_cursor() local stack = self.buffer.ui:get_component_stack_under_cursor()
local c = stack[#stack] local c = stack[#stack]
c.children[2]:toggle_hidden() c.children[2]:toggle_hidden()
self.buffer.ui:update() self.buffer.ui:update()
vim.fn.feedkeys "zz" vim.fn.feedkeys("zz")
end end,
} },
}, },
render = function() render = function()
return ui.LogView(self.data, self.show_graph) return ui.LogView(self.data, self.show_graph)
end end,
} }
end end

View file

@ -1,6 +1,6 @@
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local Component = require 'neogit.lib.ui.component' local Component = require("neogit.lib.ui.component")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local col = Ui.col local col = Ui.col
local row = Ui.row local row = Ui.row
@ -21,46 +21,44 @@ local M = {}
M.Commit = Component.new(function(commit, show_graph) M.Commit = Component.new(function(commit, show_graph)
return col { return col {
row { row {
text(show_graph text(show_graph and ("* "):rep(commit.level + 1) or "* ", { highlight = "Character" }),
and ("* "):rep(commit.level + 1) text(commit.oid:sub(1, 7), { highlight = "Number" }),
or "* ", { highlight = "Character" }), text(" "),
text(commit.oid:sub(1, 7), { highlight = "Number" }), text(commit.description[1]),
text " ",
text(commit.description[1])
}, },
col.hidden(true).padding_left((commit.level + 1) * 2) { col.hidden(true).padding_left((commit.level + 1) * 2) {
row { row {
text "Author: ", text("Author: "),
text(commit.author_name), text(commit.author_name),
text " <", text(" <"),
text(commit.author_date), text(commit.author_date),
text ">" text(">"),
}, },
row { row {
text "AuthorDate: ", text("AuthorDate: "),
text(commit.author_date) text(commit.author_date),
}, },
row { row {
text "Commit: ", text("Commit: "),
text(commit.committer_name), text(commit.committer_name),
text " <", text(" <"),
text(commit.committer_date), text(commit.committer_date),
text ">" text(">"),
}, },
row { row {
text "CommitDate: ", text("CommitDate: "),
text(commit.committer_date) text(commit.committer_date),
}, },
text " ", text(" "),
col(map(commit.description, text), { padding_left = 4 }) col(map(commit.description, text), { padding_left = 4 }),
} },
} }
end) end)
function M.LogView(data, show_graph) function M.LogView(data, show_graph)
return map(data, function(row) return map(data, function(row)
return M.Commit(row, show_graph) return M.Commit(row, show_graph)
end) end)
end end

View file

@ -1,5 +1,5 @@
local Buffer = require 'neogit.lib.buffer' local Buffer = require("neogit.lib.buffer")
local ui = require 'neogit.buffers.status.ui' local ui = require("neogit.buffers.status.ui")
local M = {} local M = {}
@ -14,10 +14,10 @@ function M.new(state)
local x = { local x = {
is_open = false, is_open = false,
state = state, state = state,
buffer = nil buffer = nil,
} }
setmetatable(x, { __index = M }) setmetatable(x, { __index = M })
return x return x
end end
function M:open(kind) function M:open(kind)
@ -34,7 +34,7 @@ function M:open(kind)
end, end,
render = function() render = function()
return ui.Status(self.state) return ui.Status(self.state)
end end,
} }
end end

View file

@ -1,9 +1,9 @@
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local Component = require 'neogit.lib.ui.component' local Component = require("neogit.lib.ui.component")
local Job = require 'neogit.lib.job' local Job = require("neogit.lib.job")
local difflib = require 'neogit.lib.git.diff' local difflib = require("neogit.lib.git.diff")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local common = require 'neogit.buffers.common' local common = require("neogit.buffers.common")
local col = Ui.col local col = Ui.col
local row = Ui.row local row = Ui.row
@ -22,16 +22,16 @@ local _mode_to_text = {
D = "Deleted", D = "Deleted",
C = "Copied", C = "Copied",
U = "Updated", U = "Updated",
R = "Renamed" R = "Renamed",
} }
local RemoteHeader = Component.new(function(props) local RemoteHeader = Component.new(function(props)
return row { return row {
text(props.name), text(props.name),
text ": ", text(": "),
text(props.branch), text(props.branch),
text " ", text(" "),
text(props.msg or '(no commits)'), text(props.msg or "(no commits)"),
} }
end) end)
@ -39,11 +39,11 @@ local Section = Component.new(function(props)
return col { return col {
row { row {
text(props.title), text(props.title),
text ' (', text(" ("),
text(#props.items), text(#props.items),
text ')', text(")"),
}, },
col(props.items) col(props.items),
} }
end) end)
@ -54,52 +54,53 @@ function M.Status(state)
items = { items = {
col { col {
RemoteHeader { RemoteHeader {
name = "Head", name = "Head",
branch = state.head.branch, branch = state.head.branch,
msg = state.head.commit_message msg = state.head.commit_message,
}, },
state.upstream.branch and RemoteHeader { state.upstream.branch and RemoteHeader {
name = "Upstream", name = "Upstream",
branch = state.upstream.branch, branch = state.upstream.branch,
msg = state.upstream.commit_message msg = state.upstream.commit_message,
}, },
}, },
-- #state.untracked_files > 0 and Section { -- #state.untracked_files > 0 and Section {
-- title = "Untracked files", -- title = "Untracked files",
-- items = map(state.untracked_files, Diff) -- items = map(state.untracked_files, Diff)
-- }, -- },
-- #state.unstaged_changes > 0 and Section { -- #state.unstaged_changes > 0 and Section {
-- title = "Unstaged changes", -- title = "Unstaged changes",
-- items = map(state.unstaged_changes, Diff) -- items = map(state.unstaged_changes, Diff)
-- }, -- },
-- #state.staged_changes > 0 and Section { -- #state.staged_changes > 0 and Section {
-- title = "Staged changes", -- title = "Staged changes",
-- items = map(state.staged_changes, Diff) -- items = map(state.staged_changes, Diff)
-- }, -- },
#state.stashes > 0 and Section { #state.stashes > 0
title = "Stashes", and Section {
items = map(state.stashes, function(s) title = "Stashes",
return row { items = map(state.stashes, function(s)
text.highlight("Comment")("stash@{", s.idx, "}: "), return row {
text(s.message) text.highlight("Comment")("stash@{", s.idx, "}: "),
} text(s.message),
end) }
}, end),
-- #state.unpulled_changes > 0 and Section { },
-- #state.unpulled_changes > 0 and Section {
-- title = "Unpulled changes", -- title = "Unpulled changes",
-- items = map(state.unpulled_changes, Diff) -- items = map(state.unpulled_changes, Diff)
-- }, -- },
-- #state.unmerged_changes > 0 and Section { -- #state.unmerged_changes > 0 and Section {
-- title = "Unmerged changes", -- title = "Unmerged changes",
-- items = map(state.unmerged_changes, Diff) -- items = map(state.unmerged_changes, Diff)
-- }, -- },
} },
} },
} }
end end
function _load_diffs(repo) function _load_diffs(repo)
local cli = require 'neogit.lib.git.cli' local cli = require("neogit.lib.git.cli")
local unstaged_jobs = map(repo.unstaged.items, function(f) local unstaged_jobs = map(repo.unstaged.items, function(f)
return cli.diff.shortstat.patch.files(f.name).to_job() return cli.diff.shortstat.patch.files(f.name).to_job()
@ -111,7 +112,7 @@ function _load_diffs(repo)
local jobs = {} local jobs = {}
for _, x in ipairs({ unstaged_jobs, staged_jobs }) do for _, x in ipairs { unstaged_jobs, staged_jobs } do
for _, j in ipairs(x) do for _, j in ipairs(x) do
table.insert(jobs, j) table.insert(jobs, j)
end end
@ -130,18 +131,24 @@ function _load_diffs(repo)
end end
function _TEST() function _TEST()
local repo = require('neogit').repo local repo = require("neogit").repo
require('neogit.buffers.status').new({ require("neogit.buffers.status")
head = repo.head, .new({
upstream = repo.upstream, head = repo.head,
untracked_files = repo.untracked.items, upstream = repo.upstream,
unstaged_changes = map(repo.unstaged.items, function(f) return f.diff end), untracked_files = repo.untracked.items,
staged_changes = map(repo.staged.items, function(f) return f.diff end), unstaged_changes = map(repo.unstaged.items, function(f)
stashes = repo.stashes.items, return f.diff
unpulled_changes = repo.unpulled.items, end),
unmerged_changes = repo.unmerged.items, staged_changes = map(repo.staged.items, function(f)
recent_changes = repo.recent.items, return f.diff
}):open() end),
stashes = repo.stashes.items,
unpulled_changes = repo.unpulled.items,
unmerged_changes = repo.unmerged.items,
recent_changes = repo.recent.items,
})
:open()
end end
return M return M

View file

@ -19,32 +19,32 @@ M.values = {
signs = { signs = {
hunk = { "", "" }, hunk = { "", "" },
item = { ">", "v" }, item = { ">", "v" },
section = { ">", "v" } section = { ">", "v" },
}, },
integrations = { integrations = {
diffview = false diffview = false,
}, },
sections = { sections = {
untracked = { untracked = {
folded = false folded = false,
}, },
unstaged = { unstaged = {
folded = false folded = false,
}, },
staged = { staged = {
folded = false folded = false,
}, },
stashes = { stashes = {
folded = true folded = true,
}, },
unpulled = { unpulled = {
folded = true folded = true,
}, },
unmerged = { unmerged = {
folded = false folded = false,
}, },
recent = { recent = {
folded = true folded = true,
}, },
}, },
mappings = { mappings = {
@ -77,8 +77,8 @@ M.values = {
["L"] = "LogPopup", ["L"] = "LogPopup",
["Z"] = "StashPopup", ["Z"] = "StashPopup",
["b"] = "BranchPopup", ["b"] = "BranchPopup",
} },
} },
} }
function M.ensure_integration(name) function M.ensure_integration(name)

View file

@ -1,32 +1,31 @@
local M = {} local M = {}
local dv = require 'diffview' local dv = require("diffview")
local dv_config = require 'diffview.config' local dv_config = require("diffview.config")
local Rev = require'diffview.git.rev'.Rev local Rev = require("diffview.git.rev").Rev
local RevType = require'diffview.git.rev'.RevType local RevType = require("diffview.git.rev").RevType
local CDiffView = require'diffview.api.views.diff.diff_view'.CDiffView local CDiffView = require("diffview.api.views.diff.diff_view").CDiffView
local dv_lib = require'diffview.lib' local dv_lib = require("diffview.lib")
local dv_utils = require("diffview.utils") local dv_utils = require("diffview.utils")
local neogit = require 'neogit' local neogit = require("neogit")
local status = require'neogit.status' local status = require("neogit.status")
local a = require 'plenary.async' local a = require("plenary.async")
local old_config local old_config
M.diffview_mappings = { M.diffview_mappings = {
close = function() close = function()
vim.cmd [[tabclose]] vim.cmd([[tabclose]])
neogit.dispatch_refresh() neogit.dispatch_refresh()
dv.setup(old_config) dv.setup(old_config)
end end,
} }
local function cb(name) local function cb(name)
return string.format(":lua require('neogit.integrations.diffview').diffview_mappings['%s']()<CR>", name) return string.format(":lua require('neogit.integrations.diffview').diffview_mappings['%s']()<CR>", name)
end end
local function get_local_diff_view(selected_file_name) local function get_local_diff_view(selected_file_name)
local left = Rev(RevType.INDEX) local left = Rev(RevType.INDEX)
local right = Rev(RevType.LOCAL) local right = Rev(RevType.LOCAL)
@ -37,7 +36,7 @@ local function get_local_diff_view(selected_file_name)
local repo = neogit.get_repo() local repo = neogit.get_repo()
local sections = { local sections = {
working = repo.unstaged, working = repo.unstaged,
staged = repo.staged staged = repo.staged,
} }
for kind, section in pairs(sections) do for kind, section in pairs(sections) do
files[kind] = {} files[kind] = {}
@ -47,11 +46,11 @@ local function get_local_diff_view(selected_file_name)
status = item.mode, status = item.mode,
stats = (item.diff and item.diff.stats) and { stats = (item.diff and item.diff.stats) and {
additions = item.diff.stats.additions or 0, additions = item.diff.stats.additions or 0,
deletions = item.diff.stats.deletions or 0 deletions = item.diff.stats.deletions or 0,
} or nil, } or nil,
left_null = vim.tbl_contains({ "A", "?" }, item.mode), left_null = vim.tbl_contains({ "A", "?" }, item.mode),
right_null = false, right_null = false,
selected = item.name == selected_file_name selected = item.name == selected_file_name,
} }
table.insert(files[kind], file) table.insert(files[kind], file)
@ -63,7 +62,7 @@ local function get_local_diff_view(selected_file_name)
local files = update_files() local files = update_files()
local view = CDiffView({ local view = CDiffView {
git_root = git_root, git_root = git_root,
left = left, left = left,
right = right, right = right,
@ -77,15 +76,13 @@ local function get_local_diff_view(selected_file_name)
end end
return neogit.cli.show.file(unpack(args)).call_sync() return neogit.cli.show.file(unpack(args)).call_sync()
elseif kind == "working" then elseif kind == "working" then
return side == "left" return side == "left" and neogit.cli.show.file(path).call_sync() or nil
and neogit.cli.show.file(path).call_sync()
or nil
end end
end end,
}) }
view:on_files_staged(a.void(function (_) view:on_files_staged(a.void(function(_)
status.refresh({ status = true, diffs = true }) status.refresh { status = true, diffs = true }
view:update_files() view:update_files()
end)) end))
@ -101,13 +98,13 @@ function M.open(section_name, item_name)
key_bindings = { key_bindings = {
view = { view = {
["q"] = cb("close"), ["q"] = cb("close"),
["<esc>"] = cb("close") ["<esc>"] = cb("close"),
}, },
file_panel = { file_panel = {
["q"] = cb("close"), ["q"] = cb("close"),
["<esc>"] = cb("close") ["<esc>"] = cb("close"),
} },
} },
}) })
dv.setup(config) dv.setup(config)
@ -116,10 +113,10 @@ function M.open(section_name, item_name)
if section_name == "recent" or section_name == "unmerged" then if section_name == "recent" or section_name == "unmerged" then
local commit_id = item_name:match("[a-f0-9]+") local commit_id = item_name:match("[a-f0-9]+")
view = dv_lib.diffview_open(dv_utils.tbl_pack(commit_id.."^.."..commit_id)) view = dv_lib.diffview_open(dv_utils.tbl_pack(commit_id .. "^.." .. commit_id))
elseif section_name == "stashes" then elseif section_name == "stashes" then
local stash_id = item_name:match("stash@{%d+}") local stash_id = item_name:match("stash@{%d+}")
view = dv_lib.diffview_open(dv_utils.tbl_pack(stash_id.."^.."..stash_id)) view = dv_lib.diffview_open(dv_utils.tbl_pack(stash_id .. "^.." .. stash_id))
else else
view = get_local_diff_view(item_name) view = get_local_diff_view(item_name)
end end

View file

@ -1,4 +1,4 @@
package.loaded['neogit.buffer'] = nil package.loaded["neogit.buffer"] = nil
__BUFFER_AUTOCMD_STORE = {} __BUFFER_AUTOCMD_STORE = {}
@ -15,7 +15,7 @@ function Buffer:new(handle)
handle = handle, handle = handle,
border = nil, border = nil,
mmanager = mappings_manager.new(), mmanager = mappings_manager.new(),
kind = nil -- how the buffer was opened. For more information look at the create function kind = nil, -- how the buffer was opened. For more information look at the create function
} }
this.ui = Ui.new(this) this.ui = Ui.new(this)
@ -134,21 +134,21 @@ function Buffer:open_fold(line, reset_pos)
pos = vim.fn.getpos() pos = vim.fn.getpos()
end end
vim.fn.setpos('.', {self.handle, line, 0, 0}) vim.fn.setpos(".", { self.handle, line, 0, 0 })
vim.cmd('normal zo') vim.cmd("normal zo")
if reset_pos == true then if reset_pos == true then
vim.fn.setpos('.', pos) vim.fn.setpos(".", pos)
end end
end end
function Buffer:add_highlight(line, col_start, col_end, name, ns_id) function Buffer:add_highlight(line, col_start, col_end, name, ns_id)
local ns_id = ns_id or 0 local ns_id = ns_id or 0
vim.api.nvim_buf_add_highlight(self.handle, ns_id, name, line, col_start, col_end) vim.api.nvim_buf_add_highlight(self.handle, ns_id, name, line, col_start, col_end)
end end
function Buffer:unplace_sign(id) function Buffer:unplace_sign(id)
vim.cmd('sign unplace ' .. id) vim.cmd("sign unplace " .. id)
end end
function Buffer:place_sign(line, name, group, id) function Buffer:place_sign(line, name, group, id)
-- Sign IDs should be unique within a group, however there's no downside as -- Sign IDs should be unique within a group, however there's no downside as
@ -158,11 +158,11 @@ function Buffer:place_sign(line, name, group, id)
-- There's an equivalent function sign_place() which can automatically use -- There's an equivalent function sign_place() which can automatically use
-- a free ID, but is considerable slower, so we use the command for now -- a free ID, but is considerable slower, so we use the command for now
local cmd = 'sign place '..sign_id..' line='..line..' name='..name local cmd = "sign place " .. sign_id .. " line=" .. line .. " name=" .. name
if group ~= nil then if group ~= nil then
cmd = cmd..' group='..group cmd = cmd .. " group=" .. group
end end
cmd = cmd..' buffer='..self.handle cmd = cmd .. " buffer=" .. self.handle
vim.cmd(cmd) vim.cmd(cmd)
return sign_id return sign_id
@ -172,12 +172,12 @@ function Buffer:get_sign_at_line(line, group)
group = group or "*" group = group or "*"
return vim.fn.sign_getplaced(self.handle, { return vim.fn.sign_getplaced(self.handle, {
group = group, group = group,
lnum = line lnum = line,
})[1] })[1]
end end
function Buffer:clear_sign_group(group) function Buffer:clear_sign_group(group)
vim.cmd('sign unplace * group='..group..' buffer='..self.handle) vim.cmd("sign unplace * group=" .. group .. " buffer=" .. self.handle)
end end
function Buffer:set_filetype(ft) function Buffer:set_filetype(ft)
@ -226,8 +226,8 @@ function Buffer.create(config)
buffer = Buffer:new(vim.api.nvim_get_current_buf()) buffer = Buffer:new(vim.api.nvim_get_current_buf())
elseif kind == "floating" then elseif kind == "floating" then
-- Creates the border window -- Creates the border window
local vim_height = vim.api.nvim_eval [[&lines]] local vim_height = vim.api.nvim_eval([[&lines]])
local vim_width = vim.api.nvim_eval [[&columns]] local vim_width = vim.api.nvim_eval([[&columns]])
local width = math.floor(vim_width * 0.8) + 3 local width = math.floor(vim_width * 0.8) + 3
local height = math.floor(vim_height * 0.7) local height = math.floor(vim_height * 0.7)
local col = vim_width * 0.1 - 1 local col = vim_width * 0.1 - 1
@ -235,14 +235,14 @@ function Buffer.create(config)
local content_buffer = vim.api.nvim_create_buf(true, true) local content_buffer = vim.api.nvim_create_buf(true, true)
local content_window = vim.api.nvim_open_win(content_buffer, true, { local content_window = vim.api.nvim_open_win(content_buffer, true, {
relative = 'editor', relative = "editor",
width = width, width = width,
height = height, height = height,
col = col, col = col,
row = row, row = row,
style = 'minimal', style = "minimal",
focusable = false, focusable = false,
border = 'single', border = "single",
}) })
vim.api.nvim_win_set_cursor(content_window, { 1, 0 }) vim.api.nvim_win_set_cursor(content_window, { 1, 0 })
@ -271,12 +271,12 @@ function Buffer.create(config)
if config.mappings then if config.mappings then
for mode, val in pairs(config.mappings) do for mode, val in pairs(config.mappings) do
for key, cb in pairs(val) do for key, cb in pairs(val) do
buffer.mmanager.mappings[key] = { buffer.mmanager.mappings[key] = {
mode, mode,
function() function()
cb(buffer) cb(buffer)
end, end,
mode:find("v") ~= nil mode:find("v") ~= nil,
} }
end end
end end

View file

@ -37,8 +37,8 @@ function M.filter(tbl, func)
local result = {} local result = {}
for _, item in ipairs(tbl) do for _, item in ipairs(tbl) do
if func(item) then if func(item) then
table.insert(result, item) table.insert(result, item)
end end
end end
@ -52,22 +52,24 @@ function M.each(tbl, func)
end end
function M.reduce(tbl, func, ...) function M.reduce(tbl, func, ...)
local acc = {...} local acc = { ... }
tbl:each(function (item) tbl:each(function(item)
acc = {func(item, unpack(acc))} acc = { func(item, unpack(acc)) }
end) end)
return unpack(acc) return unpack(acc)
end end
function M.find(tbl, func) function M.find(tbl, func)
for _, item in ipairs(tbl) do for _, item in ipairs(tbl) do
if func(item) then return item end if func(item) then
return item
end
end end
return nil return nil
end end
return setmetatable(M, { return setmetatable(M, {
__call = function (_, tbl) __call = function(_, tbl)
return M.new(tbl) return M.new(tbl)
end end,
}) })

View file

@ -1,15 +1,10 @@
local cli = require 'neogit.lib.git.cli' local cli = require("neogit.lib.git.cli")
local M = {} local M = {}
function M.relpath_from_repository(path) function M.relpath_from_repository(path)
local result = cli['ls-files'] local result = cli["ls-files"].others.cached.modified.deleted.full_name
.others .cwd("<current>")
.cached
.modified
.deleted
.full_name
.cwd('<current>')
.args(path) .args(path)
.show_popup(false) .show_popup(false)
.call() .call()

View file

@ -1,13 +1,13 @@
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local collect = require 'neogit.lib.collection' local collect = require("neogit.lib.collection")
local M = {} local M = {}
function M.dot(chain) function M.dot(chain)
local parts = collect(util.split(chain, '%.')) local parts = collect(util.split(chain, "%."))
return function (tbl) return function(tbl)
parts:each(function (p) parts:each(function(p)
if tbl then if tbl then
tbl = tbl[p] tbl = tbl[p]
end end
end) end)
return tbl return tbl
@ -15,9 +15,9 @@ function M.dot(chain)
end end
function M.compose(...) function M.compose(...)
local funcs = collect({...}) local funcs = collect { ... }
return function (...) return function(...)
return funcs:reduce(function (cur, ...) return funcs:reduce(function(cur, ...)
return cur(...) return cur(...)
end, ...) end, ...)
end end
@ -25,10 +25,9 @@ end
M.C = M.compose M.C = M.compose
function M.eq(a) function M.eq(a)
return function (b) return function(b)
return a == b return a == b
end end
end end
return M return M

View file

@ -1,12 +1,12 @@
local a = require 'plenary.async' local a = require("plenary.async")
local cli = require('neogit.lib.git.cli') local cli = require("neogit.lib.git.cli")
local input = require('neogit.lib.input') local input = require("neogit.lib.input")
local M = {} local M = {}
local function parse_branches(branches) local function parse_branches(branches)
local other_branches = {} local other_branches = {}
for _, b in ipairs(branches) do for _, b in ipairs(branches) do
local branch_name = b:match('^ (.+)') local branch_name = b:match("^ (.+)")
if branch_name then if branch_name then
table.insert(other_branches, branch_name) table.insert(other_branches, branch_name)
end end
@ -16,26 +16,19 @@ local function parse_branches(branches)
end end
function M.get_local_branches() function M.get_local_branches()
local branches = cli.branch local branches = cli.branch.list.call_sync()
.list
.call_sync()
return parse_branches(branches) return parse_branches(branches)
end end
function M.get_remote_branches() function M.get_remote_branches()
local branches = cli.branch local branches = cli.branch.remotes.call_sync()
.remotes
.call_sync()
return parse_branches(branches) return parse_branches(branches)
end end
function M.get_all_branches() function M.get_all_branches()
local branches = cli.branch local branches = cli.branch.list.all.call_sync()
.list
.all
.call_sync()
return parse_branches(branches) return parse_branches(branches)
end end
@ -45,10 +38,7 @@ function M.get_upstream()
local current = cli.branch.current.show_popup(false).call() local current = cli.branch.current.show_popup(false).call()
if #full_name > 0 and #current > 0 then if #full_name > 0 and #current > 0 then
local remote = cli.config local remote = cli.config.show_popup(false).get(string.format("branch.%s.remote", current[1])).call()
.show_popup(false)
.get(string.format("branch.%s.remote", current[1]))
.call()
if #remote > 0 then if #remote > 0 then
return { return {
remote = remote[1], remote = remote[1],
@ -60,11 +50,13 @@ end
function M.prompt_for_branch(options) function M.prompt_for_branch(options)
a.util.scheduler() a.util.scheduler()
local chosen = input.get_user_input_with_completion('branch > ', options) local chosen = input.get_user_input_with_completion("branch > ", options)
if not chosen or chosen == '' then return nil end if not chosen or chosen == "" then
return nil
end
local truncate_remote_name = chosen:match('.+/.+/(.+)') local truncate_remote_name = chosen:match(".+/.+/(.+)")
if truncate_remote_name and truncate_remote_name ~= '' then if truncate_remote_name and truncate_remote_name ~= "" then
return truncate_remote_name return truncate_remote_name
end end
@ -76,7 +68,9 @@ function M.checkout_local()
a.util.scheduler() a.util.scheduler()
local chosen = M.prompt_for_branch(branches) local chosen = M.prompt_for_branch(branches)
if not chosen then return end if not chosen then
return
end
cli.checkout.branch(chosen).call() cli.checkout.branch(chosen).call()
end end
@ -85,14 +79,18 @@ function M.checkout()
a.util.scheduler() a.util.scheduler()
local chosen = M.prompt_for_branch(branches) local chosen = M.prompt_for_branch(branches)
if not chosen then return end if not chosen then
return
end
cli.checkout.branch(chosen).call() cli.checkout.branch(chosen).call()
end end
function M.create() function M.create()
a.util.scheduler() a.util.scheduler()
local name = input.get_user_input('branch > ') local name = input.get_user_input("branch > ")
if not name or name == '' then return end if not name or name == "" then
return
end
cli.interactive_git_cmd(tostring(cli.branch.name(name))) cli.interactive_git_cmd(tostring(cli.branch.name(name)))
@ -104,7 +102,9 @@ function M.delete()
a.util.scheduler() a.util.scheduler()
local chosen = M.prompt_for_branch(branches) local chosen = M.prompt_for_branch(branches)
if not chosen then return end if not chosen then
return
end
cli.interactive_git_cmd(tostring(cli.branch.delete.name(chosen))) cli.interactive_git_cmd(tostring(cli.branch.delete.name(chosen)))
@ -113,8 +113,10 @@ end
function M.checkout_new() function M.checkout_new()
a.util.scheduler() a.util.scheduler()
local name = input.get_user_input('branch > ') local name = input.get_user_input("branch > ")
if not name or name == '' then return end if not name or name == "" then
return
end
cli.interactive_git_cmd(tostring(cli.checkout.new_branch(name))) cli.interactive_git_cmd(tostring(cli.checkout.new_branch(name)))
end end

View file

@ -1,10 +1,10 @@
local notif = require("neogit.lib.notification") local notif = require("neogit.lib.notification")
local logger = require 'neogit.logger' local logger = require("neogit.logger")
local a = require 'plenary.async' local a = require("plenary.async")
local process = require('neogit.process') local process = require("neogit.process")
local Job = require 'neogit.lib.job' local Job = require("neogit.lib.job")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local split = require('neogit.lib.util').split local split = require("neogit.lib.util").split
local function config(setup) local function config(setup)
setup = setup or {} setup = setup or {}
@ -16,55 +16,55 @@ local function config(setup)
end end
local configurations = { local configurations = {
show = config({ show = config {
flags = { flags = {
stat = "--stat", stat = "--stat",
oneline = "--oneline" oneline = "--oneline",
}, },
options = { options = {
format = "--format" format = "--format",
}, },
aliases = { aliases = {
file = function(tbl) file = function(tbl)
return function(name, rev) return function(name, rev)
return tbl.args((rev or "") .. ":" .. name) return tbl.args((rev or "") .. ":" .. name)
end end
end end,
} },
}), },
status = config({ status = config {
flags = { flags = {
short = "-s", short = "-s",
branch = "-b", branch = "-b",
verbose = "-v", verbose = "-v",
null_terminated = "-z" null_terminated = "-z",
}, },
options = { options = {
porcelain = "--porcelain", porcelain = "--porcelain",
}, },
}), },
log = config({ log = config {
flags = { flags = {
oneline = "--oneline", oneline = "--oneline",
branches = "--branches", branches = "--branches",
remotes = "--remotes", remotes = "--remotes",
all = "--all", all = "--all",
graph = "--graph" graph = "--graph",
}, },
options = { options = {
pretty = "--pretty", pretty = "--pretty",
max_count = "--max-count", max_count = "--max-count",
format = "--format" format = "--format",
}, },
aliases = { aliases = {
for_range = function (tbl) for_range = function(tbl)
return function (range) return function(range)
return tbl.args(range) return tbl.args(range)
end end
end end,
} },
}), },
config = config({ config = config {
flags = { flags = {
_get = "--get", _get = "--get",
}, },
@ -73,212 +73,212 @@ local configurations = {
return function(path) return function(path)
return tbl._get.args(path) return tbl._get.args(path)
end end
end end,
}
}),
diff = config({
flags = {
null_terminated = '-z',
cached = '--cached',
shortstat = '--shortstat',
patch = '--patch',
name_only = '--name-only',
no_ext_diff = "--no-ext-diff"
}, },
}), },
stash = config({ diff = config {
flags = { flags = {
apply = 'apply', null_terminated = "-z",
drop = 'drop', cached = "--cached",
index = '--index' shortstat = "--shortstat",
} patch = "--patch",
}), name_only = "--name-only",
rebase = config({}), no_ext_diff = "--no-ext-diff",
reset = config({ },
},
stash = config {
flags = { flags = {
hard = '--hard', apply = "apply",
drop = "drop",
index = "--index",
},
},
rebase = config {},
reset = config {
flags = {
hard = "--hard",
}, },
aliases = { aliases = {
commit = function (tbl) commit = function(tbl)
return function (cm) return function(cm)
return tbl.args(cm) return tbl.args(cm)
end end
end end,
} },
}), },
checkout = config({ checkout = config {
short_opts = { short_opts = {
b = '-b', b = "-b",
}, },
aliases = { aliases = {
branch = function (tbl) branch = function(tbl)
return function (branch) return function(branch)
return tbl.args(branch) return tbl.args(branch)
end end
end, end,
new_branch = function (tbl) new_branch = function(tbl)
return function (branch) return function(branch)
return tbl.b(branch) return tbl.b(branch)
end end
end, end,
new_branch_with_start_point = function (tbl) new_branch_with_start_point = function(tbl)
return function (branch, start_point) return function(branch, start_point)
return tbl.args(branch, start_point).b() return tbl.args(branch, start_point).b()
end end
end end,
} },
}), },
remote = config({ remote = config {
flags = { flags = {
push = '--push' push = "--push",
}, },
aliases = { aliases = {
get_url = function (tbl) get_url = function(tbl)
return function(remote) return function(remote)
tbl.prefix("get-url") tbl.prefix("get-url")
return tbl.args(remote) return tbl.args(remote)
end end
end end,
} },
}), },
apply = config({ apply = config {
flags = { flags = {
cached = '--cached', cached = "--cached",
reverse = '--reverse', reverse = "--reverse",
index = '--index' index = "--index",
}, },
aliases = { aliases = {
with_patch = function (tbl) with_patch = function(tbl)
return tbl.input return tbl.input
end end,
}
}),
add = config({
flags = {
update = '-u',
all = '-A'
}, },
}), },
commit = config({ add = config {
flags = { flags = {
amend = '--amend', update = "-u",
only = '--only', all = "-A",
dry_run = '--dry-run', },
no_edit = '--no-edit' },
commit = config {
flags = {
amend = "--amend",
only = "--only",
dry_run = "--dry-run",
no_edit = "--no-edit",
}, },
options = { options = {
commit_message_file = '--file' commit_message_file = "--file",
} },
}), },
push = config({ push = config {
flags = { flags = {
delete = '--delete', delete = "--delete",
}, },
aliases = { aliases = {
remote = function (tbl) remote = function(tbl)
return function (remote) return function(remote)
return tbl.prefix(remote) return tbl.prefix(remote)
end end
end, end,
to = function (tbl) to = function(tbl)
return function (to) return function(to)
return tbl.args(to) return tbl.args(to)
end end
end end,
}
}),
pull = config({
flags = {
no_commit = '--no-commit'
}, },
}), },
branch = config({ pull = config {
flags = { flags = {
list = '--list', no_commit = "--no-commit",
all = '-a', },
delete = '-d', },
remotes = '-r', branch = config {
current = '--show-current', flags = {
very_verbose = '-vv', list = "--list",
move = '-m', all = "-a",
delete = "-d",
remotes = "-r",
current = "--show-current",
very_verbose = "-vv",
move = "-m",
}, },
aliases = { aliases = {
name = function (tbl) name = function(tbl)
return function (name) return function(name)
return tbl.args(name) return tbl.args(name)
end end
end end,
} },
}), },
['read-tree'] = config({ ["read-tree"] = config {
flags = { flags = {
merge = '-m' merge = "-m",
}, },
options = { options = {
index_output = '--index-output' index_output = "--index-output",
}, },
aliases = { aliases = {
tree = function (tbl) tree = function(tbl)
return function (tree) return function(tree)
return tbl.args(tree) return tbl.args(tree)
end end
end end,
} },
}), },
['write-tree'] = config({}), ["write-tree"] = config {},
['commit-tree'] = config({ ["commit-tree"] = config {
flags = { flags = {
no_gpg_sign = "--no-gpg-sign" no_gpg_sign = "--no-gpg-sign",
}, },
short_opts = { short_opts = {
parent = "-p", parent = "-p",
message = "-m" message = "-m",
}, },
aliases = { aliases = {
parents = function (tbl) parents = function(tbl)
return function (...) return function(...)
for _, p in ipairs({...}) do for _, p in ipairs { ... } do
tbl.parent(p) tbl.parent(p)
end end
return tbl return tbl
end end
end, end,
tree = function (tbl) tree = function(tbl)
return function (tree) return function(tree)
return tbl.args(tree) return tbl.args(tree)
end end
end end,
} },
}), },
['update-index'] = config({ ["update-index"] = config {
flags = { flags = {
add = '--add', add = "--add",
remove = '--remove' remove = "--remove",
} },
}), },
['show-ref'] = config({ ["show-ref"] = config {
flags = { flags = {
verify = '--verify', verify = "--verify",
} },
}), },
['update-ref'] = config({ ["update-ref"] = config {
flags = { flags = {
create_reflog = '--create-reflog' create_reflog = "--create-reflog",
}, },
short_opts = { short_opts = {
message = '-m' message = "-m",
}
}),
['ls-files'] = config({
flags = {
others = '--others',
deleted = '--deleted',
modified = '--modified',
cached = '--cached',
full_name = '--full-name'
}, },
}), },
['rev-parse'] = config({ ["ls-files"] = config {
flags = {
others = "--others",
deleted = "--deleted",
modified = "--modified",
cached = "--cached",
full_name = "--full-name",
},
},
["rev-parse"] = config {
flags = { flags = {
revs_only = "--revs-only", revs_only = "--revs-only",
no_revs = "--no-revs", no_revs = "--no-revs",
@ -290,11 +290,11 @@ local configurations = {
options = { options = {
abbrev_ref = "--abbrev-ref", abbrev_ref = "--abbrev-ref",
}, },
}), },
} }
local function git_root() local function git_root()
return util.trim(process.spawn({cmd = 'git', args = {'rev-parse', '--show-toplevel'}})) return util.trim(process.spawn { cmd = "git", args = { "rev-parse", "--show-toplevel" } })
end end
local git_root_sync = function() local git_root_sync = function()
@ -318,7 +318,7 @@ local function handle_new_cmd(job, popup, hidden_text)
stdout = job.stdout, stdout = job.stdout,
stderr = job.stderr, stderr = job.stderr,
code = job.code, code = job.code,
time = job.time time = job.time,
}) })
do do
@ -334,8 +334,11 @@ local function handle_new_cmd(job, popup, hidden_text)
end end
if popup and job.code ~= 0 then if popup and job.code ~= 0 then
vim.schedule(function () vim.schedule(function()
notif.create("Git Error (" .. job.code .. "), press $ to see the git command history", vim.log.levels.ERROR) notif.create(
"Git Error (" .. job.code .. "), press $ to see the git command history",
vim.log.levels.ERROR
)
end) end)
end end
end end
@ -350,29 +353,29 @@ local function exec(cmd, args, cwd, stdin, env, show_popup, hide_text)
if not cwd then if not cwd then
cwd = git_root() cwd = git_root()
elseif cwd == '<current>' then elseif cwd == "<current>" then
cwd = nil cwd = nil
end end
local time = os.clock() local time = os.clock()
local opts = { local opts = {
cmd = 'git', cmd = "git",
args = args, args = args,
env = env, env = env,
input = stdin, input = stdin,
cwd = cwd cwd = cwd,
} }
local result, code, errors = process.spawn(opts) local result, code, errors = process.spawn(opts)
local stdout = split(result, '\n') local stdout = split(result, "\n")
local stderr = split(errors, '\n') local stderr = split(errors, "\n")
handle_new_cmd({ handle_new_cmd({
cmd = 'git ' .. table.concat(args, ' '), cmd = "git " .. table.concat(args, " "),
stdout = stdout, stdout = stdout,
stderr = stderr, stderr = stderr,
code = code, code = code,
time = os.clock() - time time = os.clock() - time,
}, show_popup, hide_text) }, show_popup, hide_text)
--print('git', table.concat(args, ' '), '->', code, errors) --print('git', table.concat(args, ' '), '->', code, errors)
@ -381,19 +384,19 @@ end
local function new_job(cmd, args, cwd, _stdin, _env, show_popup, hide_text) local function new_job(cmd, args, cwd, _stdin, _env, show_popup, hide_text)
args = args or {} args = args or {}
if show_popup == nil then if show_popup == nil then
show_popup = true show_popup = true
end end
table.insert(args, 1, cmd) table.insert(args, 1, cmd)
if not cwd then if not cwd then
cwd = git_root_sync() cwd = git_root_sync()
elseif cwd == '<current>' then elseif cwd == "<current>" then
cwd = nil cwd = nil
end end
local cmd = "git " .. table.concat(args, ' ') local cmd = "git " .. table.concat(args, " ")
local job = Job.new({ cmd = cmd }) local job = Job.new { cmd = cmd }
job.cwd = cwd job.cwd = cwd
handle_new_cmd(job, show_popup, hide_text) handle_new_cmd(job, show_popup, hide_text)
@ -415,48 +418,48 @@ local k_config = {}
local k_command = {} local k_command = {}
local mt_builder = { local mt_builder = {
__index = function (tbl, action) __index = function(tbl, action)
if action == 'args' or action == 'arguments' then if action == "args" or action == "arguments" then
return function (...) return function(...)
for _, v in ipairs({...}) do for _, v in ipairs { ... } do
table.insert(tbl[k_state].arguments, v) table.insert(tbl[k_state].arguments, v)
end end
return tbl return tbl
end end
end end
if action == 'files' or action == 'paths' then if action == "files" or action == "paths" then
return function (...) return function(...)
for _, v in ipairs({...}) do for _, v in ipairs { ... } do
table.insert(tbl[k_state].files, v) table.insert(tbl[k_state].files, v)
end end
return tbl return tbl
end end
end end
if action == 'input' or action == 'stdin' then if action == "input" or action == "stdin" then
return function (value) return function(value)
tbl[k_state].input = value tbl[k_state].input = value
return tbl return tbl
end end
end end
if action == 'cwd' then if action == "cwd" then
return function (cwd) return function(cwd)
tbl[k_state].cwd = cwd tbl[k_state].cwd = cwd
return tbl return tbl
end end
end end
if action == 'prefix' then if action == "prefix" then
return function (x) return function(x)
tbl[k_state].prefix = x tbl[k_state].prefix = x
return tbl return tbl
end end
end end
if action == 'env' then if action == "env" then
return function (cfg) return function(cfg)
for k, v in pairs(cfg) do for k, v in pairs(cfg) do
tbl[k_state].env[k] = v tbl[k_state].env[k] = v
end end
@ -464,15 +467,15 @@ local mt_builder = {
end end
end end
if action == 'show_popup' then if action == "show_popup" then
return function (show_popup) return function(show_popup)
tbl[k_state].show_popup = show_popup tbl[k_state].show_popup = show_popup
return tbl return tbl
end end
end end
if action == 'hide_text' then if action == "hide_text" then
return function (hide_text) return function(hide_text)
tbl[k_state].hide_text = hide_text tbl[k_state].hide_text = hide_text
return tbl return tbl
end end
@ -484,7 +487,7 @@ local mt_builder = {
end end
if tbl[k_config].options[action] then if tbl[k_config].options[action] then
return function (value) return function(value)
if value then if value then
table.insert(tbl[k_state].options, string.format("%s=%s", tbl[k_config].options[action], value)) table.insert(tbl[k_state].options, string.format("%s=%s", tbl[k_config].options[action], value))
else else
@ -495,7 +498,7 @@ local mt_builder = {
end end
if tbl[k_config].short_opts[action] then if tbl[k_config].short_opts[action] then
return function (value) return function(value)
table.insert(tbl[k_state].options, tbl[k_config].short_opts[action]) table.insert(tbl[k_state].options, tbl[k_config].short_opts[action])
table.insert(tbl[k_state].options, value) table.insert(tbl[k_state].options, value)
return tbl return tbl
@ -508,22 +511,24 @@ local mt_builder = {
error("unknown field: " .. action) error("unknown field: " .. action)
end, end,
__tostring = function (tbl) __tostring = function(tbl)
return string.format('git %s %s %s -- %s', return string.format(
"git %s %s %s -- %s",
tbl[k_command], tbl[k_command],
table.concat(tbl[k_state].options, ' '), table.concat(tbl[k_state].options, " "),
table.concat(tbl[k_state].arguments, ' '), table.concat(tbl[k_state].arguments, " "),
table.concat(tbl[k_state].files, ' ')) table.concat(tbl[k_state].files, " ")
)
end, end,
__call = function (tbl, ...) __call = function(tbl, ...)
return tbl.call(...) return tbl.call(...)
end end,
} }
local function new_builder(subcommand) local function new_builder(subcommand)
local configuration = configurations[subcommand] local configuration = configurations[subcommand]
if not configuration then if not configuration then
error("Command not found") error("Command not found")
end end
local state = { local state = {
@ -533,72 +538,72 @@ local function new_builder(subcommand)
input = nil, input = nil,
show_popup = true, show_popup = true,
cwd = nil, cwd = nil,
env = {} env = {},
} }
return setmetatable({ return setmetatable({
[k_state] = state, [k_state] = state,
[k_config] = configuration, [k_config] = configuration,
[k_command] = subcommand, [k_command] = subcommand,
call = function () call = function()
local args = {} local args = {}
for _,o in ipairs(state.options) do for _, o in ipairs(state.options) do
table.insert(args, o) table.insert(args, o)
end end
for _,a in ipairs(state.arguments) do for _, a in ipairs(state.arguments) do
table.insert(args, a) table.insert(args, a)
end end
if #state.files > 0 then if #state.files > 0 then
table.insert(args, '--') table.insert(args, "--")
end end
for _,f in ipairs(state.files) do for _, f in ipairs(state.files) do
table.insert(args, f) table.insert(args, f)
end end
if state.prefix then if state.prefix then
table.insert(args, 1, state.prefix) table.insert(args, 1, state.prefix)
end end
logger.debug(string.format("[CLI]: Executing '%s %s'", subcommand, table.concat(args, ' '))) logger.debug(string.format("[CLI]: Executing '%s %s'", subcommand, table.concat(args, " ")))
return exec(subcommand, args, state.cwd, state.input, state.env, state.show_popup, state.hide_text) return exec(subcommand, args, state.cwd, state.input, state.env, state.show_popup, state.hide_text)
end, end,
call_sync = function() call_sync = function()
local args = {} local args = {}
for _,o in ipairs(state.options) do for _, o in ipairs(state.options) do
table.insert(args, o) table.insert(args, o)
end end
for _,a in ipairs(state.arguments) do for _, a in ipairs(state.arguments) do
table.insert(args, a) table.insert(args, a)
end end
if #state.files > 0 then if #state.files > 0 then
table.insert(args, '--') table.insert(args, "--")
end end
for _,f in ipairs(state.files) do for _, f in ipairs(state.files) do
table.insert(args, f) table.insert(args, f)
end end
if state.prefix then if state.prefix then
table.insert(args, 1, state.prefix) table.insert(args, 1, state.prefix)
end end
logger.debug(string.format("[CLI]: Executing '%s %s'", subcommand, table.concat(args, ' '))) logger.debug(string.format("[CLI]: Executing '%s %s'", subcommand, table.concat(args, " ")))
return exec_sync(subcommand, args, state.cwd, state.input, state.env, state.show_popup, state.hide_text) return exec_sync(subcommand, args, state.cwd, state.input, state.env, state.show_popup, state.hide_text)
end, end,
to_job = function() to_job = function()
local args = {} local args = {}
for _,o in ipairs(state.options) do for _, o in ipairs(state.options) do
table.insert(args, o) table.insert(args, o)
end end
for _,a in ipairs(state.arguments) do for _, a in ipairs(state.arguments) do
table.insert(args, a) table.insert(args, a)
end end
if #state.files > 0 then if #state.files > 0 then
table.insert(args, '--') table.insert(args, "--")
end end
for _,f in ipairs(state.files) do for _, f in ipairs(state.files) do
table.insert(args, f) table.insert(args, f)
end end
if state.prefix then if state.prefix then
@ -606,7 +611,7 @@ local function new_builder(subcommand)
end end
return new_job(subcommand, args, state.cwd, state.input, state.env, state.show_popup) return new_job(subcommand, args, state.cwd, state.input, state.env, state.show_popup)
end end,
}, mt_builder) }, mt_builder)
end end
@ -614,18 +619,22 @@ local function new_parallel_builder(calls)
local state = { local state = {
calls = calls, calls = calls,
show_popup = true, show_popup = true,
cwd = nil cwd = nil,
} }
local function call() local function call()
if #state.calls == 0 then return end if #state.calls == 0 then
return
end
if not state.cwd then if not state.cwd then
state.cwd = git_root() state.cwd = git_root()
end end
if not state.cwd or state.cwd == "" then return end if not state.cwd or state.cwd == "" then
return
end
for _,c in ipairs(state.calls) do for _, c in ipairs(state.calls) do
c.cwd(state.cwd).show_popup(state.show_popup) c.cwd(state.cwd).show_popup(state.show_popup)
end end
@ -638,81 +647,79 @@ local function new_parallel_builder(calls)
end end
return setmetatable({ return setmetatable({
call = call call = call,
}, { }, {
__index = function (tbl, action) __index = function(tbl, action)
if action == 'cwd' then if action == "cwd" then
return function (cwd) return function(cwd)
state.cwd = cwd state.cwd = cwd
return tbl return tbl
end end
end end
if action == 'show_popup' then if action == "show_popup" then
return function (show_popup) return function(show_popup)
state.show_popup = show_popup state.show_popup = show_popup
return tbl return tbl
end end
end end
end, end,
__call = call __call = call,
}) })
end end
local meta = { local meta = {
__index = function (_tbl, key) __index = function(_tbl, key)
if configurations[key] then if configurations[key] then
return new_builder(key) return new_builder(key)
end end
error("unknown field") error("unknown field")
end end,
} }
local function handle_interactive_password_questions(chan, line) local function handle_interactive_password_questions(chan, line)
logger.debug(string.format("Matching interactive cmd output: '%s'", line)) logger.debug(string.format("Matching interactive cmd output: '%s'", line))
if vim.startswith(line, "Are you sure you want to continue connecting ") then if vim.startswith(line, "Are you sure you want to continue connecting ") then
logger.debug "[CLI]: Confirming whether to continue with unauthenticated host" logger.debug("[CLI]: Confirming whether to continue with unauthenticated host")
local prompt = line local prompt = line
local value = vim.fn.input { local value = vim.fn.input {
prompt = "The authenticity of the host can't be established. " .. prompt .. " ", prompt = "The authenticity of the host can't be established. " .. prompt .. " ",
cancelreturn = "__CANCEL__" cancelreturn = "__CANCEL__",
} }
if value ~= "__CANCEL__" then if value ~= "__CANCEL__" then
logger.debug "[CLI]: Received answer" logger.debug("[CLI]: Received answer")
vim.fn.chansend(chan, value .. "\n") vim.fn.chansend(chan, value .. "\n")
else else
logger.debug "[CLI]: Cancelling the interactive cmd" logger.debug("[CLI]: Cancelling the interactive cmd")
vim.fn.chanclose(chan) vim.fn.chanclose(chan)
end end
elseif vim.startswith(line, "Username for ") then elseif vim.startswith(line, "Username for ") then
logger.debug "[CLI]: Asking for username" logger.debug("[CLI]: Asking for username")
local prompt = line:match("(.*:?):.*") local prompt = line:match("(.*:?):.*")
local value = vim.fn.input { local value = vim.fn.input {
prompt = prompt .. " ", prompt = prompt .. " ",
cancelreturn = "__CANCEL__" cancelreturn = "__CANCEL__",
} }
if value ~= "__CANCEL__" then if value ~= "__CANCEL__" then
logger.debug "[CLI]: Received username" logger.debug("[CLI]: Received username")
vim.fn.chansend(chan, value .. "\n") vim.fn.chansend(chan, value .. "\n")
else else
logger.debug "[CLI]: Cancelling the interactive cmd" logger.debug("[CLI]: Cancelling the interactive cmd")
vim.fn.chanclose(chan) vim.fn.chanclose(chan)
end end
elseif vim.startswith(line, "Enter passphrase") elseif vim.startswith(line, "Enter passphrase") or vim.startswith(line, "Password for") then
or vim.startswith(line, "Password for") logger.debug("[CLI]: Asking for password")
then
logger.debug "[CLI]: Asking for password"
local prompt = line:match("(.*:?):.*") local prompt = line:match("(.*:?):.*")
local value = vim.fn.inputsecret { local value = vim.fn.inputsecret {
prompt = prompt .. " ", prompt = prompt .. " ",
cancelreturn = "__CANCEL__" cancelreturn = "__CANCEL__",
} }
if value ~= "__CANCEL__" then if value ~= "__CANCEL__" then
logger.debug "[CLI]: Received password" logger.debug("[CLI]: Received password")
vim.fn.chansend(chan, value .. "\n") vim.fn.chansend(chan, value .. "\n")
else else
logger.debug "[CLI]: Cancelling the interactive cmd" logger.debug("[CLI]: Cancelling the interactive cmd")
vim.fn.chanclose(chan) vim.fn.chanclose(chan)
end end
else else
@ -736,7 +743,7 @@ local cli = setmetatable({
local started_at = os.clock() local started_at = os.clock()
logger.debug(string.format("[CLI]: Starting interactive git cmd '%s'", cmd)) logger.debug(string.format("[CLI]: Starting interactive git cmd '%s'", cmd))
chan = vim.fn.jobstart(vim.fn.has('win32') == 1 and { "cmd", "/C", cmd } or cmd, { chan = vim.fn.jobstart(vim.fn.has("win32") == 1 and { "cmd", "/C", cmd } or cmd, {
pty = true, pty = true,
width = 100, width = 100,
on_stdout = function(_, data) on_stdout = function(_, data)
@ -749,7 +756,7 @@ local cli = setmetatable({
table.insert(stdout, data) table.insert(stdout, data)
local lines = vim.split(data, "\r?[\r\n]") local lines = vim.split(data, "\r?[\r\n]")
for i=1,#lines do for i = 1, #lines do
if lines[i] ~= "" then if lines[i] ~= "" then
if skip_count > 0 then if skip_count > 0 then
skip_count = skip_count - 1 skip_count = skip_count - 1
@ -767,12 +774,12 @@ local cli = setmetatable({
stdout = stdout, stdout = stdout,
stderr = stdout, stderr = stdout,
code = code, code = code,
time = (os.clock() - started_at) * 1000 time = (os.clock() - started_at) * 1000,
} }
cb({ cb {
code = code, code = code,
stdout = stdout stdout = stdout,
}) }
end, end,
}) })
@ -783,7 +790,7 @@ local cli = setmetatable({
git_root_sync = git_root_sync, git_root_sync = git_root_sync,
git_dir_path_sync = git_dir_path_sync, git_dir_path_sync = git_dir_path_sync,
in_parallel = function(...) in_parallel = function(...)
local calls = {...} local calls = { ... }
return new_parallel_builder(calls) return new_parallel_builder(calls)
end, end,
}, meta) }, meta)

View file

@ -1,9 +1,9 @@
local a = require 'plenary.async' local a = require("plenary.async")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local logger = require 'neogit.logger' local logger = require("neogit.logger")
local cli = require('neogit.lib.git.cli') local cli = require("neogit.lib.git.cli")
local Collection = require('neogit.lib.collection') local Collection = require("neogit.lib.collection")
local md5 = require 'neogit.lib.md5' local md5 = require("neogit.lib.md5")
local function parse_diff_stats(raw) local function parse_diff_stats(raw)
if type(raw) == "string" then if type(raw) == "string" then
@ -11,7 +11,7 @@ local function parse_diff_stats(raw)
end end
local stats = { local stats = {
additions = 0, additions = 0,
deletions = 0 deletions = 0,
} }
-- local matches raw:match('1 file changed, (%d+ insertions?%(%+%))?(, )?(%d+ deletions?%(%-%))?') -- local matches raw:match('1 file changed, (%d+ insertions?%(%+%))?(, )?(%d+ deletions?%(%-%))?')
for _, part in ipairs(raw) do for _, part in ipairs(raw) do
@ -37,7 +37,7 @@ local function parse_diff(output, with_stats)
lines = {}, lines = {},
file = "", file = "",
hunks = {}, hunks = {},
stats = {} stats = {},
} }
local start_idx = 1 local start_idx = 1
@ -49,8 +49,8 @@ local function parse_diff(output, with_stats)
do do
local header = {} local header = {}
for i=start_idx,#output do for i = start_idx, #output do
if output[i]:match('^@@@*.*@@@*') then if output[i]:match("^@@@*.*@@@*") then
start_idx = i start_idx = i
break break
end end
@ -69,36 +69,35 @@ local function parse_diff(output, with_stats)
diff.file = header[4]:match("%-%-%- a/(.*)") diff.file = header[4]:match("%-%-%- a/(.*)")
end end
else else
logger.debug "TODO: diff parser" logger.debug("TODO: diff parser")
logger.debug(vim.inspect(header)) logger.debug(vim.inspect(header))
end end
end end
for i=start_idx,#output do for i = start_idx, #output do
table.insert(diff.lines, output[i]) table.insert(diff.lines, output[i])
end end
local len = #diff.lines local len = #diff.lines
local hunk = nil local hunk = nil
local hunk_content = '' local hunk_content = ""
for i=1,len do for i = 1, len do
local line = diff.lines[i] local line = diff.lines[i]
if not vim.startswith(line, "+++") then if not vim.startswith(line, "+++") then
local index_from, index_len, disk_from, disk_len local index_from, index_len, disk_from, disk_len
if vim.startswith(line, "@@@") then if vim.startswith(line, "@@@") then
-- Combined diff header -- Combined diff header
index_from, index_len, disk_from, disk_len = line:match('@@@* %-(%d+),?(%d*) .* %+(%d+),?(%d*) @@@*') index_from, index_len, disk_from, disk_len = line:match("@@@* %-(%d+),?(%d*) .* %+(%d+),?(%d*) @@@*")
else else
-- Normal diff header -- Normal diff header
index_from, index_len, disk_from, disk_len = line:match('@@ %-(%d+),?(%d*) %+(%d+),?(%d*) @@') index_from, index_len, disk_from, disk_len = line:match("@@ %-(%d+),?(%d*) %+(%d+),?(%d*) @@")
end end
if index_from then if index_from then
if hunk ~= nil then if hunk ~= nil then
hunk.hash = md5.sumhexa(hunk_content) hunk.hash = md5.sumhexa(hunk_content)
hunk_content = '' hunk_content = ""
table.insert(diff.hunks, hunk) table.insert(diff.hunks, hunk)
end end
hunk = { hunk = {
@ -108,11 +107,11 @@ local function parse_diff(output, with_stats)
disk_len = tonumber(disk_len) or 1, disk_len = tonumber(disk_len) or 1,
line = line, line = line,
diff_from = i, diff_from = i,
diff_to = i diff_to = i,
} }
else else
hunk_content = hunk_content .. '\n' .. line hunk_content = hunk_content .. "\n" .. line
if hunk then if hunk then
hunk.diff_to = hunk.diff_to + 1 hunk.diff_to = hunk.diff_to + 1
end end
end end
@ -132,19 +131,18 @@ local diff = {
parse_stats = parse_diff_stats, parse_stats = parse_diff_stats,
get_stats = function(name) get_stats = function(name)
return parse_diff_stats(cli.diff.no_ext_diff.shortstat.files(name).call_sync()) return parse_diff_stats(cli.diff.no_ext_diff.shortstat.files(name).call_sync())
end end,
} }
local ItemFilter = {} local ItemFilter = {}
function ItemFilter.new (tbl) function ItemFilter.new(tbl)
return setmetatable(tbl, { __index = ItemFilter }) return setmetatable(tbl, { __index = ItemFilter })
end end
function ItemFilter.accepts (tbl, section, item) function ItemFilter.accepts(tbl, section, item)
for _, f in ipairs(tbl) do for _, f in ipairs(tbl) do
if (f.section == "*" or f.section == section) if (f.section == "*" or f.section == section) and (f.file == "*" or f.file == item) then
and (f.file == "*" or f.file == item) then
return true return true
end end
end end
@ -153,15 +151,15 @@ function ItemFilter.accepts (tbl, section, item)
end end
function diff.register(meta) function diff.register(meta)
meta.load_diffs = function (repo, filter) meta.load_diffs = function(repo, filter)
filter = filter or false filter = filter or false
local executions = {} local executions = {}
if type(filter) == 'table' then if type(filter) == "table" then
filter = ItemFilter.new(Collection.new(filter):map(function (item) filter = ItemFilter.new(Collection.new(filter):map(function(item)
local section, file = item:match("^([^:]+):(.*)$") local section, file = item:match("^([^:]+):(.*)$")
if not section then if not section then
error('Invalid filter item: '..item, 3) error("Invalid filter item: " .. item, 3)
end end
return { section = section, file = file } return { section = section, file = file }
@ -169,8 +167,8 @@ function diff.register(meta)
end end
for _, f in ipairs(repo.unstaged.items) do for _, f in ipairs(repo.unstaged.items) do
if f.mode ~= 'D' and f.mode ~= 'F' and (not filter or filter:accepts('unstaged', f.name)) then if f.mode ~= "D" and f.mode ~= "F" and (not filter or filter:accepts("unstaged", f.name)) then
table.insert(executions, function () table.insert(executions, function()
local raw_diff = cli.diff.no_ext_diff.files(f.name).call() local raw_diff = cli.diff.no_ext_diff.files(f.name).call()
local raw_stats = cli.diff.no_ext_diff.shortstat.files(f.name).call() local raw_stats = cli.diff.no_ext_diff.shortstat.files(f.name).call()
f.diff = parse_diff(raw_diff) f.diff = parse_diff(raw_diff)
@ -180,8 +178,8 @@ function diff.register(meta)
end end
for _, f in ipairs(repo.staged.items) do for _, f in ipairs(repo.staged.items) do
if f.mode ~= 'D' and f.mode ~= 'F' and (not filter or filter:accepts('staged', f.name)) then if f.mode ~= "D" and f.mode ~= "F" and (not filter or filter:accepts("staged", f.name)) then
table.insert(executions, function () table.insert(executions, function()
local raw_diff = cli.diff.no_ext_diff.cached.files(f.name).call() local raw_diff = cli.diff.no_ext_diff.cached.files(f.name).call()
local raw_stats = cli.diff.no_ext_diff.cached.shortstat.files(f.name).call() local raw_stats = cli.diff.no_ext_diff.cached.shortstat.files(f.name).call()
f.diff = parse_diff(raw_diff) f.diff = parse_diff(raw_diff)

View file

@ -4,13 +4,13 @@ local config = require("neogit.config")
local function parse_log(output) local function parse_log(output)
if type(output) == "string" then if type(output) == "string" then
output = vim.split(output, '\n') output = vim.split(output, "\n")
end end
local output_len = #output local output_len = #output
local commits = {} local commits = {}
for i=1,output_len do for i = 1, output_len do
local level, hash, rest = output[i]:match("([| *]*)([a-zA-Z0-9]+) (.*)") local level, hash, rest = output[i]:match("([| *]*)([a-zA-Z0-9]+) (.*)")
if level ~= nil then if level ~= nil then
local remote, message = rest:match("%((.-)%) (.*)") local remote, message = rest:match("%((.-)%) (.*)")
@ -22,7 +22,7 @@ local function parse_log(output)
level = util.str_count(level, "|"), level = util.str_count(level, "|"),
hash = hash, hash = hash,
remote = remote or "", remote = remote or "",
message = message message = message,
} }
table.insert(commits, commit) table.insert(commits, commit)
end end
@ -37,25 +37,21 @@ local function update_recent(state)
return return
end end
local result = cli.log.oneline local result = cli.log.oneline.max_count(count).show_popup(false).call()
.max_count(count)
.show_popup(false)
.call()
state.recent.items = util.map(result, function (x) state.recent.items = util.map(result, function(x)
return { name = x } return { name = x }
end) end)
end end
return { return {
list = function(options) list = function(options)
options = util.split(options, ' ') options = util.split(options, " ")
local output = cli.log.oneline.args(unpack(options)).call() local output = cli.log.oneline.args(unpack(options)).call()
return parse_log(output) return parse_log(output)
end, end,
register = function(meta) register = function(meta)
meta.update_recent = update_recent meta.update_recent = update_recent
end, end,
parse_log = parse_log parse_log = parse_log,
} }

View file

@ -1,5 +1,5 @@
local cli = require('neogit.lib.git.cli') local cli = require("neogit.lib.git.cli")
local util = require('neogit.lib.util') local util = require("neogit.lib.util")
local M = {} local M = {}
@ -10,13 +10,14 @@ function M.pull_interactive(remote, branch, args)
end end
local function update_unpulled(state) local function update_unpulled(state)
if not state.upstream.branch then return end if not state.upstream.branch then
return
end
local result = local result = cli.log.oneline.for_range("..@{upstream}").show_popup(false).call()
cli.log.oneline.for_range('..@{upstream}').show_popup(false).call()
state.unpulled.items = util.map(result, function (x) state.unpulled.items = util.map(result, function(x)
return { name = x } return { name = x }
end) end)
end end

View file

@ -1,5 +1,5 @@
local cli = require('neogit.lib.git.cli') local cli = require("neogit.lib.git.cli")
local util = require('neogit.lib.util') local util = require("neogit.lib.util")
local M = {} local M = {}
@ -10,13 +10,14 @@ function M.push_interactive(remote, branch, args)
end end
local function update_unmerged(state) local function update_unmerged(state)
if not state.upstream.branch then return end if not state.upstream.branch then
return
end
local result = local result = cli.log.oneline.for_range("@{upstream}..").show_popup(false).call()
cli.log.oneline.for_range('@{upstream}..').show_popup(false).call()
state.unmerged.items = util.map(result, function (x) state.unmerged.items = util.map(result, function(x)
return { name = x } return { name = x }
end) end)
end end

View file

@ -1,44 +1,44 @@
local M = {} local M = {}
local meta = { local meta = {
__index = {} __index = {},
} }
local modules = { 'status', 'diff', 'stash', 'pull', 'push', 'log' } local modules = { "status", "diff", "stash", "pull", "push", "log" }
for _, m in ipairs(modules) do for _, m in ipairs(modules) do
require('neogit.lib.git.'..m).register(meta.__index) require("neogit.lib.git." .. m).register(meta.__index)
end end
M.create = function (_path) M.create = function(_path)
local cache = { local cache = {
head = { head = {
branch = nil, branch = nil,
commit_message = '' commit_message = "",
}, },
upstream = { upstream = {
breanch = nil, breanch = nil,
commit_message = '' commit_message = "",
}, },
untracked = { untracked = {
items = {} items = {},
}, },
unstaged = { unstaged = {
items = {} items = {},
}, },
staged = { staged = {
items = {} items = {},
}, },
stashes = { stashes = {
items = {} items = {},
}, },
unpulled = { unpulled = {
items = {} items = {},
}, },
unmerged = { unmerged = {
items = {} items = {},
}, },
recent = { recent = {
items = {} items = {},
}, },
} }

View file

@ -1,10 +1,10 @@
local cli = require('neogit.lib.git.cli') local cli = require("neogit.lib.git.cli")
local function parse(output) local function parse(output)
local result = {} local result = {}
for _, line in ipairs(output) do for _, line in ipairs(output) do
local stash_num, stash_desc = line:match('stash@{(%d*)}: (.*)') local stash_num, stash_desc = line:match("stash@{(%d*)}: (.*)")
table.insert(result, { idx = tonumber(stash_num), name = line, message = stash_desc}) table.insert(result, { idx = tonumber(stash_num), name = line, message = stash_desc })
end end
return result return result
end end
@ -14,61 +14,40 @@ local function trim_null_terminator(str)
end end
local function perform_stash(include) local function perform_stash(include)
if not include then return end if not include then
return
local index =
cli['commit-tree']
.no_gpg_sign
.parent('HEAD')
.tree(cli['write-tree'].call())
.call()
cli['read-tree']
.merge
.index_output('.git/NEOGIT_TMP_INDEX')
.args(index)
.call()
if include.worktree then
local files =
cli.diff
.no_ext_diff
.name_only
.null_terminated
.args('HEAD')
.env({
GIT_INDEX_FILE = '.git/NEOGIT_TMP_INDEX'
})
.call()
files = vim.split(trim_null_terminator(files), '\0')
cli['update-index']
.add
.remove
.files(unpack(files))
.env({
GIT_INDEX_FILE = '.git/NEOGIT_TMP_INDEX'
})
.call()
end end
local tree = local index = cli["commit-tree"].no_gpg_sign.parent("HEAD").tree(cli["write-tree"].call()).call()
cli['commit-tree']
.no_gpg_sign cli["read-tree"].merge.index_output(".git/NEOGIT_TMP_INDEX").args(index).call()
.parents('HEAD', index)
.tree(cli['write-tree'].call()) if include.worktree then
local files = cli.diff.no_ext_diff.name_only.null_terminated
.args("HEAD")
.env({ .env({
GIT_INDEX_FILE = '.git/NEOGIT_TMP_INDEX' GIT_INDEX_FILE = ".git/NEOGIT_TMP_INDEX",
}) })
.call() .call()
files = vim.split(trim_null_terminator(files), "\0")
cli["update-index"].add.remove
cli['update-ref'] .files(unpack(files))
.create_reflog .env({
.args('refs/stash', tree) GIT_INDEX_FILE = ".git/NEOGIT_TMP_INDEX",
})
.call() .call()
end
local tree = cli["commit-tree"].no_gpg_sign
.parents("HEAD", index)
.tree(cli["write-tree"].call())
.env({
GIT_INDEX_FILE = ".git/NEOGIT_TMP_INDEX",
})
.call()
cli["update-ref"].create_reflog.args("refs/stash", tree).call()
-- selene: allow(empty_if) -- selene: allow(empty_if)
if include.worktree and include.index then if include.worktree and include.index then
@ -76,92 +55,58 @@ local function perform_stash(include)
-- leaves a malformed stash entry, so reverting the changes is -- leaves a malformed stash entry, so reverting the changes is
-- destructive until fixed. -- destructive until fixed.
-- --
--cli.reset --cli.reset
--.hard --.hard
--.commit('HEAD') --.commit('HEAD')
--.call() --.call()
elseif include.index then elseif include.index then
local diff = local diff = cli.diff.no_ext_diff.cached.call() .. "\n"
cli.diff
.no_ext_diff
.cached
.call() .. '\n'
cli.apply.reverse.cached.input(diff).call()
cli.apply
.reverse cli.apply.reverse.input(diff).call()
.cached
.input(diff)
.call()
cli.apply
.reverse
.input(diff)
.call()
end end
end end
local function update_stashes(state) local function update_stashes(state)
local result = cli.stash.args('list').call() local result = cli.stash.args("list").call()
state.stashes.items = parse(result) state.stashes.items = parse(result)
end end
return { return {
parse = parse, parse = parse,
stash_all = function () stash_all = function()
cli.stash.call() cli.stash.call()
-- this should work, but for some reason doesn't. -- this should work, but for some reason doesn't.
--return perform_stash({ worktree = true, index = true }) --return perform_stash({ worktree = true, index = true })
end, end,
stash_index = function () stash_index = function()
return perform_stash({ worktree = false, index = true }) return perform_stash { worktree = false, index = true }
end, end,
pop = function (stash) pop = function(stash)
local _, code = cli.stash local _, code = cli.stash.apply.index.args(stash).show_popup(false).call()
.apply
.index
.args(stash)
.show_popup(false)
.call()
if code == 0 then if code == 0 then
cli.stash cli.stash.drop.args(stash).call()
.drop
.args(stash)
.call()
else else
cli.stash cli.stash.apply.args(stash).call()
.apply
.args(stash)
.call()
end end
end, end,
apply = function (stash) apply = function(stash)
local _, code = cli.stash local _, code = cli.stash.apply.index.args(stash).show_popup(false).call()
.apply
.index
.args(stash)
.show_popup(false)
.call()
if code ~= 0 then if code ~= 0 then
cli.stash cli.stash.apply.args(stash).call()
.apply
.args(stash)
.call()
end end
end, end,
drop = function (stash) drop = function(stash)
cli.stash cli.stash.drop.args(stash).call()
.drop
.args(stash)
.call()
end, end,
register = function (meta) register = function(meta)
meta.update_stashes = update_stashes meta.update_stashes = update_stashes
end end,
} }

View file

@ -1,87 +1,80 @@
local git = { local git = {
cli = require("neogit.lib.git.cli"), cli = require("neogit.lib.git.cli"),
stash = require("neogit.lib.git.stash") stash = require("neogit.lib.git.stash"),
} }
local a = require 'plenary.async' local a = require("plenary.async")
local util = require("neogit.lib.util") local util = require("neogit.lib.util")
local Collection = require('neogit.lib.collection') local Collection = require("neogit.lib.collection")
local function update_status(state) local function update_status(state)
local result = local result = git.cli.status.porcelain(2).branch.null_terminated.call()
git.cli.status
.porcelain(2)
.branch
.null_terminated
.call()
local untracked_files, unstaged_files, staged_files = {}, {}, {} local untracked_files, unstaged_files, staged_files = {}, {}, {}
local append_original_path local append_original_path
local old_files_hash = { local old_files_hash = {
staged_files = Collection.new(state.staged.items or {}):key_by('name'), staged_files = Collection.new(state.staged.items or {}):key_by("name"),
unstaged_files = Collection.new(state.unstaged.items or {}):key_by('name') unstaged_files = Collection.new(state.unstaged.items or {}):key_by("name"),
} }
local head = {} local head = {}
local upstream = {} local upstream = {}
for _, l in ipairs(util.split(result[1], '\0')) do for _, l in ipairs(util.split(result[1], "\0")) do
if append_original_path then if append_original_path then
append_original_path(l) append_original_path(l)
else else
local header, value = l:match('# ([%w%.]+) (.+)') local header, value = l:match("# ([%w%.]+) (.+)")
if header then if header then
if header == 'branch.head' then if header == "branch.head" then
head.branch = value head.branch = value
elseif header == 'branch.oid' then elseif header == "branch.oid" then
head.oid = value head.oid = value
elseif header == 'branch.upstream' then elseif header == "branch.upstream" then
upstream.branch = value upstream.branch = value
end end
else else
local kind, rest = l:match('(.) (.+)') local kind, rest = l:match("(.) (.+)")
if kind == '?' then if kind == "?" then
table.insert(untracked_files, { table.insert(untracked_files, {
name = rest name = rest,
}) })
-- selene: allow(empty_if) -- selene: allow(empty_if)
elseif kind == '!' then elseif kind == "!" then
-- we ignore ignored files for now -- we ignore ignored files for now
elseif kind == '1' then elseif kind == "1" then
local mode_staged, mode_unstaged, _, _, _, _, _, _, name = local mode_staged, mode_unstaged, _, _, _, _, _, _, name =
rest:match('(.)(.) (....) (%d+) (%d+) (%d+) (%w+) (%w+) (.+)') rest:match("(.)(.) (....) (%d+) (%d+) (%d+) (%w+) (%w+) (.+)")
if mode_staged ~= '.' then if mode_staged ~= "." then
table.insert(staged_files, { table.insert(staged_files, {
mode = mode_staged, mode = mode_staged,
name = name, name = name,
diff = old_files_hash.staged_files[name] diff = old_files_hash.staged_files[name] and old_files_hash.staged_files[name].diff,
and old_files_hash.staged_files[name].diff
}) })
end end
if mode_unstaged ~= '.' then if mode_unstaged ~= "." then
table.insert(unstaged_files, { table.insert(unstaged_files, {
mode = mode_unstaged, mode = mode_unstaged,
name = name, name = name,
diff = old_files_hash.unstaged_files[name] diff = old_files_hash.unstaged_files[name] and old_files_hash.unstaged_files[name].diff,
and old_files_hash.unstaged_files[name].diff
}) })
end end
elseif kind == '2' then elseif kind == "2" then
local mode_staged, mode_unstaged, _, _, _, _, _, _, _, name = local mode_staged, mode_unstaged, _, _, _, _, _, _, _, name =
rest:match('(.)(.) (....) (%d+) (%d+) (%d+) (%w+) (%w+) (%a%d+) (.+)') rest:match("(.)(.) (....) (%d+) (%d+) (%d+) (%w+) (%w+) (%a%d+) (.+)")
local entry = { local entry = {
name = name name = name,
} }
if mode_staged ~= '.' then if mode_staged ~= "." then
entry.mode = mode_staged entry.mode = mode_staged
table.insert(staged_files, entry) table.insert(staged_files, entry)
end end
if mode_unstaged ~= '.' then if mode_unstaged ~= "." then
entry.mode = mode_unstaged entry.mode = mode_unstaged
table.insert(unstaged_files, entry) table.insert(unstaged_files, entry)
end end
append_original_path = function (orig) append_original_path = function(orig)
entry.original_name = orig entry.original_name = orig
append_original_path = nil append_original_path = nil
end end
@ -90,11 +83,11 @@ local function update_status(state)
end end
end end
if head.branch == state.head.branch then if head.branch == state.head.branch then
head.commit_message = state.head.commit_message head.commit_message = state.head.commit_message
end end
if upstream.branch == state.upstream.branch then if upstream.branch == state.upstream.branch then
upstream.commit_message = state.upstream.commit_message upstream.commit_message = state.upstream.commit_message
end end
state.head = head state.head = head
@ -107,15 +100,15 @@ end
local function update_branch_information(state) local function update_branch_information(state)
local tasks = {} local tasks = {}
if state.head.oid ~= '(initial)' then if state.head.oid ~= "(initial)" then
table.insert(tasks, function () table.insert(tasks, function()
local result = git.cli.log.max_count(1).pretty('%B').call() local result = git.cli.log.max_count(1).pretty("%B").call()
state.head.commit_message = result[1] state.head.commit_message = result[1]
end) end)
if state.upstream.branch then if state.upstream.branch then
table.insert(tasks, function () table.insert(tasks, function()
local result = git.cli.log.max_count(1).pretty('%B').for_range('@{upstream}').show_popup(false).call() local result = git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call()
state.upstream.commit_message = result[1] state.upstream.commit_message = result[1]
end) end)
end end
@ -144,7 +137,7 @@ local status = {
end, end,
} }
status.register = function (meta) status.register = function(meta)
meta.update_status = update_status meta.update_status = update_status
meta.update_branch_information = update_branch_information meta.update_branch_information = update_branch_information
end end

View file

@ -22,29 +22,33 @@ local M = {}
---@param group string Syntax group name. ---@param group string Syntax group name.
---@param opt HiSpec ---@param opt HiSpec
function M.hi(group, opt) function M.hi(group, opt)
vim.cmd(string.format( vim.cmd(
"hi %s %s guifg=%s guibg=%s gui=%s guisp=%s blend=%s", string.format(
opt.default and "default" or "", "hi %s %s guifg=%s guibg=%s gui=%s guisp=%s blend=%s",
group, opt.default and "default" or "",
opt.fg or "NONE", group,
opt.bg or "NONE", opt.fg or "NONE",
opt.gui or "NONE", opt.bg or "NONE",
opt.sp or "NONE", opt.gui or "NONE",
opt.blend or "NONE" opt.sp or "NONE",
)) opt.blend or "NONE"
)
)
end end
---@param from string Syntax group name. ---@param from string Syntax group name.
---@param to string Syntax group name. ---@param to string Syntax group name.
---@param opt HiLinkSpec ---@param opt HiLinkSpec
function M.hi_link(from, to, opt) function M.hi_link(from, to, opt)
vim.cmd(string.format( vim.cmd(
"hi%s %s link %s %s", string.format(
opt.force and "!" or "", "hi%s %s link %s %s",
opt.default and "default" or "", opt.force and "!" or "",
from, opt.default and "default" or "",
to or "" from,
)) to or ""
)
)
end end
---@param name string Syntax group name. ---@param name string Syntax group name.
@ -99,7 +103,7 @@ function M.get_gui(group_name, trans)
"standout", "standout",
"underline", "underline",
"undercurl", "undercurl",
"strikethrough" "strikethrough",
} }
for _, attr in ipairs(attributes) do for _, attr in ipairs(attributes) do

View file

@ -1,42 +1,44 @@
local M = {} local M = {}
-- selene: allow(global_usage) -- selene: allow(global_usage)
if not _G.__NEOGIT then if not _G.__NEOGIT then
_G.__NEOGIT = {} _G.__NEOGIT = {}
end end
-- selene: allow(global_usage) -- selene: allow(global_usage)
if not _G.__NEOGIT.completers then if not _G.__NEOGIT.completers then
_G.__NEOGIT.completers = {} _G.__NEOGIT.completers = {}
end end
local function user_input_prompt(prompt, default_value, completion_function) local function user_input_prompt(prompt, default_value, completion_function)
vim.fn.inputsave() vim.fn.inputsave()
local args = { local args = {
prompt = prompt prompt = prompt,
} }
if default_value then if default_value then
args.default = default_value args.default = default_value
end end
if completion_function then if completion_function then
args.completion = 'customlist,v:lua.__NEOGIT.completers.'..completion_function args.completion = "customlist,v:lua.__NEOGIT.completers." .. completion_function
end end
local status, result = pcall(vim.fn.input, args) local status, result = pcall(vim.fn.input, args)
vim.fn.inputrestore() vim.fn.inputrestore()
if not status then return nil end if not status then
return nil
end
return result return result
end end
local COMPLETER_SEQ = 1 local COMPLETER_SEQ = 1
local function make_completion_function(options) local function make_completion_function(options)
local id = 'completer'..tostring(COMPLETER_SEQ) local id = "completer" .. tostring(COMPLETER_SEQ)
COMPLETER_SEQ = COMPLETER_SEQ + 1 COMPLETER_SEQ = COMPLETER_SEQ + 1
-- selene: allow(global_usage) -- selene: allow(global_usage)
_G.__NEOGIT.completers[id] = function (arg_lead) _G.__NEOGIT.completers[id] = function(arg_lead)
local result = {} local result = {}
for _, v in ipairs(options) do for _, v in ipairs(options) do
if v:match(arg_lead) then if v:match(arg_lead) then
@ -73,7 +75,9 @@ function M.get_secret_user_input(prompt)
vim.fn.inputrestore() vim.fn.inputrestore()
if not status then return nil end if not status then
return nil
end
return result return result
end end

View file

@ -13,10 +13,10 @@ local Job = {
done = false, done = false,
on_stdout = nil, on_stdout = nil,
on_stderr = nil, on_stderr = nil,
on_exit = nil on_exit = nil,
} }
local is_win = vim.fn.has('win32') == 1 local is_win = vim.fn.has("win32") == 1
--- Creates a new Job --- Creates a new Job
--@tparam string cmd the command to be executed --@tparam string cmd the command to be executed
@ -43,11 +43,9 @@ function Job:start()
local task = self.cmd local task = self.cmd
if type(task) == "string" if type(task) == "string" and is_win then
and is_win
then
task = task:gsub("%^", "%^%^") task = task:gsub("%^", "%^%^")
task = { 'cmd', '/C', task } task = { "cmd", "/C", task }
end end
local stdout_line_buffer = "" local stdout_line_buffer = ""
@ -68,7 +66,7 @@ function Job:start()
on_stdout = function(_, data) on_stdout = function(_, data)
data[1] = stdout_line_buffer .. data[1] data[1] = stdout_line_buffer .. data[1]
for i=1,#data-1 do for i = 1, #data - 1 do
local data = data[i]:gsub("\r", "") local data = data[i]:gsub("\r", "")
if type(self.on_stdout) == "function" then if type(self.on_stdout) == "function" then
self.on_stdout(data) self.on_stdout(data)
@ -81,7 +79,7 @@ function Job:start()
on_stderr = function(_, data) on_stderr = function(_, data)
data[1] = stderr_line_buffer .. data[1] data[1] = stderr_line_buffer .. data[1]
for i=1,#data-1 do for i = 1, #data - 1 do
local data = data[i]:gsub("\r", "") local data = data[i]:gsub("\r", "")
if type(self.on_stderr) == "function" then if type(self.on_stderr) == "function" then
self.on_stderr(data) self.on_stderr(data)
@ -96,7 +94,7 @@ end
--- Returns when the job is finished --- Returns when the job is finished
function Job:wait() function Job:wait()
vim.fn.jobwait({ self.channel }) vim.fn.jobwait { self.channel }
end end
--- Writes the given strings to the stdin --- Writes the given strings to the stdin
@ -109,19 +107,19 @@ end
function Job.batch(cmds) function Job.batch(cmds)
return util.map(cmds, function(cmd) return util.map(cmds, function(cmd)
return Job.new({ cmd = cmd }) return Job.new { cmd = cmd }
end) end)
end end
function Job.start_all(jobs) function Job.start_all(jobs)
for _,job in pairs(jobs) do for _, job in pairs(jobs) do
job:start() job:start()
end end
end end
function Job.wait_all(jobs) function Job.wait_all(jobs)
vim.fn.jobwait(util.map(jobs, function(job) vim.fn.jobwait(util.map(jobs, function(job)
return job.channel return job.channel
end)) end))
end end

View file

@ -2,21 +2,22 @@ local M = {}
function M.new(initial_value) function M.new(initial_value)
initial_value = initial_value or {} initial_value = initial_value or {}
if type(initial_value) ~= "table" then if type(initial_value) ~= "table" then
error("Initial value must be a table", 2) error("Initial value must be a table", 2)
end end
return setmetatable(initial_value, { __index = M }) return setmetatable(initial_value, { __index = M })
end end
function M.append(tbl, data) function M.append(tbl, data)
if type(data) == 'string' then table.insert(tbl, data) if type(data) == "string" then
elseif type(data) == 'table' then table.insert(tbl, data)
for _, r in ipairs(data) do elseif type(data) == "table" then
table.insert(tbl, r) for _, r in ipairs(data) do
table.insert(tbl, r)
end end
else else
error('invalid data type: ' .. type(data), 2) error("invalid data type: " .. type(data), 2)
end end
return tbl return tbl
end end

View file

@ -25,11 +25,11 @@ local function new()
mappings = mappings, mappings = mappings,
map_id_to_key = map_id_to_key, map_id_to_key = map_id_to_key,
register = function() register = function()
for k,mapping in pairs(mappings) do for k, mapping in pairs(mappings) do
local map_id = #map_id_to_key + 1 local map_id = #map_id_to_key + 1
local f_call = build_call_string(id, map_id) local f_call = build_call_string(id, map_id)
if type(mapping) == "table" then if type(mapping) == "table" then
for _,m in pairs(vim.split(mapping[1], "")) do for _, m in pairs(vim.split(mapping[1], "")) do
if type(mapping[2]) == "string" then if type(mapping[2]) == "string" then
f_call = mapping[2] f_call = mapping[2]
elseif mapping[3] and m == "v" then elseif mapping[3] and m == "v" then
@ -38,7 +38,7 @@ local function new()
vim.api.nvim_buf_set_keymap(id, m, k, f_call, { vim.api.nvim_buf_set_keymap(id, m, k, f_call, {
silent = true, silent = true,
noremap = true, noremap = true,
nowait = true nowait = true,
}) })
end end
else else
@ -48,13 +48,13 @@ local function new()
vim.api.nvim_buf_set_keymap(id, "n", k, f_call, { vim.api.nvim_buf_set_keymap(id, "n", k, f_call, {
silent = true, silent = true,
noremap = true, noremap = true,
nowait = true nowait = true,
}) })
end end
table.insert(map_id_to_key, k) table.insert(map_id_to_key, k)
end end
end end,
} }
managers[id] = manager managers[id] = manager
@ -70,5 +70,5 @@ return {
new = new, new = new,
build_call_string = build_call_string, build_call_string = build_call_string,
delete = delete, delete = delete,
invoke = invoke invoke = invoke,
} }

View file

@ -1,10 +1,10 @@
--# selene: allow(parenthese_conditions, multiple_statements, incorrect_standard_library_use) --# selene: allow(parenthese_conditions, multiple_statements, incorrect_standard_library_use)
local md5 = { local md5 = {
_VERSION = "md5.lua 1.1.0", _VERSION = "md5.lua 1.1.0",
_DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)", _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)",
_URL = "https://github.com/kikito/md5.lua", _URL = "https://github.com/kikito/md5.lua",
_LICENSE = [[ _LICENSE = [[
MIT LICENSE MIT LICENSE
Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software
@ -27,23 +27,21 @@ local md5 = {
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]] ]],
} }
-- bit lib implementions -- bit lib implementions
local char, byte, format, rep, sub = local char, byte, format, rep, sub = string.char, string.byte, string.format, string.rep, string.sub
string.char, string.byte, string.format, string.rep, string.sub
local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift
local ok, bit = pcall(require, 'bit') local ok, bit = pcall(require, "bit")
if ok then if ok then
bit_or, bit_and, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bxor, bit.rshift, bit.lshift bit_or, bit_and, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bxor, bit.rshift, bit.lshift
else else
ok, bit = pcall(require, 'bit32') ok, bit = pcall(require, "bit32")
if ok then if ok then
bit_not = bit.bnot bit_not = bit.bnot
local tobit = function(n) local tobit = function(n)
@ -51,16 +49,14 @@ else
end end
local normalize = function(f) local normalize = function(f)
return function(a,b) return function(a, b)
return tobit(f(tobit(a), tobit(b))) return tobit(f(tobit(a), tobit(b)))
end end
end end
bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor) bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor)
bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift) bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift)
else else
local function tbl2number(tbl) local function tbl2number(tbl)
local result = 0 local result = 0
local power = 1 local power = 1
@ -98,7 +94,7 @@ else
end end
-- defined as local above -- defined as local above
to_bits = function (n) to_bits = function(n)
if n < 0 then if n < 0 then
-- negative -- negative
return to_bits(bit_not(math.abs(n)) + 1) return to_bits(bit_not(math.abs(n)) + 1)
@ -108,10 +104,10 @@ else
local cnt = 1 local cnt = 1
local last local last
while n > 0 do while n > 0 do
last = n % 2 last = n % 2
tbl[cnt] = last tbl[cnt] = last
n = (n-last)/2 n = (n - last) / 2
cnt = cnt + 1 cnt = cnt + 1
end end
return tbl return tbl
@ -141,7 +137,7 @@ else
local tbl = {} local tbl = {}
for i = 1, #tbl_m do for i = 1, #tbl_m do
if tbl_m[i]== 0 or tbl_n[i] == 0 then if tbl_m[i] == 0 or tbl_n[i] == 0 then
tbl[i] = 0 tbl[i] = 0
else else
tbl[i] = 1 tbl[i] = 1
@ -178,8 +174,8 @@ else
local floor = math.floor local floor = math.floor
for _=1, bits do for _ = 1, bits do
n = n/2 n = n / 2
n = bit_or(floor(n), high_bit) n = bit_or(floor(n), high_bit)
end end
return floor(n) return floor(n)
@ -191,8 +187,8 @@ else
n = bit_not(math.abs(n)) + 1 n = bit_not(math.abs(n)) + 1
end end
for _=1, bits do for _ = 1, bits do
n = n*2 n = n * 2
end end
return bit_and(n, 0xFFFFFFFF) return bit_and(n, 0xFFFFFFFF)
end end
@ -201,16 +197,16 @@ end
-- convert little-endian 32-bit int to a 4-char string -- convert little-endian 32-bit int to a 4-char string
local function lei2str(i) local function lei2str(i)
local f=function (s) local f = function(s)
return char( bit_and( bit_rshift(i, s), 255)) return char(bit_and(bit_rshift(i, s), 255))
end end
return f(0)..f(8)..f(16)..f(24) return f(0) .. f(8) .. f(16) .. f(24)
end end
-- convert raw string to big-endian int -- convert raw string to big-endian int
local function str2bei(s) local function str2bei(s)
local v=0 local v = 0
for i=1, #s do for i = 1, #s do
v = v * 256 + byte(s, i) v = v * 256 + byte(s, i)
end end
return v return v
@ -218,18 +214,18 @@ end
-- convert raw string to little-endian int -- convert raw string to little-endian int
local function str2lei(s) local function str2lei(s)
local v=0 local v = 0
for i = #s,1,-1 do for i = #s, 1, -1 do
v = v*256 + byte(s, i) v = v * 256 + byte(s, i)
end end
return v return v
end end
-- cut up a string in little-endian ints of given size -- cut up a string in little-endian ints of given size
local function cut_le_str(s,...) local function cut_le_str(s, ...)
local o, r = 1, {} local o, r = 1, {}
local args = {...} local args = { ... }
for i=1, #args do for i = 1, #args do
table.insert(r, str2lei(sub(s, o, o + args[i] - 1))) table.insert(r, str2lei(sub(s, o, o + args[i] - 1)))
o = o + args[i] o = o + args[i]
end end
@ -240,109 +236,170 @@ end
-- 10/02/2001 jcw@equi4.com -- 10/02/2001 jcw@equi4.com
local CONSTS = { local CONSTS = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xd76aa478,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0xe8c7b756,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x242070db,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xc1bdceee,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xf57c0faf,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x4787c62a,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa8304613,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfd469501,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0x698098d8,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x8b44f7af,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xffff5bb1,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0x895cd7be,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x6b901122,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0xfd987193,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xa679438e,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, 0x49b40821,
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 0xf61e2562,
0xc040b340,
0x265e5a51,
0xe9b6c7aa,
0xd62f105d,
0x02441453,
0xd8a1e681,
0xe7d3fbc8,
0x21e1cde6,
0xc33707d6,
0xf4d50d87,
0x455a14ed,
0xa9e3e905,
0xfcefa3f8,
0x676f02d9,
0x8d2a4c8a,
0xfffa3942,
0x8771f681,
0x6d9d6122,
0xfde5380c,
0xa4beea44,
0x4bdecfa9,
0xf6bb4b60,
0xbebfbc70,
0x289b7ec6,
0xeaa127fa,
0xd4ef3085,
0x04881d05,
0xd9d4d039,
0xe6db99e5,
0x1fa27cf8,
0xc4ac5665,
0xf4292244,
0x432aff97,
0xab9423a7,
0xfc93a039,
0x655b59c3,
0x8f0ccc92,
0xffeff47d,
0x85845dd1,
0x6fa87e4f,
0xfe2ce6e0,
0xa3014314,
0x4e0811a1,
0xf7537e82,
0xbd3af235,
0x2ad7d2bb,
0xeb86d391,
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
} }
local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end local f = function(x, y, z)
local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end return bit_or(bit_and(x, y), bit_and(-x - 1, z))
local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end end
local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end local g = function(x, y, z)
local z=function (ff,a,b,c,d,x,s,ac) return bit_or(bit_and(x, z), bit_and(y, -z - 1))
a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF) end
local h = function(x, y, z)
return bit_xor(x, bit_xor(y, z))
end
local i = function(x, y, z)
return bit_xor(y, bit_or(x, -z - 1))
end
local z = function(ff, a, b, c, d, x, s, ac)
a = bit_and(a + ff(b, c, d) + x + ac, 0xFFFFFFFF)
-- be *very* careful that left shift does not cause rounding! -- be *very* careful that left shift does not cause rounding!
return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b return bit_or(bit_lshift(bit_and(a, bit_rshift(0xFFFFFFFF, s)), s), bit_rshift(a, 32 - s)) + b
end end
local function transform(A,B,C,D,X) local function transform(A, B, C, D, X)
local a,b,c,d=A,B,C,D local a, b, c, d = A, B, C, D
local t=CONSTS local t = CONSTS
a=z(f,a,b,c,d,X[ 0], 7,t[ 1]) a = z(f, a, b, c, d, X[0], 7, t[1])
d=z(f,d,a,b,c,X[ 1],12,t[ 2]) d = z(f, d, a, b, c, X[1], 12, t[2])
c=z(f,c,d,a,b,X[ 2],17,t[ 3]) c = z(f, c, d, a, b, X[2], 17, t[3])
b=z(f,b,c,d,a,X[ 3],22,t[ 4]) b = z(f, b, c, d, a, X[3], 22, t[4])
a=z(f,a,b,c,d,X[ 4], 7,t[ 5]) a = z(f, a, b, c, d, X[4], 7, t[5])
d=z(f,d,a,b,c,X[ 5],12,t[ 6]) d = z(f, d, a, b, c, X[5], 12, t[6])
c=z(f,c,d,a,b,X[ 6],17,t[ 7]) c = z(f, c, d, a, b, X[6], 17, t[7])
b=z(f,b,c,d,a,X[ 7],22,t[ 8]) b = z(f, b, c, d, a, X[7], 22, t[8])
a=z(f,a,b,c,d,X[ 8], 7,t[ 9]) a = z(f, a, b, c, d, X[8], 7, t[9])
d=z(f,d,a,b,c,X[ 9],12,t[10]) d = z(f, d, a, b, c, X[9], 12, t[10])
c=z(f,c,d,a,b,X[10],17,t[11]) c = z(f, c, d, a, b, X[10], 17, t[11])
b=z(f,b,c,d,a,X[11],22,t[12]) b = z(f, b, c, d, a, X[11], 22, t[12])
a=z(f,a,b,c,d,X[12], 7,t[13]) a = z(f, a, b, c, d, X[12], 7, t[13])
d=z(f,d,a,b,c,X[13],12,t[14]) d = z(f, d, a, b, c, X[13], 12, t[14])
c=z(f,c,d,a,b,X[14],17,t[15]) c = z(f, c, d, a, b, X[14], 17, t[15])
b=z(f,b,c,d,a,X[15],22,t[16]) b = z(f, b, c, d, a, X[15], 22, t[16])
a=z(g,a,b,c,d,X[ 1], 5,t[17]) a = z(g, a, b, c, d, X[1], 5, t[17])
d=z(g,d,a,b,c,X[ 6], 9,t[18]) d = z(g, d, a, b, c, X[6], 9, t[18])
c=z(g,c,d,a,b,X[11],14,t[19]) c = z(g, c, d, a, b, X[11], 14, t[19])
b=z(g,b,c,d,a,X[ 0],20,t[20]) b = z(g, b, c, d, a, X[0], 20, t[20])
a=z(g,a,b,c,d,X[ 5], 5,t[21]) a = z(g, a, b, c, d, X[5], 5, t[21])
d=z(g,d,a,b,c,X[10], 9,t[22]) d = z(g, d, a, b, c, X[10], 9, t[22])
c=z(g,c,d,a,b,X[15],14,t[23]) c = z(g, c, d, a, b, X[15], 14, t[23])
b=z(g,b,c,d,a,X[ 4],20,t[24]) b = z(g, b, c, d, a, X[4], 20, t[24])
a=z(g,a,b,c,d,X[ 9], 5,t[25]) a = z(g, a, b, c, d, X[9], 5, t[25])
d=z(g,d,a,b,c,X[14], 9,t[26]) d = z(g, d, a, b, c, X[14], 9, t[26])
c=z(g,c,d,a,b,X[ 3],14,t[27]) c = z(g, c, d, a, b, X[3], 14, t[27])
b=z(g,b,c,d,a,X[ 8],20,t[28]) b = z(g, b, c, d, a, X[8], 20, t[28])
a=z(g,a,b,c,d,X[13], 5,t[29]) a = z(g, a, b, c, d, X[13], 5, t[29])
d=z(g,d,a,b,c,X[ 2], 9,t[30]) d = z(g, d, a, b, c, X[2], 9, t[30])
c=z(g,c,d,a,b,X[ 7],14,t[31]) c = z(g, c, d, a, b, X[7], 14, t[31])
b=z(g,b,c,d,a,X[12],20,t[32]) b = z(g, b, c, d, a, X[12], 20, t[32])
a=z(h,a,b,c,d,X[ 5], 4,t[33]) a = z(h, a, b, c, d, X[5], 4, t[33])
d=z(h,d,a,b,c,X[ 8],11,t[34]) d = z(h, d, a, b, c, X[8], 11, t[34])
c=z(h,c,d,a,b,X[11],16,t[35]) c = z(h, c, d, a, b, X[11], 16, t[35])
b=z(h,b,c,d,a,X[14],23,t[36]) b = z(h, b, c, d, a, X[14], 23, t[36])
a=z(h,a,b,c,d,X[ 1], 4,t[37]) a = z(h, a, b, c, d, X[1], 4, t[37])
d=z(h,d,a,b,c,X[ 4],11,t[38]) d = z(h, d, a, b, c, X[4], 11, t[38])
c=z(h,c,d,a,b,X[ 7],16,t[39]) c = z(h, c, d, a, b, X[7], 16, t[39])
b=z(h,b,c,d,a,X[10],23,t[40]) b = z(h, b, c, d, a, X[10], 23, t[40])
a=z(h,a,b,c,d,X[13], 4,t[41]) a = z(h, a, b, c, d, X[13], 4, t[41])
d=z(h,d,a,b,c,X[ 0],11,t[42]) d = z(h, d, a, b, c, X[0], 11, t[42])
c=z(h,c,d,a,b,X[ 3],16,t[43]) c = z(h, c, d, a, b, X[3], 16, t[43])
b=z(h,b,c,d,a,X[ 6],23,t[44]) b = z(h, b, c, d, a, X[6], 23, t[44])
a=z(h,a,b,c,d,X[ 9], 4,t[45]) a = z(h, a, b, c, d, X[9], 4, t[45])
d=z(h,d,a,b,c,X[12],11,t[46]) d = z(h, d, a, b, c, X[12], 11, t[46])
c=z(h,c,d,a,b,X[15],16,t[47]) c = z(h, c, d, a, b, X[15], 16, t[47])
b=z(h,b,c,d,a,X[ 2],23,t[48]) b = z(h, b, c, d, a, X[2], 23, t[48])
a=z(i,a,b,c,d,X[ 0], 6,t[49]) a = z(i, a, b, c, d, X[0], 6, t[49])
d=z(i,d,a,b,c,X[ 7],10,t[50]) d = z(i, d, a, b, c, X[7], 10, t[50])
c=z(i,c,d,a,b,X[14],15,t[51]) c = z(i, c, d, a, b, X[14], 15, t[51])
b=z(i,b,c,d,a,X[ 5],21,t[52]) b = z(i, b, c, d, a, X[5], 21, t[52])
a=z(i,a,b,c,d,X[12], 6,t[53]) a = z(i, a, b, c, d, X[12], 6, t[53])
d=z(i,d,a,b,c,X[ 3],10,t[54]) d = z(i, d, a, b, c, X[3], 10, t[54])
c=z(i,c,d,a,b,X[10],15,t[55]) c = z(i, c, d, a, b, X[10], 15, t[55])
b=z(i,b,c,d,a,X[ 1],21,t[56]) b = z(i, b, c, d, a, X[1], 21, t[56])
a=z(i,a,b,c,d,X[ 8], 6,t[57]) a = z(i, a, b, c, d, X[8], 6, t[57])
d=z(i,d,a,b,c,X[15],10,t[58]) d = z(i, d, a, b, c, X[15], 10, t[58])
c=z(i,c,d,a,b,X[ 6],15,t[59]) c = z(i, c, d, a, b, X[6], 15, t[59])
b=z(i,b,c,d,a,X[13],21,t[60]) b = z(i, b, c, d, a, X[13], 21, t[60])
a=z(i,a,b,c,d,X[ 4], 6,t[61]) a = z(i, a, b, c, d, X[4], 6, t[61])
d=z(i,d,a,b,c,X[11],10,t[62]) d = z(i, d, a, b, c, X[11], 10, t[62])
c=z(i,c,d,a,b,X[ 2],15,t[63]) c = z(i, c, d, a, b, X[2], 15, t[63])
b=z(i,b,c,d,a,X[ 9],21,t[64]) b = z(i, b, c, d, a, X[9], 21, t[64])
return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF), return bit_and(A + a, 0xFFFFFFFF),
bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF) bit_and(B + b, 0xFFFFFFFF),
bit_and(C + c, 0xFFFFFFFF),
bit_and(D + d, 0xFFFFFFFF)
end end
---------------------------------------------------------------- ----------------------------------------------------------------
@ -351,12 +408,12 @@ local function md5_update(self, s)
self.pos = self.pos + #s self.pos = self.pos + #s
s = self.buf .. s s = self.buf .. s
for ii = 1, #s - 63, 64 do for ii = 1, #s - 63, 64 do
local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4) local X = cut_le_str(sub(s, ii, ii + 63), 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4)
assert(#X == 16) assert(#X == 16)
X[0] = table.remove(X,1) -- zero based! X[0] = table.remove(X, 1) -- zero based!
self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X) self.a, self.b, self.c, self.d = transform(self.a, self.b, self.c, self.d, X)
end end
self.buf = sub(s, math.floor(#s/64)*64 + 1, #s) self.buf = sub(s, math.floor(#s / 64) * 64 + 1, #s)
return self return self
end end
@ -364,14 +421,18 @@ local function md5_finish(self)
local msgLen = self.pos local msgLen = self.pos
local padLen = 56 - msgLen % 64 local padLen = 56 - msgLen % 64
if msgLen % 64 > 56 then padLen = padLen + 64 end if msgLen % 64 > 56 then
padLen = padLen + 64
end
if padLen == 0 then padLen = 64 end if padLen == 0 then
padLen = 64
end
local s = char(128) local s = char(128)
.. rep(char(0),padLen-1) .. rep(char(0), padLen - 1)
.. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(bit_and(8 * msgLen, 0xFFFFFFFF))
.. lei2str(math.floor(msgLen/0x20000000)) .. lei2str(math.floor(msgLen / 0x20000000))
md5_update(self, s) md5_update(self, s)
assert(self.pos % 64 == 0) assert(self.pos % 64 == 0)
@ -381,11 +442,16 @@ end
---------------------------------------------------------------- ----------------------------------------------------------------
function md5.new() function md5.new()
return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68], return {
pos = 0, a = CONSTS[65],
buf = '', b = CONSTS[66],
update = md5_update, c = CONSTS[67],
finish = md5_finish } d = CONSTS[68],
pos = 0,
buf = "",
update = md5_update,
finish = md5_finish,
}
end end
function md5.tohex(s) function md5.tohex(s)

View file

@ -19,7 +19,7 @@ local function create(message, level, delay)
notification_count = notification_count + 1 notification_count = notification_count + 1
local prev_notification = notifications[notification_count - 1] local prev_notification = notifications[notification_count - 1]
or {height = 0, row = vim.api.nvim_get_option("lines") - 2} or { height = 0, row = vim.api.nvim_get_option("lines") - 2 }
local width = #message local width = #message
local height = 1 local height = 1
local padding = 2 + prev_notification.height local padding = 2 + prev_notification.height
@ -40,7 +40,7 @@ local function create(message, level, delay)
width = width, width = width,
anchor = "SE", anchor = "SE",
height = height, height = height,
style = "minimal" style = "minimal",
}) })
local border_buf = vim.api.nvim_create_buf(false, true) local border_buf = vim.api.nvim_create_buf(false, true)
@ -52,7 +52,7 @@ local function create(message, level, delay)
table.insert(border_buf_lines, string.format("╭%s╮", string.rep("", width))) table.insert(border_buf_lines, string.format("╭%s╮", string.rep("", width)))
for _=1,height do for _ = 1, height do
table.insert(border_buf_lines, string.format("│%s│", string.rep(" ", width))) table.insert(border_buf_lines, string.format("│%s│", string.rep(" ", width)))
end end
@ -67,7 +67,7 @@ local function create(message, level, delay)
width = width + 3, width = width + 3,
anchor = "SE", anchor = "SE",
height = height + 2, height = height + 2,
style = "minimal" style = "minimal",
}) })
if level == vim.log.levels.INFO then if level == vim.log.levels.INFO then
@ -91,7 +91,7 @@ local function create(message, level, delay)
col = col, col = col,
border = { border = {
window = border_win, window = border_win,
buffer = border_buf buffer = border_buf,
}, },
content = message, content = message,
delete = function() delete = function()
@ -113,14 +113,14 @@ local function create(message, level, delay)
table.insert(message_history, { table.insert(message_history, {
content = message, content = message,
level = level level = level,
}) })
if vim.fn.winbufnr(window) ~= -1 then if vim.fn.winbufnr(window) ~= -1 then
vim.api.nvim_win_close(window, false) vim.api.nvim_win_close(window, false)
vim.api.nvim_win_close(border_win, false) vim.api.nvim_win_close(border_win, false)
end end
end end,
} }
table.insert(notifications, notification) table.insert(notifications, notification)
@ -139,5 +139,7 @@ return {
end end
notifications = {} notifications = {}
end, end,
get_history = function() return message_history end get_history = function()
return message_history
end,
} }

View file

@ -1,9 +1,9 @@
local PopupBuilder = require 'neogit.lib.popup.builder' local PopupBuilder = require("neogit.lib.popup.builder")
local Buffer = require 'neogit.lib.buffer' local Buffer = require("neogit.lib.buffer")
local common = require 'neogit.buffers.common' local common = require("neogit.buffers.common")
local Ui = require 'neogit.lib.ui' local Ui = require("neogit.lib.ui")
local logger = require 'neogit.logger' local logger = require("neogit.logger")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local col = Ui.col local col = Ui.col
local row = Ui.row local row = Ui.row
@ -22,7 +22,7 @@ end
function M.new(state) function M.new(state)
local instance = { local instance = {
state = state, state = state,
buffer = nil buffer = nil,
} }
setmetatable(instance, { __index = M }) setmetatable(instance, { __index = M })
return instance return instance
@ -88,11 +88,11 @@ function M:toggle_switch(switch)
end end
function M:set_option(option) function M:set_option(option)
option.value = vim.fn.input({ option.value = vim.fn.input {
prompt = option.cli .. "=", prompt = option.cli .. "=",
default = option.value, default = option.value,
cancelreturn = option.value cancelreturn = option.value,
}) }
local c = self.buffer.ui:find_component(function(c) local c = self.buffer.ui:find_component(function(c)
return c.options.id == option.id return c.options.id == option.id
end) end)
@ -103,53 +103,53 @@ end
local Switches = Component.new(function(props) local Switches = Component.new(function(props)
return col { return col {
text.highlight("NeogitPopupSectionTitle") "Switches", text.highlight("NeogitPopupSectionTitle")("Switches"),
col(map(props.state, function(switch) col(map(props.state, function(switch)
return row.tag("Switch").value(switch) { return row.tag("Switch").value(switch) {
row.highlight("NeogitPopupSwitchKey") { row.highlight("NeogitPopupSwitchKey") {
text " -", text(" -"),
text(switch.key), text(switch.key),
}, },
text " ", text(" "),
text(switch.description), text(switch.description),
text " (", text(" ("),
row.id(switch.id).highlight(get_highlight_for_switch(switch)) { row.id(switch.id).highlight(get_highlight_for_switch(switch)) {
text "--", text("--"),
text(switch.cli) text(switch.cli),
}, },
text ")" text(")"),
} }
end)) end)),
} }
end) end)
local Options = Component.new(function(props) local Options = Component.new(function(props)
return col { return col {
text.highlight("NeogitPopupSectionTitle") "Options", text.highlight("NeogitPopupSectionTitle")("Options"),
col(map(props.state, function(option) col(map(props.state, function(option)
return row.tag("Option").value(option) { return row.tag("Option").value(option) {
row.highlight("NeogitPopupOptionKey") { row.highlight("NeogitPopupOptionKey") {
text " =", text(" ="),
text(option.key), text(option.key),
}, },
text " ", text(" "),
text(option.description), text(option.description),
text " (", text(" ("),
row.id(option.id).highlight(get_highlight_for_option(option)) { row.id(option.id).highlight(get_highlight_for_option(option)) {
text "--", text("--"),
text(option.cli), text(option.cli),
text "=", text("="),
text(option.value or "") text(option.value or ""),
}, },
text ")" text(")"),
} }
end)) end)),
} }
end) end)
local Actions = Component.new(function(props) local Actions = Component.new(function(props)
return col { return col {
text.highlight("NeogitPopupSectionTitle") "Actions", text.highlight("NeogitPopupSectionTitle")("Actions"),
Grid.padding_left(1) { Grid.padding_left(1) {
items = props.state, items = props.state,
gap = 1, gap = 1,
@ -157,18 +157,18 @@ local Actions = Component.new(function(props)
if not item.callback then if not item.callback then
return row.highlight("NeogitPopupActionDisabled") { return row.highlight("NeogitPopupActionDisabled") {
text(item.key), text(item.key),
text " ", text(" "),
text(item.description), text(item.description),
} }
end end
return row { return row {
text.highlight("NeogitPopupActionKey")(item.key), text.highlight("NeogitPopupActionKey")(item.key),
text " ", text(" "),
text(item.description), text(item.description),
} }
end end,
} },
} }
end) end)
@ -181,7 +181,7 @@ function M:show()
["<tab>"] = function() ["<tab>"] = function()
local stack = self.buffer.ui:get_component_stack_under_cursor() local stack = self.buffer.ui:get_component_stack_under_cursor()
for _,x in ipairs(stack) do for _, x in ipairs(stack) do
if x.options.tag == "Switch" then if x.options.tag == "Switch" then
self:toggle_switch(x.options.value) self:toggle_switch(x.options.value)
break break
@ -191,7 +191,7 @@ function M:show()
end end
end end
end, end,
} },
} }
for _, switch in pairs(self.state.switches) do for _, switch in pairs(self.state.switches) do
@ -219,7 +219,7 @@ function M:show()
end end
else else
mappings.n[action.key] = function() mappings.n[action.key] = function()
local notif = require 'neogit.lib.notification' local notif = require("neogit.lib.notification")
notif.create(action.description .. " has not been implemented yet", vim.log.levels.WARN) notif.create(action.description .. " has not been implemented yet", vim.log.levels.WARN)
end end
end end
@ -238,15 +238,15 @@ function M:show()
items = { items = {
Switches { state = self.state.switches }, Switches { state = self.state.switches },
Options { state = self.state.options }, Options { state = self.state.options },
Actions { state = self.state.actions } Actions { state = self.state.actions },
} },
} },
} }
end end,
} }
end end
M.deprecated_create = require 'neogit.lib.popup.lib'.create M.deprecated_create = require("neogit.lib.popup.lib").create
return M return M
-- return require("neogit.lib.popup.lib") -- return require("neogit.lib.popup.lib")

View file

@ -1,4 +1,4 @@
local a = require 'plenary.async' local a = require("plenary.async")
local M = {} local M = {}
@ -8,10 +8,10 @@ function M.new(builder_fn)
name = nil, name = nil,
switches = {}, switches = {},
options = {}, options = {},
actions = {{}}, actions = { {} },
env = {}, env = {},
}, },
builder_fn = builder_fn builder_fn = builder_fn,
} }
setmetatable(instance, { __index = M }) setmetatable(instance, { __index = M })
@ -34,7 +34,7 @@ function M:new_action_group()
return self return self
end end
--@param parse Whether the switch is internal to neogit or should be included in the cli command. --@param parse Whether the switch is internal to neogit or should be included in the cli command.
-- If `false` we don't include it in the cli comand. -- If `false` we don't include it in the cli comand.
function M:switch(key, cli, description, enabled, parse) function M:switch(key, cli, description, enabled, parse)
if enabled == nil then if enabled == nil then
@ -46,12 +46,12 @@ function M:switch(key, cli, description, enabled, parse)
end end
table.insert(self.state.switches, { table.insert(self.state.switches, {
id = '-' .. key, id = "-" .. key,
key = key, key = key,
cli = cli, cli = cli,
description = description, description = description,
enabled = enabled, enabled = enabled,
parse = parse parse = parse,
}) })
return self return self
@ -59,7 +59,7 @@ end
function M:option(key, cli, value, description) function M:option(key, cli, value, description)
table.insert(self.state.options, { table.insert(self.state.options, {
id = '=' .. key, id = "=" .. key,
key = key, key = key,
cli = cli, cli = cli,
value = value, value = value,
@ -73,7 +73,7 @@ function M:action(key, description, callback)
table.insert(self.state.actions[#self.state.actions], { table.insert(self.state.actions[#self.state.actions], {
key = key, key = key,
description = description, description = description,
callback = callback and a.void(callback) or nil callback = callback and a.void(callback) or nil,
}) })
return self return self
@ -84,7 +84,7 @@ function M:action_if(cond, key, description, callback)
table.insert(self.state.actions[#self.state.actions], { table.insert(self.state.actions[#self.state.actions], {
key = key, key = key,
description = description, description = description,
callback = callback and a.void(callback) or nil callback = callback and a.void(callback) or nil,
}) })
end end

View file

@ -1,6 +1,6 @@
package.loaded['neogit.lib.popup.lib'] = nil package.loaded["neogit.lib.popup.lib"] = nil
local a = require 'plenary.async' local a = require("plenary.async")
local util = require("neogit.lib.util") local util = require("neogit.lib.util")
local popups = {} local popups = {}
@ -27,7 +27,7 @@ local function draw_popup(popup)
col = 6 + #switch.key + #switch.description, col = 6 + #switch.key + #switch.description,
length = 2 + #switch.cli, length = 2 + #switch.cli,
id = 0, id = 0,
enabled = switch.enabled enabled = switch.enabled,
} }
end end
@ -41,7 +41,7 @@ local function draw_popup(popup)
col = 6 + #option.key + #option.description, col = 6 + #option.key + #option.description,
length = 3 + #option.cli, length = 3 + #option.cli,
id = 0, id = 0,
enabled = #option.value ~= 0 enabled = #option.value ~= 0,
} }
end end
@ -72,13 +72,13 @@ local function draw_popup(popup)
end end
table.insert(columns, { table.insert(columns, {
k_width = k_width, k_width = k_width,
d_width = d_width, d_width = d_width,
items = col items = col,
}) })
end end
for i=1,actions_grid_height do for i = 1, actions_grid_height do
local result = " " local result = " "
for index, col in pairs(columns) do for index, col in pairs(columns) do
local item = col.items[i] local item = col.items[i]
@ -88,15 +88,12 @@ local function draw_popup(popup)
if item == nil then if item == nil then
local key = next_col and util.str_right_pad("", col.k_width + 1, " ") or "" local key = next_col and util.str_right_pad("", col.k_width + 1, " ") or ""
local description = next_col local description = next_col and util.str_right_pad("", col.d_width + 6, " ") or ""
and util.str_right_pad("", col.d_width + 6, " ")
or ""
result = result .. key .. description result = result .. key .. description
else else
local key = util.str_right_pad(item.key, col.k_width + 1, " ") local key = util.str_right_pad(item.key, col.k_width + 1, " ")
local description = has_neighbour local description = has_neighbour and util.str_right_pad(item.description, col.d_width + 6, " ")
and util.str_right_pad(item.description, col.d_width + 6, " ")
or item.description or item.description
result = result .. key .. description result = result .. key .. description
@ -165,18 +162,18 @@ local function toggle_popup_option(buf_handle, key)
vim.api.nvim_buf_set_option(buf_handle, "modifiable", false) vim.api.nvim_buf_set_option(buf_handle, "modifiable", false)
end end
option.value = vim.fn.input({ option.value = vim.fn.input {
prompt = option.cli .. "=", prompt = option.cli .. "=",
default = option.value, default = option.value,
cancelreturn = option.value cancelreturn = option.value,
}) }
h.enabled = #option.value ~= 0 h.enabled = #option.value ~= 0
if h.enabled then if h.enabled then
vim.api.nvim_win_set_cursor(0, { h.line, h.col + h.length - 1 }) vim.api.nvim_win_set_cursor(0, { h.line, h.col + h.length - 1 })
vim.api.nvim_buf_set_option(buf_handle, "modifiable", true) vim.api.nvim_buf_set_option(buf_handle, "modifiable", true)
vim.api.nvim_put({option.value}, "c", false, false) vim.api.nvim_put({ option.value }, "c", false, false)
vim.api.nvim_buf_set_option(buf_handle, "modifiable", false) vim.api.nvim_buf_set_option(buf_handle, "modifiable", false)
end end
@ -191,7 +188,7 @@ local function toggle_popup_option(buf_handle, key)
end end
local function toggle(buf_handle) local function toggle(buf_handle)
local line = vim.fn.getline('.') local line = vim.fn.getline(".")
local matches = vim.fn.matchlist(line, "^ \\([-=]\\)\\([a-zA-Z]\\)") local matches = vim.fn.matchlist(line, "^ \\([-=]\\)\\([a-zA-Z]\\)")
local is_switch = matches[2] == "-" local is_switch = matches[2] == "-"
local key = matches[3] local key = matches[3]
@ -228,9 +225,9 @@ local function create_popup(id, switches, options, actions, env)
local flags = collect_arguments() local flags = collect_arguments()
return table.concat(flags, " ") return table.concat(flags, " ")
end, end,
get_arguments = function () get_arguments = function()
return collect_arguments() return collect_arguments()
end end,
} }
local buf_handle = vim.fn.bufnr(popup.id) local buf_handle = vim.fn.bufnr(popup.id)
@ -268,11 +265,15 @@ local function create_popup(id, switches, options, actions, env)
buf_handle, buf_handle,
"n", "n",
"-" .. switch.key, "-" .. switch.key,
string.format("<cmd>lua require'neogit.lib.popup.lib'.toggle_switch(%d, '%s')<CR>", buf_handle, switch.key), string.format(
"<cmd>lua require'neogit.lib.popup.lib'.toggle_switch(%d, '%s')<CR>",
buf_handle,
switch.key
),
{ {
noremap = true, noremap = true,
silent = true, silent = true,
nowait = true nowait = true,
} }
) )
end end
@ -282,11 +283,15 @@ local function create_popup(id, switches, options, actions, env)
buf_handle, buf_handle,
"n", "n",
"=" .. option.key, "=" .. option.key,
string.format("<cmd>lua require'neogit.lib.popup.lib'.toggle_option(%d, '%s')<CR>", buf_handle, option.key), string.format(
"<cmd>lua require'neogit.lib.popup.lib'.toggle_option(%d, '%s')<CR>",
buf_handle,
option.key
),
{ {
noremap = true, noremap = true,
silent = true, silent = true,
nowait = true nowait = true,
} }
) )
end end
@ -301,23 +306,17 @@ local function create_popup(id, switches, options, actions, env)
{ {
noremap = true, noremap = true,
silent = true, silent = true,
nowait = true nowait = true,
} }
) )
end end
end end
vim.api.nvim_buf_set_keymap( vim.api.nvim_buf_set_keymap(buf_handle, "n", "q", "<cmd>bw<CR>", {
buf_handle, noremap = true,
"n", silent = true,
"q", nowait = true,
"<cmd>bw<CR>", })
{
noremap = true,
silent = true,
nowait = true
}
)
vim.api.nvim_buf_set_keymap( vim.api.nvim_buf_set_keymap(
buf_handle, buf_handle,
"n", "n",
@ -326,7 +325,7 @@ local function create_popup(id, switches, options, actions, env)
{ {
noremap = true, noremap = true,
silent = true, silent = true,
nowait = true nowait = true,
} }
) )
end end
@ -337,16 +336,16 @@ local function new()
name = nil, name = nil,
switches = {}, switches = {},
options = {}, options = {},
actions = {{}}, actions = { {} },
env = {} env = {},
} },
} }
function builder.name(name) function builder.name(name)
builder.state.name = name builder.state.name = name
return builder return builder
end end
function builder.env(env) function builder.env(env)
builder.state.env = env builder.state.env = env
return builder return builder
@ -366,7 +365,7 @@ local function new()
key = key, key = key,
cli = cli, cli = cli,
description = description, description = description,
enabled = enabled enabled = enabled,
}) })
return builder return builder
@ -387,7 +386,7 @@ local function new()
table.insert(builder.state.actions[#builder.state.actions], { table.insert(builder.state.actions[#builder.state.actions], {
key = key, key = key,
description = description, description = description,
callback = callback and a.void(callback) or function() end callback = callback and a.void(callback) or function() end,
}) })
return builder return builder
@ -416,5 +415,5 @@ return {
toggle = toggle, toggle = toggle,
toggle_switch = toggle_popup_switch, toggle_switch = toggle_popup_switch,
toggle_option = toggle_popup_option, toggle_option = toggle_popup_option,
do_action = do_action do_action = do_action,
} }

View file

@ -5,10 +5,10 @@ function M.setup()
for key, val in pairs(config.values.signs) do for key, val in pairs(config.values.signs) do
if key == "hunk" or key == "item" or key == "section" then if key == "hunk" or key == "item" or key == "section" then
vim.fn.sign_define("NeogitClosed:" .. key, { vim.fn.sign_define("NeogitClosed:" .. key, {
text = val[1] text = val[1],
}) })
vim.fn.sign_define("NeogitOpen:" .. key, { vim.fn.sign_define("NeogitOpen:" .. key, {
text = val[2] text = val[2],
}) })
end end
end end

View file

@ -1,8 +1,8 @@
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local default_component_options = { local default_component_options = {
folded = false, folded = false,
hidden = false hidden = false,
} }
local Component = {} local Component = {}
@ -45,7 +45,7 @@ function Component:is_under_cursor(cursor)
local col = cursor[2] local col = cursor[2]
local from, to = self:row_range_abs() local from, to = self:row_range_abs()
local row_ok = from <= row and row <= to local row_ok = from <= row and row <= to
local col_ok = self.position.col_end == -1 local col_ok = self.position.col_end == -1
or (self.position.col_start <= col and col <= self.position.col_end) or (self.position.col_start <= col and col <= self.position.col_end)
return row_ok and col_ok return row_ok and col_ok
end end
@ -57,7 +57,7 @@ function Component:get_width()
if self.tag == "row" then if self.tag == "row" then
local width = 0 local width = 0
for i=1,#self.children do for i = 1, #self.children do
width = width + self.children[i]:get_width() width = width + self.children[i]:get_width()
end end
return width return width
@ -65,7 +65,7 @@ function Component:get_width()
if self.tag == "col" then if self.tag == "col" then
local width = 0 local width = 0
for i=1,#self.children do for i = 1, #self.children do
local c_width = self.children[i]:get_width() local c_width = self.children[i]:get_width()
if c_width > width then if c_width > width then
width = c_width width = c_width
@ -95,7 +95,7 @@ end
function Component.new(f) function Component.new(f)
local x = {} local x = {}
setmetatable(x, { setmetatable(x, {
__call = function(tbl, ...) __call = function(tbl, ...)
local x = f(...) local x = f(...)
local options = vim.tbl_extend("force", default_component_options, tbl, x.options or {}) local options = vim.tbl_extend("force", default_component_options, tbl, x.options or {})
@ -115,7 +115,7 @@ function Component.new(f)
end end
return value return value
end end,
}) })
return x return x
end end

View file

@ -1,14 +1,14 @@
local Component = require 'neogit.lib.ui.component' local Component = require("neogit.lib.ui.component")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local filter = util.filter local filter = util.filter
local Ui = {} local Ui = {}
function Ui.new(buf) function Ui.new(buf)
local this = { local this = {
buf = buf, buf = buf,
layout = {} layout = {},
} }
setmetatable(this, { __index = Ui }) setmetatable(this, { __index = Ui })
return this return this
@ -39,7 +39,7 @@ function Ui._print_component(indent, c, _options)
output = output .. " '" .. c.value .. "'" output = output .. " '" .. c.value .. "'"
end end
for k,v in pairs(c.options) do for k, v in pairs(c.options) do
if k ~= "tag" and k ~= "hidden" then if k ~= "tag" and k ~= "hidden" then
output = output .. " " .. k .. "=" .. tostring(v) output = output .. " " .. k .. "=" .. tostring(v)
end end
@ -51,9 +51,10 @@ end
function Ui._visualize_tree(indent, components, options) function Ui._visualize_tree(indent, components, options)
for _, c in ipairs(components) do for _, c in ipairs(components) do
Ui._print_component(indent, c, options) Ui._print_component(indent, c, options)
if (c.tag == "col" or c.tag == "row") if
(c.tag == "col" or c.tag == "row")
and not (options.collapse_hidden_components and c.options.hidden) and not (options.collapse_hidden_components and c.options.hidden)
then then
Ui._visualize_tree(indent + 1, c.children, options) Ui._visualize_tree(indent + 1, c.children, options)
end end
end end
@ -129,7 +130,7 @@ end
function Ui:_render(first_line, first_col, parent, components, flags) function Ui:_render(first_line, first_col, parent, components, flags)
local curr_line = first_line local curr_line = first_line
if flags.in_row then if flags.in_row then
local col_start = first_col local col_start = first_col
local col_end local col_end
@ -156,7 +157,7 @@ function Ui:_render(first_line, first_col, parent, components, flags)
table.insert(highlights, { table.insert(highlights, {
from = col_start, from = col_start,
to = col_end, to = col_end,
name = highlight name = highlight,
}) })
end end
col_start = col_end col_start = col_end
@ -193,7 +194,7 @@ function Ui:_render(first_line, first_col, parent, components, flags)
if flags.in_nested_row then if flags.in_nested_row then
return { return {
text = text, text = text,
highlights = highlights highlights = highlights,
} }
end end
@ -253,9 +254,9 @@ function Ui:_render(first_line, first_col, parent, components, flags)
end end
function Ui:render(...) function Ui:render(...)
self.layout = {...} self.layout = { ... }
self.layout = filter(self.layout, function(x) self.layout = filter(self.layout, function(x)
return type(x) == "table" return type(x) == "table"
end) end)
self:update() self:update()
end end
@ -263,12 +264,18 @@ end
-- This shouldn't be called often as it completely rewrites the whole buffer -- This shouldn't be called often as it completely rewrites the whole buffer
function Ui:update() function Ui:update()
self.buf:unlock() self.buf:unlock()
local lines_used = self:_render(1, 0, Component.new(function() local lines_used = self:_render(
return { 1,
tag = "_root", 0,
children = self.layout Component.new(function()
} return {
end)(), self.layout, {}) tag = "_root",
children = self.layout,
}
end)(),
self.layout,
{}
)
self.buf:set_lines(lines_used, -1, false, {}) self.buf:set_lines(lines_used, -1, false, {})
self.buf:lock() self.buf:lock()
end end
@ -279,22 +286,26 @@ function Ui:print_layout_tree(options)
end end
function Ui:debug(...) function Ui:debug(...)
Ui.visualize_tree({...}, {}) Ui.visualize_tree({ ... }, {})
end end
Ui.col = Component.new(function(children, options) Ui.col = Component.new(function(children, options)
return { return {
tag = "col", tag = "col",
children = filter(children, function(x) return type(x) == "table" end), children = filter(children, function(x)
options = options return type(x) == "table"
end),
options = options,
} }
end) end)
Ui.row = Component.new(function(children, options) Ui.row = Component.new(function(children, options)
return { return {
tag = "row", tag = "row",
children = filter(children, function(x) return type(x) == "table" end), children = filter(children, function(x)
options = options return type(x) == "table"
end),
options = options,
} }
end) end)
@ -304,16 +315,16 @@ Ui.text = Component.new(function(value, options, ...)
end end
vim.validate { vim.validate {
options = {options, "table", true} options = { options, "table", true },
} }
return { return {
tag = "text", tag = "text",
value = value or "", value = value or "",
options = type(options) == "table" and options or nil options = type(options) == "table" and options or nil,
} }
end) end)
Ui.Component = require 'neogit.lib.ui.component' Ui.Component = require("neogit.lib.ui.component")
return Ui return Ui

View file

@ -1,8 +1,8 @@
local a = require 'plenary.async' local a = require("plenary.async")
local function map(tbl, f) local function map(tbl, f)
local t = {} local t = {}
for k,v in pairs(tbl) do for k, v in pairs(tbl) do
t[k] = f(v) t[k] = f(v)
end end
return t return t
@ -43,7 +43,7 @@ local function range(from, to, step)
to = from to = from
from = 1 from = 1
end end
for i=from, to, step do for i = from, to, step do
table.insert(t, i) table.insert(t, i)
end end
return t return t
@ -52,7 +52,7 @@ end
local function intersperse(tbl, sep) local function intersperse(tbl, sep)
local t = {} local t = {}
local len = #tbl local len = #tbl
for i=1,len do for i = 1, len do
table.insert(t, tbl[i]) table.insert(t, tbl[i])
if i ~= len then if i ~= len then
@ -67,7 +67,7 @@ local function filter(tbl, f)
end end
local function print_tbl(tbl) local function print_tbl(tbl)
for _,x in pairs(tbl) do for _, x in pairs(tbl) do
print("| " .. x) print("| " .. x)
end end
end end
@ -75,12 +75,9 @@ end
local function get_keymaps(mode, startswith) local function get_keymaps(mode, startswith)
local maps = vim.api.nvim_get_keymap(mode) local maps = vim.api.nvim_get_keymap(mode)
if startswith then if startswith then
return filter( return filter(maps, function(x)
maps, return vim.startswith(x.lhs, startswith)
function (x) end)
return vim.startswith(x.lhs, startswith)
end
)
else else
return maps return maps
end end
@ -104,7 +101,7 @@ local function str_right_pad(str, len, sep)
return str .. sep:rep(len - #str) return str .. sep:rep(len - #str)
end end
local function slice (tbl, s, e) local function slice(tbl, s, e)
local pos, new = 1, {} local pos, new = 1, {}
if e == nil then if e == nil then
@ -122,7 +119,7 @@ end
local function str_count(str, target) local function str_count(str, target)
local count = 0 local count = 0
local str_len = #str local str_len = #str
for i=1,str_len do for i = 1, str_len do
if str:sub(i, i) == target then if str:sub(i, i) == target then
count = count + 1 count = count + 1
end end
@ -131,18 +128,22 @@ local function str_count(str, target)
end end
local function split(str, sep) local function split(str, sep)
if str == "" then return {} end if str == "" then
return {}
end
return vim.split(str, sep) return vim.split(str, sep)
end end
local function split_lines(str) local function split_lines(str)
if str == "" then return {} end if str == "" then
return {}
end
-- we need \r? to support windows -- we need \r? to support windows
return vim.split(str, '\r?\n') return vim.split(str, "\r?\n")
end end
local function parse_command_args(...) local function parse_command_args(...)
local args = {...} local args = { ... }
local tbl = {} local tbl = {}
for _, val in pairs(args) do for _, val in pairs(args) do
@ -174,6 +175,5 @@ return {
split_lines = split_lines, split_lines = split_lines,
deepcopy = deepcopy, deepcopy = deepcopy,
trim = trim, trim = trim,
parse_command_args = parse_command_args parse_command_args = parse_command_args,
} }

View file

@ -1,19 +1,27 @@
local a = require 'plenary.async' local a = require("plenary.async")
local M = {} local M = {}
function M.read_file(path) function M.read_file(path)
local err, fd = a.uv.fs_open(path, "r", 438) local err, fd = a.uv.fs_open(path, "r", 438)
if err then return err end if err then
return err
end
local err, stat = a.uv.fs_fstat(fd) local err, stat = a.uv.fs_fstat(fd)
if err then return err end if err then
return err
end
local err, data = a.uv.fs_read(fd, stat.size, 0) local err, data = a.uv.fs_read(fd, stat.size, 0)
if err then return err end if err then
return err
end
local err = a.uv.fs_close(fd) local err = a.uv.fs_close(fd)
if err then return err end if err then
return err
end
return nil, data return nil, data
end end

View file

@ -1,8 +1,8 @@
local log = require "plenary.log" local log = require("plenary.log")
return log.new { return log.new {
plugin = "neogit", plugin = "neogit",
highlights = false, highlights = false,
use_console = false, use_console = false,
level = "debug" level = "debug",
} }

View file

@ -1,4 +1,4 @@
local a = require 'plenary.async' local a = require("plenary.async")
-- This is a table to look up pending neogit operations. -- This is a table to look up pending neogit operations.
-- An operation is loosely defined as a user-triggered, top-level execution -- An operation is loosely defined as a user-triggered, top-level execution
-- like "commit", "stash" or "pull". -- like "commit", "stash" or "pull".
@ -14,14 +14,16 @@ local M = {}
local meta = {} local meta = {}
function M.wait(key, time) function M.wait(key, time)
if M[key] == nil then return end if M[key] == nil then
vim.fn.wait(time or 1000, function () return
return M[key] == false end
vim.fn.wait(time or 1000, function()
return M[key] == false
end, 100) end, 100)
end end
function meta.__call(_tbl, key, async_func) function meta.__call(_tbl, key, async_func)
return a.void(function (...) return a.void(function(...)
M[key] = true M[key] = true
async_func(...) async_func(...)
M[key] = false M[key] = false

View file

@ -1,104 +1,151 @@
local M = {} local M = {}
local status = require 'neogit.status' local status = require("neogit.status")
local cli = require 'neogit.lib.git.cli' local cli = require("neogit.lib.git.cli")
local popup = require('neogit.lib.popup') local popup = require("neogit.lib.popup")
local branch = require('neogit.lib.git.branch') local branch = require("neogit.lib.git.branch")
local operation = require('neogit.operations') local operation = require("neogit.operations")
local BranchSelectViewBuffer = require 'neogit.buffers.branch_select_view' local BranchSelectViewBuffer = require("neogit.buffers.branch_select_view")
local input = require('neogit.lib.input') local input = require("neogit.lib.input")
local function format_branches(list) local function format_branches(list)
local branches = {} local branches = {}
for _,name in ipairs(list) do for _, name in ipairs(list) do
local name_formatted = name:match("^remotes/(.*)") or name local name_formatted = name:match("^remotes/(.*)") or name
if not name_formatted:match('^(.*)/HEAD') then if not name_formatted:match("^(.*)/HEAD") then
table.insert(branches, name_formatted) table.insert(branches, name_formatted)
end
end end
end
return branches return branches
end end
local function parse_remote_branch_name(remote_name) local function parse_remote_branch_name(remote_name)
local offset = remote_name:find('/') local offset = remote_name:find("/")
if not offset then return nil, nil end if not offset then
return nil, nil
end
local remote = remote_name:sub(1, offset-1) local remote = remote_name:sub(1, offset - 1)
local branch_name = remote_name:sub(offset+1, remote_name:len()) local branch_name = remote_name:sub(offset + 1, remote_name:len())
return remote, branch_name return remote, branch_name
end end
function M.create() function M.create()
local p = popup.builder() local p = popup
:name('NeogitBranchPopup') .builder()
:action("n", "create branch", operation('create_branch', function () :name("NeogitBranchPopup")
branch.create() :action(
status.refresh(true) "n",
end)) "create branch",
:action("b", "checkout branch/revision", operation('checkout_branch', function () operation("create_branch", function()
local branches = format_branches(branch.get_all_branches()) branch.create()
BranchSelectViewBuffer.new(branches, function (selected_branch) status.refresh(true)
if selected_branch == '' then return end end)
)
:action(
"b",
"checkout branch/revision",
operation("checkout_branch", function()
local branches = format_branches(branch.get_all_branches())
BranchSelectViewBuffer.new(branches, function(selected_branch)
if selected_branch == "" then
return
end
cli.checkout.branch(selected_branch).call_sync() cli.checkout.branch(selected_branch).call_sync()
status.dispatch_refresh(true) status.dispatch_refresh(true)
end):open() end):open()
end)) end)
:action("d", "delete local branch", operation('delete_branch', function () )
local branches = branch.get_local_branches() :action(
BranchSelectViewBuffer.new(branches, function (selected_branch) "d",
cli.branch.delete.name(selected_branch).call_sync() "delete local branch",
status.dispatch_refresh(true) operation("delete_branch", function()
end):open() local branches = branch.get_local_branches()
end)) BranchSelectViewBuffer.new(branches, function(selected_branch)
:action("D", "delete local branch and remote", operation('delete_branch', function () cli.branch.delete.name(selected_branch).call_sync()
local branches = format_branches(branch.get_remote_branches()) status.dispatch_refresh(true)
BranchSelectViewBuffer.new(branches, function (selected_branch) end):open()
if selected_branch == '' then return end end)
)
:action(
"D",
"delete local branch and remote",
operation("delete_branch", function()
local branches = format_branches(branch.get_remote_branches())
BranchSelectViewBuffer.new(branches, function(selected_branch)
if selected_branch == "" then
return
end
local remote, branch_name = parse_remote_branch_name(selected_branch) local remote, branch_name = parse_remote_branch_name(selected_branch)
if not remote or not branch_name then return end if not remote or not branch_name then
return
end
cli.branch.delete.name(branch_name).call_sync() cli.branch.delete.name(branch_name).call_sync()
cli.push.remote(remote).delete.to(branch_name).call_sync() cli.push.remote(remote).delete.to(branch_name).call_sync()
status.dispatch_refresh(true) status.dispatch_refresh(true)
end):open() end):open()
end)) end)
:action("l", "checkout local branch", operation('checkout_local-branch', function () )
local branches = branch.get_local_branches() :action(
BranchSelectViewBuffer.new(branches, function (selected_branch) "l",
if selected_branch == '' then return end "checkout local branch",
cli.checkout.branch(selected_branch).call_sync() operation("checkout_local-branch", function()
status.dispatch_refresh(true) local branches = branch.get_local_branches()
end):open() BranchSelectViewBuffer.new(branches, function(selected_branch)
end)) if selected_branch == "" then
:action("c", "checkout new branch", operation('checkout_create-branch', function () return
local branches = format_branches(branch.get_all_branches()) end
BranchSelectViewBuffer.new(branches, function(selected_branch) cli.checkout.branch(selected_branch).call_sync()
if selected_branch == '' then return end status.dispatch_refresh(true)
end):open()
end)
)
:action(
"c",
"checkout new branch",
operation("checkout_create-branch", function()
local branches = format_branches(branch.get_all_branches())
BranchSelectViewBuffer.new(branches, function(selected_branch)
if selected_branch == "" then
return
end
local name = input.get_user_input('branch > ') local name = input.get_user_input("branch > ")
if not name or name == '' then return end if not name or name == "" then
return
end
cli.checkout.new_branch_with_start_point(name, selected_branch).call_sync() cli.checkout.new_branch_with_start_point(name, selected_branch).call_sync()
status.dispatch_refresh(true) status.dispatch_refresh(true)
end):open() end):open()
end)) end)
:action("m", "rename branch", operation('rename_branch', function () )
local current_branch = branch.current() or '' :action(
local branches = branch.get_local_branches() "m",
table.insert(branches, current_branch) "rename branch",
operation("rename_branch", function()
local current_branch = branch.current() or ""
local branches = branch.get_local_branches()
table.insert(branches, current_branch)
BranchSelectViewBuffer.new(branches, function (selected_branch) BranchSelectViewBuffer.new(branches, function(selected_branch)
if selected_branch == '' then return end if selected_branch == "" then
return
end
local new_name = input.get_user_input('new branch name > ') local new_name = input.get_user_input("new branch name > ")
if not new_name or new_name == '' then return end if not new_name or new_name == "" then
return
end
cli.branch.move.args(selected_branch, new_name).call_sync() cli.branch.move.args(selected_branch, new_name).call_sync()
status.dispatch_refresh(true) status.dispatch_refresh(true)
end):open() end):open()
end)) end)
)
:build() :build()
p:show() p:show()

View file

@ -1,22 +1,22 @@
local popup = require("neogit.lib.popup") local popup = require("neogit.lib.popup")
local notif = require("neogit.lib.notification") local notif = require("neogit.lib.notification")
local status = require 'neogit.status' local status = require("neogit.status")
local cli = require("neogit.lib.git.cli") local cli = require("neogit.lib.git.cli")
local input = require("neogit.lib.input") local input = require("neogit.lib.input")
local Buffer = require("neogit.lib.buffer") local Buffer = require("neogit.lib.buffer")
local config = require("neogit.config") local config = require("neogit.config")
local a = require 'plenary.async' local a = require("plenary.async")
local split = require('neogit.lib.util').split local split = require("neogit.lib.util").split
local uv_utils = require 'neogit.lib.uv' local uv_utils = require("neogit.lib.uv")
local M = {} local M = {}
local function get_commit_file() local function get_commit_file()
return cli.git_dir_path_sync() .. '/' .. 'NEOGIT_COMMIT_EDITMSG' return cli.git_dir_path_sync() .. "/" .. "NEOGIT_COMMIT_EDITMSG"
end end
-- selene: allow(global_usage) -- selene: allow(global_usage)
local get_commit_message = a.wrap(function (content, cb) local get_commit_message = a.wrap(function(content, cb)
local written = false local written = false
Buffer.create { Buffer.create {
name = get_commit_file(), name = get_commit_file(),
@ -31,12 +31,14 @@ local get_commit_message = a.wrap(function (content, cb)
end, end,
["BufUnload"] = function() ["BufUnload"] = function()
if written then if written then
if config.values.disable_commit_confirmation or if
input.get_confirmation("Are you sure you want to commit?") then config.values.disable_commit_confirmation
vim.cmd [[ or input.get_confirmation("Are you sure you want to commit?")
then
vim.cmd([[
silent g/^#/d silent g/^#/d
silent w! silent w!
]] ]])
cb() cb()
end end
end end
@ -46,15 +48,15 @@ local get_commit_message = a.wrap(function (content, cb)
n = { n = {
["q"] = function(buffer) ["q"] = function(buffer)
buffer:close(true) buffer:close(true)
end end,
} },
}, },
initialize = function(buffer) initialize = function(buffer)
buffer:set_lines(0, -1, false, content) buffer:set_lines(0, -1, false, content)
if not config.values.disable_insert_on_commit then if not config.values.disable_insert_on_commit then
vim.cmd(":startinsert") vim.cmd(":startinsert")
end end
end end,
} }
end, 2) end, 2)
@ -117,7 +119,8 @@ local function do_commit(popup, data, cmd, skip_gen)
end end
function M.create() function M.create()
local p = popup.builder() local p = popup
.builder()
:name("NeogitCommitPopup") :name("NeogitCommitPopup")
:switch("a", "all", "Stage all modified and deleted files", false) :switch("a", "all", "Stage all modified and deleted files", false)
:switch("e", "allow-empty", "Allow empty commit", false) :switch("e", "allow-empty", "Allow empty commit", false)
@ -134,10 +137,15 @@ function M.create()
local commit_file = get_commit_file() local commit_file = get_commit_file()
local _, data = uv_utils.read_file(commit_file) local _, data = uv_utils.read_file(commit_file)
local skip_gen = data ~= nil local skip_gen = data ~= nil
data = data or '' data = data or ""
-- we need \r? to support windows -- we need \r? to support windows
data = split(data, '\r?\n') data = split(data, "\r?\n")
do_commit(popup, data, tostring(cli.commit.commit_message_file(commit_file).args(unpack(popup:get_arguments()))), skip_gen) do_commit(
popup,
data,
tostring(cli.commit.commit_message_file(commit_file).args(unpack(popup:get_arguments()))),
skip_gen
)
end) end)
:action("e", "Extend", function(popup) :action("e", "Extend", function(popup)
do_commit(popup, nil, tostring(cli.commit.no_edit.amend)) do_commit(popup, nil, tostring(cli.commit.no_edit.amend))
@ -145,14 +153,14 @@ function M.create()
:action("w", "Reword", function(popup) :action("w", "Reword", function(popup)
a.util.scheduler() a.util.scheduler()
local commit_file = get_commit_file() local commit_file = get_commit_file()
local msg = cli.log.max_count(1).pretty('%B').call() local msg = cli.log.max_count(1).pretty("%B").call()
do_commit(popup, msg, tostring(cli.commit.commit_message_file(commit_file).amend.only)) do_commit(popup, msg, tostring(cli.commit.commit_message_file(commit_file).amend.only))
end) end)
:action("a", "Amend", function(popup) :action("a", "Amend", function(popup)
a.util.scheduler() a.util.scheduler()
local commit_file = get_commit_file() local commit_file = get_commit_file()
local msg = cli.log.max_count(1).pretty('%B').call() local msg = cli.log.max_count(1).pretty("%B").call()
do_commit(popup, msg, tostring(cli.commit.commit_message_file(commit_file).amend), true) do_commit(popup, msg, tostring(cli.commit.commit_message_file(commit_file).amend), true)
end) end)

View file

@ -1,17 +1,18 @@
local M = {} local M = {}
local config = require 'neogit.config' local config = require("neogit.config")
local popup = require 'neogit.lib.popup' local popup = require("neogit.lib.popup")
function M.create() function M.create()
if not config.ensure_integration 'diffview' then if not config.ensure_integration("diffview") then
return return
end end
local p = popup.builder() local p = popup
.builder()
:name("NeogitDiffPopup") :name("NeogitDiffPopup")
:action("D", "diff against head", function() :action("D", "diff against head", function()
require 'neogit.integrations.diffview'.open() require("neogit.integrations.diffview").open()
end) end)
:build() :build()

View file

@ -1,34 +1,35 @@
local popup = require("neogit.lib.popup") local popup = require("neogit.lib.popup")
local status = require 'neogit.status' local status = require("neogit.status")
local GitCommandHistory = require("neogit.buffers.git_command_history") local GitCommandHistory = require("neogit.buffers.git_command_history")
local M = {} local M = {}
function M.create(env) function M.create(env)
local m = env.use_magit_keybindings local m = env.use_magit_keybindings
local p = popup.builder() local p = popup
.builder()
:name("NeogitHelpPopup") :name("NeogitHelpPopup")
:action(m and "F" or "p", "Pull", function() :action(m and "F" or "p", "Pull", function()
require('neogit.popups.pull').create() require("neogit.popups.pull").create()
end) end)
:action("P", "Push", function() :action("P", "Push", function()
require('neogit.popups.push').create() require("neogit.popups.push").create()
end) end)
:action("Z", "Stash", function(_popup) :action("Z", "Stash", function(_popup)
require('neogit.popups.stash').create(env.get_stash()) require("neogit.popups.stash").create(env.get_stash())
end) end)
:action("L", "Log", function() :action("L", "Log", function()
require('neogit.popups.log').create() require("neogit.popups.log").create()
end) end)
:action("r", "Rebase", function() :action("r", "Rebase", function()
require("neogit.popups.rebase").create() require("neogit.popups.rebase").create()
end) end)
:new_action_group() :new_action_group()
:action("c", "Commit", function() :action("c", "Commit", function()
require('neogit.popups.commit').create() require("neogit.popups.commit").create()
end) end)
:action("b", "Branch", function() :action("b", "Branch", function()
require('neogit.popups.branch').create() require("neogit.popups.branch").create()
end) end)
:action("$", "Git Command History", function() :action("$", "Git Command History", function()
GitCommandHistory:new():show() GitCommandHistory:new():show()
@ -40,8 +41,8 @@ function M.create(env)
:build() :build()
p:show() p:show()
return p return p
end end
return M return M

View file

@ -1,7 +1,7 @@
local popup = require("neogit.lib.popup") local popup = require("neogit.lib.popup")
local LogViewBuffer = require 'neogit.buffers.log_view' local LogViewBuffer = require("neogit.buffers.log_view")
local git = require("neogit.lib.git") local git = require("neogit.lib.git")
local util = require 'neogit.lib.util' local util = require("neogit.lib.util")
local M = {} local M = {}
@ -45,7 +45,7 @@ local function parse(raw)
commit.level = util.str_count(s1, "|") + util.str_count(s2, "|") commit.level = util.str_count(s1, "|") + util.str_count(s2, "|")
local start_idx = #s1 + #s2 + 1 local start_idx = #s1 + #s2 + 1
local function ladvance() local function ladvance()
local line = advance() local line = advance()
return line and line:sub(start_idx + 1, -1) or nil return line and line:sub(start_idx + 1, -1) or nil
@ -55,22 +55,17 @@ local function parse(raw)
local line = ladvance() local line = ladvance()
if vim.startswith(line, "Merge:") then if vim.startswith(line, "Merge:") then
commit.merge = line commit.merge = line:match("Merge:%s*(%w+) (%w+)")
:match("Merge:%s*(%w+) (%w+)")
line = ladvance() line = ladvance()
end end
commit.author_name, commit.author_email = line commit.author_name, commit.author_email = line:match("Author:%s*(.+) <(.+)>")
:match("Author:%s*(.+) <(.+)>")
end end
commit.author_date = ladvance() commit.author_date = ladvance():match("AuthorDate:%s*(.+)")
:match("AuthorDate:%s*(.+)") commit.committer_name, commit.committer_email = ladvance():match("Commit:%s*(.+) <(.+)>")
commit.committer_name, commit.committer_email = ladvance() commit.committer_date = ladvance():match("CommitDate:%s*(.+)")
:match("Commit:%s*(.+) <(.+)>")
commit.committer_date = ladvance()
:match("CommitDate:%s*(.+)")
advance() advance()
@ -95,7 +90,8 @@ end
-- inspect(parse(git.cli.log.args("--max-count=5", "--graph", "--format=fuller").call_sync())) -- inspect(parse(git.cli.log.args("--max-count=5", "--graph", "--format=fuller").call_sync()))
function M.create() function M.create()
local p = popup.builder() local p = popup
.builder()
:name("NeogitLogPopup") :name("NeogitLogPopup")
:switch("g", "graph", "Show graph", true, false) :switch("g", "graph", "Show graph", true, false)
:switch("c", "color", "Show graph in color", true, false) :switch("c", "color", "Show graph in color", true, false)
@ -112,41 +108,30 @@ function M.create()
-- :option("G", "", "", "Search changes") -- :option("G", "", "", "Search changes")
-- :option("S", "", "", "Search occurences") -- :option("S", "", "", "Search occurences")
-- :option("L", "", "", "Trace line evolution") -- :option("L", "", "", "Trace line evolution")
:action("l", "Log current", function(popup) :action(
local output = git.cli.log.format("fuller").args("--graph", unpack(popup:get_arguments())).call_sync() "l",
local parse_args = popup:get_parse_arguments() "Log current",
LogViewBuffer.new(parse(output), parse_args.graph):open() function(popup)
end) local output = git.cli.log.format("fuller").args("--graph", unpack(popup:get_arguments())).call_sync()
local parse_args = popup:get_parse_arguments()
LogViewBuffer.new(parse(output), parse_args.graph):open()
end
)
:action("o", "Log other") :action("o", "Log other")
:action("h", "Log HEAD", function(popup) :action("h", "Log HEAD", function(popup)
local output = local output =
git.cli.log git.cli.log.format("fuller").args(unpack(popup:get_arguments())).for_range("HEAD").call_sync()
.format("fuller")
.args(unpack(popup:get_arguments()))
.for_range('HEAD')
.call_sync()
LogViewBuffer.new(parse(output)):open() LogViewBuffer.new(parse(output)):open()
end) end)
:new_action_group() :new_action_group()
:action("b", "Log all branches", function(popup) :action("b", "Log all branches", function(popup)
local output = local output =
git.cli.log git.cli.log.format("fuller").args(unpack(popup:get_arguments())).branches.remotes.call_sync()
.format("fuller")
.args(unpack(popup:get_arguments()))
.branches
.remotes
.call_sync()
LogViewBuffer.new(parse(output)):open() LogViewBuffer.new(parse(output)):open()
end) end)
:action("a", "Log all references", function(popup) :action("a", "Log all references", function(popup)
local output = local output = git.cli.log.format("fuller").args(unpack(popup:get_arguments())).all.call_sync()
git.cli.log
.format("fuller")
.args(unpack(popup:get_arguments()))
.all
.call_sync()
LogViewBuffer.new(parse(output)):open() LogViewBuffer.new(parse(output)):open()
end) end)
:new_action_group() :new_action_group()

View file

@ -1,10 +1,10 @@
local popup = require("neogit.lib.popup") local popup = require("neogit.lib.popup")
local status = require 'neogit.status' local status = require("neogit.status")
local input = require 'neogit.lib.input' local input = require("neogit.lib.input")
local notif = require("neogit.lib.notification") local notif = require("neogit.lib.notification")
local git = require("neogit.lib.git") local git = require("neogit.lib.git")
local pull_lib = require 'neogit.lib.git.pull' local pull_lib = require("neogit.lib.git.pull")
local a = require 'plenary.async' local a = require("plenary.async")
local M = {} local M = {}
@ -21,7 +21,8 @@ local function pull_from(popup, name, remote, branch)
end end
function M.create() function M.create()
local p = popup.builder() local p = popup
.builder()
:name("NeogitPullPopup") :name("NeogitPullPopup")
:switch("r", "rebase", "Rebase local commits", false) :switch("r", "rebase", "Rebase local commits", false)
:action("p", "Pull from pushremote", function(popup) :action("p", "Pull from pushremote", function(popup)

View file

@ -1,12 +1,12 @@
local M = {} local M = {}
local popup = require("neogit.lib.popup") local popup = require("neogit.lib.popup")
local input = require 'neogit.lib.input' local input = require("neogit.lib.input")
local push_lib = require 'neogit.lib.git.push' local push_lib = require("neogit.lib.git.push")
local status = require 'neogit.status' local status = require("neogit.status")
local notif = require("neogit.lib.notification") local notif = require("neogit.lib.notification")
local logger = require 'neogit.logger' local logger = require("neogit.logger")
local git = require("neogit.lib.git") local git = require("neogit.lib.git")
local a = require 'plenary.async' local a = require("plenary.async")
local function push_to(popup, name, remote, branch) local function push_to(popup, name, remote, branch)
logger.debug("Pushing to " .. name) logger.debug("Pushing to " .. name)
@ -26,7 +26,8 @@ local function push_to(popup, name, remote, branch)
end end
function M.create() function M.create()
local p = popup.builder() local p = popup
.builder()
:name("NeogitPushPopup") :name("NeogitPushPopup")
:switch("f", "force-with-lease", "Force with lease") :switch("f", "force-with-lease", "Force with lease")
:switch("F", "force", "Force") :switch("F", "force", "Force")
@ -44,10 +45,7 @@ function M.create()
return return
end end
push_to(popup, push_to(popup, upstream.remote .. " " .. upstream.branch, upstream.remote, upstream.branch)
upstream.remote.." "..upstream.branch,
upstream.remote,
upstream.branch)
end) end)
:action("e", "Push to elsewhere", function(popup) :action("e", "Push to elsewhere", function(popup)
local remote = input.get_user_input("remote: ") local remote = input.get_user_input("remote: ")
@ -57,7 +55,7 @@ function M.create()
:build() :build()
p:show() p:show()
return p return p
end end

View file

@ -5,14 +5,15 @@ local git = require("neogit.lib.git")
local popup = require("neogit.lib.popup") local popup = require("neogit.lib.popup")
function M.create() function M.create()
local p = popup.builder() local p = popup
.builder()
:name("NeogitRebasePopup") :name("NeogitRebasePopup")
:action("p", "Rebase onto master", function() :action("p", "Rebase onto master", function()
cli.rebase.args("master").call_sync() cli.rebase.args("master").call_sync()
end) end)
:action("e", "Rebase onto elsewhere", function() :action("e", "Rebase onto elsewhere", function()
local branch = git.branch.prompt_for_branch(git.branch.get_all_branches()) local branch = git.branch.prompt_for_branch(git.branch.get_all_branches())
cli.rebase.args(branch).call_sync() cli.rebase.args(branch).call_sync()
end) end)
:build() :build()

View file

@ -1,11 +1,12 @@
local status = require 'neogit.status' local status = require("neogit.status")
local stash_lib = require 'neogit.lib.git.stash' local stash_lib = require("neogit.lib.git.stash")
local popup = require('neogit.lib.popup') local popup = require("neogit.lib.popup")
local M = {} local M = {}
function M.create(stash) function M.create(stash)
local p = popup.builder() local p = popup
.builder()
:name("NeogitStashPopup") :name("NeogitStashPopup")
:switch("a", "all", "", false) :switch("a", "all", "", false)
:switch("u", "include-untracked", "", false) :switch("u", "include-untracked", "", false)

View file

@ -1,14 +1,14 @@
local a = require 'plenary.async' local a = require("plenary.async")
local function trim_newlines(s) local function trim_newlines(s)
return (string.gsub(s, "^(.-)\n*$", "%1")) return (string.gsub(s, "^(.-)\n*$", "%1"))
end end
local function spawn(options, cb) local function spawn(options, cb)
assert(options ~= nil, 'Options parameter must be given') assert(options ~= nil, "Options parameter must be given")
assert(options.cmd, 'A command needs to be given!') assert(options.cmd, "A command needs to be given!")
local return_code, output, errors = nil, '', '' local return_code, output, errors = nil, "", ""
local stdin, stdout, stderr = vim.loop.new_pipe(false), vim.loop.new_pipe(false), vim.loop.new_pipe(false) local stdin, stdout, stderr = vim.loop.new_pipe(false), vim.loop.new_pipe(false), vim.loop.new_pipe(false)
local process_closed, stdout_closed, stderr_closed = false, false, false local process_closed, stdout_closed, stderr_closed = false, false, false
local function raise_if_fully_closed() local function raise_if_fully_closed()
@ -18,29 +18,29 @@ local function spawn(options, cb)
end end
local params = { local params = {
stdio = {stdin, stdout, stderr}, stdio = { stdin, stdout, stderr },
} }
if options.cwd then if options.cwd then
params.cwd = options.cwd params.cwd = options.cwd
end end
if options.args then if options.args then
params.args = options.args params.args = options.args
end end
if options.env and #options.env > 0 then if options.env and #options.env > 0 then
params.env = {} params.env = {}
-- setting 'env' completely overrides the parent environment, so we need to -- setting 'env' completely overrides the parent environment, so we need to
-- append all variables that are necessary for git to work in addition to -- append all variables that are necessary for git to work in addition to
-- all variables from passed object. -- all variables from passed object.
table.insert(params.env, string.format('%s=%s', 'HOME', os.getenv('HOME'))) table.insert(params.env, string.format("%s=%s", "HOME", os.getenv("HOME")))
table.insert(params.env, string.format('%s=%s', 'GNUPGHOME', os.getenv('GNUPGHOME'))) table.insert(params.env, string.format("%s=%s", "GNUPGHOME", os.getenv("GNUPGHOME")))
for k, v in pairs(options.env) do for k, v in pairs(options.env) do
table.insert(params.env, string.format('%s=%s', k, v)) table.insert(params.env, string.format("%s=%s", k, v))
end end
end end
local handle, err local handle, err
handle, err = vim.loop.spawn(options.cmd, params, function (code, _) handle, err = vim.loop.spawn(options.cmd, params, function(code, _)
handle:close() handle:close()
--print('finished process', vim.inspect(params), vim.inspect({trim_newlines(output), errors})) --print('finished process', vim.inspect(params), vim.inspect({trim_newlines(output), errors}))
@ -70,7 +70,7 @@ local function spawn(options, cb)
output = output .. data output = output .. data
end) end)
vim.loop.read_start(stderr, function (err, data) vim.loop.read_start(stderr, function(err, data)
assert(not err, err) assert(not err, err)
if not data then if not data then
stderr:read_stop() stderr:read_stop()
@ -81,7 +81,7 @@ local function spawn(options, cb)
end end
--print('STDERR', err, data) --print('STDERR', err, data)
errors = errors .. (data or '') errors = errors .. (data or "")
end) end)
if options.input ~= nil then if options.input ~= nil then
@ -93,7 +93,7 @@ end
local M = { local M = {
spawn = a.wrap(spawn, 2), spawn = a.wrap(spawn, 2),
spawn_sync = spawn spawn_sync = spawn,
} }
return M return M

View file

@ -2,17 +2,17 @@ local Buffer = require("neogit.lib.buffer")
local GitCommandHistory = require("neogit.buffers.git_command_history") local GitCommandHistory = require("neogit.buffers.git_command_history")
local CommitView = require("neogit.buffers.commit_view") local CommitView = require("neogit.buffers.commit_view")
local git = require("neogit.lib.git") local git = require("neogit.lib.git")
local cli = require('neogit.lib.git.cli') local cli = require("neogit.lib.git.cli")
local notif = require("neogit.lib.notification") local notif = require("neogit.lib.notification")
local config = require("neogit.config") local config = require("neogit.config")
local a = require 'plenary.async' local a = require("plenary.async")
local logger = require 'neogit.logger' local logger = require("neogit.logger")
local repository = require 'neogit.lib.git.repository' local repository = require("neogit.lib.git.repository")
local Collection = require 'neogit.lib.collection' local Collection = require("neogit.lib.collection")
local F = require 'neogit.lib.functional' local F = require("neogit.lib.functional")
local LineBuffer = require 'neogit.lib.line_buffer' local LineBuffer = require("neogit.lib.line_buffer")
local fs = require 'neogit.lib.fs' local fs = require("neogit.lib.fs")
local input = require 'neogit.lib.input' local input = require("neogit.lib.input")
local M = {} local M = {}
@ -24,9 +24,9 @@ M.status_buffer = nil
M.commit_view = nil M.commit_view = nil
M.locations = {} M.locations = {}
local hunk_header_matcher = vim.regex('^@@.*@@') local hunk_header_matcher = vim.regex("^@@.*@@")
local diff_add_matcher = vim.regex('^+') local diff_add_matcher = vim.regex("^+")
local diff_delete_matcher = vim.regex('^-') local diff_delete_matcher = vim.regex("^-")
local function get_section_idx_for_line(linenr) local function get_section_idx_for_line(linenr)
for i, l in pairs(M.locations) do for i, l in pairs(M.locations) do
@ -77,53 +77,57 @@ local mode_to_text = {
C = "Copied", C = "Copied",
U = "Updated", U = "Updated",
UU = "Both Modified", UU = "Both Modified",
R = "Renamed" R = "Renamed",
} }
local function draw_sign_for_item(item, name) local function draw_sign_for_item(item, name)
if item.folded then if item.folded then
M.status_buffer:place_sign(item.first, "NeogitClosed:"..name, "fold_markers") M.status_buffer:place_sign(item.first, "NeogitClosed:" .. name, "fold_markers")
else else
M.status_buffer:place_sign(item.first, "NeogitOpen:"..name, "fold_markers") M.status_buffer:place_sign(item.first, "NeogitOpen:" .. name, "fold_markers")
end end
end end
local function draw_signs() local function draw_signs()
if config.values.disable_signs then return end if config.values.disable_signs then
return
end
for _, l in ipairs(M.locations) do for _, l in ipairs(M.locations) do
draw_sign_for_item(l, 'section') draw_sign_for_item(l, "section")
if not l.folded then if not l.folded then
Collection.new(l.files) Collection.new(l.files):filter(F.dot("hunks")):each(function(f)
:filter(F.dot('hunks')) draw_sign_for_item(f, "item")
:each(function (f) if not f.folded then
draw_sign_for_item(f, 'item') Collection.new(f.hunks):each(function(h)
if not f.folded then draw_sign_for_item(h, "hunk")
Collection.new(f.hunks):each(function (h) end)
draw_sign_for_item(h, 'hunk') end
end) end)
end
end)
end end
end end
end end
local function draw_buffer() local function draw_buffer()
M.status_buffer:clear_sign_group('hl') M.status_buffer:clear_sign_group("hl")
M.status_buffer:clear_sign_group('fold_markers') M.status_buffer:clear_sign_group("fold_markers")
local output = LineBuffer.new() local output = LineBuffer.new()
if not config.values.disable_hint then if not config.values.disable_hint then
output:append("Hint: [<tab>] toggle diff | [s]tage | [u]nstage | [x] discard | [c]ommit | [?] more help") output:append("Hint: [<tab>] toggle diff | [s]tage | [u]nstage | [x] discard | [c]ommit | [?] more help")
output:append("") output:append("")
end end
output:append(string.format("Head: %s %s", M.repo.head.branch, M.repo.head.commit_message or '(no commits)')) output:append(
string.format("Head: %s %s", M.repo.head.branch, M.repo.head.commit_message or "(no commits)")
)
if M.repo.upstream.branch then if M.repo.upstream.branch then
output:append(string.format("Push: %s %s", M.repo.upstream.branch, M.repo.upstream.commit_message or '(no commits)')) output:append(
string.format("Push: %s %s", M.repo.upstream.branch, M.repo.upstream.commit_message or "(no commits)")
)
end end
output:append('') output:append("")
local new_locations = {} local new_locations = {}
local locations_lookup = Collection.new(M.locations):key_by('name') local locations_lookup = Collection.new(M.locations):key_by("name")
local function render_section(header, key) local function render_section(header, key)
local section_config = config.values.sections[key] local section_config = config.values.sections[key]
@ -131,40 +135,44 @@ local function draw_buffer()
return return
end end
local data = M.repo[key] local data = M.repo[key]
if #data.items == 0 then return end if #data.items == 0 then
output:append(string.format('%s (%d)', header, #data.items)) return
end
output:append(string.format("%s (%d)", header, #data.items))
local location = locations_lookup[key] or { local location = locations_lookup[key]
name = key, or {
folded = section_config.folded, name = key,
files = {} folded = section_config.folded,
} files = {},
}
location.first = #output location.first = #output
if not location.folded then if not location.folded then
local files_lookup = Collection.new(location.files):key_by('name') local files_lookup = Collection.new(location.files):key_by("name")
location.files = {} location.files = {}
for _, f in ipairs(data.items) do for _, f in ipairs(data.items) do
if f.mode and f.original_name then if f.mode and f.original_name then
output:append(string.format('%s %s -> %s', mode_to_text[f.mode], f.original_name, f.name)) output:append(string.format("%s %s -> %s", mode_to_text[f.mode], f.original_name, f.name))
elseif f.mode then output:append(string.format('%s %s', mode_to_text[f.mode], f.name)) elseif f.mode then
else output:append(string.format("%s %s", mode_to_text[f.mode], f.name))
output:append(f.name) else
output:append(f.name)
end end
local file = files_lookup[f.name] or { folded = true } local file = files_lookup[f.name] or { folded = true }
file.first = #output file.first = #output
if f.diff and not file.folded then if f.diff and not file.folded then
local hunks_lookup = Collection.new(file.hunks or {}):key_by('hash') local hunks_lookup = Collection.new(file.hunks or {}):key_by("hash")
local hunks = {} local hunks = {}
for _, h in ipairs(f.diff.hunks) do for _, h in ipairs(f.diff.hunks) do
local current_hunk = hunks_lookup[h.hash] or { folded = false } local current_hunk = hunks_lookup[h.hash] or { folded = false }
output:append(f.diff.lines[h.diff_from]) output:append(f.diff.lines[h.diff_from])
M.status_buffer:place_sign(#output, 'NeogitHunkHeader', 'hl') M.status_buffer:place_sign(#output, "NeogitHunkHeader", "hl")
current_hunk.first = #output current_hunk.first = #output
if not current_hunk.folded then if not current_hunk.folded then
@ -172,9 +180,9 @@ local function draw_buffer()
local l = f.diff.lines[i] local l = f.diff.lines[i]
output:append(l) output:append(l)
if diff_add_matcher:match_str(l) then if diff_add_matcher:match_str(l) then
M.status_buffer:place_sign(#output, 'NeogitDiffAdd', 'hl') M.status_buffer:place_sign(#output, "NeogitDiffAdd", "hl")
elseif diff_delete_matcher:match_str(l) then elseif diff_delete_matcher:match_str(l) then
M.status_buffer:place_sign(#output, 'NeogitDiffDelete', 'hl') M.status_buffer:place_sign(#output, "NeogitDiffDelete", "hl")
end end
end end
end end
@ -193,17 +201,17 @@ local function draw_buffer()
end end
location.last = #output location.last = #output
output:append('') output:append("")
table.insert(new_locations, location) table.insert(new_locations, location)
end end
render_section('Untracked files', 'untracked') render_section("Untracked files", "untracked")
render_section('Unstaged changes', 'unstaged') render_section("Unstaged changes", "unstaged")
render_section('Staged changes', 'staged') render_section("Staged changes", "staged")
render_section('Stashes', 'stashes') render_section("Stashes", "stashes")
render_section('Unpulled changes', 'unpulled') render_section("Unpulled changes", "unpulled")
render_section('Unmerged changes', 'unmerged') render_section("Unmerged changes", "unmerged")
render_section('Recent commits', 'recent') render_section("Recent commits", "recent")
M.status_buffer:replace_content_with(output) M.status_buffer:replace_content_with(output)
M.locations = new_locations M.locations = new_locations
@ -216,26 +224,26 @@ end
-- the relative offset of the found item and the string is it's identifier. -- the relative offset of the found item and the string is it's identifier.
-- The remaining 2 numbers are the first and last line of the found section. -- The remaining 2 numbers are the first and last line of the found section.
local function save_cursor_location() local function save_cursor_location()
local line = vim.fn.line('.') local line = vim.fn.line(".")
local section_loc, file_loc, hunk_loc, first, last local section_loc, file_loc, hunk_loc, first, last
for li, loc in ipairs(M.locations) do for li, loc in ipairs(M.locations) do
if line == loc.first then if line == loc.first then
section_loc = {li, loc.name} section_loc = { li, loc.name }
first, last = loc.first, loc.last first, last = loc.first, loc.last
break break
elseif line >= loc.first and line <= loc.last then elseif line >= loc.first and line <= loc.last then
section_loc = {li, loc.name} section_loc = { li, loc.name }
for fi, file in ipairs(loc.files) do for fi, file in ipairs(loc.files) do
if line == file.first then if line == file.first then
file_loc = {fi, file.name} file_loc = { fi, file.name }
first, last = file.first, file.last first, last = file.first, file.last
break break
elseif line >= file.first and line <= file.last then elseif line >= file.first and line <= file.last then
file_loc = {fi, file.name} file_loc = { fi, file.name }
for hi, hunk in ipairs(file.hunks) do for hi, hunk in ipairs(file.hunks) do
if line <= hunk.last then if line <= hunk.last then
hunk_loc = {hi, hunk.hash} hunk_loc = { hi, hunk.hash }
first, last = hunk.first, hunk.last first, last = hunk.first, hunk.last
break break
end end
@ -251,36 +259,40 @@ local function save_cursor_location()
end end
local function restore_cursor_location(section_loc, file_loc, hunk_loc) local function restore_cursor_location(section_loc, file_loc, hunk_loc)
if #M.locations == 0 then return vim.fn.setpos('.', {0, 1, 0, 0}) end if #M.locations == 0 then
if not section_loc then return vim.fn.setpos(".", { 0, 1, 0, 0 })
section_loc = {1, ''} end
if not section_loc then
section_loc = { 1, "" }
end end
local section = Collection.new(M.locations):find(function (s) local section = Collection.new(M.locations):find(function(s)
return s.name == section_loc[2] return s.name == section_loc[2]
end) end)
if not section then if not section then
file_loc, hunk_loc = nil, nil file_loc, hunk_loc = nil, nil
section = M.locations[section_loc[1]] or M.locations[#M.locations] section = M.locations[section_loc[1]] or M.locations[#M.locations]
end end
if not file_loc or not section.files or #section.files == 0 then if not file_loc or not section.files or #section.files == 0 then
return vim.fn.setpos('.', {0, section.first, 0, 0}) return vim.fn.setpos(".", { 0, section.first, 0, 0 })
end end
local file = Collection.new(section.files):find(function (f) local file = Collection.new(section.files):find(function(f)
return f.name == file_loc[2] return f.name == file_loc[2]
end) end)
if not file then if not file then
hunk_loc = nil hunk_loc = nil
file = section.files[file_loc[1]] or section.files[#section.files] file = section.files[file_loc[1]] or section.files[#section.files]
end end
if not hunk_loc or not file.hunks or #file.hunks == 0 then return vim.fn.setpos('.', {0, file.first, 0, 0}) end if not hunk_loc or not file.hunks or #file.hunks == 0 then
return vim.fn.setpos(".", { 0, file.first, 0, 0 })
end
local hunk = Collection.new(file.hunks):find(function (h) return h.hash == hunk_loc[2] end) local hunk = Collection.new(file.hunks):find(function(h)
or file.hunks[hunk_loc[1]] return h.hash == hunk_loc[2]
or file.hunks[#file.hunks] end) or file.hunks[hunk_loc[1]] or file.hunks[#file.hunks]
vim.fn.setpos('.', {0, hunk.first, 0, 0}) vim.fn.setpos(".", { 0, hunk.first, 0, 0 })
end end
local function refresh_status() local function refresh_status()
@ -290,37 +302,37 @@ local function refresh_status()
M.status_buffer:unlock() M.status_buffer:unlock()
logger.debug "[STATUS BUFFER]: Redrawing" logger.debug("[STATUS BUFFER]: Redrawing")
draw_buffer() draw_buffer()
draw_signs() draw_signs()
logger.debug "[STATUS BUFFER]: Finished Redrawing" logger.debug("[STATUS BUFFER]: Finished Redrawing")
M.status_buffer:lock() M.status_buffer:lock()
vim.cmd('redraw') vim.cmd("redraw")
end end
local refresh_lock = a.control.Semaphore.new(1) local refresh_lock = a.control.Semaphore.new(1)
local function refresh (which) local function refresh(which)
which = which or true which = which or true
logger.debug "[STATUS BUFFER]: Starting refresh" logger.debug("[STATUS BUFFER]: Starting refresh")
if refresh_lock.permits == 0 then if refresh_lock.permits == 0 then
logger.debug "[STATUS BUFFER]: Refresh lock not available. Aborting refresh" logger.debug("[STATUS BUFFER]: Refresh lock not available. Aborting refresh")
a.util.scheduler() a.util.scheduler()
refresh_status() refresh_status()
return return
end end
local permit = refresh_lock:acquire() local permit = refresh_lock:acquire()
logger.debug "[STATUS BUFFER]: Acquired refresh lock" logger.debug("[STATUS BUFFER]: Acquired refresh lock")
a.util.scheduler() a.util.scheduler()
local s, f, h = save_cursor_location() local s, f, h = save_cursor_location()
if cli.git_root() ~= '' then if cli.git_root() ~= "" then
if which == true or which.status then if which == true or which.status then
M.repo:update_status() M.repo:update_status()
a.util.scheduler() a.util.scheduler()
@ -329,27 +341,27 @@ local function refresh (which)
local refreshes = {} local refreshes = {}
if which == true or which.branch_information then if which == true or which.branch_information then
table.insert(refreshes, function() table.insert(refreshes, function()
logger.debug("[STATUS BUFFER]: Refreshing branch information") logger.debug("[STATUS BUFFER]: Refreshing branch information")
M.repo:update_branch_information() M.repo:update_branch_information()
end) end)
end end
if which == true or which.stashes then if which == true or which.stashes then
table.insert(refreshes, function() table.insert(refreshes, function()
logger.debug("[STATUS BUFFER]: Refreshing stash") logger.debug("[STATUS BUFFER]: Refreshing stash")
M.repo:update_stashes() M.repo:update_stashes()
end) end)
end end
if which == true or which.unpulled then if which == true or which.unpulled then
table.insert(refreshes, function() table.insert(refreshes, function()
logger.debug("[STATUS BUFFER]: Refreshing unpulled commits") logger.debug("[STATUS BUFFER]: Refreshing unpulled commits")
M.repo:update_unpulled() M.repo:update_unpulled()
end) end)
end end
if which == true or which.unmerged then if which == true or which.unmerged then
table.insert(refreshes, function() table.insert(refreshes, function()
logger.debug("[STATUS BUFFER]: Refreshing unpushed commits") logger.debug("[STATUS BUFFER]: Refreshing unpushed commits")
M.repo:update_unmerged() M.repo:update_unmerged()
end) end)
end end
if which == true or which.recent then if which == true or which.recent then
@ -359,41 +371,43 @@ local function refresh (which)
end) end)
end end
if which == true or which.diffs then if which == true or which.diffs then
local filter = (type(which) == "table" and type(which.diffs) == "table") local filter = (type(which) == "table" and type(which.diffs) == "table") and which.diffs or nil
and which.diffs
or nil
table.insert(refreshes, function() table.insert(refreshes, function()
logger.debug("[STATUS BUFFER]: Refreshing diffs") logger.debug("[STATUS BUFFER]: Refreshing diffs")
M.repo:load_diffs(filter) M.repo:load_diffs(filter)
end) end)
end end
logger.debug(string.format("[STATUS BUFFER]: Running %d refresh(es)", #refreshes)) logger.debug(string.format("[STATUS BUFFER]: Running %d refresh(es)", #refreshes))
a.util.join(refreshes) a.util.join(refreshes)
logger.debug "[STATUS BUFFER]: Refreshes completed" logger.debug("[STATUS BUFFER]: Refreshes completed")
a.util.scheduler() a.util.scheduler()
refresh_status() refresh_status()
vim.cmd [[do <nomodeline> User NeogitStatusRefreshed]] vim.cmd([[do <nomodeline> User NeogitStatusRefreshed]])
end end
a.util.scheduler() a.util.scheduler()
if vim.fn.bufname() == 'NeogitStatus' then if vim.fn.bufname() == "NeogitStatus" then
restore_cursor_location(s, f, h) restore_cursor_location(s, f, h)
end end
logger.debug "[STATUS BUFFER]: Finished refresh" logger.debug("[STATUS BUFFER]: Finished refresh")
logger.debug "[STATUS BUFFER]: Refresh lock is now free" logger.debug("[STATUS BUFFER]: Refresh lock is now free")
permit:forget() permit:forget()
end end
local dispatch_refresh = a.void(refresh) local dispatch_refresh = a.void(refresh)
local refresh_manually = a.void(function (fname) local refresh_manually = a.void(function(fname)
if not fname or fname == "" then return end if not fname or fname == "" then
return
end
local path = fs.relpath_from_repository(fname) local path = fs.relpath_from_repository(fname)
if not path then return end if not path then
refresh({ status = true, diffs = { "*:" .. path } }) return
end
refresh { status = true, diffs = { "*:" .. path } }
end) end)
--- Compatibility endpoint to refresh data from an autocommand. --- Compatibility endpoint to refresh data from an autocommand.
@ -401,13 +415,15 @@ end)
-- resolving the file name to the path relative to the repository root and -- resolving the file name to the path relative to the repository root and
-- refresh that file's cache data. -- refresh that file's cache data.
local function refresh_viml_compat(fname) local function refresh_viml_compat(fname)
if not config.values.auto_refresh then return end if not config.values.auto_refresh then
return
end
refresh_manually(fname) refresh_manually(fname)
end end
local function current_line_is_hunk() local function current_line_is_hunk()
local _,_,h = save_cursor_location() local _, _, h = save_cursor_location()
return h ~= nil return h ~= nil
end end
@ -444,19 +460,21 @@ local function toggle()
if on_hunk then if on_hunk then
local hunk = get_current_hunk_of_item(item) local hunk = get_current_hunk_of_item(item)
hunk.folded = not hunk.folded hunk.folded = not hunk.folded
elseif item then elseif item then
item.folded = not item.folded item.folded = not item.folded
else else
section.folded = not section.folded section.folded = not section.folded
end end
refresh_status() refresh_status()
end end
local reset = function () local reset = function()
M.repo = repository.create() M.repo = repository.create()
M.locations = {} M.locations = {}
if not config.values.auto_refresh then return end if not config.values.auto_refresh then
return
end
refresh(true) refresh(true)
end end
local dispatch_reset = a.void(reset) local dispatch_reset = a.void(reset)
@ -469,7 +487,7 @@ local function close(skip_close)
M.status_buffer = nil M.status_buffer = nil
vim.o.autochdir = M.prev_autochdir vim.o.autochdir = M.prev_autochdir
if M.cwd_changed then if M.cwd_changed then
vim.cmd "cd -" vim.cmd("cd -")
end end
end end
@ -498,19 +516,18 @@ local function generate_patch_from_selection(item, hunk, from, to, reverse)
len_offset = len_offset + (operand == "+" and 1 or -1) len_offset = len_offset + (operand == "+" and 1 or -1)
table.insert(diff_content, v) table.insert(diff_content, v)
else else
-- If we want to apply the patch normally, we need to include every `-` line we skip as a normal line, -- If we want to apply the patch normally, we need to include every `-` line we skip as a normal line,
-- since we want to keep that line. -- since we want to keep that line.
if not reverse then if not reverse then
if operand == "-" then if operand == "-" then
table.insert(diff_content, " "..line) table.insert(diff_content, " " .. line)
end end
-- If we want to apply the patch in reverse, we need to include every `+` line we skip as a normal line, since -- If we want to apply the patch in reverse, we need to include every `+` line we skip as a normal line, since
-- it's unchanged as far as the diff is concerned and should not be reversed. -- it's unchanged as far as the diff is concerned and should not be reversed.
-- We also need to adapt the original line offset based on if we skip or not -- We also need to adapt the original line offset based on if we skip or not
elseif reverse then elseif reverse then
if operand == "+" then if operand == "+" then
table.insert(diff_content, " "..line) table.insert(diff_content, " " .. line)
end end
len_start = len_start + (operand == "-" and -1 or 1) len_start = len_start + (operand == "-" and -1 or 1)
end end
@ -520,13 +537,8 @@ local function generate_patch_from_selection(item, hunk, from, to, reverse)
end end
end end
local diff_header = string.format( local diff_header =
"@@ -%d,%d +%d,%d @@", string.format("@@ -%d,%d +%d,%d @@", hunk.index_from, len_start, hunk.index_from, len_start + len_offset)
hunk.index_from,
len_start,
hunk.index_from,
len_start + len_offset
)
table.insert(diff_content, 1, diff_header) table.insert(diff_content, 1, diff_header)
table.insert(diff_content, 1, string.format("+++ b/%s", item.name)) table.insert(diff_content, 1, string.format("+++ b/%s", item.name))
@ -535,7 +547,6 @@ local function generate_patch_from_selection(item, hunk, from, to, reverse)
return table.concat(diff_content, "\n") return table.concat(diff_content, "\n")
end end
--- Validates the current selection and acts accordingly --- Validates the current selection and acts accordingly
--@return nil --@return nil
--@return number, number --@return number, number
@ -546,11 +557,7 @@ local function get_selection()
local first_section, first_item = get_section_item_for_line(first_line) local first_section, first_item = get_section_item_for_line(first_line)
local last_section, last_item = get_section_item_for_line(last_line) local last_section, last_item = get_section_item_for_line(last_line)
if not first_section or if not first_section or not first_item or not last_section or not last_item then
not first_item or
not last_section or
not last_item
then
return nil return nil
end end
@ -561,9 +568,10 @@ local function get_selection()
return nil return nil
end end
if first_section.name ~= last_section.name or if
first_item.name ~= last_item.name or first_section.name ~= last_section.name
first_hunk.first ~= last_hunk.first or first_item.name ~= last_item.name
or first_hunk.first ~= last_hunk.first
then then
return nil return nil
end end
@ -572,13 +580,15 @@ local function get_selection()
last_line = last_line - last_item.first last_line = last_line - last_item.first
-- both hunks are the same anyway so only have to check one -- both hunks are the same anyway so only have to check one
if first_hunk.diff_from == first_line or if first_hunk.diff_from == first_line or first_hunk.diff_from == last_line then
first_hunk.diff_from == last_line
then
return nil return nil
end end
return first_section, first_item, first_hunk, first_line - first_hunk.diff_from, last_line - first_hunk.diff_from return first_section,
first_item,
first_hunk,
first_line - first_hunk.diff_from,
last_line - first_hunk.diff_from
end end
local stage_selection = function() local stage_selection = function()
@ -602,9 +612,11 @@ local stage = function()
local section, item = get_current_section_item() local section, item = get_current_section_item()
local mode = vim.api.nvim_get_mode() local mode = vim.api.nvim_get_mode()
if section == nil if
section == nil
or (section.name ~= "unstaged" and section.name ~= "untracked" and section.name ~= "unmerged") or (section.name ~= "unstaged" and section.name ~= "untracked" and section.name ~= "unmerged")
or (mode.mode == "V" and item == nil) then or (mode.mode == "V" and item == nil)
then
return return
end end
@ -616,9 +628,9 @@ local stage = function()
if section.name == "unstaged" then if section.name == "unstaged" then
git.status.stage_modified() git.status.stage_modified()
elseif section.name == "untracked" then elseif section.name == "untracked" then
local add = git.cli.add; local add = git.cli.add
for i,_ in ipairs(section.files) do for i, _ in ipairs(section.files) do
local item = section.files[i]; local item = section.files[i]
add.files(item.name) add.files(item.name)
end end
add.call() add.call()
@ -628,16 +640,16 @@ local stage = function()
return return
else else
if on_hunk and section.name ~= "untracked" then if on_hunk and section.name ~= "untracked" then
local hunk = get_current_hunk_of_item(item) local hunk = get_current_hunk_of_item(item)
local patch = generate_patch_from_selection(item, hunk) local patch = generate_patch_from_selection(item, hunk)
cli.apply.cached.with_patch(patch).call() cli.apply.cached.with_patch(patch).call()
else else
git.status.stage(item.name) git.status.stage(item.name)
end end
end end
end end
refresh({status = true, diffs = {"*:"..item.name}}) refresh { status = true, diffs = { "*:" .. item.name } }
M.current_operation = nil M.current_operation = nil
end end
@ -671,7 +683,7 @@ local unstage = function()
end end
end end
refresh({status = true, diffs = {"*:"..item.name}}) refresh { status = true, diffs = { "*:" .. item.name } }
M.current_operation = nil M.current_operation = nil
end end
@ -683,10 +695,12 @@ local discard = function()
end end
M.current_operation = "discard" M.current_operation = "discard"
if not input.get_confirmation("Discard '"..item.name.."' ?", { if
values = { "&Yes", "&No" }, not input.get_confirmation("Discard '" .. item.name .. "' ?", {
default = 2 values = { "&Yes", "&No" },
}) then default = 2,
})
then
return return
end end
@ -703,16 +717,16 @@ local discard = function()
elseif section.name == "untracked" then elseif section.name == "untracked" then
local repo_root = cli.git_root() local repo_root = cli.git_root()
a.util.scheduler() a.util.scheduler()
vim.fn.delete(repo_root .. '/' .. item.name) vim.fn.delete(repo_root .. "/" .. item.name)
else else
local on_hunk = current_line_is_hunk() local on_hunk = current_line_is_hunk()
if on_hunk then if on_hunk then
local hunk, lines = get_current_hunk_of_item(item) local hunk, lines = get_current_hunk_of_item(item)
lines[1] = string.format('@@ -%d,%d +%d,%d @@', hunk.index_from, hunk.index_len, hunk.index_from, hunk.disk_len) lines[1] =
string.format("@@ -%d,%d +%d,%d @@", hunk.index_from, hunk.index_len, hunk.index_from, hunk.disk_len)
local diff = table.concat(lines, "\n") local diff = table.concat(lines, "\n")
diff = table.concat({'--- a/'..item.name, '+++ b/'..item.name, diff, ""}, "\n") diff = table.concat({ "--- a/" .. item.name, "+++ b/" .. item.name, diff, "" }, "\n")
if section.name == "staged" then if section.name == "staged" then
cli.apply.reverse.index.with_patch(diff).call() cli.apply.reverse.index.with_patch(diff).call()
else else
@ -724,23 +738,22 @@ local discard = function()
cli.reset.files(item.name).call() cli.reset.files(item.name).call()
cli.checkout.files(item.name).call() cli.checkout.files(item.name).call()
end end
end end
refresh(true) refresh(true)
M.current_operation = nil M.current_operation = nil
a.util.scheduler() a.util.scheduler()
vim.cmd "checktime" vim.cmd("checktime")
end end
local set_folds = function(to) local set_folds = function(to)
Collection.new(M.locations):each(function (l) Collection.new(M.locations):each(function(l)
l.folded = to[1] l.folded = to[1]
Collection.new(l.files):each(function (f) Collection.new(l.files):each(function(f)
f.folded = to[2] f.folded = to[2]
if f.hunks then if f.hunks then
Collection.new(f.hunks):each(function (h) Collection.new(f.hunks):each(function(h)
h.folded = to[3] h.folded = to[3]
end) end)
end end
@ -749,41 +762,40 @@ local set_folds = function(to)
refresh(true) refresh(true)
end end
--- These needs to be a function to avoid a circular dependency --- These needs to be a function to avoid a circular dependency
-- between this module and the popup modules -- between this module and the popup modules
local cmd_func_map = function () local cmd_func_map = function()
return { return {
["Close"] = function() ["Close"] = function()
M.status_buffer:close() M.status_buffer:close()
end, end,
["Depth1"] = a.void(function() ["Depth1"] = a.void(function()
set_folds({ true, true, false }) set_folds { true, true, false }
end), end),
["Depth2"] = a.void(function() ["Depth2"] = a.void(function()
set_folds({ false, true, false }) set_folds { false, true, false }
end), end),
["Depth3"] = a.void(function() ["Depth3"] = a.void(function()
set_folds({ false, false, true }) set_folds { false, false, true }
end), end),
["Depth4"] = a.void(function() ["Depth4"] = a.void(function()
set_folds({ false, false, false }) set_folds { false, false, false }
end), end),
["Toggle"] = toggle, ["Toggle"] = toggle,
["Discard"] = { "nv", a.void(discard), true }, ["Discard"] = { "nv", a.void(discard), true },
["Stage"] = { "nv", a.void(stage), true }, ["Stage"] = { "nv", a.void(stage), true },
["StageUnstaged"] = a.void(function () ["StageUnstaged"] = a.void(function()
git.status.stage_modified() git.status.stage_modified()
refresh({status = true, diffs = true}) refresh { status = true, diffs = true }
end), end),
["StageAll"] = a.void(function() ["StageAll"] = a.void(function()
git.status.stage_all() git.status.stage_all()
refresh({status = true, diffs = true}) refresh { status = true, diffs = true }
end), end),
["Unstage"] = { "nv", a.void(unstage), true }, ["Unstage"] = { "nv", a.void(unstage), true },
["UnstageStaged"] = a.void(function () ["UnstageStaged"] = a.void(function()
git.status.unstage_all() git.status.unstage_all()
refresh({status = true, diffs = true}) refresh { status = true, diffs = true }
end), end),
["CommandHistory"] = function() ["CommandHistory"] = function()
GitCommandHistory:new():show() GitCommandHistory:new():show()
@ -813,7 +825,7 @@ local cmd_func_map = function ()
notif.delete_all() notif.delete_all()
M.status_buffer:close() M.status_buffer:close()
local relpath = vim.fn.fnamemodify(repo_root .. '/' .. path, ':.') local relpath = vim.fn.fnamemodify(repo_root .. "/" .. path, ":.")
if not vim.o.hidden and vim.bo.buftype == "" and not vim.bo.readonly and vim.fn.bufname() ~= "" then if not vim.o.hidden and vim.bo.buftype == "" and not vim.bo.readonly and vim.fn.bufname() ~= "" then
vim.cmd("update") vim.cmd("update")
@ -824,7 +836,6 @@ local cmd_func_map = function ()
if hunk then if hunk then
vim.cmd(tostring(hunk.disk_from)) vim.cmd(tostring(hunk.disk_from))
end end
elseif vim.tbl_contains({ "unmerged", "unpulled", "recent", "stashes" }, section.name) then elseif vim.tbl_contains({ "unmerged", "unpulled", "recent", "stashes" }, section.name) then
if M.commit_view and M.commit_view.is_open then if M.commit_view and M.commit_view.is_open then
M.commit_view:close() M.commit_view:close()
@ -836,24 +847,26 @@ local cmd_func_map = function ()
end end
end end
end), end),
["RefreshBuffer"] = function() dispatch_refresh(true) end, ["RefreshBuffer"] = function()
["HelpPopup"] = function () dispatch_refresh(true)
end,
["HelpPopup"] = function()
local line = M.status_buffer:get_current_line() local line = M.status_buffer:get_current_line()
require("neogit.popups.help").create { require("neogit.popups.help").create {
get_stash = function() get_stash = function()
return { return {
name = line[1]:match('^(stash@{%d+})') name = line[1]:match("^(stash@{%d+})"),
} }
end, end,
use_magit_keybindings = config.values.use_magit_keybindings use_magit_keybindings = config.values.use_magit_keybindings,
} }
end, end,
["DiffAtFile"] = function() ["DiffAtFile"] = function()
if not config.ensure_integration 'diffview' then if not config.ensure_integration("diffview") then
return return
end end
local dv = require 'neogit.integrations.diffview' local dv = require("neogit.integrations.diffview")
local section, item = get_current_section_item() local section, item = get_current_section_item()
if section and item then if section and item then
@ -866,11 +879,11 @@ local cmd_func_map = function ()
["PushPopup"] = require("neogit.popups.push").create, ["PushPopup"] = require("neogit.popups.push").create,
["CommitPopup"] = require("neogit.popups.commit").create, ["CommitPopup"] = require("neogit.popups.commit").create,
["LogPopup"] = require("neogit.popups.log").create, ["LogPopup"] = require("neogit.popups.log").create,
["StashPopup"] = function () ["StashPopup"] = function()
local line = M.status_buffer:get_current_line() local line = M.status_buffer:get_current_line()
require("neogit.popups.stash").create { require("neogit.popups.stash").create {
name = line[1]:match('^(stash@{%d+})') name = line[1]:match("^(stash@{%d+})"),
} }
end, end,
["BranchPopup"] = require("neogit.popups.branch").create, ["BranchPopup"] = require("neogit.popups.branch").create,
@ -881,19 +894,19 @@ local function create(kind, cwd)
kind = kind or config.values.kind kind = kind or config.values.kind
if M.status_buffer then if M.status_buffer then
logger.debug "Status buffer already exists. Focusing the existing one" logger.debug("Status buffer already exists. Focusing the existing one")
M.status_buffer:focus() M.status_buffer:focus()
return return
end end
logger.debug "[STATUS BUFFER]: Creating..." logger.debug("[STATUS BUFFER]: Creating...")
Buffer.create { Buffer.create {
name = "NeogitStatus", name = "NeogitStatus",
filetype = "NeogitStatus", filetype = "NeogitStatus",
kind = kind, kind = kind,
initialize = function(buffer) initialize = function(buffer)
logger.debug "[STATUS BUFFER]: Initializing..." logger.debug("[STATUS BUFFER]: Initializing...")
M.status_buffer = buffer M.status_buffer = buffer
@ -917,45 +930,47 @@ local function create(kind, cwd)
elseif type(val) == "function" then elseif type(val) == "function" then
mappings[key] = val mappings[key] = val
elseif type(val) == "string" then elseif type(val) == "string" then
mappings[key] = function() mappings[key] = function()
vim.cmd(val) vim.cmd(val)
end end
end end
end end
end end
logger.debug "[STATUS BUFFER]: Dispatching initial render" logger.debug("[STATUS BUFFER]: Dispatching initial render")
dispatch_refresh(true) dispatch_refresh(true)
end end,
} }
end end
local highlight_group = vim.api.nvim_create_namespace("section-highlight") local highlight_group = vim.api.nvim_create_namespace("section-highlight")
local function update_highlight() local function update_highlight()
if not M.status_buffer then if not M.status_buffer then
return
end
if config.values.disable_context_highlighting then
return return
end end
if config.values.disable_context_highlighting then return end
vim.api.nvim_buf_clear_namespace(0, highlight_group, 0, -1) vim.api.nvim_buf_clear_namespace(0, highlight_group, 0, -1)
M.status_buffer:clear_sign_group('ctx') M.status_buffer:clear_sign_group("ctx")
local _,_,_, first, last = save_cursor_location() local _, _, _, first, last = save_cursor_location()
if first == nil or last == nil then if first == nil or last == nil then
return return
end end
for i=first,last do for i = first, last do
local line = vim.fn.getline(i) local line = vim.fn.getline(i)
if hunk_header_matcher:match_str(line) then if hunk_header_matcher:match_str(line) then
M.status_buffer:place_sign(i, 'NeogitHunkHeaderHighlight', 'ctx') M.status_buffer:place_sign(i, "NeogitHunkHeaderHighlight", "ctx")
elseif diff_add_matcher:match_str(line) then elseif diff_add_matcher:match_str(line) then
M.status_buffer:place_sign(i, 'NeogitDiffAddHighlight', 'ctx') M.status_buffer:place_sign(i, "NeogitDiffAddHighlight", "ctx")
elseif diff_delete_matcher:match_str(line) then elseif diff_delete_matcher:match_str(line) then
M.status_buffer:place_sign(i, 'NeogitDiffDeleteHighlight', 'ctx') M.status_buffer:place_sign(i, "NeogitDiffDeleteHighlight", "ctx")
else else
M.status_buffer:place_sign(i, 'NeogitDiffContextHighlight', 'ctx') M.status_buffer:place_sign(i, "NeogitDiffContextHighlight", "ctx")
end end
end end
end end
@ -985,8 +1000,8 @@ function M.get_status()
end end
function M.wait_on_current_operation(ms) function M.wait_on_current_operation(ms)
vim.wait(ms or 1000, function() vim.wait(ms or 1000, function()
return not M.current_operation return not M.current_operation
end) end)
end end

View file

@ -1,37 +1,45 @@
require 'plenary.async'.tests.add_to_env() require("plenary.async").tests.add_to_env()
local eq = assert.are.same local eq = assert.are.same
local operations = require'neogit.operations' local operations = require("neogit.operations")
local harness = require'tests.git_harness' local harness = require("tests.git_harness")
local in_prepared_repo = harness.in_prepared_repo local in_prepared_repo = harness.in_prepared_repo
local get_current_branch = harness.get_current_branch local get_current_branch = harness.get_current_branch
--local status = require'neogit.status' --local status = require'neogit.status'
local input = require'tests.mocks.input' local input = require("tests.mocks.input")
local function act(normal_cmd) local function act(normal_cmd)
vim.cmd('normal '..normal_cmd) vim.cmd("normal " .. normal_cmd)
end end
describe('branch popup', function () describe("branch popup", function()
it('can switch to another branch in the repository', in_prepared_repo(function () it(
input.value = 'second-branch' "can switch to another branch in the repository",
act('bb') in_prepared_repo(function()
operations.wait('checkout_branch') input.value = "second-branch"
eq('second-branch', get_current_branch()) act("bb")
end)) operations.wait("checkout_branch")
eq("second-branch", get_current_branch())
end)
)
it('can switch to another local branch in the repository', in_prepared_repo(function () it(
input.value = 'second-branch' "can switch to another local branch in the repository",
act('bl') in_prepared_repo(function()
operations.wait('checkout_local-branch') input.value = "second-branch"
eq('second-branch', get_current_branch()) act("bl")
end)) operations.wait("checkout_local-branch")
eq("second-branch", get_current_branch())
end)
)
it('can create a new branch', in_prepared_repo(function () it(
input.value = 'branch-from-test' "can create a new branch",
act('bc') in_prepared_repo(function()
operations.wait('checkout_create-branch') input.value = "branch-from-test"
eq('branch-from-test', get_current_branch()) act("bc")
end)) operations.wait("checkout_create-branch")
eq("branch-from-test", get_current_branch())
end)
)
end) end)

View file

@ -1,37 +1,37 @@
local status = require'neogit.status' local status = require("neogit.status")
local a = require 'plenary.async' local a = require("plenary.async")
local M = {} local M = {}
local project_dir = vim.api.nvim_exec('pwd', true) local project_dir = vim.api.nvim_exec("pwd", true)
-- very naiive implementation, we only use this to generate unique folder names -- very naiive implementation, we only use this to generate unique folder names
local function random_string(length) local function random_string(length)
math.randomseed(os.clock()^5) math.randomseed(os.clock() ^ 5)
local res = "" local res = ""
for _ = 1, length do for _ = 1, length do
local r = math.random(97, 122) local r = math.random(97, 122)
res = res .. string.char(r) res = res .. string.char(r)
end end
return res return res
end end
local function prepare_repository(dir) local function prepare_repository(dir)
vim.cmd('silent !cp -r tests/.repo/ /tmp/'..dir) vim.cmd("silent !cp -r tests/.repo/ /tmp/" .. dir)
vim.cmd('cd /tmp/'..dir) vim.cmd("cd /tmp/" .. dir)
vim.cmd('silent !cp -r .git.orig/ .git/') vim.cmd("silent !cp -r .git.orig/ .git/")
end end
local function cleanup_repository(dir) local function cleanup_repository(dir)
vim.cmd('cd '..project_dir) vim.cmd("cd " .. project_dir)
vim.cmd('silent !rm -rf /tmp/'..dir) vim.cmd("silent !rm -rf /tmp/" .. dir)
end end
function M.in_prepared_repo(cb) function M.in_prepared_repo(cb)
return function () return function()
local dir = 'neogit_test_'..random_string(5) local dir = "neogit_test_" .. random_string(5)
prepare_repository(dir) prepare_repository(dir)
vim.cmd('Neogit') vim.cmd("Neogit")
a.util.block_on(status.reset()) a.util.block_on(status.reset())
local _, err = pcall(cb) local _, err = pcall(cb)
cleanup_repository(dir) cleanup_repository(dir)
@ -42,41 +42,45 @@ function M.in_prepared_repo(cb)
end end
function M.get_git_status(files) function M.get_git_status(files)
local result = vim.api.nvim_exec('!git status -s --porcelain=1 -- ' .. (files or ''), true) local result = vim.api.nvim_exec("!git status -s --porcelain=1 -- " .. (files or ""), true)
local lines = vim.split(result, '\n') local lines = vim.split(result, "\n")
local output = {} local output = {}
for i=3,#lines do for i = 3, #lines do
table.insert(output, lines[i]) table.insert(output, lines[i])
end end
return table.concat(output, '\n') return table.concat(output, "\n")
end end
function M.get_git_diff(files, flags) function M.get_git_diff(files, flags)
local result = vim.api.nvim_exec('!git diff '..(flags or '')..' -- ' ..(files or ''), true) local result = vim.api.nvim_exec("!git diff " .. (flags or "") .. " -- " .. (files or ""), true)
local lines = vim.split(result, '\n') local lines = vim.split(result, "\n")
local output = {} local output = {}
for i=5,#lines do for i = 5, #lines do
table.insert(output, lines[i]) table.insert(output, lines[i])
end end
return table.concat(output, '\n') return table.concat(output, "\n")
end end
function M.get_git_branches() function M.get_git_branches()
local result = vim.api.nvim_exec('!git branch --list --all', true) local result = vim.api.nvim_exec("!git branch --list --all", true)
local lines = vim.split(result, '\n') local lines = vim.split(result, "\n")
local output = {} local output = {}
local current_branch = nil local current_branch = nil
for _, l in ipairs(lines) do for _, l in ipairs(lines) do
local branch_state, name = l:match('^([* ]) (.+)') local branch_state, name = l:match("^([* ]) (.+)")
if branch_state == '*' then current_branch = name end if branch_state == "*" then
if name then table.insert(output, name) end current_branch = name
end
if name then
table.insert(output, name)
end
end end
return output, current_branch return output, current_branch
end end
function M.get_current_branch() function M.get_current_branch()
local result = vim.api.nvim_exec('!git branch --show-current', true) local result = vim.api.nvim_exec("!git branch --show-current", true)
local lines = vim.split(result, '\n') local lines = vim.split(result, "\n")
return lines[#lines - 1] return lines[#lines - 1]
end end

View file

@ -1,52 +1,60 @@
local eq = assert.are.same local eq = assert.are.same
local generate_patch_from_selection = require'neogit.status'.generate_patch_from_selection local generate_patch_from_selection = require("neogit.status").generate_patch_from_selection
-- Helper-function to keep the testsuite clean, since the interface to the -- Helper-function to keep the testsuite clean, since the interface to the
-- function under test is quite bloated -- function under test is quite bloated
local function run_with_hunk(hunk, from, to, reverse) local function run_with_hunk(hunk, from, to, reverse)
local lines = vim.split(hunk, '\n') local lines = vim.split(hunk, "\n")
local header_matches = vim.fn.matchlist(lines[1], "@@ -\\(\\d\\+\\),\\(\\d\\+\\) +\\(\\d\\+\\),\\(\\d\\+\\) @@") local header_matches =
return generate_patch_from_selection( vim.fn.matchlist(lines[1], "@@ -\\(\\d\\+\\),\\(\\d\\+\\) +\\(\\d\\+\\),\\(\\d\\+\\) @@")
{ return generate_patch_from_selection({
name = 'test.txt', name = "test.txt",
diff = { lines = lines } diff = { lines = lines },
}, }, {
{ first = 1,
first = 1, last = #lines,
last = #lines, index_from = header_matches[2],
index_from = header_matches[2], index_len = header_matches[3],
index_len = header_matches[3], diff_from = 1,
diff_from = 1, diff_to = #lines,
diff_to = #lines }, from, to, reverse)
},
from, to, reverse)
end end
describe('patch creation', function () describe("patch creation", function()
it('creates a patch-formatted string from a hunk', function () it("creates a patch-formatted string from a hunk", function()
local patch = run_with_hunk( local patch = run_with_hunk(
[['@@ -1,1 +1,1 @@ [['@@ -1,1 +1,1 @@
-some line -some line
+another line]], 1, 2) +another line]],
1,
2
)
eq([[--- a/test.txt eq(
[[--- a/test.txt
+++ b/test.txt +++ b/test.txt
@@ -1,1 +1,1 @@ @@ -1,1 +1,1 @@
-some line -some line
+another line +another line
]], patch) ]],
patch
)
end) end)
it('can take only part of a hunk', function () it("can take only part of a hunk", function()
local patch = run_with_hunk( local patch = run_with_hunk(
[[@@ -1,3 +1,3 @@ [[@@ -1,3 +1,3 @@
line1 line1
-line2 -line2
+line two +line two
line3]], 2, 3) line3]],
2,
3
)
eq([[--- a/test.txt eq(
[[--- a/test.txt
+++ b/test.txt +++ b/test.txt
@@ -1,3 +1,3 @@ @@ -1,3 +1,3 @@
line1 line1
@ -54,76 +62,103 @@ line3]], 2, 3)
+line two +line two
line3 line3
]], patch) ]],
patch
)
end) end)
it('removes added lines outside of the selection', function () it("removes added lines outside of the selection", function()
local patch = run_with_hunk( local patch = run_with_hunk(
[[@@ -1,1 +1,4 @@ [[@@ -1,1 +1,4 @@
line1 line1
+line2 +line2
+line3 +line3
+line4]], 3, 3) +line4]],
3,
3
)
eq([[--- a/test.txt eq(
[[--- a/test.txt
+++ b/test.txt +++ b/test.txt
@@ -1,1 +1,2 @@ @@ -1,1 +1,2 @@
line1 line1
+line3 +line3
]], patch) ]],
patch
)
end) end)
it('keeps removed lines outside of the selection as normal lines', function () it("keeps removed lines outside of the selection as normal lines", function()
local patch = run_with_hunk( local patch = run_with_hunk(
[[@@ -1,2 +1,2 @@ [[@@ -1,2 +1,2 @@
line1 line1
-line2 -line2
+line two]], 3, 3) +line two]],
3,
3
)
eq([[--- a/test.txt eq(
[[--- a/test.txt
+++ b/test.txt +++ b/test.txt
@@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
line1 line1
line2 line2
+line two +line two
]], patch) ]],
patch
)
end) end)
describe('in reverse', function () describe("in reverse", function()
it('removes removed lines outside of the selection', function () it("removes removed lines outside of the selection", function()
local patch = run_with_hunk( local patch = run_with_hunk(
[[@@ -1,2 +1,2 @@ [[@@ -1,2 +1,2 @@
line1 line1
-line2 -line2
+line two]], 3, 3, true) +line two]],
3,
3,
true
)
eq([[--- a/test.txt eq(
[[--- a/test.txt
+++ b/test.txt +++ b/test.txt
@@ -1,1 +1,2 @@ @@ -1,1 +1,2 @@
line1 line1
+line two +line two
]], patch) ]],
patch
)
end) end)
it('keeps added lines outside of the selection as normal lines', function () it("keeps added lines outside of the selection as normal lines", function()
local patch = run_with_hunk( local patch = run_with_hunk(
[[@@ -1,2 +1,2 @@ [[@@ -1,2 +1,2 @@
line1 line1
-line2 -line2
+line two]], 2, 2, true) +line two]],
2,
2,
true
)
eq([[--- a/test.txt eq(
[[--- a/test.txt
+++ b/test.txt +++ b/test.txt
@@ -1,3 +1,2 @@ @@ -1,3 +1,2 @@
line1 line1
-line2 -line2
line two line two
]], patch) ]],
patch
)
end) end)
end) end)
end) end)

View file

@ -1,21 +1,19 @@
local input = require'neogit.lib.input' local input = require("neogit.lib.input")
local M = { local M = {
value = '', value = "",
confirmed = true confirmed = true,
} }
input.get_user_input = function (_) input.get_user_input = function(_)
return M.value return M.value
end end
input.get_user_input_with_completion = function (_, _) input.get_user_input_with_completion = function(_, _)
return M.value return M.value
end end
input.get_confirmation = function (_, _) input.get_confirmation = function(_, _)
return M.confirmed return M.confirmed
end end
return M return M

View file

@ -1,21 +1,21 @@
local eq = assert.are.same local eq = assert.are.same
describe('proof of concept', function () describe("proof of concept", function()
it('should work', function () it("should work", function()
assert(true) assert(true)
end) end)
it('should have access to vim global', function () it("should have access to vim global", function()
assert.is_not_nil(vim) assert.is_not_nil(vim)
end) end)
it('should be able to interact with vim', function () it("should be able to interact with vim", function()
vim.cmd("let g:val = v:true") vim.cmd("let g:val = v:true")
eq(true, vim.g.val) eq(true, vim.g.val)
end) end)
it('has access to buffers', function () it("has access to buffers", function()
vim.cmd('Neogit') vim.cmd("Neogit")
-- 1 is most likely the initial buffer nvim openes when starting? -- 1 is most likely the initial buffer nvim openes when starting?
-- 2 is the neogit buffer just opened -- 2 is the neogit buffer just opened
eq({ 1, 2 }, vim.api.nvim_list_bufs()) eq({ 1, 2 }, vim.api.nvim_list_bufs())

View file

@ -1,38 +1,47 @@
local eq = assert.are.same local eq = assert.are.same
local status = require'neogit.status' local status = require("neogit.status")
local harness = require'tests.git_harness' local harness = require("tests.git_harness")
local _ = require 'tests.mocks.input' local _ = require("tests.mocks.input")
local in_prepared_repo = harness.in_prepared_repo local in_prepared_repo = harness.in_prepared_repo
local get_git_status = harness.get_git_status local get_git_status = harness.get_git_status
local get_git_diff = harness.get_git_diff local get_git_diff = harness.get_git_diff
local function act(normal_cmd) local function act(normal_cmd)
vim.fn.feedkeys(vim.api.nvim_replace_termcodes(normal_cmd, true, true, true)) vim.fn.feedkeys(vim.api.nvim_replace_termcodes(normal_cmd, true, true, true))
vim.fn.feedkeys('', 'x') -- flush typeahead vim.fn.feedkeys("", "x") -- flush typeahead
status.wait_on_current_operation() status.wait_on_current_operation()
end end
describe('status buffer', function () describe("status buffer", function()
describe('staging files - s', function () describe("staging files - s", function()
it('can stage an untracked file under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 5, 1, 0}) "can stage an untracked file under the cursor",
act('s') in_prepared_repo(function()
local result = get_git_status('untracked.txt') vim.fn.setpos(".", { 0, 5, 1, 0 })
eq('A untracked.txt\n', result) act("s")
end)) local result = get_git_status("untracked.txt")
eq("A untracked.txt\n", result)
end)
)
it('can stage a tracked file under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can stage a tracked file under the cursor",
act('s') in_prepared_repo(function()
local result = get_git_status('a.txt') vim.fn.setpos(".", { 0, 8, 1, 0 })
eq('M a.txt\n', result) act("s")
end)) local result = get_git_status("a.txt")
eq("M a.txt\n", result)
end)
)
it('can stage a hunk under the cursor of a tracked file', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can stage a hunk under the cursor of a tracked file",
act('<tab>jjs') in_prepared_repo(function()
eq('MM a.txt\n', get_git_status('a.txt')) vim.fn.setpos(".", { 0, 8, 1, 0 })
eq([[--- a/a.txt act("<tab>jjs")
eq("MM a.txt\n", get_git_status("a.txt"))
eq(
[[--- a/a.txt
+++ b/a.txt +++ b/a.txt
@@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
This is a text file under version control. This is a text file under version control.
@ -41,14 +50,20 @@ describe('status buffer', function ()
Here are some lines we can change during the tests. Here are some lines we can change during the tests.
]], get_git_diff('a.txt', '--cached')) ]],
end)) get_git_diff("a.txt", "--cached")
)
end)
)
it('can stage a subsequent hunk under the cursor of a tracked file', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can stage a subsequent hunk under the cursor of a tracked file",
act('<tab>8js') in_prepared_repo(function()
eq('MM a.txt\n', get_git_status('a.txt')) vim.fn.setpos(".", { 0, 8, 1, 0 })
eq([[--- a/a.txt act("<tab>8js")
eq("MM a.txt\n", get_git_status("a.txt"))
eq(
[[--- a/a.txt
+++ b/a.txt +++ b/a.txt
@@ -7,4 +7,5 @@ Here are some lines we can change during the tests. @@ -7,4 +7,5 @@ Here are some lines we can change during the tests.
@ -56,14 +71,20 @@ describe('status buffer', function ()
It also has some line we can manipulate. It also has some line we can manipulate.
+Adding a new line right here! +Adding a new line right here!
Here is some more. Here is some more.
]], get_git_diff('a.txt', '--cached')) ]],
end)) get_git_diff("a.txt", "--cached")
)
end)
)
it('can stage from a selection in a hunk', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can stage from a selection in a hunk",
act('<tab>jjjjVs') in_prepared_repo(function()
eq('MM a.txt\n', get_git_status('a.txt')) vim.fn.setpos(".", { 0, 8, 1, 0 })
eq([[--- a/a.txt act("<tab>jjjjVs")
eq("MM a.txt\n", get_git_status("a.txt"))
eq(
[[--- a/a.txt
+++ b/a.txt +++ b/a.txt
@@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
This is a text file under version control. This is a text file under version control.
@ -72,37 +93,52 @@ describe('status buffer', function ()
Here are some lines we can change during the tests. Here are some lines we can change during the tests.
]], get_git_diff('a.txt', '--cached')) ]],
end)) get_git_diff("a.txt", "--cached")
)
end)
)
end) end)
describe('unstaging files - u', function () describe("unstaging files - u", function()
it('can unstage a staged file under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can unstage a staged file under the cursor",
act('u') in_prepared_repo(function()
local result = get_git_status('b.txt') vim.fn.setpos(".", { 0, 11, 1, 0 })
eq(' M b.txt\n', result) act("u")
end)) local result = get_git_status("b.txt")
eq(" M b.txt\n", result)
end)
)
it('can unstage a hunk under the cursor of a staged file', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can unstage a hunk under the cursor of a staged file",
act('<tab>jju') in_prepared_repo(function()
eq('MM b.txt\n', get_git_status('b.txt')) vim.fn.setpos(".", { 0, 11, 1, 0 })
eq([[--- a/b.txt act("<tab>jju")
eq("MM b.txt\n", get_git_status("b.txt"))
eq(
[[--- a/b.txt
+++ b/b.txt +++ b/b.txt
@@ -7,3 +7,4 @@ This way, unstaging staged changes can be tested. @@ -7,3 +7,4 @@ This way, unstaging staged changes can be tested.
Some more lines down here to force a second hunk. Some more lines down here to force a second hunk.
I can't think of anything else. I can't think of anything else.
Duh. Duh.
+And here as well +And here as well
]], get_git_diff('b.txt', '--cached')) ]],
end)) get_git_diff("b.txt", "--cached")
)
end)
)
it('can unstage from a selection in a hunk', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can unstage from a selection in a hunk",
act('<tab>jjjjVu') in_prepared_repo(function()
eq('MM b.txt\n', get_git_status('b.txt')) vim.fn.setpos(".", { 0, 11, 1, 0 })
eq([[--- a/b.txt act("<tab>jjjjVu")
eq("MM b.txt\n", get_git_status("b.txt"))
eq(
[[--- a/b.txt
+++ b/b.txt +++ b/b.txt
@@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
This is another test file. This is another test file.
@ -110,37 +146,52 @@ describe('status buffer', function ()
This way, unstaging staged changes can be tested. This way, unstaging staged changes can be tested.
]], get_git_diff('b.txt')) ]],
end)) get_git_diff("b.txt")
)
end)
)
it('can unstage a subsequent hunk from a staged file', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can unstage a subsequent hunk from a staged file",
act('<tab>8ju') in_prepared_repo(function()
eq('MM b.txt\n', get_git_status('b.txt')) vim.fn.setpos(".", { 0, 11, 1, 0 })
eq([[--- a/b.txt act("<tab>8ju")
eq("MM b.txt\n", get_git_status("b.txt"))
eq(
[[--- a/b.txt
+++ b/b.txt +++ b/b.txt
@@ -7,3 +7,4 @@ This way, unstaging staged changes can be tested. @@ -7,3 +7,4 @@ This way, unstaging staged changes can be tested.
Some more lines down here to force a second hunk. Some more lines down here to force a second hunk.
I can't think of anything else. I can't think of anything else.
Duh. Duh.
+And here as well +And here as well
]], get_git_diff('b.txt')) ]],
end)) get_git_diff("b.txt")
)
end)
)
end) end)
describe('discarding files - x', function () describe("discarding files - x", function()
it('can discard the changes of a file under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can discard the changes of a file under the cursor",
act('x') in_prepared_repo(function()
local result = get_git_status('a.txt') vim.fn.setpos(".", { 0, 8, 1, 0 })
eq('', result) act("x")
end)) local result = get_git_status("a.txt")
eq("", result)
end)
)
it('can discard a hunk under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can discard a hunk under the cursor",
act('<tab>jjx') in_prepared_repo(function()
eq(' M a.txt\n', get_git_status('a.txt')) vim.fn.setpos(".", { 0, 8, 1, 0 })
eq([[--- a/a.txt act("<tab>jjx")
eq(" M a.txt\n", get_git_status("a.txt"))
eq(
[[--- a/a.txt
+++ b/a.txt +++ b/a.txt
@@ -7,4 +7,5 @@ Here are some lines we can change during the tests. @@ -7,4 +7,5 @@ Here are some lines we can change during the tests.
@ -148,14 +199,20 @@ describe('status buffer', function ()
It also has some line we can manipulate. It also has some line we can manipulate.
+Adding a new line right here! +Adding a new line right here!
Here is some more. Here is some more.
]], get_git_diff('a.txt')) ]],
end)) get_git_diff("a.txt")
)
end)
)
it('can discard a selection of a hunk', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 8, 1, 0}) "can discard a selection of a hunk",
act('<tab>jjjjVx') in_prepared_repo(function()
eq(' M a.txt\n', get_git_status('a.txt')) vim.fn.setpos(".", { 0, 8, 1, 0 })
eq([[--- a/a.txt act("<tab>jjjjVx")
eq(" M a.txt\n", get_git_status("a.txt"))
eq(
[[--- a/a.txt
+++ b/a.txt +++ b/a.txt
@@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
This is a text file under version control. This is a text file under version control.
@ -169,42 +226,60 @@ describe('status buffer', function ()
It also has some line we can manipulate. It also has some line we can manipulate.
+Adding a new line right here! +Adding a new line right here!
Here is some more. Here is some more.
]], get_git_diff('a.txt')) ]],
end)) get_git_diff("a.txt")
)
end)
)
it('can delete an untracked file', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 5, 1, 0}) "can delete an untracked file",
act('x') in_prepared_repo(function()
local result = get_git_status('untracked.txt') vim.fn.setpos(".", { 0, 5, 1, 0 })
eq('', result) act("x")
end)) local result = get_git_status("untracked.txt")
eq("", result)
end)
)
it('can discard the changes of a staged file under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can discard the changes of a staged file under the cursor",
act('x') in_prepared_repo(function()
local result = get_git_status('b.txt') vim.fn.setpos(".", { 0, 11, 1, 0 })
eq('', result) act("x")
end)) local result = get_git_status("b.txt")
eq("", result)
end)
)
it('can discard a hunk of the staged file under the cursor', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can discard a hunk of the staged file under the cursor",
act('<tab>jjx') in_prepared_repo(function()
eq('M b.txt\n', get_git_status('b.txt')) vim.fn.setpos(".", { 0, 11, 1, 0 })
eq([[--- a/b.txt act("<tab>jjx")
eq("M b.txt\n", get_git_status("b.txt"))
eq(
[[--- a/b.txt
+++ b/b.txt +++ b/b.txt
@@ -7,3 +7,4 @@ This way, unstaging staged changes can be tested. @@ -7,3 +7,4 @@ This way, unstaging staged changes can be tested.
Some more lines down here to force a second hunk. Some more lines down here to force a second hunk.
I can't think of anything else. I can't think of anything else.
Duh. Duh.
+And here as well +And here as well
]], get_git_diff('b.txt', '--cached')) ]],
end)) get_git_diff("b.txt", "--cached")
)
end)
)
it('can discard a selection of a staged file', in_prepared_repo(function () it(
vim.fn.setpos('.', {0, 11, 1, 0}) "can discard a selection of a staged file",
act('<tab>jjjjVx') in_prepared_repo(function()
eq('M b.txt\n', get_git_status('b.txt')) vim.fn.setpos(".", { 0, 11, 1, 0 })
eq([[--- a/b.txt act("<tab>jjjjVx")
eq("M b.txt\n", get_git_status("b.txt"))
eq(
[[--- a/b.txt
+++ b/b.txt +++ b/b.txt
@@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
This is another test file. This is another test file.
@ -217,7 +292,10 @@ describe('status buffer', function ()
I can't think of anything else. I can't think of anything else.
Duh. Duh.
+And here as well +And here as well
]], get_git_diff('b.txt', '--cached')) ]],
end)) get_git_diff("b.txt", "--cached")
)
end)
)
end) end)
end) end)