summaryrefslogtreecommitdiff
path: root/ftplugin/aichat.vim
blob: e15626494b688c9dde57d180bb6a1f27bd404d49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
" Highlighting code blocks in .aichat files
" Inspired and based on https://github.com/preservim/vim-markdown

if exists('g:vim_markdown_fenced_languages')
  let s:filetype_dict = {}
  for s:filetype in g:vim_markdown_fenced_languages
    let key = matchstr(s:filetype, '[^=]*')
    let val = matchstr(s:filetype, '[^=]*$')
    let s:filetype_dict[key] = val
  endfor
else
  let s:filetype_dict = {
        \ 'c++': 'cpp',
        \ 'viml': 'vim',
        \ 'bash': 'sh',
        \ 'ini': 'dosini',
        \ 'js': 'javascript',
        \ 'jsx': 'javascriptreact',
        \ 'ts': 'typescript',
        \ 'tsx': 'typescriptreact',
        \ }
endif

function! s:MarkdownHighlightSources(force)
  " Syntax highlight source code embedded in notes.
  " Look for code blocks in the current file
  let filetypes = {}
  for line in getline(1, '$')
    let ft = matchstr(line, '\(`\{3,}\|\~\{3,}\)\s*\zs[0-9A-Za-z_+-]*\ze.*')
    if !empty(ft) && ft !~# '^\d*$' | let filetypes[ft] = 1 | endif
  endfor
  if !exists('b:aichat_known_filetypes')
    let b:aichat_known_filetypes = {}
  endif
  if !exists('b:aichat_included_filetypes')
    " set syntax file name included
    let b:aichat_included_filetypes = {}
  endif
  if !a:force && (b:aichat_known_filetypes == filetypes || empty(filetypes))
    return
  endif

  " Now we're ready to actually highlight the code blocks.
  let startgroup = 'aichatCodeStart'
  let endgroup = 'aichatCodeEnd'
  for ft in keys(filetypes)
    if a:force || !has_key(b:aichat_known_filetypes, ft)
      if has_key(s:filetype_dict, ft)
        let filetype = s:filetype_dict[ft]
      else
        let filetype = ft
      endif
      let group = 'aichatSnippet' . toupper(substitute(filetype, '[+-]', '_', 'g'))
      if !has_key(b:aichat_included_filetypes, filetype)
        let include = s:SyntaxInclude(filetype)
        let b:aichat_included_filetypes[filetype] = 1
      else
        let include = '@' . toupper(filetype)
      endif
      let command_backtick = 'syntax region %s matchgroup=%s start="^\s*`\{3,}\s*%s.*$" matchgroup=%s end="\s*`\{3,}\s*$" keepend contains=%s'
      let command_tilde    = 'syntax region %s matchgroup=%s start="^\s*\~\{3,}\s*%s.*$" matchgroup=%s end="\s*\~\{3,}\s*$" keepend contains=%s'
      execute printf(command_backtick, group, startgroup, ft, endgroup, include)
      execute printf(command_tilde,    group, startgroup, ft, endgroup, include)
      execute printf('syntax cluster aichatNonListItem add=%s', group)

      let b:aichat_known_filetypes[ft] = 1
    endif
  endfor
endfunction

function! s:MarkdownHighlightChatOptions(force)
  " use jproperties syntax to highlight chat options
  let filetype = 'jproperties'
  if a:force || !has_key(b:aichat_known_filetypes, filetype)
    if !has_key(b:aichat_included_filetypes, filetype)
      let include = s:SyntaxInclude(filetype)
      let b:aichat_included_filetypes[filetype] = 1
    else
      let include = '@' . toupper(filetype)
    endif
    syntax region aichatOptions start="\[chat-options\]" end="^$" contains=@JPROPERTIES
    let b:aichat_known_filetypes[filetype] = 1
  endif
endfunction

function! s:SyntaxInclude(filetype)
  " Include the syntax highlighting of another {filetype}.
  let grouplistname = '@' . toupper(a:filetype)
  " Unset the name of the current syntax while including the other syntax
  " because some syntax scripts do nothing when "b:current_syntax" is set
  if exists('b:current_syntax')
    let syntax_save = b:current_syntax
    unlet b:current_syntax
  endif
  try
    execute 'syntax include' grouplistname 'syntax/' . a:filetype . '.vim'
    execute 'syntax include' grouplistname 'after/syntax/' . a:filetype . '.vim'
  catch /E484/
    " Ignore missing scripts
  endtry
  " Restore the name of the current syntax
  if exists('syntax_save')
    let b:current_syntax = syntax_save
  elseif exists('b:current_syntax')
    unlet b:current_syntax
  endif
  return grouplistname
endfunction

function! s:IsHighlightSourcesEnabledForBuffer()
  " Enable for markdown buffers, and for liquid buffers with markdown format
  return &filetype =~# 'aichat' || get(b:, 'liquid_subtype', '') =~# 'aichat'
endfunction

function! s:MarkdownRefreshSyntax(force)
  call vim_ai_config#load()
  if g:vim_ai_chat_default['ui']['code_syntax_enabled'] && &filetype =~# 'aichat'
    call s:MarkdownHighlightSources(a:force)
    call s:MarkdownHighlightChatOptions(a:force)
  endif

endfunction

function! s:MarkdownClearSyntaxVariables()
  if exists('b:aichat_included_filetypes')
    unlet! b:aichat_included_filetypes
  endif
endfunction

augroup Aichat
  autocmd! * <buffer>
  autocmd BufWinEnter <buffer> call s:MarkdownRefreshSyntax(1)
  autocmd BufUnload <buffer> call s:MarkdownClearSyntaxVariables()
  autocmd BufWritePost <buffer> call s:MarkdownRefreshSyntax(0)
  autocmd InsertEnter,InsertLeave <buffer> call s:MarkdownRefreshSyntax(0)
  autocmd CursorHold,CursorHoldI <buffer> call s:MarkdownRefreshSyntax(0)
augroup END