From 0f90dc3a0f8390548a14b475942d222e35317edc Mon Sep 17 00:00:00 2001 From: Christian Hubinger Date: Sun, 12 Mar 2017 01:23:40 +0100 Subject: [PATCH 1/4] feature: implement local ESLint support Support to use the project local eslint installation to be used to reformat javascript source with the eslint --fix command --- README.md | 10 ++++++ plugin/defaults.vim | 76 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/README.md b/README.md index d255469..9ddf89a 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,16 @@ Here is a list of formatprograms that are supported by default, and thus will be It can be installed by running `npm install -g standard` (`nodejs` is required). No more configuration needed. More information about the style guide can be found here: http://standardjs.com/. +* `ESlint (local)` for __Javascript__. http://eslint.org/ + It can be installed by running `npm install eslint`. The linter is then installed locally at ```node_modules/.bin/eslint``` + When opening a javascript file (```vim autocmd FileType javscript```) vim will walk up from the current file to search for such local installation and a + ESLint configuration file (either .eslintrc or eslintrc.json). When both are found eslint will be configured as first javascript formatter. + This linter can be disabled using global setting + ```vim + let g:formatters_javascript_eslint_local = 0 + ``` + Currently only working on *nix like OS (Linux, MacOS etc.) requires OS to provide sh like shell syntax + * `xo` for __Javascript__. It can be installed by running `npm install -g xo` (`nodejs` is required). Here is the link to the repository: https://github.com/sindresorhus/xo. diff --git a/plugin/defaults.vim b/plugin/defaults.vim index 01897f5..4fb992c 100644 --- a/plugin/defaults.vim +++ b/plugin/defaults.vim @@ -160,6 +160,82 @@ if !exists('g:formatdef_xo_javascript') let g:formatdef_xo_javascript = '"xo --fix --stdin"' endif +" Setup ESLint local. Setup is done for each file in an autocmd to make sure +" the most local instance of ESLint and corresponding config is used. +" No windows support at the moment. +if !exists('g:formatdef_eslint_local') && !has('win32') + " set disable flag when not defined already + let g:formatters_javascript_eslint_local = exists('g:formatters_javascript_eslint_local') ? g:formatters_javascript_eslint_local : 1 + " find file upwards the filesystem + function! s:find_upward(path, filename) + let l:current_folder = fnamemodify(a:path, ':p:h') + if l:current_folder == "/" + return + endif + let l:prog = l:current_folder.a:filename + if filereadable(l:prog) + return l:prog + elseif l:current_folder == "/" + return + else + return s:find_upward(fnamemodify(l:current_folder, ':h'), a:filename) + endif + endfunction + + " add 'eslint_local' as first javscript formatter + function s:register_eslint_local() + let l:index = index(g:formatters_javascript, 'eslint_local') + if l:index == -1 + call insert(g:formatters_javascript, 'eslint_local', 0) + endif + endfunction + + " remmove 'eslint_local' from javscript formatters + function s:unregister_eslint_local() + let l:index = index(g:formatters_javascript, 'eslint_local') + if l:index != -1 + call remove(g:formatters_javascript, l:index) + endif + endfunction + + function! s:setup_eslint_local_cmd(path) + let verbose = &verbose || g:autoformat_verbosemode == 1 + if g:formatters_javascript_eslint_local == 0 + echomsg 'Disable ESLint local' + call s:unregister_eslint_local() + return + endif + " find formatter & config file + let l:prog = s:find_upward(a:path, '/node_modules/.bin/eslint') + let l:cfg = s:find_upward(a:path, '/.eslintrc.json') + if empty(l:cfg) + let l:cfg = s:find_upward(a:path, '/.eslintrc') + endif + if (empty(l:cfg) || empty(l:prog)) + if verbose + echomsg 'No local ESLint program and/or config found' + endif + call s:unregister_eslint_local(); + return + endif + + " This formatter uses a temporary file as ESLint has not option to only + " print the formatted source to stdout. It will allways modify the file + " in place. + if !exists('l:eslint_js_tmp_file') + let l:eslint_js_tmp_file = fnameescape(tempname().".js") + endif + let g:formatdef_eslint_local = + \ '"rm -f '.l:eslint_js_tmp_file.'; + \ cat '.expand('%').' > '.l:eslint_js_tmp_file.'; ' + \ .l:prog.' -c '.l:cfg.' --fix '.l:eslint_js_tmp_file.' 1> /dev/null; exit_code=$? + \ cat '.l:eslint_js_tmp_file.'; rm -f '.l:eslint_js_tmp_file.'; exit $exit_code"' + call s:register_eslint_local() + endfunction + " register hook to setup lint command correctly for the current file + autocmd FileType javascript call s:setup_eslint_local_cmd(expand('')) +endif + if !exists('g:formatters_javascript') let g:formatters_javascript = [ \ 'jsbeautify_javascript', From 9e645c483d4a73d8a93ce2e842d4a12bb19b51fc Mon Sep 17 00:00:00 2001 From: Christian Hubinger Date: Tue, 14 Mar 2017 00:44:30 +0100 Subject: [PATCH 2/4] fix: generate command on the fly when executing Replaced: s:find_uwards fint builtin findfile() Removed: register/unregister logic --- README.md | 4 +-- plugin/defaults.vim | 74 +++++++++++---------------------------------- 2 files changed, 19 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 9ddf89a..d65ff12 100644 --- a/README.md +++ b/README.md @@ -160,8 +160,8 @@ Here is a list of formatprograms that are supported by default, and thus will be * `ESlint (local)` for __Javascript__. http://eslint.org/ It can be installed by running `npm install eslint`. The linter is then installed locally at ```node_modules/.bin/eslint``` - When opening a javascript file (```vim autocmd FileType javscript```) vim will walk up from the current file to search for such local installation and a - ESLint configuration file (either .eslintrc or eslintrc.json). When both are found eslint will be configured as first javascript formatter. + When running formatter vim will walk up from the current file to search for such local installation and a + ESLint configuration file (either .eslintrc or eslintrc.json). When both are found eslint is executed with the --fix argument. This linter can be disabled using global setting ```vim let g:formatters_javascript_eslint_local = 0 diff --git a/plugin/defaults.vim b/plugin/defaults.vim index 4fb992c..df07730 100644 --- a/plugin/defaults.vim +++ b/plugin/defaults.vim @@ -160,84 +160,45 @@ if !exists('g:formatdef_xo_javascript') let g:formatdef_xo_javascript = '"xo --fix --stdin"' endif -" Setup ESLint local. Setup is done for each file in an autocmd to make sure -" the most local instance of ESLint and corresponding config is used. +" Setup ESLint local. Setup is done on formatter execution if ESLint and +" corresponding config is found they are used, otherwiese the formatter fails. " No windows support at the moment. if !exists('g:formatdef_eslint_local') && !has('win32') " set disable flag when not defined already let g:formatters_javascript_eslint_local = exists('g:formatters_javascript_eslint_local') ? g:formatters_javascript_eslint_local : 1 - " find file upwards the filesystem - function! s:find_upward(path, filename) - let l:current_folder = fnamemodify(a:path, ':p:h') - if l:current_folder == "/" - return - endif - let l:prog = l:current_folder.a:filename - if filereadable(l:prog) - return l:prog - elseif l:current_folder == "/" - return - else - return s:find_upward(fnamemodify(l:current_folder, ':h'), a:filename) - endif - endfunction - " add 'eslint_local' as first javscript formatter - function s:register_eslint_local() - let l:index = index(g:formatters_javascript, 'eslint_local') - if l:index == -1 - call insert(g:formatters_javascript, 'eslint_local', 0) - endif - endfunction - - " remmove 'eslint_local' from javscript formatters - function s:unregister_eslint_local() - let l:index = index(g:formatters_javascript, 'eslint_local') - if l:index != -1 - call remove(g:formatters_javascript, l:index) - endif - endfunction - - function! s:setup_eslint_local_cmd(path) + function! g:SetupESLintLocalCmd() + let l:path = fnamemodify(expand('%'), ':p') let verbose = &verbose || g:autoformat_verbosemode == 1 if g:formatters_javascript_eslint_local == 0 - echomsg 'Disable ESLint local' - call s:unregister_eslint_local() - return + return "(>&2 echo 'ESLint local is disbaled')" endif " find formatter & config file - let l:prog = s:find_upward(a:path, '/node_modules/.bin/eslint') - let l:cfg = s:find_upward(a:path, '/.eslintrc.json') + let l:prog = findfile('node_modules/.bin/eslint', l:path.";") + let l:cfg = findfile('.eslintrc.json', l:path.";") if empty(l:cfg) - let l:cfg = s:find_upward(a:path, '/.eslintrc') + let l:cfg = findfile('.eslintrc', l:path.";") endif if (empty(l:cfg) || empty(l:prog)) if verbose - echomsg 'No local ESLint program and/or config found' + return "(>&2 echo 'No local ESLint program and/or config found')" endif - call s:unregister_eslint_local(); return endif - " This formatter uses a temporary file as ESLint has not option to only - " print the formatted source to stdout. It will allways modify the file - " in place. - if !exists('l:eslint_js_tmp_file') - let l:eslint_js_tmp_file = fnameescape(tempname().".js") - endif - let g:formatdef_eslint_local = - \ '"rm -f '.l:eslint_js_tmp_file.'; - \ cat '.expand('%').' > '.l:eslint_js_tmp_file.'; ' - \ .l:prog.' -c '.l:cfg.' --fix '.l:eslint_js_tmp_file.' 1> /dev/null; exit_code=$? - \ cat '.l:eslint_js_tmp_file.'; rm -f '.l:eslint_js_tmp_file.'; exit $exit_code"' - call s:register_eslint_local() + " This formatter uses a temporary file as ESLint has not option to print + " the formatted source to stdout without modifieing the file. + let l:eslint_js_tmp_file = fnameescape(tempname().".js") + return "rm -f ".l:eslint_js_tmp_file."; cat ".l:path." > ".l:eslint_js_tmp_file.";" + \ .l:prog." -c ".l:cfg." --fix ".l:eslint_js_tmp_file." 1> /dev/null; exit_code=$? + \ cat ".l:eslint_js_tmp_file."; rm -f ".l:eslint_js_tmp_file."; exit $exit_code" endfunction - " register hook to setup lint command correctly for the current file - autocmd FileType javascript call s:setup_eslint_local_cmd(expand('')) + let g:formatdef_eslint_local = "g:SetupESLintLocalCmd()" endif if !exists('g:formatters_javascript') let g:formatters_javascript = [ + \ 'eslint_local', \ 'jsbeautify_javascript', \ 'pyjsbeautify_javascript', \ 'jscs', @@ -246,7 +207,6 @@ if !exists('g:formatters_javascript') \ ] endif - " JSON if !exists('g:formatdef_jsbeautify_json') if filereadable('.jsbeautifyrc') From db4a01e1974f4e604fcb21b40c08938b760f983b Mon Sep 17 00:00:00 2001 From: Christian Hubinger Date: Tue, 14 Mar 2017 11:45:15 +0100 Subject: [PATCH 3/4] fix: renamed command builder function, remove disable flag moved Win32 check inside command builder --- README.md | 4 ---- plugin/defaults.vim | 13 +++++-------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d65ff12..edf7b7e 100644 --- a/README.md +++ b/README.md @@ -162,10 +162,6 @@ Here is a list of formatprograms that are supported by default, and thus will be It can be installed by running `npm install eslint`. The linter is then installed locally at ```node_modules/.bin/eslint``` When running formatter vim will walk up from the current file to search for such local installation and a ESLint configuration file (either .eslintrc or eslintrc.json). When both are found eslint is executed with the --fix argument. - This linter can be disabled using global setting - ```vim - let g:formatters_javascript_eslint_local = 0 - ``` Currently only working on *nix like OS (Linux, MacOS etc.) requires OS to provide sh like shell syntax * `xo` for __Javascript__. diff --git a/plugin/defaults.vim b/plugin/defaults.vim index df07730..0e6f53a 100644 --- a/plugin/defaults.vim +++ b/plugin/defaults.vim @@ -163,15 +163,12 @@ endif " Setup ESLint local. Setup is done on formatter execution if ESLint and " corresponding config is found they are used, otherwiese the formatter fails. " No windows support at the moment. -if !exists('g:formatdef_eslint_local') && !has('win32') - " set disable flag when not defined already - let g:formatters_javascript_eslint_local = exists('g:formatters_javascript_eslint_local') ? g:formatters_javascript_eslint_local : 1 - - function! g:SetupESLintLocalCmd() +if !exists('g:formatdef_eslint_local') + function! g:BuildESLintLocalCmd() let l:path = fnamemodify(expand('%'), ':p') let verbose = &verbose || g:autoformat_verbosemode == 1 - if g:formatters_javascript_eslint_local == 0 - return "(>&2 echo 'ESLint local is disbaled')" + if has('win32') + return "(>&2 echo 'ESLint Local not supported on win32')" endif " find formatter & config file let l:prog = findfile('node_modules/.bin/eslint', l:path.";") @@ -193,7 +190,7 @@ if !exists('g:formatdef_eslint_local') && !has('win32') \ .l:prog." -c ".l:cfg." --fix ".l:eslint_js_tmp_file." 1> /dev/null; exit_code=$? \ cat ".l:eslint_js_tmp_file."; rm -f ".l:eslint_js_tmp_file."; exit $exit_code" endfunction - let g:formatdef_eslint_local = "g:SetupESLintLocalCmd()" + let g:formatdef_eslint_local = "g:BuildESLintLocalCmd()" endif if !exists('g:formatters_javascript') From 6d2acb7635930fb04193a7393e67029dd2a47e33 Mon Sep 17 00:00:00 2001 From: Christian Hubinger Date: Tue, 14 Mar 2017 15:15:02 +0100 Subject: [PATCH 4/4] fix: linter discards unsaved changes The content of the current buffer is written to the tempfile instead of copying the file. So unsaved changes will no longer be discarded silently. --- plugin/defaults.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugin/defaults.vim b/plugin/defaults.vim index 0e6f53a..4085f02 100644 --- a/plugin/defaults.vim +++ b/plugin/defaults.vim @@ -186,8 +186,9 @@ if !exists('g:formatdef_eslint_local') " This formatter uses a temporary file as ESLint has not option to print " the formatted source to stdout without modifieing the file. let l:eslint_js_tmp_file = fnameescape(tempname().".js") - return "rm -f ".l:eslint_js_tmp_file."; cat ".l:path." > ".l:eslint_js_tmp_file.";" - \ .l:prog." -c ".l:cfg." --fix ".l:eslint_js_tmp_file." 1> /dev/null; exit_code=$? + let content = getline('1', '$') + call writefile(content, l:eslint_js_tmp_file) + return l:prog." -c ".l:cfg." --fix ".l:eslint_js_tmp_file." 1> /dev/null; exit_code=$? \ cat ".l:eslint_js_tmp_file."; rm -f ".l:eslint_js_tmp_file."; exit $exit_code" endfunction let g:formatdef_eslint_local = "g:BuildESLintLocalCmd()"