How to change JSX indentation rules in VIM? - vim

Currently, I'm using two plugins to format my JS(X) code in VIM:
pangloss/vim-javascript
mxw/vim-jsx
Our team has elected that, when we have React Components whose props spill on to multiple lines, we want those properties to line up with the property on the first line, like so:
Desired:
<Toggle label={dragString} toggled={this.props.canDrag} onToggle={this.toggleDrag}
thumbStyle={toggleStyles.thumbOff}
thumbSwitchedStyle={toggleStyles.thumbOn}
trackStyle={toggleStyles.trackOff}
trackSwitchedStyle={toggleStyles.trackOn}
/>
Instead, it looks like vim-jsx will always indent props on new lines just two spaces (which is what we have our tab size set to):
What actually happens:
<Toggle label={dragString} toggled={this.props.canDrag} onToggle={this.toggleDrag}
thumbStyle={toggleStyles.thumbOff}
thumbSwitchedStyle={toggleStyles.thumbOn}
trackStyle={toggleStyles.trackOff}
trackSwitchedStyle={toggleStyles.trackOn}
/>
Is there a change I can make in my .vimrc or to the vim-jsx plugin code to make React component props on new lines line up with the prop on the first line?

In my case, I already had indentation working as expected for .js files with this line in my .vimrc:
autocmd FileType javascript setlocal shiftwidth=2 tabstop=2
To get similar behavior in .jsx files, I needed to also add:
autocmd FileType javascriptreact setlocal shiftwidth=2 tabstop=2
You can run :set filetype in vim to display the filetype according to vim; in this case javascriptreact.

Unfortunately, there's no easy way to do this. In Vim, indentation works by invoking an "indent expression" stored in the indentexpr setting. The file that does this for JSX in your plugin is here: https://github.com/mxw/vim-jsx/blob/eb656ed96435ccf985668ebd7bb6ceb34b736213/after/indent/jsx.vim
They define a function, called GetJsxIndent, which either delegates to the XML indent, or to the JS indent, or it does something slightly different. Their particular overrides are here: https://github.com/mxw/vim-jsx/blob/eb656ed96435ccf985668ebd7bb6ceb34b736213/after/indent/jsx.vim#L92-L102
Theoretically, you could make your own changes in that area, check where the properties start in the <Toggle line and align to those, but it might end up being much more complicated than it seems at first glance. For instance, they decrease the indent by a single shiftwidth, if the current line is />. With a change like you're proposing, this won't work -- the /> couldn't look at the previous line, it has to find its starting <.
My suggestion would be to open an issue on the vim-jsx project and ask them to implement this, possibly with a setting to turn it on or off. The alternative would be to fork it yourself and try to apply your workaround, but be prepared for it to take some time and effort.

Wanted to update this post, even though it's been a few years. We've been using Prettier for auto-formatting our code, and it takes care of aligning JSX properties properly.
The fix for getting it to format in VIM was adding using the Neoformat Plugin and appending the following to my .vimrc file:
autocmd BufWritePre *.js Neoformat
autocmd FileType javascript setlocal formatprg=prettier\ --stdin\ --parser\ flow
let g:neoformat_try_formatprg = 1 " Use formatprg when available
let g:neoformat_enabled_javascript = ['prettier-eslint', 'prettier']
let g:neoformat_enabled_json = ['prettier-eslint', 'prettier']
let g:neoformat_enabled_css = ['prettier-eslint', 'prettier']
let g:neoformat_enabled_less = ['prettier-eslint', 'prettier']
All my JS(X) code will now be properly formatted every time I save the file (:w)
I'm really happy with this solution, because it eliminates the cognitive load of having to manually format code.

Related

TypeScript file is considered as xml when indenting

In a nutshell:
In my .vimrc I have the following line to have vim indent xml files correctly:
autocmd FileType xml set equalprg=xmllint\ --format\ -
the weird thing is that now vim tries to use xmllint when I try to indent TypeScript files, and I don't understand why...
More details
When I open test.ts in vim, I can see that vim correctly detects the type (ie: set filetype? returns filetype=typescript).
But when I'm trying to indent this block of code
for(var i=0 ; i < 1 ; i++){
console.log(i);
}
by putting the cursor on a curly bracket and pressing =%, then this block is replaced with
Exception : System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
[... eluded for brievity ...]
at xmllint.Program.Main(String[] args)
(which means that vim launched the xmllint command and that this command failed (that failure is expected since it's not xml. But vim shouldn't have launched xmllint in the first place)).
Two other observations which might be useful are:
when I remove this line from my .vimrc then indentation works as expected
if I change the file extension to anything else (say test.dummy) then indentation works as expected
Even more details
I'm using the TypeScript plugin https://github.com/leafgarland/typescript-vim . If I delete this plugin then set filetype? detects .ts files as xml (not sure where this matching comes from...)
:scriptnames returns
/etc/vimrc
/usr/share/vim/vim81/defaults.vim
/usr/share/vim/vim81/syntax/syntax.vim
/usr/share/vim/vim81/syntax/synload.vim
/usr/share/vim/vim81/syntax/syncolor.vim
/usr/share/vim/vim81/filetype.vim
~/.vim/ftdetect/typescript.vim
/usr/share/vim/vim81/ftplugin.vim
/usr/share/vim/vim81/indent.vim
~/.vimrc
/usr/share/vim/vim81/syntax/nosyntax.vim
/usr/share/vim/vim81/plugin/getscriptPlugin.vim
/usr/share/vim/vim81/plugin/gzip.vim
/usr/share/vim/vim81/plugin/logiPat.vim
/usr/share/vim/vim81/plugin/manpager.vim
/usr/share/vim/vim81/plugin/matchparen.vim
/usr/share/vim/vim81/plugin/netrwPlugin.vim
/usr/share/vim/vim81/plugin/rrhelper.vim
/usr/share/vim/vim81/plugin/spellfile.vim
/usr/share/vim/vim81/plugin/tarPlugin.vim
/usr/share/vim/vim81/plugin/tohtml.vim
/usr/share/vim/vim81/plugin/vimballPlugin.vim
/usr/share/vim/vim81/plugin/zipPlugin.vim
/usr/share/vim/vim81/ftplugin/xml.vim
/usr/share/vim/vim81/indent/xml.vim
/usr/share/vim/vim81/syntax/xml.vim
/usr/share/vim/vim81/syntax/dtd.vim
~/.vim/ftplugin/typescript.vim
~/.vim/compiler/typescript.vim
~/.vim/indent/typescript.vim
~/.vim/syntax/typescript.vim
Also, not sure it it's useful information, but I'm using ViM 8.1 on windows 10 (installed along with git-bash)
I'm not sure why your TypeScript is first detected as XML (maybe some other answer can dive into that), but it can happen that a generic (default, built-in) filetype is first detected, and then revised by a more specific detection (as in your case, via the TypeScript plugin). The problem you have is that the 'equalprg' lingers one, because you used the shortcut :autocmd instead of a proper filetype plugin:
autocmd FileType xml set equalprg=xmllint\ --format\ -
If you only want to enable an option for certain filetypes, use :setlocal option=value (your use of :set would make the setting be inherited by new buffers opened from that one, which is wrong), and put the corresponding :setlocal commands into ~/.vim/after/ftplugin/{filetype}.vim, where {filetype} is the actual filetype (e.g. xml here). (This requires that you have :filetype plugin on; use of the after directory allows you to override any default filetype settings done by $VIMRUNTIME/ftplugin/{filetype}.vim.) Proper plugins also undo any settings via the :help undo_ftplugin mechanism.
So, a proper configuration in your case would replace the :autocmd with ~/.vim/after/ftplugin/xml.vim:
setlocal equalprg=xmllint\ --format\ -
let b:undo_ftplugin = (exists('b:undo_ftplugin') ? b:undo_ftplugin . '|' : '') . 'setlocal equalprg<'
With that, 'equalprg' would still be briefly set as long as the filetype is XML, but it would then be undone by the command defined in b:undo_ftplugin, which Vim executes automatically when the filetype changes to typescript.

Trying to set .html files with htmljinja filetype and get correct indentation

I have just this configuration below. When I uncomment the last line, I dont get the correct indentation: the lines are put in the first column when I indent.
syntax on
autocmd FileType html setlocal shiftwidth=2 tabstop=2
"au BufRead,BufNewFile *.html set filetype=htmljinja
I need the htmljinja filetype because my html has twig code (twig's syntax is like htmljinja)
I have vim 7.3.547
In the meantime i circumvent vim's indentation capabilities and rather rely on specific indenters/reformatter wherever i can (by the way if anybody is aware of a tex/latex reformatter please let me know).
As a result the following lines found their way into my .vimrc:
au FileType c,cpp let &l:equalprg="indent -br -l62 -nce -blf -bbb -i4 -nbfde -nbfda -bad -bap -cli4 -nut"
au FileType perl let &l:equalprg="perltidy"
noremap <leader>i mzgg=G`z
A drawback of this approach is that you can't enjoy automatic indentation while writing. Nevertheless I get used to it very fast and I think of it as a very solid and suitable solution to the indentation-problem. Moreover the indented code looks great and i doubt that those fancy IDE's keep up with it.
Now, what has this got to do with your problem?
Following my approach i searched for an html-reformatter and found tidy. tidy seems to be a monster, that not only can pretty-print your html files but also can validate and correct them.
In order to cope with tidy you have to write a config file for your indentation purposes. The following one works for me:
~/.tidyrc_indent:
indent: auto
indent-spaces: 3
show-warnings: no
show-errors: 0
quiet: yes
indent-cdata: yes
output-html: yes
wrap: 80
But make sure to read the man pages of tidy because the options seems to be endless.
Now you can add
au FileType htmljinja let &equalprg="tidy -config ~/.tidyrc"
to your .vimrc and start indenting your html file with gg=G or a derived mapping that needs less keystrokes.
Please take care as tidy like any other reformatter may mess up your code. Therefore you should back up your code regularly (maybe with git or something similar). Although i never experienced this by myself wiht perltidy and indent there are some reports on the internet of angry users that complain about such events.

Disable all auto indentation in vim

In TeX vim usually screws up my indentation. Mainly when I'm in a displayed equation which I think should look like this:
\[
x=\frac{y}{z}
\]
where the whitespace infront of the x is one tab.
When I start type the equation I type the \[ and \] marks first and then go back between them, typing the tab and then the rest of the equation.
Vim doesn't do anything wrong until I have to use something that incorporates curly braces (\frac{} for example). When I type the closing } vim automatically shifts the indentation for the whole line to the left, which undoes my typed tab.
This is very anoying, how do I disable it?
my .vimrc contains:
"indentation
set smartindent
set autoindent
set tabstop=5
set shiftwidth=5
filetype indent on
I just spent a few hours working through indentation pains with javascript, and the conclusion I came to is don't remove filetype indent on from your vimrc!
This setting provides the best smart indentation for multiple file types. If you're getting bad results with this, there's likely a configuration issue at hand.
File Specific Indent Settings
So if you're like me, you probably had filetype indent on in your vimrc and had no idea what it was doing.
All this setting does is tell vim to look for files with filetype-specific indent rules. There are a few places it looks, but there are probably only two that you'd be interested in.
$VIMRUNTIME/indent/
~/.vimrc/after/indent/
The first place holds the default indent rules that come with vim. If you were to set filetype indent on on a fresh vim installation, this is where all the smart indenting would come from. For example, when you open a file called index.html in would get the rules from $VIMRUNTIME/indent/html.vim.
In my experience, these default rules are pretty darn good, but they can get messed up by other settings.
The second place (the after directory) allows you to add settings that will supercede those in the first place. This is nice because you don't have to edit the default files in order to customize them.
Flavors of Indentation
There are a few different indentation options as you've seen, and they don't all play nice together. From the Vim wiki:
autoindent
'autoindent' does nothing more than copy the indentation from the previous line, when starting a new line. It can be useful for structured text files, or when you want to control most of the indentation manually, without Vim interfering. 'autoindent' does not interfere with other indentation settings, and some file type based indentation scripts even enable it automatically.
I use filetype indent on and set autoindent in my vimrc, since they work well together. I don't have the others set.
smartindent & cindent
'smartindent' automatically inserts one extra level of indentation in some cases, and works for C-like files. 'cindent' is more customizable, but also more strict when it comes to syntax.
'smartindent' and 'cindent' might interfere with file type based indentation, and should never be used in conjunction with it.
When it comes to C and C++, file type based indentations automatically sets 'cindent', and for that reason, there is no need to set 'cindent' manually for such files. In these cases, the 'cinwords', 'cinkeys' and 'cinoptions' options still apply.
Generally, 'smartindent' or 'cindent' should only be set manually if you're not satisfied with how file type based indentation works.
indentexpr
Runs filetype indent scripts found in (vimfolder)\indent\\(indentscripts). It is mentioned in the vim documentation for filetype, alongside the others just mentioned (also, it was the cause of the problem I was having):
Reset 'autoindent', 'cindent', 'smartindent' and/or 'indentexpr' to disable indenting in an opened file.
Troubleshooting
There's a chance that some rogue plugin is changing your indent settings and that's why you're getting poor results. Luckily verbose will tell you which file was the last to change the option in question.
:verbose set autoindent?
:verbose set cindent?
:verbose set smartindent?
:verbose set indentexpr?
You may get a result such as
indentexpr=SomeMessedUpValue
Last set from ~/.vim/bundle/some_plugin/indent/plaintex.vim
If that happens, you can move that file, close and open vim, and see if it fixes your problem.
Turning Off Indent Settings for TeX
Maybe the defaults just aren't doing it for you, and you want to disable the indent settings for TeX, but leave all other file types alone. You can easily do so by setting these values to their defaults in a file in the after directory.
I don't know much about Tex or LaTex, but when I created a file with the .tex extension and ran :filetype it had the filetype as plaintex. Assuming that this is correct, you'd want to create a file, ~/.vim/after/indent/plaintex.vim. In that file:
set autoindent&
set cindent&
set smartindent&
set indentexpr&
This will set all these values to their defaults whenever you open a .tex file.
There seem to be a little mix of terms in your question. In vim the term autoindent points to a special kind of indentation that simply follows the indent level of the previous line (which is quite handy sometimes). To remove it set noautoindent by hand, or write it in your _vimrc.
There are two other automatic kinds of indentation, cindent and smartindent. Similarly, if you wish to disable them go with set nocindent and set nosmartindent
If you look in help (help autoindent, ...) they are all quite nicely explained. Which one you prefer (or don't) is mostly determined by your programming style and habits. So, try them out and see which you like most.
Unfortunatelly, I don't use LaTeX that much anymore, so I'm not familiar with its internal filetype indentation rules.
For anyone else having a similar problem, a solution that worked for me was:
Use :verbose set indentexpr? to find what file was causing the de-indentation
Find where indentexpr is changed (for me it was setlocal indentexpr=GetTeXIndent())
Change that line to setlocal indentexpr& to turn indentexpr off
This removed all de-indenting from brackets, parentheses, and braces.
Remove the lines set autoindent and set smartindent to remove all vim autoindentation.
The following command finally stopped VIM from pretending it knows how to indent files for me and only do what I explicitly tell it:
:setl noai nocin nosi inde=
Courtesy https://vim.fandom.com/wiki/How_to_stop_auto_indenting
If you are using the vim-latex plugin, set this option:
let g:tex_indent_brace=0
For other plugins, if you don't want to turn off indentexpr as in the above answers, you can find where indentkeys is set and comment out those lines. This should stop triggering re-indent when you type a closing brace.
:verbose set indentkeys?

VIM: set filetype=txt for every new file [No Name]

I tried all possible things to let vim set filetype to 'txt' to all new files I create (in a new tab) but it doesn't work.
This is p.e. what I've read on the web a few times:
au BufRead,BufNewFile *.txt setlocal ft=txt
(to put in _vimrc)
However it doesn't work.
Can anyone help me?
The following line, added to your .vimrc, will set the filetype to text if it is not already set.
autocmd BufEnter * if &filetype == "" | setlocal ft=text | endif
All files are considered plain text unless you have file-type detection turned on or explicitly set the file-type. However, Vim lets you set the file-type to any old text, so are you absolutely sure it is not working?
:set filetype=banana
:set filetype?
filetype=banana
Setting the filetype is not going to have any noticable effect unless there is a corresponding file in the ftplugin Vim directory and Vim does not ship with a txt.vim file-type file. You could, of couse, add a txt.vim here but I am not sure what this will gain you over default settings — what special behaviour would you want for text files that you would not want for the default behaviour?
(If you want syntax highlighting (whatever that may mean for text file!) then you will also have to create a txt.vim file in the syntax Vim directory.)
What effect are you trying to achieve?
It's actually way simpler than all this. Just put this as one of the first lines in your .vimrc.
set ft=txt
Whenever opening a new file, the filetype will be set to txt. But if you open a file with an known existing type it will still be overridden no problem.

Vim: How to change text from within an indent script

I recently switched from Eclipse to Vim. I'm loving it. There are a few hangups I'm working on, but one of the ones I'm having lots of trouble with is the PHP doc comments. In eclipse I could type:
/** [enter]
and the next line would auto fill with
*
So I'd have:
/**
* [comment goes here]
I'm wondering if there's anything like this for vim. It seems there are some plugins to autogenerate doc comments by running a command, but I'd love to have it do them as I'm typing.
I was playing around with the PHP indent script (http://www.vim.org/scripts/script.php?script_id=1120) and I got it to recognize when it's inside of a doc comment block, but I can't figure out how to get it to actually change the text and add a " * " after hitting enter when inside the block.
I've tried what I've seen other plugins do:
let #z = ' * '
put! z
tried this too:
exe 'normal!' '"zgp'
but no luck. Is this not possible from an indent script, and if not, how do I actually get Vim to recognize a doc comment block and act accordingly while I'm typing?
Any help would be greatly appreciated!
No need to mess around with the indentation files. Vim's formatoptions will do this for you and in a variety of languages (not just PHP).
Ensure you have r included in your formatoptions:
:setlocal fo+=r "to set
:set fo? "to query
You can include this in your .vimrc or in .vim/ftplugin/php.vim (if you just want to activate this for PHP).
For more information on formatoptions and file-type plugins, see:
:help 'formatoptions'
:help fo-table
:help ftplugins
Would adding the below code to your vimrc do something similar to what you want?
autocmd BufNewFile,BufRead *.php setlocal formatoptions+=r formatoptions+=o
autocmd BufNewFile,BufRead *.php setlocal comments=s1:/*,mb:*,ex:*/,://,:#
I currently can't quite figure out how to make it work without overriding the <!-- ---> commenting, which this does. I.e. this will break auto-indenting with <!-- --> comments.
Edit. Added ://,:# to comments as Johnsyweb's distribution does.
Try adding this to your vimrc:
let g:PHP_autoformatcomment=1
I'm on a Mac and it seems to be enabled by default. Functions exactly how you stated.

Resources