Vim: Difficulty setting up ctags. Source in subdirectories don't see tags file in project root - vim

I'm trying to get setup with (exuberant) ctags on Vim today and am having difficulty getting it to work properly. I generate my ctags file on the command line with with:
cd myproj
ctags -R
This puts the tags file in myproj root. However, Vim only seems to read from this tags file when I'm working on source that reside in root. As I navigate to deeper directories, if I try to jump to a tag using <C-]>, I get:
E433: No tags file
E426: tag not found: MyClassName
I've verified that MyClassName does have a tag in the tags file, it's just that Vim doesn't see it. Can someone please explain how to configure Vim to reference the root's tags file?
Thanks.

add this to .vimrc file set tags=tags;/
This will check the current folder for tags file and keep going one directory up all the way to the root folder.
So you can be in any sub-folder in your project and it'll be able to find the tags files.

There is an option to tell Vim where to look for tag file.
I use the following configuration:
" search first in current directory then file directory for tag file
set tags=tags,./tags
Extract from help :
When a tag file name starts with "./", the '.' is replaced with the path of
the current file. This makes it possible to use a tags file in the directory
where the current file is (no matter what the current directory is). The idea
of using "./" is that you can define which tag file is searched first: In the
current directory ("tags,./tags") or in the directory of the current file
("./tags,tags").
For example:
:set tags=./tags,tags,/home/user/commontags
And I keep my current working directory in the top project directory where my tagsfile is generated.
Use :pwd and then :cd myproj (inside Vim) to go to the directory containing your tags file.
See :help tags-option for more information on tags path.
You issue is probably that you are either in the wrong directory, or your tags option is not properly set.

#!/bin/sh
FREEZE_NAME=/* Give some version number */
mkdir $HOME/ctags/$FREEZE_NAME
V1=/* Software Path */
find $V1 -name "*.h" | xargs /usr/local/bin/ctags -a -f $HOME/ctags/$FREEZE_NAME/h.tags
find $V1 -name "*.c" | xargs /usr/local/bin/ctags -a -f $HOME/ctags/$FREEZE_NAME/c.tags
cd $HOME/ctags/$FREEZE_NAME/
rm -f all.tags
cat c.tags h.tags >> all.tags
sort all.tags > temp.tags
mv temp.tags all.tags
rm -f c.tags h.tags
Put the above code in a .sh file and run... This will generate your tags for sure.

If you generate a tags file for every project, you might like this pattern, especially if you share your .vimrc across different machines:
let repohome=substitute($REPO_HOME, "\/", "\\\\/", "g")
let &tags=substitute(expand("%:p:h"), "\\(".repohome."/.\\{-}\/\\).*", "\\1tags", "")
You would then have to set the environment variable $REPO_HOME in your .bashrc to your main repo directory without the trailing space (e.g. /home/<yourusername>/repos) and it will automatically look for a tags file in each subdirectory of $REPO_HOME with a depth of 1, e.g. /home/<yourusername>/repos/myproj/tags.

Create a .sh file with below code. And run .sh file where you want tags.
That will work for sure.
#!/bin/sh`enter code here`
filelist=`mktemp`
find . -name \*.h >> ${filelist}
find . -name \*.c >> ${filelist}
find . -name \*.cc >> ${filelist}
find . -name \*.cpp >> ${filelist}
find . -name \*.hpp >> ${filelist}
if [ "$SDKTARGETSYSROOT" != "" ]; then
find $SDKTARGETSYSROOT/usr/include -name \*.h >> ${filelist}
fi
cat ${filelist} | ctags -L -

Related

Linux find command explanation

Can someone explain me what does this command do and if I want to try the same thing using git, how should I modify this command?
find . -name CVS -print -exec rm -fr {} \;
This command looks in your current working directory for any directories or files named "CVS" and prints the full path. Then executes a forced recursive removal for each result returned by the find command.
Since there is no filetype present in the name, this command will remove any directory, within your current working directory, named CVS, including all subdirectories and files housed within.

Basic Bash Function to find and copy files based on filetype

I use the little bit of code below to find files in a folder with a given filetype and then copy them to a different folder.
find ./ -name '*.chk' -exec cp -prv '{}' "./" ';'
I tried to change this into a function in my .bash_profile so I could use it more quickly, but for some reason, it's not working. By not working, I mean nothing seems to happen when I execute "mymove .txt folder". Bash just goes to a new line, ready to accept more input.
mymove() {
find ./ -name "$1" -exec cp -prv '{}' "$2" ';';
}
Any advice?
You mention that you execute mymove with two parameters, .txt and folder:
$ mymove .txt folder
This will not work as .txt doesn't match any files, change it to:
$ mymove '*.txt' folder
Note the quotes, you do not want to shell to expand *.txt.

Convert all files of a specified extension within a directory to pdf, recursively for all sub-directories

I'm using the following code (from this answer) to convert all CPP files in the current directory to a file named code.pdf and it works well:
find . -name "*.cpp" -print0 | xargs -0 enscript -Ecpp -MLetter -fCourier8 -o - | ps2pdf - code.pdf
I would like to improve this script to:
Make it a .sh file that can take an argument specifying the
extension instead of having it hardcoded to CPP;
Have it run recursively, visiting all subdirectories of the current directory;
For each subdirectory encountered, convert all files of the specified extension to a single PDF that is named $NameOfDirectory$.PDF and is placed in that subdirectory;
First, if I understand it correctly, this requirement:
For each subdirectory encountered, convert all files of the specified extension to a single PDF that is named $NameOfDirectory$.PDF
is unwise. If that means, say, a/b/c/*.cpp gets enscripted to ./c.pdf, then you're screwed if you also have a/d/x/c/*.cpp, since both directories' contents map to the same PDF. It also means that *.cpp (i.e. CPP files in the current dir) gets enscripted to a file named ./..pdf.
Something like this, which names the PDF according to the desired extension and places it in each subdirectory alongside its source files, doesn't have those problems:
#!/usr/bin/env bash
# USAGE: ext2pdf [<ext> [<root_dir>]]
# DEFAULTS: <ext> = cpp
# <root_dir> = .
ext="${1:-cpp}"
rootdir="${2:-.}"
shopt -s nullglob
find "$rootdir" -type d | while read d; do
# With "nullglob", this loop only runs if any $d/*.$ext files exist
for f in "$d"/*.${ext}; do
out="$d/$ext".pdf
# NOTE: Uncomment the following line instead if you want to risk name collisions
#out="${rootdir}/$(basename "$d")".pdf
enscript -Ecpp -MLetter -fCourier8 -o - "$d"/*.${ext} | ps2pdf - "$out"
break # We only want this to run once
done
done
First, if I understand correctly, what you are using is in fact wrong - find will retrieve the files from all sub-directories. To work recursively, only getting files from the current directory (I named it do.bash):
#!/bin/bash
ext=$1
if ls *.$ext &> /dev/null; then
enscript -Ecpp -MLetter -fCourier8 -o - *.$ext | ps2pdf - $(basename $(pwd)).pdf
fi
for subdir in */; do
if [ "$subdir" == "*/" ]; then break; fi
cd $subdir
/path/to/do.bash $ext
cd ../
done
The checks are to make sure a file with the extension or a sub directory actually exist. This scripts operates on the current directory, and calls itself recursively - if you do not want a full path, put it in your PATH listings, though a full path is fine.

Shell Script to Recursively Loop Through Directory and print location of important files

So I am trying to write a command line shell script or a shell script that will be able to recursively loop through a directory, all its files, and sub-directories for certain files and then print the location of these files to a text file.
I know that this is possible using BASH commands such as find, locate, exec, and >.
This is what I have so far. find <top-directory> -name '*.class' -exec locate {} > location.txt \;
This does not work though. Can any BASH, Shell scripting experts help me out please?
Thank-you for reading this.
The default behavior of find (if you don't specify any other action) is to print the filename. So you can simply do:
find <top-directory> -name '*.class' > location.txt
Or if you want to be explicit about it:
find <top-directory> -name '*.class' -print > location.txt
You can save the redirection by using find's -fprint option:
find <top-directory> -name '*.class' -fprint location.txt
From the man page:
-fprint file
[...] print the full file name into file file. If file does not exist when find is run, it is created; if it does exist, it is truncated.
A less preferred way to do it is to use ls:
ls -d $PWD**/* | grep class
let's break it down:
ls -d # lists the directory (returns `.`)
ls -d $PWD # lists the directory - but this time $PWD will provide full path
ls -d $PWD/** # list the directory with full-path and every file under this directory (not recursively) - an effect which is due to `/**` part
ls -d $PWD/**/* # same like previous one, only that now do it recursively to the folders below (achieved by adding the `/*` at the end)
A better way of doing it:
After reading this due to recommendation from Charles Duffy, it appears as a bad idea to use both ls as well as find (article also says: "find is just as bad as ls in this context".) The reason it's a bad idea is because you can't control the output of ls: for example, you can't configure ls to terminate filenames with NUL. The reason it's problematic is that unix allows all kind of weird characters in a file-name (newline, pipe etc) and will "break" ls in a way you can't anticipate.
Better use a shell script for the task, and it's pretty simple task too:
Create a file my_script.sh, edit the file to contain:
for i in **/*; do
echo $PWD/$i
done
Give it execute permissions (by running: chmod +x my_script.sh).
Run it from the same directory with:
./my_script.sh
and you're good to go!

Linux kernel makefile cscope target

When I generate Linux kernel cscope database by issuing make cscope I get database file along with a list of files with relative path. This is a problem for me because later on when I attach that external kernel's database in vim editor from whatever directory but kernel's I can easily search for specific symbol BUT I can't open file that symbol is contained in.
I've written the next bash script
#!/bin/bash
SNAME="$0"
SNAME=${SNAME##*/}
function usage()
{
echo Generate Linux kernel cscope database
echo "Usage: $SNAME [PATH]"
echo -n "You must provide this script with an ABSOLUTE path of your "
echo "kernel's root directory"
}
if [[ -z "$1" ]]; then
usage
exit -1
fi
KDIR="$1"
echo -n "Collecting a list of files... "
if find "$KDIR" -path "${KDIR}/arch/*" ! -path "${KDIR}/arch/x86*" -prune -o \
-path "${KDIR}/include/asm-*" \
! -path "${KDIR}/include/asm-generic*" \
! -path "${KDIR}/include/asm-x86*" -prune -o \
-path "${KDIR}/tmp*" -prune -o \
-path "${KDIR}/Documentation*" -prune -o \
-path "${KDIR}/scripts*" -prune -o \
-name "*.[chxsS]" -print > cscope.files; then
echo done
else
echo failed
fi
echo -n "Building cscope database... "
cscope -k -qb && echo done || echo failed
that collects all files I need (x86/x86_64 architecture) using absolute path and then I successfully build cscope database manually. But I think it must be some easier way to accomplish this. Maybe some Makefile's target like make cscope_abs or make cscope_ext that I have not found yet.
Any suggestions?
First, it is simpler to create a cscope database specific to a particular architecture as follows : In Linux kernel's top folder, run
ARCH=x86 make cscope
This creates a cscope with relative paths.
Now you can ask vim to interpret the paths relative to the location of the cscope.out file in one of two ways:
Way 1 : Use cscoperelative. Output of :help csre :
If 'cscoperelative' is set, then in absence of a prefix given to cscope
(prefix is the argument of -P option of cscope), basename of cscope.out
location (usually the project root directory) will be used as the prefix
to construct an absolute path. The default is off. Note: This option is
only effective when cscope (cscopeprg) is initialized without a prefix
path (-P). Examples: >
:set csre
:set nocsre
Way 2 : (From Aaron Hs' answer in this question) When adding the cscope database in vim, specify base location. Example:
:cs add <base_location>/cscope.out <base_location>/
From vim's help page for cscope add:
USAGE :cs add {file|dir} [pre-path] [flags]
[pre-path] is the pathname used with the -P command to cscope.
make cscope
or for your specific case -
ARCH=x86 make cscope
From the cscope manpage here:
-P path
Prepend path to relative file names in a pre-built cross-reference file so you do not have to change to the directory where the
cross-reference file was built. This option is only valid with the -d
option.
So I guess the following command from the top-level kernel directory should do the trick (sorry no linux machine handy):
cscope -d -P `pwd`
my 2 cents,
You could use the following command in the root of the kernel tree to generate absolute paths:
#> make O=. cscope

Resources