SirVer / ultisnips

UltiSnips - The ultimate snippet solution for Vim. Send pull requests to SirVer/ultisnips!
GNU General Public License v3.0
7.54k stars 691 forks source link

Getting UltiSnips to work with emmet.vim #509

Closed herrbischoff closed 9 years ago

herrbischoff commented 9 years ago

After trying unsuccessfully for days to find a way to get UltiSnips to work with Emmet.vim, I'm looking for help here. So this is what I'm trying to accomplish: making both plugins expand only their respective parts while both reacting to a trigger. Kind of like Sublime Text handles this. Is this even possible in vim?

SirVer commented 9 years ago

You will have a hard time to make that work - Vim is not supportive of plugins. There is no guaranteed execution order, no clear state representation and no way of knowing which plugin did what. UltiSnips needs to understand a ton of your edit actions to work properly, I guess that is true for emmet.vim. They will likely interfere left and right.

That said, it seems that the functionality of emmet.vim could be implemented using regular expression triggers and python interpolation using UltiSnips - which I think will make emmet.vim more powerful and easier to extend.@mattn Have you ever thought about that?

herrbischoff commented 9 years ago

Yes, that's exactly what I encountered. It's pretty hard and things collide left and right, as your said. For now I have set a separate trigger for UltiSnips, as I'm using Emmet expansion far more often. Any kind of integration would be really appreciated though. This kind of "getting along between plugins" is an area in which the otherwise excellent vim editor is sorely lacking.

herrbischoff commented 9 years ago

@mattn Vim is really in need of a good, central plugin for elegantly handling tab expansions.

I understand that in backend coding this might not be essential, but writing front-end code (especially dreaded HTML) is just painful without Emmet.

@SirVer: have you seen Sparkup? Unfortunately it is not as complete as emmet.vim, no doubt due to being based on the older Zencoding style. For example, it will not transforn:

a[rel=canonical]

to

<a rel="canonical"></a>

but instead to

<a href=""></a>
<canonical></canonical>

It already uses Python for expansion. Maybe you could look into adopting it?

I'm not really experienced with Python. If I understood more how the selection process works, I could attempt the adaptation myself.

mattn commented 9 years ago

What filetype? I don't use untisnips, so i'm not sure. but a[rel=canonical] expand to <a href="" rel="canonical"></a> on my environment.

herrbischoff commented 9 years ago

@mattn: emmet.vim is fine, the example refers to the Sparkup plugin. Still, my main point is being able to tab-complete emmet abbreviations along with using the snippet feature of UltiSnips. emmet.vim and UltiSnips interfere with each other left and right. Plus, I find the <C-y>, route rather inconvenient for daily code writing. SirVer came up with the question if you might be willing to make emmet.vim an extension to UltiSnips via Python.

mattn commented 9 years ago

you don't provide any information of your vim configurations. so we can't help you.

herrbischoff commented 9 years ago

@mattn: What I'd like to accomplish I have already written above, although more generally. To give you more specifics:

I had asked if this would be possible at all. SirVer's answer had been that the configuration would likely collide and that's what I've found. Until ten I had unsuccessfully tried some own scripting (I have since deleted) and even tried suggestions from this issue, especially the last entry (adapted to UltiSnips instead of NeoComplete of course) but I couldn't get any of them to work reliably. The result is always that an UltiSnips snippet is expanded but emmet.vim is essentially never triggered.

Just for reference, here's my complete .vimrc:

" ----------------------------------------------------------------------------
" Use vim settings
" ----------------------------------------------------------------------------
set nocompatible

" ----------------------------------------------------------------------------
" Setup NeoBundle to manage plugins
" ----------------------------------------------------------------------------
if has('vim_starting')
  set runtimepath+=/Users/mbischoff/.vim/bundle/neobundle.vim/
endif
call neobundle#begin(expand('/Users/mbischoff/.vim/bundle'))
NeoBundleFetch 'Shougo/neobundle.vim'

NeoBundle 'JulesWang/css.vim'
NeoBundle 'Shougo/unite.vim'
NeoBundle 'Shutnik/jshint2.vim'
NeoBundle 'SirVer/ultisnips'
NeoBundle 'Townk/vim-autoclose'
NeoBundle 'airblade/vim-gitgutter'
NeoBundle 'edsono/vim-matchit'
NeoBundle 'ekalinin/Dockerfile.vim'
NeoBundle 'evidens/vim-twig'
NeoBundle 'flazz/vim-colorschemes'
NeoBundle 'hail2u/vim-css3-syntax'
" NeoBundle 'itchyny/lightline.vim'
NeoBundle 'bling/vim-airline'
NeoBundle 'junegunn/vim-easy-align'
NeoBundle 'jordwalke/flatlandia'
NeoBundle 'kchmck/vim-coffee-script'
NeoBundle 'majutsushi/tagbar'
NeoBundle 'mattn/emmet-vim'
NeoBundle 'mattn/gist-vim'
NeoBundle 'mattn/webapi-vim'
NeoBundle 'nathanaelkane/vim-indent-guides'
NeoBundle 'plasticboy/vim-markdown'
NeoBundle 'skammer/vim-css-color'
NeoBundle 'terryma/vim-multiple-cursors'
NeoBundle 'tomtom/tcomment_vim'
NeoBundle 'tpope/vim-fugitive'
NeoBundle 'tpope/vim-surround'
NeoBundle 'wavded/vim-stylus'
NeoBundle 'honza/vim-snippets'
NeoBundle 'chrisbra/vim-show-whitespace'
NeoBundle 'lukaszkorecki/CoffeeTags'
NeoBundle 'tpope/vim-git'
NeoBundle 'rizzatti/dash.vim'
NeoBundle 'editorconfig/editorconfig-vim.git'
NeoBundle 'herrbischoff/cobalt2.vim'
NeoBundle 'nelstrom/vim-markdown-folding'
NeoBundle 'sjl/gundo.vim'

NeoBundle 'Shougo/vimproc.vim', {
  \  'build' : {
  \    'windows' : 'tools\\update-dll-mingw',
  \    'cygwin' : 'make -f make_cygwin.mak',
  \    'mac' : 'make -f make_mac.mak',
  \    'linux' : 'make',
  \    'unix' : 'gmake',
  \  },
  \}

call neobundle#end()

" ----------------------------------------------------------------------------
" Change mapleader
" ----------------------------------------------------------------------------
let mapleader = ','

" ----------------------------------------------------------------------------
" Init
" ----------------------------------------------------------------------------
" Set shell to bash when fish is installed, prevents errors with plugins
if $SHELL =~ 'bin/fish'
  set shell=/bin/sh
endif

" Hide unnecessary gui in MacVim
if has("gui_running")
  set guioptions=egmrt
  set guioptions-=T
  set showtabline=2
end

" Use ESC to remove search highlight only when GUI is active
if has("gui_running")
  nnoremap <esc> :noh<CR><esc>
end

" ----------------------------------------------------------------------------
" Basic options
" ----------------------------------------------------------------------------
set autoindent " autoindent based on line above, works most of the time
set autoread " Reload files changed outside vim
set backspace=indent,eol,start " Enable delete over line breaks and automatically-inserted indentation
set backupdir=~/.tmp " set backup directory to ~/.tmp
set cursorline
set copyindent " copy the previous indentation on autoindenting
set directory=~/.tmp
set encoding=utf-8
set expandtab " use spaces instead of tabs
set fileencoding=utf-8
set fileformat=unix " Set unix line endings
set fileformats=unix,mac,dos
set hidden " Don't unload buffers when they are abandoned, instead stay in the background
set history=1000
set laststatus=2
set lazyredraw " Screen will not be redrawn while running macros, registers or other non-typed comments
set number
set numberwidth=3
set ruler
set shiftwidth=2 " when reading, tabs are 2 spaces
set smartindent " smarter indent for C-like languages
set softtabstop=2 " in insert mode, tabs are 2 spaces
set syntax=on
set ttyfast
set undodir=~/.tmp/undodir " set undo file location
set undofile
set visualbell " Turn off audible bell
set viminfo='100,f1 " Save up to 100 marks, enable capital marks
set visualbell
set wildmenu
set wildmode=list:longest
set undolevels=1000 " use many muchos levels of undo
set noerrorbells
set shortmess+=I

" ----------------------------------------------------------------------------
" Editor layout
" ----------------------------------------------------------------------------
colorscheme gruvbox " Set the color theme
set background=dark
set colorcolumn=80 " Make a mark for column 80
set guifont=InputMonoNarrow:h12
set linespace=4 " Set line spacing
set scrolloff=3 " Keep the cursor visible within 3 lines when scrolling

" ----------------------------------------------------------------------------
" Text options
" ----------------------------------------------------------------------------
" show invisible characters ala TextMate
set formatoptions+=t
set list
set listchars=tab:▸\ ,eol:¬,extends:❯,precedes:❮
set showbreak=↪ " Show line wrapping
set textwidth=80 " set word wrapping to 80 characters
set wrap
set linebreak

" ----------------------------------------------------------------------------
" Saving
" ----------------------------------------------------------------------------
" Automatically removing all trailing whitespace
function! <SID>StripTrailingWhitespaces()
  " Preparation: save last search, and cursor position.
  let _s=@/
  let l = line(".")
  let c = col(".")
  " Do the business:
  %s/\s\+$//e
  " Clean up: restore previous search history, and cursor position
  let @/=_s
  call cursor(l, c)
endfunction
if has("autocmd")
  autocmd BufWritePre *.py,*.js,*.php,*.twig,*.coffee :call <SID>StripTrailingWhitespaces()
endif

" Source the vimrc file after saving it
if has("autocmd")
  autocmd bufwritepost .vimrc source $MYVIMRC
endif

set nobackup
set noswapfile

" ----------------------------------------------------------------------------
" Moving and searching
" ----------------------------------------------------------------------------
set hlsearch " highlight searches by default
set ignorecase
set incsearch " find the next match as we type the search
set showmatch
set smartcase
set gdefault

" ----------------------------------------------------------------------------
" Key mappings
" ----------------------------------------------------------------------------
" Strip trailing whitespace
nnoremap <silent> <F5> :call <SID>StripTrailingWhitespaces()<CR>

" Toggle paste mode
set pastetoggle=<F2>

" Toggle EasyAlign by Enter in visual mode
vmap <Enter> <Plug>(EasyAlign)

" Enable TextMate style indenting
nmap <D-[> <<
nmap <D-]> >>
vmap <D-[> <gv
vmap <D-]> >gv

" Shortcut to rapidly toggle `set list`
nmap <leader>l :set list!<CR>

" Enable key mapping for moving lines up and down
nnoremap <D-j> :m .+1<CR>==
nnoremap <D-k> :m .-2<CR>==
inoremap <D-j> <Esc>:m .+1<CR>==gi
inoremap <D-k> <Esc>:m .-2<CR>==gi
vnoremap <D-j> :m '>+1<CR>gv=gv
vnoremap <D-k> :m '<-2<CR>gv=gv

" Toggle Tagbar
nmap <F8> :TagbarToggle<CR>

" Use <C-Space> for Vim's keyword autocomplete
inoremap <Nul> <C-n>
inoremap <C-Space> <C-n>

" Use ESC to close netrw
if has("autocmd")
  autocmd FileType netrw nmap <silent> <buffer> <Esc> :bd<cr>
endif

" map <leader>q and <leader>w to buffer prev/next buffer
" noremap <leader>q :bp<CR>
" noremap <leader>w :bn<CR>

" Select all mapping
noremap <leader>a ggVG

" Space to toggle folds
nnoremap <Space> za
vnoremap <Space> za

" Make Y not dumb
nnoremap Y y$

" Remap switch to next window
" nnoremap <C-Tab> <C-W>w

" Reloads .vimrc
nmap <Leader>s :source ~/.vimrc

" Opens .vimrc for editing
nmap <Leader>v :edit ~/.vimrc

" Map arrow keys to screen line movement instead of buffer movement
noremap <Up> gk
noremap <Down> gj

" Disable Ex mode
nnoremap Q <nop>

" Enable commenting SublimeText style
nmap <D-/> gcc
" vmap <D-/> gcgv

" Quick escaping in insert mode
inoremap jj <ESC>

" Enable w!! for saving file with root privileges
cmap w!! w !sudo tee % >/dev/null
" ----------------------------------------------------------------------------
" Filetypes
" ----------------------------------------------------------------------------
if has("autocmd")
  " Enable file type detection
  filetype on

  " Syntax of these languages is fussy over tabs Vs spaces
  autocmd FileType make setlocal ts=8 sts=8 sw=8 noexpandtab
  autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab
  autocmd FileType python setlocal ts=4 sts=4 sw=4 expandtab

  " Customisations based on house-style (arbitrary)
  " autocmd FileType html setlocal ts=2 sts=2 sw=2 expandtab
  " autocmd FileType css setlocal ts=2 sts=2 sw=2 expandtab
  " autocmd FileType javascript setlocal ts=4 sts=4 sw=4 noexpandtab

  " Treat .rss files as XML
  autocmd BufNewFile,BufRead *.rss setfiletype xml

  " .md files are markdown files
  autocmd BufNewFile,BufRead *.md setlocal ft=markdown

  " .twig files use HTML syntax
  " autocmd BufNewFile,BufRead *.twig setlocal ft=html

  " .jade files use Jade syntax
  autocmd BufNewFile,BufRead *.jade setlocal ft=jade

  " .styl files use Stylus syntax
  autocmd BufNewFile,BufReadPost *.styl set filetype=stylus
  autocmd BufNewFile,BufReadPost *.css set filetype=css
  autocmd BufNewFile,BufRead *.styl set filetype=stylus
endif

" ----------------------------------------------------------------------------
" Bundles
" ----------------------------------------------------------------------------

" Airline
let g:airline_powerline_fonts = 1
let g:airline_theme='flatlandia'

" UltiSnips
let g:UltiSnipsExpandTrigger="<c-s>"
let g:UltiSnipsJumpForwardTrigger="<c-b>"
let g:UltiSnipsJumpBackwardTrigger="<c-z>"
let g:UltiSnipsEditSplit="vertical"

" Emmet
let g:user_emmet_leader_key = '<c-y>'
let g:user_emmet_prev_key = '<c-j>'
let g:user_emmet_next_key = '<c-k>'
imap <expr> <tab> emmet#expandAbbrIntelligent("\<tab>")

" Unite
let g:unite_source_history_yank_enable = 1
call unite#filters#matcher_default#use(['matcher_fuzzy'])
nnoremap <leader>t :<C-u>Unite -buffer-name=files -start-insert file_rec/async<cr>
nnoremap <leader>c :<C-u>Unite grep<cr>
nnoremap <leader>f :<C-u>Unite -buffer-name=files -start-insert file_rec<cr>
nnoremap <leader>y :<C-u>Unite -buffer-name=yank history/yank<cr>
nnoremap <leader>e :<C-u>Unite -buffer-name=buffer buffer<cr>

" Dash
nmap <silent> <leader>d <Plug>DashSearch
let g:dash_map = {
  \ 'stylus' : ['css', 'stylus']
  \ }

" Show white space
" if has("autocmd")
"   autocmd VimEnter * ShowWhiteToggle
" endif

" EditorConfig
let g:EditorConfig_exclude_patterns = ['fugitive://.*', 'scp://.*']

" Gundo
nnoremap <F5> :GundoToggle<CR>

" ----------------------------------------------------------------------------
" Enable filetype detection, needs to be last line
" ----------------------------------------------------------------------------
filetype plugin indent on

" ----------------------------------------------------------------------------
" That's it, folks.
" ----------------------------------------------------------------------------

It now contains different triggers for UltiSnips and emmet.vim. As I use Emmet completions more often than Snippets, I have bound only emmet.vim to <Tab>, which is unfortunately not what I want to accomplish.

I believe this info is not really needed since the main question for you from SirVer and me was, if you may be willing to integrate emmet.vim into UltiSnips, as it's basically just a more complex snippet expansion anyway.

However, any help, ideas or other input regarding accomplishing my goal will be greatly appreciated.

mattn commented 9 years ago

If untisnips have <plug> maps, you will be possible to do check live below

function! g:emmet_or_ultisnip()
  if !emmet#isExpandable()
    return "\<plug>(ultisnips-expand)"
  else
    return "\<plug>(emmet-expand-abbr)"
  endif
endfunction
imap <expr> <tab> g:emmet_or_ultisnip()

but currently, ultisnips doesn't have.

SirVer commented 9 years ago

@herrbischoff That is easy to add - feel free to provide a patch to add these into map_keys.vim.

@mattn: for what it's worth, here is an implementation of a snippet that expands the example you gave further up in UltiSnips. I think emmet could profit from building on a snippet engine - a lot of stuff comes for free and it is easy for users to mix with their own snippets.

snippet '(\w+)\[(.*?)\]' "Emmet like tag expansion" r
`!p
tag = match.group(1)
attributes = ""
values = [w.split("=") for w in match.group(2).split(",")]
attributes = " " + " ".join('%s="%s"' % (k,v) for k,v in values)
snip.rv = "<%s%s></%s>" % (tag, attributes, tag)`
endsnippet
mattn commented 9 years ago

@SirVer have you use emmet.vim or emmet on atom/sublime? unfortunately, emmet is not just a way to wrap tag. For example,

ul>li*5>{your text $$$}

expand to

<ul>
    <li>your text 001</li>
    <li>your text 002</li>
    <li>your text 003</li>
    <li>your text 004</li>
    <li>your text 005</li>
</ul>
mattn commented 9 years ago

And html:5>ul>li*5>{your text $$$} expand to

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <ul>
        <li>your text 001</li>
        <li>your text 002</li>
        <li>your text 003</li>
        <li>your text 004</li>
        <li>your text 005</li>
    </ul>
    _
</body>
</html>
SirVer commented 9 years ago

mattn, luckily I am not in the position to need to write HTML on a daily basis, so no. :)

My snippet was just an example - all the stuff you just posted would be easy in UltiSnips. I think less than 100 lines to implement all the features you showed off right now. Emmet.vim seems like a nice productivity booster - but in essence it is a snippet engine, and a rather minimalistic at that. I think the project could profit from moving to a more complete snippet engine.

herrbischoff commented 9 years ago

I'm with SirVer on this. Using Emmet completion within a more complete snippet engine would greatly profit it by consolidating all expansion needs. Especially the ability to use VimScript and Python for snippet manipulation makes UltiSnips uniquely positioned for this, a lot more so than other snippet solutions like NeoComplete or SnipMate.

herrbischoff commented 9 years ago

Well, for the time being I have decided to embrace another strategy altogether and post it in here for others to find. Since I'm already relying on gulp to build my front-end files and HTML bugs the hell out of me anyway, it led me to try Jade as a templating language. This is impressive. Now I can skip using Emmet completely (except for legacy projects) and use SuperTab along with UltiSnips for my expansion needs.

For certain client projects I'm using a combination of WordPress (ugh...), Timber (for Twig templates in WP) and Jade (for actually writing the Twig HTML templates) right now. Sounds convoluted but is actually really streamlined.

mattn commented 9 years ago

I suggest to ultisnips having <plug> mappings.

mattn commented 9 years ago

I didn't make sure this fix. If you are okay. please check https://github.com/mattn/ultisnips/commit/b9de99168bd8e34a5571f61e4ac35a58d5d203eb

mattn commented 9 years ago

@herrbischoff if there are possibly the patch in above is merged, you can fix your problem with.

function! EmmetOrUltiSnips()
  if getline('.') =~ '[<>]'
    return "\<plug>(emmet-expand-abbr)"
  else
    return "\<plug>(UltiSnips#ExpandSnippet)"
  endif
endfunction
let g:UltiSnipsExpandTrigger = '<F9>'
imap <expr> <tab> EmmetOrUltiSnips()

Setting g:UltiSnipsExpandTrigger avoid to overwrite key mappings.

SirVer commented 9 years ago

I tried matt's fix, but it interacts badly with a langmap work around that is needed for vim < 7.4 patch 502 - unfortunately. I hope to get rid of this workaround eventually, maybe there are few enough users that we can require it soon.

SirVer commented 9 years ago

I do not see anything immediately actionable in this bug report for now. Closing.

jceb commented 8 years ago

FYI, I created an emmet snippet that might provide some of the functionality you're looking for: https://github.com/jceb/emmet.snippets

seletskiy commented 8 years ago

@jceb: Definitely good stuff! Great work!

seletskiy commented 8 years ago

@jceb: But, unfortunately, your code is unmaintainable :(

herrbischoff commented 8 years ago

Thanks for trying to work this out. I ditched the Emmet plugin in favor having working snippet expansion through US. What a crock. I sure wish Vim wasn't such a pool of inconsistent, non-iteroperable malarkey. Even JS web-based editors views get this right. sigh I just want a Vim-like console-based editor that works. It's amazing how many editors are out there and how they are all doing it wrong. I just don't want to switch back to Sublime Text but probably I'm going to. It's not great but it works. Opting for the least worse option I guess.

SirVer commented 8 years ago

That is pretty much my feeling about text editors too :)

I even started writing my own that would solve all of these issues and would be perfect /s. Unfortunately I cannot follow through with this right now.

I have really high hopes for Xi: https://github.com/google/xi-editor. @raphlinus is a great engineer and really passionate about this project - and he produced a lot of really valuable side stuff that is useful to other projects already, though the project is really young.

jceb commented 8 years ago

@seletskiy why would it be unmaintainable?

seletskiy commented 8 years ago

@jceb: Several reasons:

You've made really beautiful practical application of UltiSnips, I really like it. I'm pointing only that underlying code is very difficult to maintain and improve for someone else (not you).

herrbischoff commented 8 years ago

@SirVer: Nice! Seeing that it's in a very early stage right now makes me wonder how long it is going to take to be truly usable. I guess that's the problem with all kinds of text editors — you have to get the basics right or they will bite you bad down the road. I also like your idea of an editor that is essentially just a base for plugins.

jceb commented 8 years ago

@seletskiy true, with some points I agree. Hope it will get better over time :-)