7

I need to create a variable with a fool folder direct which need to concatenate from a environment variable and the end notes

function! MyFoo(arg)
    let repo=$Project + "/cfora"                #Q1      
    let build=$project + "/build/cfora/debug"   
    exec "!cd build"                            
    exec "!cmake repo"                          #02
endfunction

Question:

Q1: How can I concatenate the value of the environment variable $Project with the end node string /cfora into one string and assign it to a new variable repo?

Q2: How can I refer the variable in the string not print the variable name? For example here, I want to ultimately run cmake /root/to/the/project/repo, not cmake repo

2 Answers 2

12

In vim dot is a concatenation operator:

let repo = $Project . '/cfora'

To "refer" variable in the string, well, in your case it is just a concatenation again:

exe '!cmake ' . repo

PS

I would suggest to use built in make facilities for this.

And vim lcd instead of exe '!cd ...

1
  • If you have read this answer only, read also the answer from Zoe if you are using recent vim versions.
    – aturegano
    Apr 20, 2023 at 16:13
6

The answer has partly changed as of Vim 8.1 patch 1114, where . is becoming obsolete. While it's not currently wrong to use . in non-Vim9 vimscript (because backwards compatibility), you can use the .. operator instead.

This does have a couple subjective and objective advantages. Quoting :h expr-..:

For String concatenation ".." is preferred, since "." is ambiguous, it is also used for |Dict| member access and floating point numbers. In |Vim9| script and when |vimscript-version| is 2 or higher, using "." is not allowed.

In either case, .. is the more modern option. As an aside, it's also the operator you'll want to use if you upgrade to Vim9script, though that requires a few other changes, and I'm not covering that here. See :h Vim9 if this sounds interesting, and you have a relatively up-to-date Vim 8.2 install, or if you've installed Vim 9.0+.

With your current code, though, and Vim 8.1 with patch 1114, or newer, you can use:

let repo = $Project .. '/cfora'

This does the exact same thing as $Project . '/cfora'; whether you want to use .. over . is up to you. The arguments in favor of it were mentioned earlier in this answer.

As for executing, you don't actually need to use . or .. at all; by passing multiple strings, Vim concatenates them for you with a space:

exe '!cmake' repo

Vim interprets this as exe '!cmake <content of the repo variable without brackets>'. From :h :execute:

      Multiple arguments are concatenated, with a space in
      between.  To avoid the extra space use the ".."
      operator to concatenate strings into one argument.

This also holds for some other similar-style keywords, including echo (and the related echom and echoerr), and likely a couple others that I don't remember off the top of my head.


While I'm writing an answer, this line does nothing:

exec "!cd build"                            

You're using cd in a subprocess, and because of how cd works, the subprocess cannot change the working directory of the parent process; in your case, that's Vim. This particular subprocess also dies immediately after changing the directory, and doesn't affect any other subprocesses made afterwards.

Assuming you're using Linux, or otherwise use Vim in such a way that it can use a UNIX shell, a valid command would be:

exec "!cd" build "&& cmake" repo

Alternatively, you can use exec 'cd' build on its own line; the main difference here is that cd in this context changes Vim's working directory, rather than a child process that immediately dies. As Maxim also mentioned, you can use lcd instead of cd, but it really depends on your use. exec without ! executes the content as a Vim command, and :cd is indeed a valid Vim command.

Or even better, have CMake do it for you. I haven't tried this myself, but this answer outlines it; see the second or third bullet point, depending on what version of CMake you're running. Using those flags should let you fully drop any form of cd from the command, and maintain compatibility with shells that don't support && (particularly stock Windows). Concatenation is still needed to supply the parameters, so you should be able to do something like this (CMake 3.13+, assuming the flags work the way I think they do):

exec "!cmake -B" build "-S" repo
1
  • 1
    A note that when the string interpolation gets complex, printf might be a good alternative. Great answer :)
    – D. Ben Knoble
    Jun 19, 2022 at 14:27

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.