1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
-- How it started
--vim.api.nvim_create_user_command(
-- "Promqlfmt",
-- '<line1>,<line2>!promtool promql format --experimental "$(cat /dev/stdin)"',
-- { nargs = 0, range = "%" }
--)
-- How it's going
if vim.g.loaded_promqlfmt == 1 then
return
end
vim.g.loaded_promqlfmt = 1
-- remove once 0.10 is released
local vim_is_old = vim.version().minor < 10 and vim.version().major == 0
--- Format a PromQL query using promtool
--- @param query string @query to format
--- @param padding number|nil @number of spaces to add to each line, default 0
--- @param padchar string|nil @character for padding, default " "
--- @return string[]|nil @formatted query, one element per line or nil
local promql_format = function(query, padding, padchar)
padding = padding or 0
padchar = padchar or " "
local success, result = pcall(function()
if vim_is_old then
return {
code = 0,
stdout = vim.fn.system({
"promtool",
"promql",
"format",
"--experimental",
query,
}),
}
end
return vim
.system(
{ "promtool", "promql", "format", "--experimental", query },
{ text = true }
)
:wait()
end)
if success and result and result.code == 0 then
local lines = vim.fn.split(result.stdout, "\n")
if padding == 0 then
return lines
end
for i, line in ipairs(lines or {}) do
lines[i] = (padchar):rep(padding) .. line
end
return lines
else
print("woops error occurred while formatting query")
print(vim.inspect(result))
end
end
--- Get the last column of a line, ie the position of the last character
--- @param bufnr number
--- @param linenr number
local last_col_line = function(bufnr, linenr)
local line = vim.api.nvim_buf_get_lines(bufnr, linenr, linenr + 1, true)[1]
return #line
end
--- Get the selected text in a buffer
--- @param bufnr number|nil
--- @return table
local buf_get_selected_text = function(bufnr)
local r = {
text = "",
start_line = nil,
start_row = nil,
end_line = nil,
end_row = nil,
}
local pos1, pos2 = "'<", "'>"
if vim_is_old then
local pos = vim.fn.getpos("'<")
pos1 = { pos[2] - 1, pos[3] - 1 + pos[4] }
pos = vim.fn.getpos("'>")
pos2 = { pos[2] - 1, pos[3] - 1 + pos[4] }
if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then
pos1, pos2 = pos2, pos1
end
-- getpos() may return {0,0,0,0}
if pos1[1] < 0 or pos1[2] < 0 then
return {}
end
end
local region = vim.region(bufnr or 0, pos1, pos2, vim.fn.visualmode(), true)
local maxcol = vim.v.maxcol
for line, cols in vim.spairs(region) do
if r.start_line == nil then
r.start_line = line
r.start_row = cols[1]
end
local endcol = cols[2] == maxcol and -1 or cols[2]
local chunk =
vim.api.nvim_buf_get_text(0, line, cols[1], line, endcol, {})[1]
r.text = ("%s%s\n"):format(r.text, chunk)
r.end_line = line
r.end_row = endcol
end
-- 0.9 does not support negative end_row
if vim_is_old then
local last_row = last_col_line(0, r.end_line)
if r.end_row == -1 or r.end_row > last_row then
r.end_row = last_row
end
else
r.end_row = r.end_row > last_col_line(0, r.end_line) and -1 or r.end_row
end
return r
end
vim.api.nvim_create_user_command("Promqlfmt", function(opts)
-- it was not called from a visual selection
if opts.range == 0 then
-- get all lines
local buf_text = vim.api.nvim_buf_get_lines(0, 0, -1, true)
-- format query
local text_formatted = promql_format(table.concat(buf_text, "\n"))
if text_formatted == nil then
return
end
-- replace all text with the formatted version
vim.api.nvim_buf_set_lines(0, 0, -1, true, text_formatted)
return
end
local s = buf_get_selected_text()
-- use the position of the first non-whitespace character of the first line
-- as padding
local padding = string.find(
vim.api.nvim_buf_get_lines(0, s.start_line, s.start_line + 1, true)[1],
"%S"
) - 1
local text_formatted = promql_format(s.text, padding)
if text_formatted == nil then
return
end
-- remove padding from first line
if s.start_row ~= 0 then
text_formatted[1] = text_formatted[1]:sub(padding + 1)
end
vim.api.nvim_buf_set_text(
0,
s.start_line,
s.start_row,
s.end_line,
s.end_row,
text_formatted
)
end, { nargs = 0, range = "%" })
|