0

I have a syntax file for CSV files. To be able setting the separator, there is a function ChangeChar() and the syn match ... terms are variable.

To initialize it ; is used. But to call the SetMatches() function, it must be declared first. Is it possible to declare functions at the top of the syntax file to let it clearly arranged?

In some programming languages this is possible, in others not needed.

Calling my file there comes

error executing "/home/.../csv.vim"
line 21:
E117: unknown function: SetMatches

and it doesn't work. Calling set syntax=csvagain, it works, because the function now is defined, I think.

My syntax file (extract)

let g:seperator = ';'
call SetMatches()     " not defined yet

function! ChangeChar()
  echo "Enter separator: "
  let char = getchar()
  let char = nr2char(char)
  let g:seperator = char
  call SetMatches()
endfunction

function! SetMatches()
  let g:pattern   = '/[^' . g:seperator . ']*' . g:seperator . '\?/'
  execute 'syn match csvField1 ' . g:pattern . ' display           nextgroup=csvField2'
  execute 'syn match csvField2 ' . g:pattern . ' display contained nextgroup=csvField3'
  execute 'syn match csvField3 ' . g:pattern . ' display contained'
endfunction

hi def link csvField1 Normal
hi def link csvField2 Constant
hi def link csvField3 Special

How can I make SetMatches() be found before declared in file?


Syntax file (complete)

" Vim syntax file
" Language: csv

if exists("b:current_syntax")
    finish
endif

let s:cpo_save = &cpo
set cpo&vim

syn clear

" Syntax-Mapping
let b:syn_exec = []
let b:syn_exec = add(b:syn_exec, '')
let b:syn_exec = add(b:syn_exec, '')
let b:syn_exec = add(b:syn_exec, 'call ChangeChar()')

let g:seperator = ';'
call SetMatches()


function! ChangeChar()
  echo "Enter separator: "
  let char = getchar()
  let char = nr2char(char)
  let g:seperator = char
  call SetMatches()
  " TODO Bestätigung weg
endfunction

function! SetMatches()
  let g:pattern   = '/[^' . g:seperator . ']*' . g:seperator . '\?/'
  " TODO Prüfen, ob pattern korrekt ist
  execute 'syn match csvField1 ' . g:pattern . ' display           nextgroup=csvField2'
  execute 'syn match csvField2 ' . g:pattern . ' display contained nextgroup=csvField3'
  execute 'syn match csvField3 ' . g:pattern . ' display contained nextgroup=csvField4'
  execute 'syn match csvField4 ' . g:pattern . ' display contained nextgroup=csvField5'
  execute 'syn match csvField5 ' . g:pattern . ' display contained nextgroup=csvField6'
  execute 'syn match csvField6 ' . g:pattern . ' display contained nextgroup=csvField7'
  execute 'syn match csvField7 ' . g:pattern . ' display contained'
endfunction



hi def link csvField1 Normal
hi def link csvField2 Constant
hi def link csvField3 Special
hi def link csvField4 Identifier
hi def link csvField5 Statement
hi def link csvField6 PreProc
hi def link csvField7 Type


let b:current_syntax = "csv"

let &cpo = s:cpo_save
unlet s:cpo_save
3
  • I don't think I understand the question. What keeps you from calling the function at the bottom of the file, where it's defined? Is there some cyclic dependency that I don't get?
    – Friedrich
    Commented Feb 13, 2023 at 11:05
  • @Friedrich - This would work at moment. But there should come some more functions so that the syntax file may become unclear.
    – Andy A.
    Commented Feb 13, 2023 at 11:09
  • I don't think it is possible in VimScript. In general I believe it is only possible in compiled language where you have a linker (e.g. it is not possible in Python) Commented Feb 13, 2023 at 11:32

1 Answer 1

3

Vimscripts are sourced one line at a time, from top to bottom, so "things" have to be defined before they are consumed:

let a = 1
echomsg a
echomsg b
let b = 2
1
E121: Undefined variable: b

In your case, it should look something like:

function! SetMatches()
  " your code here
endfunction
call SetMatches()

Note that SetMatches() is found the second time around because it was defined in the global scope during the first pass. This is bad because your syntax script depends on a global "thing" that could be present or absent or, worse, overridden by some other script.

The canonical solution to your problem would be to define your function

  • before it is called,
  • in the script scope.

Example:

function! s:SetMatches()
  " your code here
endfunction
call s:SetMatches()
6
  • So my idea just doen't work. Thanks. Because of scope: I have to learn much... Thank you for advice.
    – Andy A.
    Commented Feb 13, 2023 at 12:16
  • 1
    @AndyA. not because of scope but because of how the universe we live in happens to work. You need to have something before you can use it. You need to buy a car before you can drive it. You need to bake a cake before you can eat it. You need to define a function before you can call it. The only thing where this analogy does not hold true is money because you can spend money you don't have which creates a lot of new problems.
    – Friedrich
    Commented Feb 13, 2023 at 12:44
  • @Friedrich yes, is OK. My intention was to say: "Hello script, you will have a function named ... later on. Please read the whole script before start working." - Might be a stupid idea.
    – Andy A.
    Commented Feb 13, 2023 at 12:50
  • @AndyA. don't get me wrong, I do think hiding implementation details is a good idea. So not stupid ... just impractical. Mainly, I didn't want to let a wrong explanation go uncommented.
    – Friedrich
    Commented Feb 13, 2023 at 13:29
  • @Friedrich - Ah, thanks
    – Andy A.
    Commented Feb 13, 2023 at 14:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.