How to detect if Vim is running in restricted mode? - vim

... or in any mode for that matter.
I just want to prevent some extensions from loading when that is the case, something like:
if ! currentmode('restricted')
Bundle('some-extension')
endif

You're right; a special variable like v:vimmode would be helpful, but I don't think such a thing currently exists. Why not raise this on the vim_dev mailing list?!
In the meantime, you have to detect the mode through the result of invoking something that is forbidden in restricted mode. My best idea that is the least intrusive on success is invoking writefile() with an empty file name:
silent! call writefile([], '')
" In restricted mode, this fails with E145: Shell commands not allowed in rvim
" In non-restricted mode, this fails with E482: Can't create file <empty>
let isRestricted = (v:errmsg =~# '^E145:')

I am not sure if this is a good idea:
restricted-mode disabled external commands (also some related functions). If we call external command or some certain functions in a rvim, we get Error E145.
So maybe you could just call some dummy external command via system(), then catch the Exception E145. to distinguish if it is in restricted mode. e.g.
try
call system("echo x") "or even call system("")
catch /E145/
"your codes
endtry
hope it helps

Related

How do I stop vim reading in the default syntax?

I have defined my own javascript syntax file, which is in ~/.vim/syntax/after/
The problem is that the default syntax file that exists in /usr/share/vim/vim82/syntax is loaded along side my custom syntax file (it seems to be loaded before my custom syntax).
The result is that it conflicts with my custom syntax.
How do I ONLY load my custom syntax file? Not the default one.
Of course I could just delete the default one (which works), but that seems like a not very robust fix to the problem considering it will just be put back there when vim is updated.
I have "syntax clear" at the top of my custom file but it doesn't help.
I also note that the default syntax file has the following at the top:
if !exists("main_syntax")
" quit when a syntax file was already loaded
if exists("b:current_syntax")
finish
endif
let main_syntax = 'javascript'
elseif exists("b:current_syntax") && b:current_syntax == "javascript"
finish
endif
But that doesn't do anything either.
I have also tried putting the syntax folder in .vim/syntax, as suggested on the official vim page, but that doesn't seem to do anything at all (as in, it's not read at all).
Thankyou.
In principle, the order in which syntax scripts are sourced is the following:
~/.vim/syntax/foo.vim
$VIMRUNTIME/syntax/foo.vim
~/.vim/after/syntax/foo.vim
~/.vim/after/syntax/ comes last. It is not a good place if you are writing a complete replacement of a built-in syntax script because you have at least one syntax script sourced before yours. On the other hand, it is a good place if what you want to do is either add your own stuff or selectively override stuff that has been set earlier.
$VIMRUNTIME/syntax/ is not a good place either, because it is a systemwide location. Your customisation is supposed to happen in your $HOME, right?.
~/.vim/syntax/ comes first so it is the ideal place for custom syntax scripts, if you add the following line:
let b:current_syntax = "javascript"
This is because $VIMRUNTIME/syntax/javascript.vim begins with the following boilerplate:
if !exists("main_syntax")
" quit when a syntax file was already loaded
if exists("b:current_syntax")
finish
endif
let main_syntax = 'javascript'
elseif exists("b:current_syntax") && b:current_syntax == "javascript"
finish
endif
which, essentially, throws the towel if b:current_syntax is set, and ends with:
let b:current_syntax = "javascript"
to "mark its territory", if you will.
You don't have to check for anything because you come first, but you still have to tell other syntax scripts to mind their own business.
Note that bad mannered syntax scripts may not check for that buffer-local variable so it is entirely possible to still have conflicts even if you are doing the right thing. But that's the nature of Vim.

Using vimscript to run test scripts by utilizing normal vim commands

I started using VIM as my editor around six months back and I enjoy it very much. However, there are a few work related scripts that I'd like to implement to make my life easier. If there is anyone who can help me I would be grateful.
This is my question. I have some tests written in python and I wrote a key mapping to run those tests using vim terminal. It works perfectly. However, now I want to use VimScript and some vim functions to make it look better. I'm a beginner in VimScript and therefore, I'm not sure whether this is doable.
My folder structure looks like,
.
├── my_test.py
└── test
└── testRunner.py
1 directory, 2 files
My test code looks something like,
my_test.py:
#!/bin/python
class MyTest1:
def Run():
# Test body
class MyTest2:
def Run():
# Test body
test/testRunner.py:
#!/bin/python
print "Running the test"
My current key-mapping in .vimrc looks like:
nnoremap <leader>t mZ/class<CR>Nwyiw:noh<CR>:terminal<CR>cd test<CR>python testRunner.py <C-W>"0<CR><C-W><C-W>'Z
What this does is,
Find the test name (the test that I'm currently editing)
Copy the name and run that test name in a vim-terminal
What I want it to be something which looks like:
nnoremap <leader>t :call RunThisTest()<CR>
function! RunThisTest()
RememberEditContext()
FindAndCopyTestName()
RunTestInTestDirectory()
ReturnToEditContext()
endfunction
Can someone help me in developing these functions?
Thank you in advance!
One option is to use the :normal! command directly, which allows you to run a sequence of keystrokes directly as you'd have used them in a mapping.
But it turns out we can do better, much better, so let's get to it!
Searching and Matching
You can use the search() function to look for the class you're in. You can pass it flags, such as bcnW, to have it search backwards, possibly match at the cursor position, do not move the cursor and do not wrap around the file. Putting it all together:
let line = search('^class \w', 'bcnW')
This will return a line number if there was a positive match, or 0 if there wasn't one. If there was a match, we can use getline() to get its contents and then matchlist() to capture the name of the class.
let [_, classname; _] = matchlist(getline(line), '^class \(\w\+\)')
As you can see, using Vimscript we were able to get the classname without moving the cursor and without touching the search register. So we didn't need to set any marks and we won't need to worry about recovering the current position and view!
Running a command
Now it's time to run a command on the terminal. We can simplify the process by passing it a command directly. (Note that there's a difference here, in that the terminal will run just that command, it won't leave the shell around after finished. Depending on your use case, you might prefer to do something more akin to what you're doing now.)
We can run the command in a terminal with:
:terminal ++shell cd test && python testRunner.py MyTest1
But, of course, we need to actually pass it the class name we got, not a fixed value here. We can use the :execute command for this purpose. It takes a string and runs it as a Vimscript command. We can use this to assemble the string dynamically.
execute "terminal ++shell cd test && python testRunner.py ".shellescape(classname)
Finally, to go back to the original window, we can use the :wincmd command, more specifically wincmd p.
Putting it together
The resulting function is:
function! RunThisTest() abort
let line = search('^class \w', 'bcnW')
if line == 0
echoerr "Not inside a test class!"
return
endif
let [_, classname; _] = matchlist(getline(line), '^class \(\w\+\)')
execute "terminal ++shell cd test && python testRunner.py ".shellescape(classname)
wincmd p
endfunction
nnoremap <silent> <leader>t :call RunThisTest()<CR>
There's definitely room for improvement, but this should get you started!
Saving and restoring context
We didn't go into saving and restoring context, since this case actually didn't need any of that!
If you were to develop functions that use commands that affect global context, you can use Vimscript to save and restore it.
For example, if you're going to search, you can save the #/ register and restore it after the search:
let saved_search = #/
/class
let #/ = saved_search
If you're going to yank into a register, you can save and restore it too. For example, #" for the default register. You should also save the register type, which records whether the contents were taken in a character-wise, linewise or blockwise context.
let saved_register = getreg('"')
let saved_regtype = getregtype('"')
normal! y3W
let words = getreg('"')
call setreg('"', saved_register, saved_regtype)
You can also save the current view, which includes the position your cursor is in, but also the other parameters of the window, such as what the first displayed line and column are, such that you can fully restore that context. See the winsaveview() and winrestview() functions for details on that.
Managing Terminals
There are functions to control the terminal that go way beyond what :terminal can do.
For instance, the much richer term_start() allows running a command as a list and passing options such as 'cwd' to run the command on a different directory.
So we could simplify our test execution with:
call term_start(['python', 'testRunner.py', classname], {'cwd': 'test'})
There's also term_sendkeys() which you can use to send keystrokes to the terminal. For example, if you prefer to start a shell and call the Python script through the shell:
let termbuf = term_start(&shell, {'cwd': 'test'})
call term_sendkeys(termbuf, "python testRunner.py ".shellescape(classname)."\r")
You can also use term_getline(termbuf, '.') to get the contents of the line where the cursor currently is. For instance, you could use that to detect whether the terminal is on a shell prompt (line ending in $ and whitespace) or still on an execution of a test runner.
Finally, you can even have the command running inside the terminal call Vim commands! Through special escape sequences, it can call exported functions or ask Vim to open files for editing. See :help terminal-api for details.
Learning More
This is all very neat... But how can I learn more?
My first strong recommendation would be to read the excellent "Learn Vimscript the Hard Way", by Steve Losh. It covers the basics of the language, how to interface with the editor (mappings, auto-commands, indentation expressions, filetypes) and basics of how to put together Vim plug-ins. It also covers common pitfalls of Vimscript and best practices for writing reliable code. That's a must if you want to get serious about scripting Vim.
Second suggestion is read the excellent documentation that's available through :help! Few applications are as well documented as Vim is, so knowing your way around the help system can really help a lot.
Third is using StackExchange. In particular, the Vi & Vim SE which is dedicated to the subject. Not only you'll find great answers there and you'll be able to ask great questions, you will also have the opportunity of seeing great questions, wonder how to solve them and possibly take a stab at writing an answer. (Personally, since I started using the Vi & Vim SE, my Vim-foo has greatly improved, to the point I can consider myself almost an expert.) I strongly recommend that.
Finally, practice. It typically takes a few attempts to get something really right. But the fact that the environment is fairly dynamic and flexible allows for experimentation. You can type and experiment with the commands in the editor itself, so it's usually quick to test your code and get it right as you're writing it.

Vim: report error on a single line

When an error is raised inside a Vimscript function, Vim reports an error in the following format:
Error detected while processing function <SNR>My_Function:
line X:
EXXX: Error Message Here
This is very distracting. Ideally, I would like this to be formatted in some way to fit on a single line, but if that's not possible, then I just want the last EXXX line with the actual error. Is it possible to change this?
I don't like that behavior neither, but it is how it is, and cannot be changed.
Your functions need to :catch any errors, and convert this into the desired one-line error message:
function! s:My_Function()
try
" commands here
catch /^Vim\%((\a\+)\)\=:/
echohl ErrorMsg
echomsg substitute(v:exception, '^\CVim\%((\a\+)\)\=:', '', '')
echohl None
endtry
endfunction
This is how most plugins handle it. The behavior isn't completely like that of built-in commands, though: The error is effectively "swallowed", and subsequent commands (e.g. in a sequence cmd1 | call <SID>My_Function() | cmd3) will be executed despite the error. For most uses this is okay, and most users probably are even unaware of this.
better alternative
To make the custom command behave like a built-in one, the error message needs to be returned to the custom command or mapping, and :echoerr'd there. In my ingo-library plugin, I have corresponding helper functions:
function! s:My_Function()
try
" commands here
return 1 " Success
catch /^Vim\%((\a\+)\)\=:/
call ingo#err#SetVimException()
return 0 " Failure, use stored error message.
endif
endfunction
if ! <SID>My_Function() | echoerr ingo#err#Get() | endif
There is another approach to the problem: error messages can be decoded.
In my lh-vim-lib, I've defined a function that decodes the last error message and displays the result in the qf-window.
Given lh-vim-lib is installed, you've to add something like
command! WTF call lh#exception#say_what()
to your .vimrc.
The feature by itself has been inspired from https://github.com/tweekmonster/exception.vim, the difference is that I've used stuff I've already implemented in my library plugin in order to:
support localized messages
support autoloaded functions, even when # isn't in &isk (which is possible depending on the settings associated to the current filetype/buffer)
have as few loops as possible
factorize code with my logging, and unit testing, and DbC frameworks.
Near the end of the screencast I've recorded to demonstrate my Dbc framework, I've used :WTF to display the last error in a human friendly manner.

Trojan in vim's latex_suite?

I was going through some code for latex_suite called vim_latex (http://vim-latex.sourceforge.net/) and I found few interesting lines in the file called "templates.vim":
" Back-Door to trojans !!!
function! <SID>Compute(what)
exe a:what
if exists('s:comTemp')
return s:comTemp.s:comTemp
else
return ''
endif
endfunction
Well, I'm not an expert on vim code, so I cannot interpret these lines except for the comment that freak me up a bit. Do you guys have an idea about what is happening ?
Edit:
The function seems to be called only by the following one:
" ProcessTemplate: processes the special characters in template file. {{{
" This implementation follows from Gergely Kontra's
" mu-template.vim
" http://vim.sourceforge.net/scripts/script.php?script_id=222
function! <SID>ProcessTemplate()
if exists('s:phsTemp') && s:phsTemp != ''
exec 'silent! %s/^'.s:comTemp.'\(\_.\{-}\)'.s:comTemp.'$/\=<SID>Compute(submatch(1))/ge'
exec 'silent! %s/'.s:exeTemp.'\(.\{-}\)'.s:exeTemp.'/\=<SID>Exec(submatch(1))/ge'
exec 'silent! g/'.s:comTemp.s:comTemp.'/d'
" A function only puts one item into the search history...
call Tex_CleanSearchHistory()
endif
endfunction
According to the header file description, the aim of these functions is to handle templates located into a specific directory.
I think the comment is intended as a warning. The function <SID>ProcessTemplate() goes through a template file, looks for certain (configurable) patterns, and calls <SID>Compute(what) where the argument what is text extracted from the template. Note the line :exe a:what.
If you install a template file from an untrusted source, then bad things can happen.
Of course, if you install a vim plugin from an untrusted source, equally bad things can happen. Putting malware in a template file adds a few levels of indirection, making it harder to implement and harder to diagnose.
It is possible that this code was written before the :sandbox command was added to vim, and that might be an easy way to make this code safer. I have not looked at what is allowed in the sandbox and compared it to the intended use of this template processing.

Indenting Fortran code in Vim

I have a fortran code which looks like this:
open(2,file=filenm(i),status='unknown')
do j=1,num_lines
do k=1,dime
read(2,*) z(k)
enddo
if( j .ge. 1000 ) then
do k=1,dime
sumz(k)=sumz(k)+z(k)
enddo
nsteps=nsteps+1.0
endif
enddo
close(2)
as you can see the indentation is not even, I would like to have something
like this:
open(2,file=filenm(i),status='unknown')
do j=1,num_lines
do k=1,dime
read(2,*) z(k)
enddo
if( j .ge. 1000 ) then
do k=1,dime
sumz(k)=sumz(k)+z(k)
enddo
nsteps=nsteps+1.0
endif
enddo
close(2)
I can go line by line fixing the indentation but the code is kind of big.
I appreciate any comment.
I've gone through a very similar process of trying to get Fortran indenting to work in vim. I still don't have it great, but I've made some progress. The script that arutaku posted (http://www.vim.org/scripts/script.php?script_id=2299) is where I started, but you need to make sure that filetype plugin indent on is in your vimrc.
I also needed a script for determining if I was using fixed-form or free-form syntax, since in my environment I use both. This is recommended in the documentation (http://vimdoc.sourceforge.net/htmldoc/syntax.html#ft-fortran-syntax). As it specifies, I put this script in ~/.vim/ftplugin/fortran.vim:
let s:extfname = expand("%:e")
if s:extfname ==? "f90"
let fortran_free_source=1
unlet! fortran_fixed_source
else
let fortran_fixed_source=1
unlet! fortran_free_source
endif
I also use a script so that I can press F7 to automatically complete certain constructs: http://www.vim.org/scripts/script.php?script_id=2487. I also put that in ~/.vim/ftplugin/.
I also have these in my vimrc to try to improve things further:
let fortran_do_enddo=1
let fortran_more_precise=1
let fortran_have_tabs=1
I believe those are supposed to interact with the first script to control its behavior, but I'm not convinced that they all work for me. I know the first is supposed to properly indent do/enddo blocks. The issue there is that since old-style Fortran allows a do without a matching enddo, the script can't indent them properly unless you can guarantee that you won't use unmatched 'do' statements. The let fortran_do_enddo=1 is supposed to be that guarantee, but it doesn't seem to work for me. The last one is supposed to allow usage of the tab character, which old Fortran considered bad, so it prevents them from being marked as errors. That one appears to work for me. And to be honest, I don't remember what the middle one is supposed to do nor if it works for me.
Edit:
I discovered that there was an older version of the the first script in my vim installation directory (/usr/share/vim/vim70/indent/ in my case) to which I do not have access rights. You might have those rights, and if it's causing you problems, overwrite or delete it. If, like me, you can't edit it, you can get the version in your $HOME to overwrite the functions from the first. I did this by doing two things. First I had to remove the checks to see if the functions already exist (the statements like if exists("*FortranGetFixedIndent")) and then I had to change all the function declarations to force overwriting by using the ! character, i.e. function! SebuFortranGetFreeIndent(). As far as I can tell, everything now works roughly as I would expect!
Does gg=G work?
gg: go to top
=: indent...
G: ... until the end
Try: https://sourceforge.net/projects/findent/files/
From the README:
findent: indents/beautifies/converts Fortran sources
findent supports Fortran-2008
findent can convert from fixed form to free form
binaries for Unix and Windows (findent and findent.exe respectively)
wrapper for processing one or more files in one call available for Unix and Windows (wfindent and wfindent.bat respectively)
(g)vim users: findent can act as a plug-in to format your edit file with the '=' command
gui frontent available in a separate package: jfindent
You can use auto-indentation writing <x>== (in command mode) where x is the number of lines to be indented.
If vim does not have fortran indentation you can load the following plugin and try the == combination: http://www.vim.org/scripts/script.php?script_id=2299

Resources