From 66818f40225eb2c2722ba62a3743e478d9111b03 Mon Sep 17 00:00:00 2001 From: Calvin Morrison Date: Tue, 23 Jun 2026 16:38:48 -0400 Subject: Add vim syntax and indent support for ProvideX/PVX source files Highlights comments, strings, labels, control flow, and the object method-call operator; indents to match the convention encoded in native-sage-connector's formatpvx.pl (one tab per block level). Co-Authored-By: Claude Sonnet 4.6 --- ftdetect/pvx.vim | 2 ++ ftplugin/pvx.vim | 14 +++++++++++ indent/pvx.vim | 56 +++++++++++++++++++++++++++++++++++++++++ syntax/pvx.vim | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 ftdetect/pvx.vim create mode 100644 ftplugin/pvx.vim create mode 100644 indent/pvx.vim create mode 100644 syntax/pvx.vim diff --git a/ftdetect/pvx.vim b/ftdetect/pvx.vim new file mode 100644 index 0000000..8a07ca9 --- /dev/null +++ b/ftdetect/pvx.vim @@ -0,0 +1,2 @@ +" Detect ProvideX/PVX source files used by the Sage 100 connector. +au BufRead,BufNewFile *.pvxsrc setfiletype pvx diff --git a/ftplugin/pvx.vim b/ftplugin/pvx.vim new file mode 100644 index 0000000..8134126 --- /dev/null +++ b/ftplugin/pvx.vim @@ -0,0 +1,14 @@ +" Filetype settings for ProvideX / PVX source files. +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal commentstring=!\ %s +setlocal comments=:! + +" formatpvx.pl indents with one literal tab per level; match that. +setlocal noexpandtab +setlocal tabstop=4 +setlocal shiftwidth=4 +setlocal softtabstop=4 diff --git a/indent/pvx.vim b/indent/pvx.vim new file mode 100644 index 0000000..bd3931f --- /dev/null +++ b/indent/pvx.vim @@ -0,0 +1,56 @@ +" Vim indent file +" Language: ProvideX / PVX (Sage 100 business object source, *.pvxsrc) +" Maintainer: native-sage-connector +" +" Mirrors the indent convention encoded in formatpvx.pl: a label, switch/ +" case/default, for, while, repeat, or a trailing "{" opens one level; +" return, break, next, wend, until, else, end (def/switch/...), endif, +" endcase, or a leading "}" closes one level for that line itself. + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetPvxIndent() +setlocal indentkeys=o,O,0{,0},!^F,=end,=next,=wend,=until,=else,=break,=default,=case,=endif,=endcase +setlocal autoindent +setlocal nosmartindent +setlocal nocindent + +if exists("*GetPvxIndent") + finish +endif + +" Strip leading indent, a trailing "! comment", and surrounding whitespace, +" so keyword patterns can be anchored at column 0 regardless of existing indent. +function! s:PvxStripComment(line) + let l = substitute(a:line, '\s*!.*$', '', '') + let l = substitute(l, '^\s*', '', '') + return substitute(l, '\s*$', '', '') +endfunction + +" Lines whose presence means the *next* line gets one more indent level. +let s:pvx_opener = '\c^\%(def\|switch\|case\|default\|for\|while\|repeat\)\>\|{\s*$\|^[A-Za-z_][A-Za-z0-9_.]*:\s*$' + +" Lines that get one *less* indent level applied to themselves. +let s:pvx_closer = '\c^\%(end\|endif\|endcase\|next\|wend\|until\|else\|break\|return\)\>\|^}' + +function! GetPvxIndent() + let lnum = prevnonblank(v:lnum - 1) + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + + if s:PvxStripComment(getline(lnum)) =~ s:pvx_opener + let ind += &shiftwidth + endif + + if s:PvxStripComment(getline(v:lnum)) =~ s:pvx_closer + let ind -= &shiftwidth + endif + + return ind < 0 ? 0 : ind +endfunction diff --git a/syntax/pvx.vim b/syntax/pvx.vim new file mode 100644 index 0000000..d7210a4 --- /dev/null +++ b/syntax/pvx.vim @@ -0,0 +1,76 @@ +" Vim syntax file +" Language: ProvideX / PVX (Sage 100 business object source, *.pvxsrc) +" Maintainer: native-sage-connector + +if exists("b:current_syntax") + finish +endif + +" PVX keywords are case-insensitive in practice (mixed-case is common). +syntax case ignore + +" --- Comments ------------------------------------------------------- +syn keyword pvxTodo TODO FIXME XXX NOTE contained +syn match pvxComment "!.*$" contains=pvxTodo,@Spell + +" --- Strings & literals --------------------------------------------- +" Doubled quotes ("") inside a string are a literal embedded quote. +syn region pvxString start=/"/ skip=/""/ end=/"/ + +" $$ is the "any/blank key" marker used in select/begin ranges. +syn match pvxByteLiteral "\$\$" +" $XX$ is a two-digit hex byte literal, e.g. $8A$ (field separator). +syn match pvxByteLiteral "\$[0-9A-Fa-f][0-9A-Fa-f]\$" + +syn match pvxNumber "\<\d\+\(\.\d\+\)\?\>" + +" Pseudo-labels used as err=/dom= targets (*next, *break, *resume, ...) +syn match pvxPseudoLabel "\*\h\w*" + +" Labels: BARE_WORD: alone on a line (optionally trailing a comment) +syn match pvxLabel "^\s*[A-Za-z_][A-Za-z0-9_.]*:\s*\(!.*\)\=$" + +" Object method-call operator: obj'MethodName( +syn match pvxMethodCall "'\h\w*" + +" --- Statements / control flow -------------------------------------- +syn keyword pvxConditional if then else switch case default +syn keyword pvxRepeat for to step next while wend repeat until +syn keyword pvxLabelKw break continue exit goto gosub return stop + +syn keyword pvxStatement + \ open close read write dim remove call select from begin where + \ sep iol rec key err dom execute drop object on error try catch + \ finally let print input rem + +syn keyword pvxStructure + \ def class like function perform local public global common + \ create delete required end + +syn keyword pvxOperator and or not + +" --- Built-in functions ---------------------------------------------- +syn keyword pvxFunction + \ nul pos tbl ucs lcs mid str stp pad varchar sub new find len + \ num evn clear msg dte lst vis dll max min val kec tim mod fib + \ lof eof dir exists chr asc int sqr rnd cvs cvt day abs kgen + \ ioc iol ios opt lpt ptr env exp log pi sgn dle fin fid dlm dod + +" --- Highlight links -------------------------------------------------- +hi def link pvxComment Comment +hi def link pvxTodo Todo +hi def link pvxString String +hi def link pvxByteLiteral SpecialChar +hi def link pvxNumber Number +hi def link pvxPseudoLabel Special +hi def link pvxLabel Label +hi def link pvxMethodCall Function +hi def link pvxConditional Conditional +hi def link pvxRepeat Repeat +hi def link pvxLabelKw Keyword +hi def link pvxStatement Statement +hi def link pvxStructure Structure +hi def link pvxOperator Operator +hi def link pvxFunction Function + +let b:current_syntax = "pvx" -- cgit v1.2.3