-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathchat.vim
More file actions
291 lines (253 loc) · 9.85 KB
/
chat.vim
File metadata and controls
291 lines (253 loc) · 9.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
" Copyright (c) 2025 Augment
" MIT License - See LICENSE.md for full terms
" Utilities for chat
function! s:ResetChatContents() abort
let chat_buf = bufnr('AugmentChatHistory')
if chat_buf == -1
call augment#log#Error('Chat reset failed: Could not find chat history buffer')
return
endif
call setbufvar(chat_buf, '&modifiable', v:true)
silent call deletebufline(chat_buf, 1, '$')
call augment#chat#AppendText('# Augment Chat History'
\ . "\n\n"
\ . '`:Augment chat` Send a chat message in the current conversation'
\ . "\n"
\ . '`:Augment chat-new` Start a new conversation'
\ . "\n"
\ . '`:Augment chat-toggle` Toggle the chat panel visibility'
\ . "\n"
\ . '`:Augment chat-apply` Apply code blocks from the chat panel'
\ . "\n\n")
endfunction
function! augment#chat#Toggle() abort
let chat_id = bufwinid('AugmentChatHistory')
if chat_id == -1
call augment#chat#OpenChatPanel()
else
" Don't close if it's the last window
if winnr('$') > 1
call win_execute(chat_id, 'close')
endif
endif
endfunction
function! augment#chat#OpenChatPanel() abort
let current_win = win_getid()
" Check if the panel already exists and has been setup
if bufexists('AugmentChatHistory') && !getbufvar('AugmentChatHistory', '&modifiable')
if bufwinid('AugmentChatHistory') == -1
botright 80vnew AugmentChatHistory
endif
call win_gotoid(current_win)
return
endif
" Open a buffer for the chat history with a width of 80 characters
botright 80vnew AugmentChatHistory
setlocal buftype=nofile " Buffer will never be written to a file
setlocal nomodifiable " Prevent any modifications
setlocal noswapfile " Don't create a swapfile
" NOTE(mpauly): winfixbuf is not available in some subversions of vim 9.1
if exists('&winfixbuf')
setlocal winfixbuf " Keep buffer in window when splitting
endif
setlocal bufhidden=hide " When buffer is abandoned, hide it
setlocal nobuflisted " Hide from :ls
setlocal wrap " Wrap long lines
setlocal linebreak " Wrap at word boundaries
setlocal filetype=markdown " Use markdown syntax highlighting
setlocal nonumber " Hide line numbers
setlocal norelativenumber " Hide relative line numbers
setlocal signcolumn=no " Hide sign column
setlocal nocursorline " Disable cursor line highlighting
setlocal nospell " Disable spell checking
setlocal nofoldenable " Disable folding
setlocal textwidth=0 " Disable text width limit
setlocal scrolloff=0 " Disable scrolloff
" Add the chat header to the buffer
call s:ResetChatContents()
" TODO(AU-6480): create another buffer for the chat input
" new AugmentChatInput
call win_gotoid(current_win)
endfunction
function! augment#chat#Reset() abort
call s:ResetChatContents()
call s:ResetHistory()
endfunction
function! s:ResetHistory() abort
let g:_augment_chat_history = []
endfunction
function! augment#chat#AppendText(text) abort
let chat_buf = bufnr('AugmentChatHistory')
if chat_buf == -1
call augment#log#Error('Chat append failed: Could not find chat history buffer')
return
endif
let lines = split(a:text, "\n", v:true)
let last_line = getbufline(chat_buf, '$')[0]
call setbufvar(chat_buf, '&modifiable', v:true)
call setbufline(chat_buf, '$', last_line . lines[0])
call appendbufline(chat_buf, '$', lines[1:])
call setbufvar(chat_buf, '&modifiable', v:false)
endfunction
function! augment#chat#AppendMessage(message) abort
" If not the first message, scroll to the bottom
let chat_id = bufwinid('AugmentChatHistory')
if !empty(augment#chat#GetHistory()) && chat_id != -1
let command = "call winrestview({'lnum': line('$'), 'topline': line('$')})"
call win_execute(chat_id, command)
endif
let message_text = '================================================================================'
\ . "\n\n"
\ . "\t*You*"
\ . "\n\n"
\ . a:message
\ . "\n\n"
\ . '--------------------------------------------------------------------------------'
\ . "\n\n"
\ . "\t*Augment*"
\ . "\n\n"
call augment#chat#AppendText(message_text)
endfunction
function! augment#chat#AppendHistory(request_message, response_text, request_id) abort
if !exists('g:_augment_chat_history')
let g:_augment_chat_history = []
endif
call add(g:_augment_chat_history, {
\ 'request_message': a:request_message,
\ 'response_text': a:response_text,
\ 'request_id': a:request_id,
\ })
endfunction
function! augment#chat#GetHistory() abort
if exists('g:_augment_chat_history')
return g:_augment_chat_history
endif
return []
endfunction
function! augment#chat#SaveUri() abort
if bufname('%') !=# 'AugmentChatHistory'
let g:_augment_current_uri = 'file://' . expand('%:p')
endif
endfunction
function! augment#chat#GetUri() abort
if exists('g:_augment_current_uri')
return g:_augment_current_uri
endif
return 'file://' . expand('%:p')
endfunction
function! s:GetBufSelection(line_start, col_start, line_end, col_end) abort
if a:line_start == a:line_end
return getline(a:line_start)[a:col_start - 1:a:col_end - 1]
endif
let lines = []
call add(lines, getline(a:line_start)[a:col_start - 1:])
call extend(lines, getline(a:line_start + 1, a:line_end - 1))
call add(lines, getline(a:line_end)[0:a:col_end - 1])
return join(lines, "\n")
endfunction
function! augment#chat#GetSelectedText() abort
" If in visual mode use the current selection
if mode() ==# 'v' || mode() ==# 'V'
let [line_one, col_one] = getpos('.')[1:2]
let [line_two, col_two] = getpos('v')[1:2]
" . may be before or after v, so need to do some sorting
if line_one < line_two
let line_start = line_one
let col_start = col_one
let line_end = line_two
let col_end = col_two
elseif line_one > line_two
let line_start = line_two
let col_start = col_two
let line_end = line_one
let col_end = col_one
else
" If the lines are the same, the columns may be different
let line_start = line_one
let line_end = line_two
if col_one <= col_two
let col_start = col_one
let col_end = col_two
else
let col_start = col_two
let col_end = col_one
endif
endif
" . and v return column positions one lower than '< and '>
let col_start += 1
let col_end += 1
" In visual line mode, the columns will be incorrect
if mode() ==# 'V'
let col_start = 1
let col_end = v:maxcol
endif
return s:GetBufSelection(line_start, col_start, line_end, col_end)
endif
" Otherwise, assume '< and '> are populated with the correct selection
let [line_start, col_start] = getpos("'<")[1:2]
let [line_end, col_end] = getpos("'>")[1:2]
return s:GetBufSelection(line_start, col_start, line_end, col_end)
endfunction
function! augment#chat#ApplyCodeBlocks() abort
call augment#log#Info('Applying code blocks... in the function')
let history = augment#chat#GetHistory()
if empty(history)
call augment#log#Error('No chat history found')
return
endif
" Iterate through all messages in history
for msg in history
let response = msg.response_text
let lines = split(response, "\n")
let in_block = 0
let current_block = {'path': '', 'content': []}
for line in lines
" Check for code block start with path
let block_start = matchlist(line, '````\([^[:space:]]\+\)\s\+path=\(.\+\)\s\+mode=\(.\+\)$')
if !empty(block_start)
let in_block = 1
let current_block.path = block_start[2]
let current_block.content = []
continue
endif
" Check for code block end
if line =~# '^````$'
if in_block && !empty(current_block.path)
" Apply the changes
call s:ApplyCodeBlock(current_block)
endif
let in_block = 0
let current_block = {'path': '', 'content': []}
continue
endif
" Collect content lines
if in_block
call add(current_block.content, line)
endif
endfor
endfor
endfunction
function! s:ApplyCodeBlock(block) abort
" Ensure the directory exists
let dir = fnamemodify(a:block.path, ':h')
if !isdirectory(dir)
call mkdir(dir, 'p')
endif
" Check if file exists
if filereadable(a:block.path)
" Read existing content
let existing_content = readfile(a:block.path)
" Add a newline between existing content and new content if needed
if !empty(existing_content) && !empty(a:block.content)
call add(existing_content, '')
endif
" Append new content
call extend(existing_content, a:block.content)
call writefile(existing_content, a:block.path)
call augment#log#Info('Appended changes to: ' . a:block.path)
else
" Create new file
call writefile(a:block.content, a:block.path)
call augment#log#Info('Created new file: ' . a:block.path)
endif
endfunction