runtime(beancount): Include Beancount runtime files

Commit: 
https://github.com/vim/vim/commit/1fee3cd4b9a38b18656933d38a4977548d27808b
Author: Bruno BELANYI <[email protected]>
Date:   Sat Jun 13 18:32:59 2026 +0000

    runtime(beancount): Include Beancount runtime files
    
    Include with adjustments from the upstream repo:
    https://github.com/nathangrigg/vim-beancount
    
    closes: #20373
    
    Signed-off-by: Bruno BELANYI <[email protected]>
    Signed-off-by: Nathan Grigg <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS
index fb7ebddf0..6a786bfb1 100644
--- a/.github/MAINTAINERS
+++ b/.github/MAINTAINERS
@@ -10,6 +10,8 @@
 # will be requested to review.
 
 nsis/lang/russian.nsi                                  @RestorerZ
+runtime/autoload/beancount.vim                         @nathangrigg
+runtime/autoload/beancountcomplete.vim                 @nathangrigg
 runtime/autoload/context.vim                           @lifepillar
 runtime/autoload/freebasic.vim                         @dkearns
 runtime/autoload/hare.vim                              @selenebun
@@ -50,6 +52,7 @@ runtime/colors/wildcharm.vim                          
@habamax @romainl @neutaaaaan
 runtime/colors/zaibatsu.vim                            @habamax @romainl 
@neutaaaaan
 runtime/colors/zellner.vim                             @habamax @romainl 
@neutaaaaan
 runtime/compiler/bash.vim                              @Konfekt
+runtime/compiler/bean_check.vim                                @nathangrigg
 runtime/compiler/biome.vim                             @Konfekt
 runtime/compiler/cabal.vim                             @mateoxh
 runtime/compiler/checkstyle.vim                                @dkearns
@@ -143,6 +146,7 @@ runtime/ftplugin/asy.vim                            
@avidseeker
 runtime/ftplugin/autohotkey.vim                                @telemachus
 runtime/ftplugin/awk.vim                               @dkearns
 runtime/ftplugin/basic.vim                             @dkearns
+runtime/ftplugin/beancount.vim                         @nathangrigg
 runtime/ftplugin/bicep.vim                             @scottmckendry
 runtime/ftplugin/bicep-params.vim                      @scottmckendry
 runtime/ftplugin/brighterscript.vim                    @ribru17
@@ -363,6 +367,7 @@ runtime/import/dist/vimhighlight.vim                        
@lacygoill
 runtime/indent/arduino.vim                             @k-takata
 runtime/indent/astro.vim                               @wuelnerdotexe
 runtime/indent/basic.vim                               @dkearns
+runtime/indent/beancount.vim                           @nathangrigg
 runtime/indent/bpftrace.vim                            @sgruszka
 runtime/indent/bst.vim                                 @tpope
 runtime/indent/cdl.vim                                 @dkearns
@@ -491,6 +496,7 @@ runtime/syntax/asy.vim                                      
@avidseeker
 runtime/syntax/autohotkey.vim                          @mmikeww
 runtime/syntax/awk.vim                                 @dkearns
 runtime/syntax/basic.vim                               @dkearns
+runtime/syntax/beancount.vim                           @nathangrigg
 runtime/syntax/bpftrace.vim                            @sgruszka
 runtime/syntax/bst.vim                                 @tpope
 runtime/syntax/bzl.vim                                 @dbarnett
diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt
index 2180a744b..acc1bbf37 100644
--- a/runtime/autoload/README.txt
+++ b/runtime/autoload/README.txt
@@ -11,6 +11,8 @@ paste.vim     common code for mswin.vim, menu.vim and 
macmap.vim
 spellfile.vim  downloading of a missing spell file
 
 Omni completion files:
+adacomplete.vim                Ada
+beancount.vim          Beancount
 ccomplete.vim          C
 csscomplete.vim                HTML / CSS
 htmlcomplete.vim       HTML
diff --git a/runtime/autoload/beancount.vim b/runtime/autoload/beancount.vim
new file mode 100644
index 000000000..ca1aaf09d
--- /dev/null
+++ b/runtime/autoload/beancount.vim
@@ -0,0 +1,59 @@
+" Beancount specific formatting
+" Language: beancount
+" Maintainer: Nathan Grigg
+" Latest Revision: 2021-03-06
+
+" Align currency on decimal point.
+function! beancount#align_commodity(line1, line2) abort
+    " Save cursor position to adjust it if necessary.
+    let l:cursor_col = col('.')
+    let l:cursor_line = line('.')
+
+    " Increment at start of loop, because of continue statements.
+    let l:current_line = a:line1 - 1
+    while l:current_line < a:line2
+        let l:current_line += 1
+        let l:line = getline(l:current_line)
+        " This matches an account name followed by a space in one of the two
+        " following cases:
+        "  - A posting line, i.e., the line starts with indentation followed
+        "    by an optional flag and the account.
+        "  - A balance directive, i.e., the line starts with a date followed
+        "    by the 'balance' keyword and the account.
+        "  - A price directive, i.e., the line starts with a date followed by
+        "    the 'price' keyword and a currency.
+        let l:end_account = matchend(l:line, ' ' .
+            \ 
'^[\-/[:digit:]]+\s+balance\s+([A-Z][A-Za-z0-9\-]+)(:[A-Z0-9][A-Za-z0-9\-]*)+ ' 
.
+            \ '|^[\-/[:digit:]]+\s+price\s+\S+ ' .
+            \ 
'|^\s+([!&#?%PSTCURM]\s+)?([A-Z][A-Za-z0-9\-]+)(:[A-Z0-9][A-Za-z0-9\-]*)+ '
+            \ )
+        if l:end_account < 0
+            continue
+        endif
+
+        " Where does the number begin?
+        let l:begin_number = matchend(l:line, '^ *', l:end_account)
+
+        " Look for a minus sign and a number (possibly containing commas) and
+        " align on the next column.
+        let l:separator = matchend(l:line, '^ ([-+])?[,[:digit:]]+', 
l:begin_number) + 1
+        if l:separator < 0 | continue | endif
+        let l:has_spaces = l:begin_number - l:end_account
+        let l:need_spaces = g:beancount_separator_col - l:separator + 
l:has_spaces
+        if l:need_spaces < 0 | continue | endif
+        call setline(l:current_line, l:line[0 : l:end_account - 1] . repeat(' 
', l:need_spaces) . l:line[ l:begin_number : -1])
+        if l:current_line == l:cursor_line && l:cursor_col >= l:end_account
+            " Adjust cursor position for continuity.
+            call cursor(0, l:cursor_col + l:need_spaces - l:has_spaces)
+        endif
+    endwhile
+endfunction
+
+" Call bean-doctor on the current line and dump output into a scratch buffer
+function! beancount#get_context() abort
+    let l:context = system('bean-doctor context ' . shellescape(expand('%')) . 
' ' . line('.'))
+    botright new
+    setlocal buftype=nofile bufhidden=hide noswapfile
+    call append(0, split(l:context, ' 
'))
+    normal! gg
+endfunction
diff --git a/runtime/autoload/beancountcomplete.vim 
b/runtime/autoload/beancountcomplete.vim
new file mode 100644
index 000000000..4b778d30e
--- /dev/null
+++ b/runtime/autoload/beancountcomplete.vim
@@ -0,0 +1,215 @@
+" Vim completion script
+" Language: beancount
+" Maintainer: Nathan Grigg
+" Latest Revision: 2021-03-06
+
+let s:using_python3 = has('python3') || has('python3/dyn')
+
+" Equivalent to python's startswith
+" Matches based on user's ignorecase preference
+function! s:startswith(string, prefix) abort
+    return strpart(a:string, 0, strlen(a:prefix)) == a:prefix
+endfunction
+
+function! s:count_expression(text, expression) abort
+    return len(split(a:text, a:expression, 1)) - 1
+endfunction
+
+function! s:sort_accounts_by_depth(name1, name2) abort
+    let l:depth1 = s:count_expression(a:name1, ':')
+    let l:depth2 = s:count_expression(a:name2, ':')
+    return l:depth1 == l:depth2 ? 0 : l:depth1 > l:depth2 ? 1 : -1
+endfunction
+
+let s:directives = ['open', 'close', 'commodity', 'txn', 'balance', 'pad', 
'note', 'document', 'price', 'event', 'query', 'custom']
+
+" ------------------------------
+" Completion functions
+" ------------------------------
+function! beancountcomplete#complete(findstart, base) abort
+    if a:findstart
+        let l:col = searchpos('\s', 'bn', line('.'))[1]
+        if l:col == 0
+            return -1
+        else
+            return l:col
+        endif
+    endif
+
+    let l:partial_line = strpart(getline('.'), 0, getpos('.')[2]-1)
+    " Match directive types
+    if l:partial_line =~# '^\d\d\d\d\(-\|/\)\d\d \d\d $'
+        return beancountcomplete#complete_basic(s:directives, a:base, '')
+    endif
+
+    " If we are using python3, now is a good time to load everything
+    call beancountcomplete#load_everything()
+
+    " Split out the first character (for cases where we don't want to match the
+    " leading character: ", #, etc)
+    let l:first = strpart(a:base, 0, 1)
+    let l:rest = strpart(a:base, 1)
+
+    if l:partial_line =~# '^\d\d\d\d\(-\|/\)\d\d \d\d event $' && l:first ==# 
'"'
+        return beancountcomplete#complete_basic(b:beancount_events, l:rest, 
'"')
+    endif
+
+    let l:two_tokens = searchpos('\S\+\s', 'bn', line('.'))[1]
+    let l:prev_token = strpart(getline('.'), l:two_tokens, getpos('.')[2] - 
l:two_tokens)
+    " Match curriences if previous token is number
+    if l:prev_token =~# '^\d\+\([\.,]\d\+\)*'
+        call beancountcomplete#load_currencies()
+        return beancountcomplete#complete_basic(b:beancount_currencies, 
a:base, '')
+    endif
+
+    if l:first ==# '#'
+        call beancountcomplete#load_tags()
+        return beancountcomplete#complete_basic(b:beancount_tags, l:rest, '#')
+    elseif l:first ==# '^'
+        call beancountcomplete#load_links()
+        return beancountcomplete#complete_basic(b:beancount_links, l:rest, '^')
+    elseif l:first ==# '"'
+        call beancountcomplete#load_payees()
+        return beancountcomplete#complete_basic(b:beancount_payees, l:rest, 
'"')
+    else
+        call beancountcomplete#load_accounts()
+        return beancountcomplete#complete_account(a:base)
+    endif
+endfunction
+
+function! beancountcomplete#get_root() abort
+    if exists('b:beancount_root')
+        return b:beancount_root
+    endif
+    return expand('%')
+endfunction
+
+function! beancountcomplete#load_everything() abort
+    if s:using_python3 && !exists('b:beancount_loaded')
+        let l:root = beancountcomplete#get_root()
+python3 << EOF
+import vim
+from beancount import loader
+from beancount.core import data
+
+accounts = set()
+currencies = set()
+events = set()
+links = set()
+payees = set()
+tags = set()
+
+entries, errors, options_map = loader.load_file(vim.eval('l:root'))
+for index, entry in enumerate(entries):
+    if isinstance(entry, data.Open):
+        accounts.add(entry.account)
+        if entry.currencies:
+            currencies.update(entry.currencies)
+    elif isinstance(entry, data.Commodity):
+        currencies.add(entry.currency)
+    elif isinstance(entry, data.Event):
+        events.add(entry.type)
+    elif isinstance(entry, data.Transaction):
+        if entry.tags:
+            tags.update(entry.tags)
+        if entry.links:
+            links.update(entry.links)
+        if entry.payee:
+            payees.add(entry.payee)
+
+vim.bindeval('b:')['beancount_accounts'] = sorted(accounts)
+vim.bindeval('b:')['beancount_currencies'] = sorted(currencies)
+vim.bindeval('b:')['beancount_events'] = sorted(events)
+vim.bindeval('b:')['beancount_links'] = sorted(links)
+vim.bindeval('b:')['beancount_payees'] = sorted(payees)
+vim.bindeval('b:')['beancount_tags'] = sorted(tags)
+vim.bindeval('b:')['beancount_loaded'] = 1
+EOF
+    endif
+endfunction
+
+function! beancountcomplete#load_accounts() abort
+    if !s:using_python3 && !exists('b:beancount_accounts')
+        let l:root = beancountcomplete#get_root()
+        let b:beancount_accounts = beancountcomplete#query_single(l:root, 
'select distinct account;')
+    endif
+endfunction
+
+function! beancountcomplete#load_tags() abort
+    if !s:using_python3 && !exists('b:beancount_tags')
+        let l:root = beancountcomplete#get_root()
+        let b:beancount_tags = beancountcomplete#query_single(l:root, 'select 
distinct tags;')
+    endif
+endfunction
+
+function! beancountcomplete#load_links() abort
+    if !s:using_python3 && !exists('b:beancount_links')
+        let l:root = beancountcomplete#get_root()
+        let b:beancount_links = beancountcomplete#query_single(l:root, 'select 
distinct links;')
+    endif
+endfunction
+
+function! beancountcomplete#load_currencies() abort
+    if !s:using_python3 && !exists('b:beancount_currencies')
+        let l:root = beancountcomplete#get_root()
+        let b:beancount_currencies = beancountcomplete#query_single(l:root, 
'select distinct currency;')
+    endif
+endfunction
+
+function! beancountcomplete#load_payees() abort
+    if !s:using_python3 && !exists('b:beancount_payees')
+        let l:root = beancountcomplete#get_root()
+        let b:beancount_payees = beancountcomplete#query_single(l:root, 
'select distinct payee;')
+    endif
+endfunction
+
+" General completion function
+function! beancountcomplete#complete_basic(input, base, prefix) abort
+    let l:matches = filter(copy(a:input), 's:startswith(v:val, a:base)')
+
+    return map(l:matches, 'a:prefix . v:val')
+endfunction
+
+" Complete account name.
+function! beancountcomplete#complete_account(base) abort
+    if g:beancount_account_completion ==? 'chunks'
+        let l:pattern = '^\V' . substitute(a:base, ':', '\[^:]\*:', 'g') . 
'\[^:]\*'
+    else
+        let l:pattern = '^\V\.\*' . substitute(a:base, ':', '\.\*:\.\*', 'g') 
. '\.\*'
+    endif
+
+    let l:matches = []
+    let l:index = -1
+    while 1
+        let l:index = match(b:beancount_accounts, l:pattern, l:index + 1)
+        if l:index == -1 | break | endif
+        call add(l:matches, matchstr(b:beancount_accounts[l:index], l:pattern))
+    endwhile
+
+    if g:beancount_detailed_first
+        let l:matches = reverse(sort(l:matches, 's:sort_accounts_by_depth'))
+    endif
+
+    return l:matches
+endfunction
+
+function! beancountcomplete#query_single(root_file, query) abort
+    if s:using_python3
+python3 << EOF
+import vim
+import subprocess
+
+# We intentionally want to ignore stderr so it doesn't mess up our query 
processing
+output = subprocess.check_output(
+    ['bean-query', vim.eval('a:root_file'), vim.eval('a:query')],
+    stderr=subprocess.DEVNULL,
+    text=True,
+).splitlines()
+output = output[2:]
+result_list = sorted(y for y in (x.strip() for x in output) if y)
+EOF
+        return py3eval('result_list')
+    else
+        return []
+    endif
+endfunction
diff --git a/runtime/compiler/bean_check.vim b/runtime/compiler/bean_check.vim
new file mode 100644
index 000000000..45f6b0b4d
--- /dev/null
+++ b/runtime/compiler/bean_check.vim
@@ -0,0 +1,22 @@
+" Vim compiler file
+" Compiler: bean-check
+" Maintainer: Nathan Grigg
+" Latest Revision: 2017-03-20
+
+if exists('g:current_compiler')
+    finish
+endif
+let g:current_compiler = 'bean_check'
+
+let s:cpo_save = &cpoptions
+set cpoptions-=C
+
+CompilerSet makeprg=bean-check\ %
+" File:line: message
+" Skip blank lines and indented lines.
+CompilerSet errorformat=%-G
+CompilerSet errorformat+=%f:%l:\ %m
+CompilerSet errorformat+=%-G\ %.%#
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index a9a0e9220..36edcaab2 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -1,4 +1,4 @@
-*filetype.txt* For Vim version 9.2.  Last change: 2026 May 29
+*filetype.txt* For Vim version 9.2.  Last change: 2026 Jun 13
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -482,6 +482,65 @@ Support for features specific to GNU Awk, like @include, 
can be enabled by
 setting: >
        :let g:awk_is_gawk = 1
 
+BEANCOUNT                                              *ft-beancount-plugin*
+
+Beancount omni-completion |compl-omni| is provided by beancountcomplete.vim,
+if enabled with |g:beancount_completion_enable|.
+
+To enable completion of account names, set: >
+       let g:beancount_completion_enable = 1
+
+< Note enabling this may cause beancount to load additional plugin files.
+Only enable for code you trust.
+
+Variables:
+*g:beancount_account_completion*
+                       Can be either 'default' or 'chunks'.
+
+                       Default value: 'default'
+
+*g:beancount_completion_enable*
+                       Enable omni-completion |compl-omni| for accounts.
+
+                       Default value: 0
+
+*g:beancount_detailed_first*
+                       If non-zero, accounts higher down the hierarchy will be
+                       listed first as completions.
+
+                       Default value: 0
+
+*g:beancount_separator_col*
+                       The column that the decimal separator is aligned to.
+
+                       Default value: 50
+
+*b:beancount_root*
+                       Set the root Beancount file. This is used to gather
+                       values for the completion.
+                       If not set, the current file will be used.
+
+                       Default value: not set
+
+
+Commands:
+:AlignCommodity                Adds spaces between an account and commodity so 
that the
+                       decimal points of the commodities all occur in the 
column
+                       given by |g:beancount_separator_col|.  If an amount has 
no
+                       decimal point, the imaginary decimal point to the right
+                       of the least significant digit will align.
+
+                       The command acts on a range, with the default being the
+                       current line. If the cursor happens to be inside that
+                       range and to the right of the account name, the cursor
+                       will be pushed to the right the appropriate amount, so
+                       that it remains on the same character.
+
+                       The script assumes the use of spaces for alignment. It
+                       does not understand tabs.
+
+:GetContext            Uses bean-doctor context to display the context of the
+                       current line.
 
 CHANGELOG                                              *ft-changelog-plugin*
 
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 88fad5440..d056a7eed 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -6343,6 +6343,7 @@ a{        motion.txt      /*a{*
 a}     motion.txt      /*a}*
 b      motion.txt      /*b*
 b:     eval.txt        /*b:*
+b:beancount_root       filetype.txt    /*b:beancount_root*
 b:changedtick  eval.txt        /*b:changedtick*
 b:changelog_name       filetype.txt    /*b:changelog_name*
 b:clojure_syntax_keywords      syntax.txt      /*b:clojure_syntax_keywords*
@@ -7544,6 +7545,7 @@ ft-asy-syntax     syntax.txt      /*ft-asy-syntax*
 ft-awk-plugin  filetype.txt    /*ft-awk-plugin*
 ft-bash-syntax syntax.txt      /*ft-bash-syntax*
 ft-basic-syntax        syntax.txt      /*ft-basic-syntax*
+ft-beancount-plugin    filetype.txt    /*ft-beancount-plugin*
 ft-c-omni      insert.txt      /*ft-c-omni*
 ft-c-syntax    syntax.txt      /*ft-c-syntax*
 ft-cangjie-syntax      syntax.txt      /*ft-cangjie-syntax*
@@ -7820,6 +7822,10 @@ g:ada_space_errors       ft_ada.txt      
/*g:ada_space_errors*
 g:ada_standard_types   ft_ada.txt      /*g:ada_standard_types*
 g:ada_with_gnat_project_files  ft_ada.txt      /*g:ada_with_gnat_project_files*
 g:ada_withuse_ordinary ft_ada.txt      /*g:ada_withuse_ordinary*
+g:beancount_account_completion filetype.txt    
/*g:beancount_account_completion*
+g:beancount_completion_enable  filetype.txt    /*g:beancount_completion_enable*
+g:beancount_detailed_first     filetype.txt    /*g:beancount_detailed_first*
+g:beancount_separator_col      filetype.txt    /*g:beancount_separator_col*
 g:cargo_makeprg_params ft_rust.txt     /*g:cargo_makeprg_params*
 g:cargo_shell_command_runner   ft_rust.txt     /*g:cargo_shell_command_runner*
 g:clojure_align_multiline_strings      indent.txt      
/*g:clojure_align_multiline_strings*
diff --git a/runtime/ftplugin/beancount.vim b/runtime/ftplugin/beancount.vim
new file mode 100644
index 000000000..d8bcecde9
--- /dev/null
+++ b/runtime/ftplugin/beancount.vim
@@ -0,0 +1,35 @@
+if exists('b:did_ftplugin')
+    finish
+endif
+
+let b:did_ftplugin = 1
+let b:undo_ftplugin = 'setlocal foldmethod< comments< commentstring< omnifunc<'
+let b:undo_ftplugin .= '| delc -buffer AlignCommodity'
+let b:undo_ftplugin .= '| delc -buffer GetContext'
+
+setl foldmethod=syntax
+setl comments=b:;
+setl commentstring=;\ %s
+compiler bean_check
+
+" This variable customizes the behavior of the AlignCommodity command.
+if !exists('g:beancount_separator_col')
+    let g:beancount_separator_col = 50
+endif
+if !exists('g:beancount_account_completion')
+    let g:beancount_account_completion = 'default'
+endif
+if !exists('g:beancount_detailed_first')
+    let g:beancount_detailed_first = 0
+endif
+
+command! -buffer -range AlignCommodity
+            \ :call beancount#align_commodity(<line1>, <line2>)
+
+command! -buffer -range GetContext
+            \ :call beancount#get_context()
+
+" Omnifunc for account completion.
+if get(g:, 'beancount_completion_enable', 0)
+    setl omnifunc=beancountcomplete#complete
+endif
diff --git a/runtime/indent/beancount.vim b/runtime/indent/beancount.vim
new file mode 100644
index 000000000..594ae7108
--- /dev/null
+++ b/runtime/indent/beancount.vim
@@ -0,0 +1,51 @@
+" Vim indent file
+" Language: beancount
+" Maintainer: Nathan Grigg
+" Latest Revision: 2017-03-20
+
+if exists('b:did_indent')
+    finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=GetBeancountIndent(v:lnum)
+
+let b:undo_indent = "setl inde<"
+
+if exists('*GetBeancountIndent')
+    finish
+endif
+
+function! s:IsDirective(str)
+    return a:str =~# ' 
^\s*(\d{4}-\d{2}-\d{2}|pushtag|poptag|option|plugin|include)'
+endfunction
+
+function! s:IsPosting(str)
+    return a:str =~# ' ^\s*[A-Z]\w+:'
+endfunction
+
+function! s:IsMetadata(str)
+    return a:str =~# ' ^\s*[a-z][a-zA-Z0-9\-_]+:'
+endfunction
+
+function! s:IsTransaction(str)
+    " The final \S represents the flag (e.g. * or !).
+    return a:str =~# ' ^\s*\d{4}-\d{2}-\d{2}\s+(txn\s+)?\S(\s|$)'
+endfunction
+
+function GetBeancountIndent(line_num)
+    let l:this_line = getline(a:line_num)
+    let l:prev_line = getline(a:line_num - 1)
+    " Don't touch comments
+    if l:this_line =~# ' ^\s*;' | return -1 | endif
+    " This is a new directive or previous line is blank.
+    if l:prev_line =~# '^\s*$' || s:IsDirective(l:this_line) | return 0 | endif
+    " Previous line is transaction or this is a posting.
+    if s:IsTransaction(l:prev_line) || s:IsPosting(l:this_line) | return 
&shiftwidth | endif
+    if s:IsMetadata(l:this_line)
+        let l:this_indent = indent(a:line_num - 1)
+        if ! s:IsMetadata(l:prev_line) | let l:this_indent += &shiftwidth | 
endif
+        return l:this_indent
+    endif
+    return -1
+endfunction
diff --git a/runtime/syntax/beancount.vim b/runtime/syntax/beancount.vim
new file mode 100644
index 000000000..4909c4bc0
--- /dev/null
+++ b/runtime/syntax/beancount.vim
@@ -0,0 +1,94 @@
+" Vim syntax file
+" Language: beancount
+" Maintainer: Nathan Grigg
+" Latest Revision: 2024-11-25
+
+if exists("b:current_syntax")
+    finish
+endif
+
+syntax clear
+" Basics.
+syn region beanComment start="\s*;" end="$" keepend contains=beanMarker
+syn match beanMarker " (\{\{\{|\}\}\})\d?" contained
+syn region beanString start='"' skip='\"' end='"' contained
+syn match beanAmount " [-+]?[[:digit:].,]+" nextgroup=beanCurrency contained
+            \ skipwhite
+syn match beanCurrency " \w+" contained
+" Account name: alphanumeric with at least one colon.
+syn match beanAccount " [[:alnum:]]+:[-[:alnum:]:]+" contained
+syn match beanTag " #[-[:alnum:]]+" contained
+syn match beanLink " \^\S+" contained
+" We must require a space after the flag because you can have flags per
+" transaction leg, and the letter-based flags might get confused with the
+" start of an account name.
+syn match beanFlag " [*!&#?%PSTCURM]\s\@=" contained
+
+" Most directives start with a date.
+syn match beanDate "^ \d{4}[-/]\d{2}[-/]\d{2}" skipwhite
+            \ 
nextgroup=beanOpen,beanTxn,beanClose,beanCommodity,beanNote,beanBalance,beanEvent,beanPad,beanPrice
+" Options and events have two string arguments. The first, we are matching as
+" beanOptionTitle and the second as a regular string.
+syn region beanOption matchgroup=beanKeyword start="^option" end="$"
+            \ keepend contains=beanOptionTitle,beanComment
+syn region beanOption matchgroup=beanKeyword start="^plugin" end="$"
+            \ keepend contains=beanString,beanComment
+syn region beanInclude matchgroup=beanKeyword start="^include" end="$"
+            \ keepend contains=beanString,beanComment
+syn region beanEvent matchgroup=beanKeyword start="event" end="$" contained
+            \ keepend contains=beanOptionTitle,beanComment
+syn region beanOptionTitle start='"' skip='\"' end='"' contained
+            \ nextgroup=beanString skipwhite
+syn region beanOpen matchgroup=beanKeyword start="open" end="$" keepend
+            \ contained contains=beanAccount,beanCurrency,beanComment
+syn region beanClose matchgroup=beanKeyword start="close" end="$" keepend
+            \ contained contains=beanAccount,beanComment
+syn region beanCommodity matchgroup=beanKeyword start="commodity" end="$" 
keepend
+            \ contained contains=beanCurrency,beanComment
+syn region beanNote matchgroup=beanKeyword start=" note|document" end="$"
+            \ keepend contains=beanAccount,beanString,beanComment contained
+syn region beanBalance matchgroup=beanKeyword start="balance" end="$" contained
+            \ keepend contains=beanAccount,beanAmount,beanComment
+syn region beanPrice matchgroup=beanKeyword start="price" end="$" contained
+            \ keepend contains=beanCurrency,beanAmount
+syn region beanPushTag matchgroup=beanKeyword start=" ^(push|pop)tag" end="$"
+            \ keepend contains=beanTag
+syn region beanPad matchgroup=beanKeyword start="pad" end="$" contained
+            \ keepend contains=beanAccount,beanComment
+
+syn region beanTxn matchgroup=beanKeyword start=" \s+(txn|[*!&#?%PSTCURM])" 
skip="^\s"
+            \ end="^" keepend contained fold
+            \ 
contains=beanString,beanPost,beanComment,beanTag,beanLink,beanMeta
+syn region beanPost start="^ \C\s+(([*!&#?%PSTCURM]\s+)?[A-Z])@=" end="$"
+            \ 
contains=beanFlag,beanAccount,beanAmount,beanComment,beanCost,beanPrice
+syn region beanMeta matchgroup=beanTag start="^ 
\C\s+[a-z][-_a-zA-Z0-9]*:(\s|$)@=" end="$"
+
+syn region beanCost start="{" end="}" contains=beanAmount contained
+syn match beanPrice "\V@@\?" nextgroup=beanAmount contained
+
+syn region beanHashHeaderFold
+    \ start="^\z(#\+\)"
+    \ skip="^\s*\z1#\+"
+    \ end="^\(#\)\@="
+    \ fold contains=TOP
+
+syn region beanStarHeaderFold
+    \ start="^\z(\*\+\)"
+    \ skip="^\s*\z1\*\+"
+    \ end="^\(\*\)\@="
+    \ fold contains=TOP
+
+highlight default link beanKeyword Keyword
+highlight default link beanOptionTitle Keyword
+highlight default link beanDate Keyword
+highlight default link beanString String
+highlight default link beanComment Comment
+highlight default link beanAccount Identifier
+highlight default link beanAmount Number
+highlight default link beanCurrency Number
+highlight default link beanCost Number
+highlight default link beanPrice Number
+highlight default link beanTag Tag
+highlight default link beanLink Comment
+highlight default link beanMeta Special
+highlight default link beanFlag Keyword

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wYTLs-00DQTo-Jx%40256bit.org.

Raspunde prin e-mail lui