tools

VIM: Better grepping (in Windows)

Several times I noticed that grepping in VIM (finding in files) does not work very well comparing to Visual Studio or VSCode at least. I felt it pretty hard last time when I tried to search in a medium-size web project. It took over 23 seconds to search for files and another 55 seconds to render the list of results. I’ve never experienced something similar in any other editor.  Another think that bothered me was the quality of results which frequently showed binary files, multi-line records or everything else what I could call “rubbish” in the context of file search option:

2017-04-08_22-41-56

The first guess is that alternative editors just skip files that are not source code. And that’s the thing (at least looking at Visual Studio Code file search):

2017-04-08_22-54-55

Unfortunately there is no easy way to tell VIM grepper to exclude locations from search (at least in Windows). The default findstr (you can find more about it in my previous post) does not have such option. Nevertheless there is a way to achieve such behaviour. Just add this snippet to your $VIMRC file:

function! FastGrep(what, ...) abort
 let where = a:0
 if where == ""
    let where = "*.*"
 endif
 let command = "silent ! dir /s/b/a:-d"
 let command = command . " | findstr /v \\node_modules\\"
 let command = command . " | findstr /v \\dist\\"
 let command = command . " | findstr /v \\\.git\\"
 let command = command . " | findstr /v \.map$"
 let command = command . " | findstr /v \.swp$"
 let command = command . " | findstr /v \.ts\.html$"
 let command = command . " | findstr /v ~$"
 " add excludes here ------^
 let command = command . " > \\%home\\%\\solution_files.tmp"
 execute comma              nd
 execute "grep /f:\\%home\\%\\solution_files.tmp ".a:what." ".where
endfunction

:command! -nargs=+ -complete=command Grep2 call FastGrep(<q-args>)

The idea behind the snippet is the following: get list of all the files in the working folder, remove the locations you want to exclude, and then pass the list of files to “findstr” command (/f switch allows to pass the list of input files).

Here’s a quick list of tricks used in the snippet:

execute              - run vim command
!                    - execute shell command
silent               - skip "Press ENTER to continue" after running shell
dir /s/b/a:-d        - get a raw list (/b) of files only (/a:-d) 
                       in all subdirectories (/s) 
findstr /v \.git\    - find lines that does not containing "\.git\"
%home%               - user home directory (i.e. c:\users\user_1)
FastGrep(what, ...)  - function taking one named argument and array of 
                       unnamed arguments
a:0                  - first unnamed argument
:command!            - define VIM command alias
-nargs=+             - alias must have arguments (any number)
<q-args>             - list of arguments passed to the alias

Now in order to execute such command you just need to run:

:Grep2 myFunction1
:Grep2 myFunction2 *.js

The same operation that took initially 23 seconds was executed in less than 1 second (with rendering results). Of course it showed less records, but only the meaningful ones (a side note: my medium-size web application contained 996 folders inside /node_modules !!).

Remember that you can extend it by your own excludes.

Advertisements
tools, Uncategorized

VIM: Find in files

It’s not a surprise that there is a built-in way to find in files in VIM. You can use four commands: :grep, :lgrep, :vimgrep, :lvimgrep.

2017-04-02_09-19-29

:grep will use the default “find” tool for your operating system (“grep” for linux family, “findstr” for Windows). :vimgrep will use built-in vim search (same as for “/” searching) which is slower than system search (but works if you have problems with grep/findstr/other). On the other hand vimgrep has the same syntax everywhere (:grep depends on external tool used). Search result is presented using Quickfix window (opened with :copen). :lgrep/:lvimgrep will do the same, but using location list (opened with :lopen). In practise the difference is that if you use Quickfix window (:copen) it will stick to the current editor. Location window will show in the same way, but if you select anything then the window will disappear and you will have to run :lopen again.

I guess you have to choose yourself which command you like most.  The syntax is as follows:

:grep searchpattern locationpattern

i.e.:

:grep MyMethod *.*

Will look for phrase “MyMethod” in all files in current workind directory (you can check currect directory using :pwd). By default it does not perform recursive search (it will not search in subdirectories). And here the fun part starts.

In order to search in subdirectories you would have to use **/*.* pattern. However this works for :vimgrep and :grep in linux, but does not work for Windows. In order to make it work on Windows you would need to use /S parameter (linux grep also accepts -R parameter which does the same thing). You can pass it like that:

:grep /S MyMethod *.*

This will work on Windows and will find all “MyMethod” phrases in all files in all subdirectories of current working directory. However you can still make it the default behaviour in VIM. But first you neeed to know one thing:

:set grepprg

This is a variable that controls the usage of external grep tool in VIM. Depending on your operating system you will have different results:

grepprg=findstr /n    # <--- on Windows
grepprg=grep -n $* /dev/null # <---- on Linux

So in order to use recursive search you have to modify grepprg and add a proper parameter:

grepprg=findstr /S /n                  # <--- on Windows
grepprg=grep -nR $* /dev/null          # <---- on Linux

The same way you can add more parameters or make more complex commands.

tools

CMDer/ConEmu – starting new tab in current folder

Recently I posted about a trick allowing the user to open new CMDer/ConEmu tab in current folder from command line. In response ConEmu team twitted a better solution that I would like to share:

2017-03-30_22-00-53

I don’t want to have specific shortcut for each task so I just edited default {cmd} task this way:

2017-03-30_21-51-35

The key here is to add “/dir “%CD%” as task parameter and remove “:d:”%USERPROFILE%” parameter from the textbox on the bottom. Just after that when you add new tab (in my case Ctrl + Shift + T) you can specify “Startup directory for new process” or leave it empty. In the latter case (and only then!) ConEmu will open new task in current directory.

2017-03-30_22-04-17

Why is this better?

The previous solution required active console in order to execute some command. Now everything is done by ConEmu, so you can have some process running (web server/test watch/vim etc) and at the same time start new tab in the same folder.

Thanks a lot ConEmu Team!

tools, Uncategorized

CMDer/ConEmu – starting new tab in current folder from command line

Quite frequently I found myself opening several console tabs in the same folder (for vim, for running unit tests, for git, for executing the application etc). Starting new tab and navigating to my project’s directory is not optimal so I created a short !cmd.bat snippet that I can run everywhere (added as global alias) and it will create a new tab for me in the same directory. Here’s the content:

cmd /k "%ConEmuDir%\..\init.bat" -new_console:C:"%CMDER_ROOT%\cmder.exe"

It’s taken from “ConEmu -> Settings -> Tasks” with slight modification (adding tab icon). Here’s explanation of switches used by the snippet:2017-03-26_23-52-18

/k - run initial script just after running console
-new_console - open process in a new tab
:C:"file location" - path to file containing tab icon

Alternative way is to use shortcut for creating new tab and then selecting current folder from drop-down menu (see below).

2017-03-26_23-53-00

Unfortunately because of some bug it does not work in my current version of Cmder/ConEmu (build 161022). The other this is that it requires more clicking 🙂

tools

Using several dotnet CLI versions and why you should care

Frankly speaking “several” keyword in the title is an overuse, because while writing this article I’m thinking only about two versions of dotnet CLI. Why? Because RC4 version dropped support to project.json and switched completely to the new *.csproj format. On one hand it’s good – let’s follow the hype, cool to have tooling already. On the other hand if you still maintain/develop solution based on project.json then you have a problem (you will not be able to use dotnet CLI with that).

I am in such case and have found a solution that might be worth sharing.

Prerequisites

Download dotnet CLI binaries (without installer) and extract package to some known folder. In my case it was:

C:\tools\dotnet-prev2
C:\tools\dotnet-rc4

Overriding PATH variable

The trick is to switch PATH variable to proper dotnet CLI location. Of course you could copy paste specific “set PATH=…” expression but some script may come in handy. Below is the script that works for me (I use an alias “set-dotnet.cmd”).  Remember to add script location to PATH (you may want to see this post).

@echo off
set _DOTNET2=c:\Tools\dotnet-prev2;
set _DOTNET4=c:\Tools\dotnet-rc4;
:: Batch way of string replace
set path=%path:C:\Tools\dotnet-prev2;=%
set path=%path:C:\Tools\dotnet-rc4;=%

if [%1]==[] goto usage
if "%1"=="prev2" goto prev2
if "%1"=="rc4" goto rc4
goto unknown

:prev2
@echo you selected prev2
set path=%_DOTNET2%%PATH%
exit /B 0

:rc4
@echo you selected rc4
set path=%_DOTNET4%%PATH%
exit /B 0

:unknown
@echo Unknown parameter %1
goto :usage

:usage
@echo Usage: %0 ^<ver^>
@echo        where ^<ver^> is one of: prev2, rc4
exit /B 1

This lets me switch PATH to specific dotnet version using:

set-dotnet prev2
set-dotnet rc4

Normally I do scripting in Powershell but things related to environment variables are done easier in batch scripting – neither setx nor [Environment]::SetEvironmentVariable() affect currently active console (challenge me in comments if you want).

As batch scripting is pretty exotic some explanation is needed:

Exit /b 0

This means exiting current subrouting which is the whole batch script in case of “goto” or returning from “call” (0 is errorlevel)

:: Batch way of string replace
%variable:findtext=replacewith%

This is a way to do string replace withing variable. Let’s have another example

set x=hello world
set x=%x:world=chriss%

Executing these two lines above results in x=hello chriss

Cheers!

PS: Refactoring

Further refactoring can lead to having much simpler version which is having two batch scripts:

set-dotnet-prev2.cmd:

set path=c:\Tools\dotnet-prev2;%PATH%

set-dotnet-rc4.cmd:

set path=c:\Tools\dotnet-rc4;%PATH%

As an extra credit this will enable autocomplete for set-dotnet-*

tools

Getting started with dotnet CLI

Dotnet CLI is a command line interface used for working with .NET projects. It’s not something new in .NET world. It was established in 2015. Back then there was another try with DNX in 2013 – not very successful.

Why to bother CLI?

You do not need CLI if you work with .NET code only from perspective of Visual Studio. CLI is really needed if you want to setup build pipeline, because build agent does not have to have Visual Studio installed. But let’s not forget about .NET migrating from Windows to the world. From now on you can run it everywhere (far far away there are systems without *.exe files) and build it with notepad, VIM or emacs. And that’s when dotnet CLI comes in.

It’s also valuable in Windows – from time to time you may want to automate your job by running specific tests just before “git commit” or run the application simply, without knowing full “../bin/Debug/net461” location.

Solution file

When working with dotnet CLI you can see that the solution file is no longer needed. Actually now it is fully based on solution folder containing necessary projects. If you want to build/run some project, you need to run “dotnet run <path/to/project/folder>”. It should build all dependencies as well. You can also run “dotnet restore” command in “solution” folder which should restore dependencies for all the projects inside. It’s very useful when creating new solutions. The only thing to do is preparing folder structure for the new solution and running “dotnet new” in each of them.

Download

New dotnet CLI can be downloaded from here: https://github.com/dotnet/core/blob/master/release-notes/download-archive.md

While installing, I prefer to download *.zip package and extract it to my known place without affecting the whole system. Doing so remember about adding “dotnet” location to PATH or adding alias.

Finally you should be able to run the following commands:

# list all available project templates
dotnet new

# create new xunit test project
dotnet new xunit

# restore projects dependencies (can be run in solution folder)
dotnet restore

# build specific project (run without parameter when in project's folder)
dotnet build .\src\project1

# run tests in project (run without parameter when in project's folder)
dotnet test .\test\project1.Tests

# add package to project
dotnet add .\src\syncfiles\Syncfiles.csproj package Newtonsoft.Json

# add project reference
dotnet add .\test\Syncfiles.Tests\Syncfiles.Tests.csproj reference .\src\syncfiles\Syncfiles.csproj

# create deployment package
dotnet publish .\src\syncfiles

Other commands

There is a list of commands we can use when working with .NET projects:

new Creates a new project, configuration file or solution based on the specified template
migrate Migrates existing project from project.json to *.csproj version
restore Restores dependencies of a project.
run Runs the project. Alternative to running produced *.exe file in Windows. Unified way of running .NET project for all supporter operating systems. It runs “dotnet build” when needed.
build Builds the project. It does not run “dotnet restore” automatically – you have to run it manually prior to building the project
test Runs test project with specific test runner (restored with other dependencies). You don’t have to select anything.
publish Packs the application and all of its dependencies into a folder getting it ready for publishing. Such folder can then be deployed to desired environment.
pack Packs the code into a NuGet package.
clean Cleans build outputs
sln Add/remove projects in solution
add Add project reference or package from nuget repository
remove Remove project reference or package
list List project dependencies
nuget Publish package to nuget server or delete it
msbuild Wrapper for msbuild.exe
vstest Wrapper for vstest.console.exe
help Shows help screen
tools

git console: changing brach interactively

Some time ago I wrote about some helpful script that let me copy branch name without mouse. Later on I wrote about some simple console menu in Powershell. The second one seems to be a far better option here. Here’s the code:

#uncomment if you don't have ps-menu (powershellget is needed)
#install-module ps-menu -scope currentuser

$selection = menu (iex "git branch")
if ($selection -ne $null)
{
    $branch = $selection.Trim(" ", "*")
    iex "git checkout $branch"
}

Here is how it works:

branching