Difference between revisions of "Little Helpers"
(→Compiling a source from within Vim: Much more sophisticated version of the makeprg setting; all done automatically now with Ctrl+M) |
|||
Line 558: | Line 558: | ||
</pre> | </pre> | ||
+ | == MSDEV Open Header File == | ||
+ | |||
+ | In the MSDEV you open a header file when the cursor stands on a #include with the Edit.GoTo command which is normally bound to the short cut CTRL+G. To open files from OOo you have to add the folders where MSDEV should look into. This is done in Tools->Options->Projects VC++ Directories. There in the upper right corner select "Include files". Here you can add all folders which are important. E.g. | ||
+ | <pre> | ||
+ | $(SOLAR_STLP) | ||
+ | $(SOLARVER)\$(COMMON_OUTDIR)$(PROEXT)\idl$(UPDMINOREXT) | ||
+ | $(SOLARVER)\$(OUTPATH)$(PROEXT)\inc$(UPDMINOREXT)\offuh | ||
+ | $(SOLARVER)\$(OUTPATH)$(PROEXT)\inc$(UPDMINOREXT) | ||
+ | </pre> | ||
[[Category:Development]] | [[Category:Development]] |
Revision as of 09:14, 30 August 2006
Here are some Little Helpers for the enthusiastic developer, such as editor configurations and small scripts that help to tame the OOo code base.
Tools for maintaining identifier databases
GNU ID-utils
The GNU ID-utils are a powerful set of utilities to generate and query a database of file names and identifiers, invoke your favourite editor with the resulting set of files, or accomplish any other task with the resultset. Be sure to not use the original version 3.2, use the Debian package instead, or maybe some other distribution's package. For details why and how to patch a self-compiled version 3.2d see this blog entry.
The ID-utils come with an
;;; id-utils.el -- emacs interface to `lid -R grep', a.k.a. `gid'
For how to interface with the vim editor see below.
Generally, the editor invoking functionality of ID-utils uses the shell
variables VISUAL or EDITOR, EIDARG, EIDLDEL and EIDRDEL, so
it should be possible to invoke any capable editor. Citing from the
documentation:
`eid' invokes the editor defined by the environment variable `VISUAL'. If `VISUAL' is undefined, it uses the environment variable `EDITOR' instead. If `EDITOR' is undefined, it defaults to `vi'. It is possible for `eid' to pass the editor an initial search pattern so that your cursor will immediately alight on the token of interest. This feature is controlled by the following environment variables: `EIDARG' A printf(3) format string for the editor argument to search for the matching token. For `vi', this should be `+/%s/'. `EIDLDEL' The regular-expression meta-character(s) for delimiting the beginning of a word (the ``eid' Left DELimiter'). `eid' inserts this in front of the matching token when a word-search is desired. For `vi', this should be `\<'. `EIDRDEL' The regular-expression meta-character(s) for delimiting the end of a word (the ``eid' Right DELimiter'). `eid' inserts this in end of the matching token when a word-search is desired. For `vi', this should be `\>'.
The ID-utils use an extension → language mapping file id-lang.map. Either edit the system-wide file of your installation or a local copy thereof. Here we're assuming a local copy as $HOME/devbase/id-lang.map, add the following entries:
# Treat OOo/SO resource header files as C files *.hrc C # Treat OOo/SO resource files as C files *.src C # Treat OOo/SO *.mk files as makefiles *.mk make
Note: The *.mk make entry doesn't seem to work, the makefile.mk's IDs are not added to the database, didn't investigate yet why.
Then the script below may be invoked either
- In a module's directory without parameters, creating an ID database for just the single module.
- In the $SRC_ROOT directory for all modules:
mkid-script '*'
Note the literal '*' asterisk that is not to be expanded by the shell. Also note that the positive list has to be maintained whenever a module introduces an unforseen directory structure, which fortunately happens very rarely. - In the $SRC_ROOT directory for specified modules:
mkid-script '{module1,module2,solver}'
Again, note the literal argument enclosed in single quotes for the file name expansion, which is done by the script. If the argument was not passed literally, only the first module would be included, and other strange things may happen.. Wildcards may be used as usual.
It is a good idea to include the solver module to gather identifiers from delivered header files.
Ignore messages like
mkid: can't lstat `unxlngi4.pro' from `.../SRC680/src.m180/solver' or
mkid: can't lstat `build' from `.../SRC680/src.m180/module1/unxlngi4.pro/misc'
They result from the INPATH relevant arguments passed for each module and don't harm.
You may also manually invoke mkid in the $SRC_ROOT directory for all
modules, this is a safe bet but may include more than you want if you have
output directories for multiple platforms, all identifiers present there will
get duplicated, triplicated, ... the command line for this is
mkid --lang-map=$HOME/devbase/id-lang.map --statistics
The module-wise script, suggested name: $HOME/devbase/mkid-script
#!/bin/tcsh # Either the current module, or all modules can be ID'ed if $1 is # specified as literal '*' unexpanded. Will be expanded here. # Instead of '*' _any_ shell wildcard should be possible, e.g. '{.,bf_*}'. if ( { ( which mkid >> /dev/null ) } ) then echo generating IDs if ( "$1" == "" ) then set module="." #set exclude=./{common,unxlngi6,unxsols4,wntmsci10}{,.pro} # wildcards not only make updates unnecessary but also suppress "no match" messages set exclude=./{common*,unxlng*,unxsol*,wntmsc*} else if ( "$1" == "*" ) then set module="$1" echo module: $module # prevent command line overflow with a full exclude set exclude="x" echo exclude: $exclude else set module="$1" echo module: $module set exclude=$1/{common*,unxlng*,unxsol*,wntmsc*} echo exclude: $exclude endif if ( "$1" == "*" ) then # only positive list set dirs="$module/inc $module/unx $module/mow $module/win{,32} $module/mac{,osx} $module/$INPATH/inc $module/$INPATH/misc/build $module/source $module/src $module/osl $module/rtl $module/systools $module/textenc" echo dirs: $dirs mkid --lang-map=$HOME/devbase/id-lang.map --statistics $dirs else # all but negative list plus INPATH relevants mkid --lang-map=$HOME/devbase/id-lang.map --statistics --prune="$exclude" $module $module/$INPATH/inc $module/$INPATH/misc/build endif # all, for copy&paste # mkid --lang-map=$HOME/devbase/id-lang.map --statistics else echo no ID-utils endif
Exuberant Ctags
The Exuberant Ctags utility interfaces nicely with Emacs and Vim and enables quick lookup for declarations and definitions of macros, typedefs, constants, enums, variables, structs and classes and their methods and implementation. You should get the latest version available at http://ctags.sourceforge.net/, which currently (2006-08-27) is v5.6, older versions included by distributions for example don't automatically recognize .mk files as makefiles, and v5.6 also added a patch needed for the vim 7.0 omni-completion feature. Ctags creates a file tags in the current directory, if not told otherwise.
For use with the OOo code base a script with a syntax similar to the ID-utils script above comes handy, suggested name: $HOME/devbase/ctags-script
#!/bin/tcsh # Either the current module, or all modules can be ctag'ed if $1 is # specified as literal '*' unexpanded. Will be expanded here. # Instead of '*' _any_ shell wildcard should be possible, e.g. '{.,bf_*}'. # NOTE: ctags on '*' exceeds 2GB file size limit if for _all_ OOo modules. if ( { ( which ctags >> /dev/null ) } ) then echo generating ctags # Options necessary for the Vim OmniCppComplete plugin, # http://www.vim.org/scripts/script.php?script_id=1520 # --c++-kinds=+p : Adds prototypes in the database for C++ files. # --fields=+iaS : Adds inheritance (i), access (a) and function # signatures (S) informations. # --extra=+q : Adds context to the tag name. Note: Without this # option, the script cannot get class members. set omnicppoptions="--c++-kinds=+p --fields=+iaS --extra=+q" if ( "$1" == "--global" ) then if ( ! $?UPDMINOREXT ) set UPDMINOREXT="" ctags -h "+.hdl.hrc" --langmap=c:+.hdl.hrc.src $omnicppoptions -R $SOLARVERSION/$INPATH/inc$UPDMINOREXT else if ( "$1" == "" ) then set module="." else set module="$1" echo module: $module endif set dirs="$module/inc $module/unx $module/mow $module/win{,32} $module/mac{,osx} $module/$INPATH/inc $module/$INPATH/misc/build $module/source $module/src $module/osl $module/rtl $module/systools $module/textenc" ctags -h "+.hdl.hrc" --langmap=c:+.hdl.hrc.src $omnicppoptions -R $dirs endif else echo no ctags endif
Ignore messages like
ctags: Warning: cannot open source file "./win32" : No such file or directory
or consider them purely informational.
Note that also here, if more than one module is to be specified, the shell
filename glob-patterns and metanotations have to be passed in single quotes.
Though tagging an entire OOo code base would result in a "file overflow" with
more than 2GB this isn't a problem in practice, at least with Vim, don't know
about other editors, since it may search the tags files going up the
directory hierarchy if a tag is not found in the current directory's tags file.
So generate the tags file in the modules you are interested in, using the
script without parameters, then cd $SRC_ROOT
and invoke
scriptname --global
to produce a tags file for the solver.
Cscope
Cscope is another utility to query a database for code elements. It comes with a cumbersome screen-oriented interactive tool to browse source files, but also interfaces nicely with Vim and Emacs. This script may be used to generate a cscope.out database file, suggested name: $HOME/devbase/cscope-script
#!/bin/tcsh # Either the current module, or all modules can be tagID'ed if $1 is # specified as literal '*' unexpanded. Will be expanded here. # Instead of '*' _any_ shell wildcard should be possible, e.g. '{.,bf_*}'. # NOTE: cscope on '*' hangs if for _all_ OOo modules. if ( { ( which cscope >> /dev/null ) } ) then echo generating cscope if ( "$1" == "" ) then set module="." else set module="$1" echo module: $module endif set dirs="$module/inc $module/unx $module/mow $module/win{,32} $module/mac{,osx} $module/$INPATH/inc $module/$INPATH/misc/build $module/source $module/src $module/osl $module/rtl $module/systools $module/textenc" ( find $dirs -name '*.[hc]' -o -name '*.[hc]xx' -o -name '*.[hc]pp' -o -name '*.[hs]rc' >cscope.files ) >>& /dev/null if ( `uname` == "SunOS" ) then cscope -b -c else cscope -b -c -q endif else echo no cscope endif
Note that the find command is executed in a subshell with stderr redirected to /dev/null to suppress all the find: ./win32: No such file or directory and the like warning messages. Remove the parentheses and the redirection in case you suspect errors in your environment.
All together now
Surely we don't want to invoke all these scripts separately all the times, so here's a small wrapper just to prove that the suggested names actually make sense ;-) let's call it $HOME/devbase/tagsID
#!/bin/tcsh # Either the current module, or all modules can be tagID'ed if $1 is # specified as literal '*' unexpanded. Will be expanded here. # Instead of '*' _any_ shell wildcard should be possible, e.g. '{.,bf_*}'. # NOTE: ctags on '*' exceeds 2GB file size limit if for _all_ OOo modules. # NOTE: cscope on '*' hangs if for _all_ OOo modules. # Which actually is the reason we invoke it last just in case we forgot.. ${0:h}/ctags-script "$1" ${0:h}/mkid-script "$1" ${0:h}/cscope-script "$1"
The same syntax as with the other scripts applies: filename glob-patterns and metanotations have to be passed as literals, enquoted with single quotes. Invoking it in a module's directory without passing an argument creates databases for that single module.
Vim
There exist only two real editors in the programmer's world ;-) One is Vim, and the other is the eier-legende-woll-milch-sau (sorry for that German phrase being a modified translation of allrounder - all-in-one device suitable for every purpose, couldn't resist ;-) (X)Emacs. So here we go for Vim.
General Vim settings
In .vimrc have the following settings. For the tabstops=4 setting it may
not be desired to have this globally effective, you may want to restrict it to
an OOo/SO environment by evaluating the $SOLARENV environment variable,
if $SOLARENV != ""
set ts=4 " tabstops are 4 (for all code) set sw=4 " shiftwidth is 4 set expandtab " expand tabs to spaces set showmatch " briefly jump to matching brackets set showmode " the mode we're in if version >= 600 filetype on " enable file type detection filetype plugin on " enable file type plugins filetype indent on " enable file type indents endif " previous and next compiler error (quickfix) map <C-P> :cp<CR> map <C-N> :cn<CR> " Only do this part when compiled with support for autocommands. if has("autocmd") " In text files, always limit the width of text to 78 characters, tabs are 8 autocmd BufRead *.txt,*.doc,*.dok setlocal tw=78 ts=8 sw=8 fo+=rn1 " mail bodies have a textwidth of 72 characters, activate numbering " formatting, reset comments to default, no indenting autocmd FileType mail setlocal tw=72 fo+=rn1 comments& nocindent " AutoDoc comments in C/C++ files without '*' middle part autocmd FileType c,cpp setlocal comments^=s:/**,mb:\ ,e:*/ " AutoDoc comments in IDL files have a textwidth of 72 characters autocmd FileType idl setlocal comments^=s:/**,mb:\ ,e:*/ tw=72 " OOo .xcu configuration files and the like autocmd FileType xml setlocal sw=2 " HTML files autocmd FileType html,css setlocal sw=2 endif
Vim and the GNU ID-utils
As mentioned above, the eid command makes use of some environment variables when invoking the editor:
setenv VISUAL vim setenv EIDARG '+/%s/' setenv EIDLDEL '\<' setenv EIDRDEL '\>'
To be able to invoke the lid utility from within Vim we need a key mapping and function call, you may place this section into your .vimrc file:
" GNU id-utils, taken from :h ident-search " Generate the ID file in the current directory or in .. or ../.. or ../../.. " To use it, place the cursor on a word, type "_u" and vim will load the file " that contains the word. Search for the next occurrence of the word in the " same file with "n". Go to the next file with "_n". map _u :call ID_search()<Bar>execute "/\\<" . g:word . "\\>"<CR> map _n :n<Bar>execute "/\\<" . g:word . "\\>"<CR> function ID_search() let g:word = expand("<cword>") let x = system("lid --key=none ". g:word) let x = substitute(x, "\n", " ", "g") execute "next " . x endfun
Vim and Ctags
That's easy, as vim has builtin support for ctags, see :h tagsrch.txt, in .vimrc just tell it where to search for the tags files:
" Tags files are searched first relative to the current file, then relative to " the current working directory, and last in the $HOME directory. set tags=./tags,./../tags,./../../tags,./../../../tags,./../../../../tags,./../../../../../tags,tags,../tags,../../tags,../../../tags,../../../../tags,../../../../../tags,~/tags
Vim and Cscope
Vim has also builtin (well, if compiled in) support for cscope, see :h cscope. In .vimrc you may want to setup some things, but this is also a matter of work habits. Consider the following an example and consult the fine manual.
" Cscope settings if has("cscope") " quickfix window usage set cscopequickfix=s-,c-,d-,i-,t-,e- " :tag and the like use :cstag set cscopetag " first search cscope, than tags file set cscopetagorder=0 set nocsverb " add any database in current directory if filereadable("cscope.out") cs add cscope.out " else add database pointed to by environment elseif $CSCOPE_DB != "" cs add $CSCOPE_DB endif set csverb endif
Include file searches in Vim
Set the path variable in .vimrc:
" Where to look for files in gf and ^Wf and similar commands, " and for include files for i_^X^I and i_^X^D commands. " To use VIM_INC in OOo/SO environment define " alias solvim 'if ( ! $?UPDMINOREXT ) setenv UPDMINOREXT "" ; setenv VIM_INC ./${INPATH}/inc,../${INPATH}/inc,../../${INPATH}/inc,../../../${INPATH}/inc,`echo $SOLARINC|sed -e "s/^-I//" -e "s/ *-I/,/g"`,${SOLARVERSION}/${INPATH}/inc${UPDMINOREXT}/offuh' " Note the two space characters in the second substitute, and execute the alias after a setsolar. " Under 4NT you need some rubbish like (note this isn't updated since ages) " alias solvim=set _uq_%0=%@unique[%tmp%] && echos VIM_INC=>>_uq_%0 && echo %SOLARINC|sed -e "s/^-I//" -e "s/ \+-I/,/g">>_uq_%0 && set /r _uq_%0 && del /q _uq_%0 set path=.,./inc,./../inc,./../../inc,./../../../inc,./../../../../inc,$VIM_INC,,/usr/local/include,/usr/include
In case you missed it in the comments above: you need to invoke a shell alias after having sourced the environment:
tcsh alias solvim 'if ( ! $?UPDMINOREXT ) setenv UPDMINOREXT "" ; setenv VIM_INC ./${INPATH}/inc,../${INPATH}/inc,../../${INPATH}/inc,../../../${INPATH}/inc,`echo $SOLARINC|sed -e "s/^-I//" -e "s/ *-I/,/g"`,${SOLARVERSION}/${INPATH}/inc${UPDMINOREXT}/offuh' source LinuxIntelEnv.Set solvim
See also: in Vim :h include-search
Compiling a source from within Vim
Compiling the source from within the editor is of course desired, as warnings and errors generated by the compiler let you directly jump to the line in question. There are some quirks to be solved for the OOo/SO environment, but no real problem, just examine the working .vimrc sections below. Pressing Ctrl+M changes the current window's directory locally to the one of the file loaded, if there is a makfile.mk in that directory, sets the makeprg variable to the command needed to dmake just the current file, and executes it.
For the quickfix window to pull its content from the compiler output we need to have a valid error file setting, and as there's always the hassle with DOS based systems, we also need to take care of that:
" This uses the TMP environment variable, be sure to have it set! if $COMSPEC =~ "\\" " seems like we're having any DOS set makeef=$TMP\\vim##.err " :make errorfile else set makeef=$TMP/vim##.err " :make errorfile endif if $TMP =~ "\\" && ($COMSPEC =~ "4" || $SHELL =~ "4") " seems like we're having some DOS assumed to be 4DOS compatible set shellpipe=\|&\ tee endif
Now the makeprg setting:
" Call appropriate makeprg with Ctrl+M map <C-M> :call Make()<CR> if $SOLARENV == "" " Normal makeprg, not in OpenOffice.org/StarOffice environment function Make() make endfun else " Special dmake, only in OpenOffice.org/StarOffice environment set makeprg=dmake function SetMakeprg() " For a detached gvim we need to source the environment, assuming " the current working directory being source/core/tool/ or " similar, and having a copy of the setsolar -file environment " file as module/../ENV.$INPATH or in $SRC_ROOT set a " ln -s LinuxIntelEnv.Set ENV.$INPATH " For terminal vim re-sourcing the environment isn't necessary. if has("gui_running") if filereadable( "./prj/d.lst" ) set makeprg=source\ ../ENV.$INPATH\ \&\&\ dmake\ wall=t\ debug=t\ ./$INPATH/slo/%:t:r.obj elseif filereadable( "../prj/d.lst" ) set makeprg=source\ ../../ENV.$INPATH\ \&\&\ dmake\ wall=t\ debug=t\ ../$INPATH/slo/%:t:r.obj elseif filereadable( "../../prj/d.lst" ) set makeprg=source\ ../../../ENV.$INPATH\ \&\&\ dmake\ wall=t\ debug=t\ ../../$INPATH/slo/%:t:r.obj elseif filereadable( "../../../prj/d.lst" ) set makeprg=source\ ../../../../ENV.$INPATH\ \&\&\ dmake\ wall=t\ debug=t\ ../../../$INPATH/slo/%:t:r.obj elseif filereadable( "../../../../prj/d.lst" ) set makeprg=source\ ../../../../../ENV.$INPATH\ \&\&\ dmake\ wall=t\ debug=t\ ../../../../$INPATH/slo/%:t:r.obj else set makeprg=source\ ./ENV.$INPATH\ \&\&\ dmake\ wall=t\ debug=t endif else if filereadable( "./prj/d.lst" ) set makeprg=dmake\ wall=t\ debug=t\ ./$INPATH/slo/%:t:r.obj elseif filereadable( "../prj/d.lst" ) set makeprg=dmake\ wall=t\ debug=t\ ../$INPATH/slo/%:t:r.obj elseif filereadable( "../../prj/d.lst" ) set makeprg=dmake\ wall=t\ debug=t\ ../../$INPATH/slo/%:t:r.obj elseif filereadable( "../../../prj/d.lst" ) set makeprg=dmake\ wall=t\ debug=t\ ../../../$INPATH/slo/%:t:r.obj elseif filereadable( "../../../../prj/d.lst" ) set makeprg=dmake\ wall=t\ debug=t\ ../../../../$INPATH/slo/%:t:r.obj else set makeprg=dmake\ wall=t\ debug=t endif endif " Just some copy&paste versions: " Entire module, edit ENV.... and set BUILD_COMMAND to content of build alias "set makeprg=source\ ../../../ENV.$INPATH\ \&\&\ $BUILD_COMMAND "set makeprg=source\ ../../../ENV.$INPATH\ \&\&\ dmake\ ../../../$INPATH/slo/%:t:r.obj "set makeprg=dmake\ product=full " temporary override, no wall, no debug "if filereadable( "./prj/d.lst" ) " set makeprg=dmake\ ./$INPATH/slo/%:t:r.obj "elseif filereadable( "../prj/d.lst" ) " set makeprg=dmake\ ../$INPATH/slo/%:t:r.obj "elseif filereadable( "../../prj/d.lst" ) " set makeprg=dmake\ ../../$INPATH/slo/%:t:r.obj "elseif filereadable( "../../../prj/d.lst" ) " set makeprg=dmake\ ../../../$INPATH/slo/%:t:r.obj "elseif filereadable( "../../../../prj/d.lst" ) " set makeprg=dmake\ ../../../../$INPATH/slo/%:t:r.obj "else " set makeprg=dmake "endif endfun function Make() let my_local_path = expand("%:h") if (my_local_path == "") let my_local_path = "." endif if filereadable( my_local_path . "/makefile.mk" ) exec 'lcd ' . my_local_path call SetMakeprg() make else echo "No makefile.mk in " . my_local_path endif endfun endif
Emacs
Emacs and Cscope
Emacs has a premade file for cscope, generally packaged with cscope. So adding
;;cscope integration (require 'xcscope)
is enough to activate it. Major shortcut are :
- Find-symbol : C-c s s
- Find-global-definition : C-c s g
- Next-symbol : C-c s n
See the documentation for more
A note for developers on WIN\DOS
Please don't complicate life for developers on other platforms by interspersing sources with CarriageReturn characters before linefeeds, which especially is extremely nasty in patches contributed. As you probably can't patch your kernel's IO to simply not automatically write CrLf instead of Lf in text files, when using Vim use this setting:
"set fileformats=dos,unix " Vim's default on DOS/WIN/OS2 set fileformats=unix,dos " Vim's default on UNX, also use it on DOS
MSDEV Open Header File
In the MSDEV you open a header file when the cursor stands on a #include with the Edit.GoTo command which is normally bound to the short cut CTRL+G. To open files from OOo you have to add the folders where MSDEV should look into. This is done in Tools->Options->Projects VC++ Directories. There in the upper right corner select "Include files". Here you can add all folders which are important. E.g.
$(SOLAR_STLP) $(SOLARVER)\$(COMMON_OUTDIR)$(PROEXT)\idl$(UPDMINOREXT) $(SOLARVER)\$(OUTPATH)$(PROEXT)\inc$(UPDMINOREXT)\offuh $(SOLARVER)\$(OUTPATH)$(PROEXT)\inc$(UPDMINOREXT)