Highlight under-cursor code block? - syntax-highlighting

I'm looking for a way to highlight the code block currently under the cursor while in normal mode.
This would work kinda like the set showmatch config options does, but the detection and highlighting would stretch over the entire block.
Any way to achieve this functionality either with config options or a (preferably existing) script ?

Short answer: no.
Long answer: there are ways to achieve this, but you'll have to sacrifice a lot of other fairly essential highlighting features.
There is certainly no way to do it with a simple option. The problem that you may come across is that Vim doesn't allow overlapping regions, so if you have rainbow.vim installed or anything else that makes a region in the area of your block, it's lost. It's also difficult (although I'd welcome any corrections) to have multiple highlighted groups, so one that sets the background colour and another that sets the foreground. This is very limiting as you'll see.
However, if fancy playing around, read on.
I'm making the assumption here that you're using C code in the same coding style as me, but this could easily be changed...
Here's a simple function that should help show you what's involved:
function! HighlightBlock()
" Search backwards and forwards for an opening and closing brace
" at the start of the line (change this according to your coding
" style or how you define a block).
let startmatch = search('^{', 'bcnW')
let endmatch = search('^}', 'cnW')
" Search in the other direction for each to catch the situation
" where we're in between blocks.
let checkstart = search('^{', 'cnW')
let checkend = search('^}', 'bcnW')
" Clear BlockRegion if it exists already (requires Vim 7 probably)
try
syn clear BlockRegion
catch
endtry
" If we're not in a block, give up
if ((startmatch < checkstart) && (endmatch > checkstart))
\ || ((startmatch < checkend) && (endmatch > checkend))
\ || (startmatch == 0)
\ || (endmatch == 0)
return
endif
" Create a new syntax region called "BlockRegion" that starts
" on the specific lines found above (see :help \%l for more
" information).
exec 'syn region BlockRegion'
\ 'start=' . '"\%' . startmatch . 'l"'
\ 'end=' . '"\%' . (endmatch+1) . 'l"'
" Add "contains=ALL" onto the end for a different way of
" highlighting, but it's not much better...
" Define the colours - not an ideal place to do this,
" but good for an example
if &background == 'light'
hi default BlockRegion guibg='#AAAAAA'
else
hi default BlockRegion guibg='#333333'
endif
endfunction
To use the function, source it from somewhere and then create an autocmd to call it whenever something changes, e.g.
au CursorMoved *.c call HighlightBlock()
See the following for some autocommands you may want to consider:
:help CursorHold
:help CursorHoldI
:help CursorMoved
:help CursorMovedI

Related

NeoVim: Broken Syntax Highlighting after heredoc "lua << EOF" in VimScript

I'm facing the problem that my init.vim becomes not highlighted properly after the line with lua << EOF in NeoVim. The weird behaviour is 1) paired brackets are colored differently; 2) After lua << EOF a Lua context does begin, yet it doesn't seem to be ended after the next EOF, instead it continues being highlighted in a Lua syntax (all lines later on are affected). From the screenshot below you can see that the brackets in line 59 are displayed as white and red separately, and the Lua syntax stays after line 60.
The code itself is assumed as okay, since it can be sourced without any error or warning, and the editing functions normally. It should namly only be a problem with the highlighting.
Sadly I can't tell the context of this problem. I first noticed it today without doing anything special (e.g. installing a new plugin) and I'm not sure when it occured. I have no clue what is causing this, even after doing research on Google for more than one hour - I haven't seen any post describing a similar situation.
The problem remains with the default color scheme.
I was guessing the CoC extension coc-vimlsp could be relevant, but the problem remains after I disabled it. Otherwise I can't remember any NeoVim plugin that could have something to do with the highlighting.
EDIT: I noticed that the broken highlighting after EOF is relevant to the broken brackets. If I write no brackets in the heredoc block, the highlighting will work correctly. Looks like the Lua highlighting remains after the heredoc block because it thinks the brackets aren't closed properly. And this is only about round brackets (), other brackets like [] {} "" would cause no problem.
My init.vim:
" Indentation
set shiftwidth=4
set ai
set si
" Show line numbers
set nu
" Show command at the bottom right of the screen
set sc
" Limit the number of items shown in popup
set ph=20
" Set the minimal number of lines below the cursor
set so=15
" Disable auto comment insertion
au Filetype * setlocal fo-=c fo-=o fo-=r
" vim-plug config
call plug#begin()
" Themes
Plug 'catppuccin/nvim', {'as': 'catppuccin'}
Plug 'tiagovla/tokyodark.nvim'
" Icon support
Plug 'ryanoasis/vim-devicons'
" Statusbar
Plug 'nvim-lualine/lualine.nvim'
" Fish support
Plug 'dag/vim-fish'
" Makrdown support
Plug 'preservim/vim-markdown'
" Markdown preview
Plug 'iamcco/markdown-preview.nvim', { 'for': ['markdown', 'vim-plug'] }
" TeX support
Plug 'lervag/vimtex'
" Auto close XML-like tags
Plug 'alvan/vim-closetag'
" Auto close brackets
Plug 'jiangmiao/auto-pairs'
" CoC completion engine
Plug 'neoclide/coc.nvim', { 'branch': 'release' }
call plug#end()
" catppuccin config
let g:catppuccin_flavour = "mocha" " latte, frappe, macchiato, mocha
lua << EOF
require("catppuccin").setup()
EOF
" Set colorscheme
colorscheme catppuccin
" lualine config
lua << EOF
require('lualine').setup({
options = {
theme = "horizon"
}
})
EOF
" vim-markdown config
let g:tex_conceal = ""
let g:vim_markdown_math = 1
let g:vim_markdown_folding_disabled = 1
let g:vim_markdown_frontmatter = 1
let g:vim_markdown_new_list_item_indent = 0
" Enable vimtex for Markdown files
" Not ideal, since this enables ALL features of vimtex
au Filetype md,markdown call vimtex#init()
" VimTeX config
let g:vimtex_compiler_latexmk = {'continuous': 0}
" CoC config
exe 'so ~/.config/nvim/coc_config.vim'
Operating system: MacOS Monterey 12.4
Output of nvim -v:
NVIM v0.8.0
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by brew#Monterey
Features: +acl +iconv +tui
See ...
The issue raised in both of Neovim and Vim repository.
Neovim side: https://github.com/neovim/neovim/issues/20456
Vim side: https://github.com/vim/vim/issues/11277
Workaround from the comment: reverting to v0.7.2 lua syntax:
curl -sS https://raw.githubusercontent.com/neovim/neovim/v0.7.2/runtime/syntax/lua.vim > $VIMRUNTIME/syntax/lua.vim

How to execute an external command asynchronously in gVim 8.1 without starting a new cmd every time and append the output to an existing buffer

I want to execute a batch file and display the output on a new buffer. I know I can use !start for this, but I can't figure out how to make it reuse the same command-line instead of starting a new one every time.
I managed to get close by using :terminal instead with send_keys(), checking if the terminal buffer still exists and only starting a new one when it doesn't. The problem with this is that what I get is an interactive shell, so I have to switch to Terminal-Normal mode to be able to navigate the contents of the buffer otherwise cmd steals focus from vim and doesn't let me move around. Also the prompt itself is part of the output, which is annoying.
Here's the code:
fun! MatchAnyInList(list, value)
return index(map(a:list, 'v:val =~# "' . a:value . '"'), 1) >= 0
endfun
" I have no idea what I'm doing
fun! RunBuildBatchFile()
if !exists("g:terminal_bufnum") || MatchAnyInList(['finished', ''], term_getstatus(g:terminal_bufnum))
let g:terminal_bufnum = term_start("cmd", {'hidden': 1})
call term_sendkeys(g:terminal_bufnum, "vcvarsall x64\<CR>")
endif
if bufwinnr(g:terminal_bufnum) < 0
execute "botright sb " . g:terminal_bufnum
call term_setsize(g:terminal_bufnum, 4, 0)
endif
call term_sendkeys(g:terminal_bufnum, "cls\<CR>")
call term_sendkeys(g:terminal_bufnum, "C:\\Path\To\Executable\Command.exe")
if term_getstatus(g:terminal_bufnum)=~# "normal"
call feedkeys("i")
endif
endfun
There's probably a much better way of doing this. I tried looking for information about this stuff online and in the docs but I didn't get anywhere. Any ideas?

How to make a ruby command line application with pager?

I'm making a command line tool using Ruby. It will print a lot of text on screen. Currently, I'm using shell pipeline (may_app | more) to do so. But I think it's better to has a default pager.
It's just like what you see when execute git log . One can disable pager by using git --nopager log.
I've done quite much google work and find one gem: hirb , but it seems a little overkill.
After many tries, I'm current using shell wrapper to do so:
#!/bin/bash
# xray.rb is the core script
# doing the main logic and will
# output many rows of text on
# screen
XRAY=$HOME/fdev-xray/xray.rb
if [ "--nopager" == "$1" ]; then
shift
$XRAY $*
else
$XRAY $* | more
fi
It works. But is there a better way?
You are doing it right. But instead using more you'd better get a pager from $PAGER environment variable, if any.
Some people prefer less to more for example, and others have their favorite parser options set in this var.
You can use the pipe in Ruby via a call to system and provide the options (along with a nice help interface) like so:
require 'optparse'
pager = ENV['PAGER'] || 'more'
option_parser = OptionParser.new do |opts|
opts.on("--[no-]pager",
"[don't] page output using #{pager} (default on)") do |use_pager|
pager = nil unless use_pager
end
end
option_parser.parse!
command = "cat #{ARGV[0]}"
command += " | #{pager}" unless pager.nil?
unless system(command)
STDERR.puts "Problem running #{command}"
exit 1
end
Now, you support --pager and --no-pager on the command line, which is nice to do.

How do I get the output of an Xcode user script to auto indent?

The Problem
I want to press a key when I have a line highlighted and convert from a single line:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1 to:date2 intoMOC:mockRawMOC];
to a multiline statement:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1
to:date2
intoMOC:mockRawMOC];
What I've Tried
I've got a simple ruby script that almost gets me there.
#!/usr/bin/env ruby
s = STDIN.read
s.gsub!(/(:.+?\w) (\w.+?)/,'\1' + "\n\t" +'\2')
print s
When I set the output to "Replace Selection", I get this:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1
to:date2
intoMOC:mockRawMOC];
When I set the output to "Place on Clipboard", then paste it in, I get the desired result:
JGLogEntry *logEntry = [JGLogEntry applicationNoWindowsFrom:date1
to:date2
intoMOC:mockRawMOC];
However, this is two keypresses which is clumsy.
Any ideas how I can get the replaced text to obey Xcode's auto indent rules?
Check the pre-installed script for "Convert tabs to spaces", and how it executes an in-line applescript. Use that to tell XCode to perform the menu item
Edit > Format > Re-Indent
I'm not sure how you do that with ruby, nor the details about the applescript content, but I would wager it's fairly straight-forward..

How to enable ruby methods visualizing in vim

a have googled the question, but found nothing - maybe I do not know how to define the search keywords properly in this case.
I like to use folding in vim when I'm developing Ruby on Rails applications. And my foldcolumn is set to 4. But its visualizing of the start and the end of the ruby method is not so simple and obvious ("-" - "def", "|" - "end"):
-def foo
bar = 1
|end
The question is - is there any plugin for vim, that will show markers (arrows or stmh) near every "def" and "end" like it is done in TextMate (1)?
v def foo
bar = 1
^ end
Also, as I do not have much experience in vim/ruby, maybe there is another, more elegant way to check that all def-end pairs are closed in a particular file? (matchit.vim is not very comfortable for this need)
I hope there is more convenient way to catch lost "ends" than to read "Syntax error" in the console :)
I'm not sure whether it's quite what you need, but have you tried the 'foldcolumn' option? For example, with:
:set foldcolumn=4
You'll get something like this:
- def foo
| bar = 1
| end
- def foo2
| bar = 2
|- if x == 1
|| bar = 3
|| end
| end
See :help 'foldcolumn' for more information. Note that you can click on the - signs to close the folds if your Vim is mouse-enabled.
Edit
If you don't like the fold method, you could use signs (assuming your Vim is signs enabled). Try something like this:
command! RubySigns call RubySigns()
" Optional:
au BufReadPost *.rb call RubySigns()
function! RubySigns()
sign define ruby_end text=^
sign define ruby_def text=v
sign unplace *
g/^\s*\(def\|class\|begin\)\>/exe 'sign place '.line('.').' line='.line('.').' name=ruby_def buffer='.bufnr('%')
g/^\s*end\>/exe 'sign place '.line('.').' line='.line('.').' name=ruby_end buffer='.bufnr('%')
endfunction
It's probably not perfect (I don't know ruby), but it might give you something to get started.

Resources