From f264fed3498a9a30911e715fe95d098b49928ced Mon Sep 17 00:00:00 2001 From: Andrew Radev Date: Sun, 24 Jul 2016 18:53:03 +0300 Subject: [PATCH 1/4] Initial work on :Unpack --- autoload/ember_tools.vim | 3 ++ autoload/ember_tools/unpack.vim | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 autoload/ember_tools/unpack.vim diff --git a/autoload/ember_tools.vim b/autoload/ember_tools.vim index c499eb0..53657d8 100644 --- a/autoload/ember_tools.vim +++ b/autoload/ember_tools.vim @@ -18,6 +18,9 @@ function! ember_tools#Init() setlocal includeexpr=ember_tools#Includeexpr() command! -count=0 -nargs=1 -buffer Extract call ember_tools#extract#Run(, , ) + + command! -count=0 -buffer Unpack call ember_tools#unpack#Run() + command! -count=0 -buffer Pack call ember_tools#unpack#Reverse() endfunction function! ember_tools#Includeexpr() diff --git a/autoload/ember_tools/unpack.vim b/autoload/ember_tools/unpack.vim new file mode 100644 index 0000000..e693b4d --- /dev/null +++ b/autoload/ember_tools/unpack.vim @@ -0,0 +1,58 @@ +function! ember_tools#unpack#Run(visual) + " TODO (2016-07-06) Visual + " TODO (2016-07-06) Nested unpacking: + " const { computed, Controller, inject: { service }, observer } = Ember; + " TODO (2016-07-06) Update *all* instances of namespace.member? + " TODO (2016-07-06) repeat.vim support + + let saved_view = winsaveview() + + if !search('\%(\k\|\.\)\+', 'bc', line('.')) + return + endif + + let namespace = expand('') + normal! "_df. + let member = expand('') + + " Look for an existing unpacking + if search('const {.*'.member.'.*}\s\+=\s\+'.namespace, 'n') + " this member of the namespace is already unpacked, nothing to do + return + endif + + if search('const {.*}\s\+=\s\+'.namespace) + " we found an existing unpacking without this member, unpack it here + let unpacking = getline('.') + let unpacking = substitute(unpacking, + \ '\s*}\(\s\+=\s\+'.namespace.'\)', + \ ', '.member.' } = '.namespace, + \ 'g') + call setline('.', unpacking) + + call winrestview(saved_view) + return + endif + + " if we're here, there's no existing unpacking + if search('^const {', 'bW') + " we can add it after the last unpacking + call append(line('.'), ['']) + normal! j + elseif search('^import', 'bW') + " we can add it after the last import + call append(line('.'), ['', '']) + normal! jj + else + " just add it at the top of the file + call append(0, ['', '']) + normal! gg + endif + + call setline('.', 'const { '.member.' } = '.namespace.';') + call winrestview(saved_view) +endfunction + +function! ember_tools#unpack#Reverse(visual) + " code +endfunction From 3e9bd426acee2cb26430f3f274219523999c7549 Mon Sep 17 00:00:00 2001 From: Andrew Radev Date: Sun, 7 Aug 2016 19:37:32 +0300 Subject: [PATCH 2/4] Repeat.vim support for :Unpack --- autoload/ember_tools/unpack.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/autoload/ember_tools/unpack.vim b/autoload/ember_tools/unpack.vim index e693b4d..7d62caf 100644 --- a/autoload/ember_tools/unpack.vim +++ b/autoload/ember_tools/unpack.vim @@ -1,9 +1,8 @@ function! ember_tools#unpack#Run(visual) - " TODO (2016-07-06) Visual + " TODO (2016-08-07) Multiline imports (look for the closing ; of the line?) + " TODO (2016-08-07) Visual mode support " TODO (2016-07-06) Nested unpacking: " const { computed, Controller, inject: { service }, observer } = Ember; - " TODO (2016-07-06) Update *all* instances of namespace.member? - " TODO (2016-07-06) repeat.vim support let saved_view = winsaveview() @@ -31,6 +30,7 @@ function! ember_tools#unpack#Run(visual) call setline('.', unpacking) call winrestview(saved_view) + silent! call repeat#set(":call ember_tools#unpack#Run(0)\") return endif @@ -51,6 +51,7 @@ function! ember_tools#unpack#Run(visual) call setline('.', 'const { '.member.' } = '.namespace.';') call winrestview(saved_view) + silent! call repeat#set(":call ember_tools#unpack#Run(0)\") endfunction function! ember_tools#unpack#Reverse(visual) From 9d5b0a3e909c1a4bacb55c09ca2c8538bcbd9dae Mon Sep 17 00:00:00 2001 From: Andrew Radev Date: Mon, 8 Aug 2016 20:49:43 +0300 Subject: [PATCH 3/4] Reverse an :Unpack with :Inline --- autoload/ember_tools.vim | 4 +- autoload/ember_tools/unpack.vim | 52 ++++++++++++-- spec/plugin/inline_spec.rb | 118 ++++++++++++++++++++++++++++++++ spec/plugin/unpack_spec.rb | 88 ++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 spec/plugin/inline_spec.rb create mode 100644 spec/plugin/unpack_spec.rb diff --git a/autoload/ember_tools.vim b/autoload/ember_tools.vim index 53657d8..e8b3099 100644 --- a/autoload/ember_tools.vim +++ b/autoload/ember_tools.vim @@ -19,8 +19,8 @@ function! ember_tools#Init() setlocal includeexpr=ember_tools#Includeexpr() command! -count=0 -nargs=1 -buffer Extract call ember_tools#extract#Run(, , ) - command! -count=0 -buffer Unpack call ember_tools#unpack#Run() - command! -count=0 -buffer Pack call ember_tools#unpack#Reverse() + command! -buffer Unpack call ember_tools#unpack#Run() + command! -buffer Inline call ember_tools#unpack#Reverse() endfunction function! ember_tools#Includeexpr() diff --git a/autoload/ember_tools/unpack.vim b/autoload/ember_tools/unpack.vim index 7d62caf..d397251 100644 --- a/autoload/ember_tools/unpack.vim +++ b/autoload/ember_tools/unpack.vim @@ -1,6 +1,5 @@ -function! ember_tools#unpack#Run(visual) +function! ember_tools#unpack#Run() " TODO (2016-08-07) Multiline imports (look for the closing ; of the line?) - " TODO (2016-08-07) Visual mode support " TODO (2016-07-06) Nested unpacking: " const { computed, Controller, inject: { service }, observer } = Ember; @@ -51,9 +50,52 @@ function! ember_tools#unpack#Run(visual) call setline('.', 'const { '.member.' } = '.namespace.';') call winrestview(saved_view) - silent! call repeat#set(":call ember_tools#unpack#Run(0)\") + silent! call repeat#set(":call ember_tools#unpack#Run()\") endfunction -function! ember_tools#unpack#Reverse(visual) - " code +function! ember_tools#unpack#Reverse() + let saved_view = winsaveview() + let variable = expand('') + + if searchpair('const {', '', '}\s*=\s*\zs\k\+', 'W') <= 0 + return + endif + + let prefix = expand('') + + call search('\<'.variable.'\>', 'bW') + + " Remove variable from const line + exe 's/,\s*\%#'.variable.'//e' + exe 's/\%#'.variable.',\=\s*\ze\%(\k\| }\)//e' + + " Handle empty const blocks + if getline('.') =~ '^const {\s*} =' + let next_lineno = nextnonblank(line('.') + 1) + + if getline(next_lineno) !~ '^const' + " it's something other than another const line, let's delete all the + " whitespace up until that point + exe line('.').','.(next_lineno - 1).'delete _' + else + " just delete this line + delete _ + endif + endif + + " Add prefix everywhere + normal! G$ + let search_flags = "w" + let variable_pattern = '\%('.prefix.'\.\)\@' + + while search(variable_pattern, search_flags) > 0 + if synIDattr(synID(line('.'), col('.'), 1), 'name') !~ 'String\|Comment' + exe 'normal! i'.prefix.'.' + " go back to the search + call search(variable_pattern) + endif + let search_flags = "W" + endwhile + + call winrestview(saved_view) endfunction diff --git a/spec/plugin/inline_spec.rb b/spec/plugin/inline_spec.rb new file mode 100644 index 0000000..d7785d8 --- /dev/null +++ b/spec/plugin/inline_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +describe ":Inline" do + describe "const unpacking" do + specify "deletes a const line if nothing is left of it after inlining" do + edit_file 'test.js', <<-EOF + const { Controller } = Ember; + + export default Controller.extend({}); + EOF + + vim.search 'const { \zsController' + vim.command 'Inline' + vim.write + + expect_file_contents current_file, <<-EOF + export default Ember.Controller.extend({}); + EOF + end + + specify "deletes an const line with another const line following" do + edit_file 'test.js', <<-EOF + const { Controller } = Ember; + const { foo } = bar; + + export default Controller.extend({}); + EOF + + vim.search 'const { \zsController' + vim.command 'Inline' + vim.write + + expect_file_contents current_file, <<-EOF + const { foo } = bar; + + export default Ember.Controller.extend({}); + EOF + end + + specify "inlines entries from the beginning" do + edit_file 'test.js', <<-EOF + import Ember from 'ember'; + + const { computed, Controller, isPresent } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + + vim.search 'const.*\zscomputed' + vim.command 'Inline' + vim.write + + expect_file_contents current_file, <<-EOF + import Ember from 'ember'; + + const { Controller, isPresent } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + end + + specify "inlines entries in the middle" do + edit_file 'test.js', <<-EOF + import Ember from 'ember'; + + const { computed, Controller, isPresent } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + + vim.search 'const.*\zsController' + vim.command 'Inline' + vim.write + + expect_file_contents current_file, <<-EOF + import Ember from 'ember'; + + const { computed, isPresent } = Ember; + + export default Ember.Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + end + + specify "inlines entries at the end" do + edit_file 'test.js', <<-EOF + import Ember from 'ember'; + + const { computed, Controller, isPresent } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + + vim.search 'const.*\zsisPresent' + vim.command 'Inline' + vim.write + + expect_file_contents current_file, <<-EOF + import Ember from 'ember'; + + const { computed, Controller } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + end + end +end diff --git a/spec/plugin/unpack_spec.rb b/spec/plugin/unpack_spec.rb new file mode 100644 index 0000000..f65ca14 --- /dev/null +++ b/spec/plugin/unpack_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' + +describe ":Unpack" do + specify "creates a const line if there is none, at the top of the file" do + edit_file 'test.js', <<-EOF + export default Ember.Controller.extend({}); + EOF + + vim.search 'Ember\.Controller' + vim.command 'Unpack' + vim.write + + expect_file_contents current_file, <<-EOF + const { Controller } = Ember; + + export default Controller.extend({}); + EOF + end + + specify "creates a const line if there is none, after the import lines" do + edit_file 'test.js', <<-EOF + import Ember from 'ember'; + + export default Ember.Controller.extend({}); + EOF + + vim.search 'Ember\.Controller' + vim.command 'Unpack' + vim.write + + expect_file_contents current_file, <<-EOF + import Ember from 'ember'; + + const { Controller } = Ember; + + export default Controller.extend({}); + EOF + end + + specify "adds entries to the const line if it exists" do + edit_file 'test.js', <<-EOF + import Ember from 'ember'; + + const { Controller } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); + EOF + + vim.search 'Ember\.computed' + vim.command 'Unpack' + vim.write + + expect_file_contents current_file, <<-EOF + import Ember from 'ember'; + + const { Controller, computed } = Ember; + + export default Controller.extend({ + foo: computed.equal('bar', 'baz') + }); + EOF + end + + specify "adds a new const line for nested unpacking" do + edit_file 'test.js', <<-EOF + import Ember from 'ember'; + + const { Controller } = Ember; + + export default Controller.extend({}); + EOF + + vim.search 'Controller\.extend' + vim.command 'Unpack' + vim.write + + expect_file_contents current_file, <<-EOF + import Ember from 'ember'; + + const { Controller } = Ember; + const { extend } = Controller; + + export default extend({}); + EOF + end +end From 0394ca7ea5717e2044e59a9738c6d7bc072308af Mon Sep 17 00:00:00 2001 From: Andrew Radev Date: Mon, 22 Aug 2016 21:40:09 +0300 Subject: [PATCH 4/4] Documentation --- doc/ember_tools.txt | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/doc/ember_tools.txt b/doc/ember_tools.txt index 709292e..6cf8ae8 100644 --- a/doc/ember_tools.txt +++ b/doc/ember_tools.txt @@ -197,6 +197,81 @@ emblem template, but if you'd like to specify explicitly what templates you prefer, set the |g:ember_tools_default_logic_filetype| and/or |g:ember_tools_default_template_filetype| configuration variables. + *ember_tools-:Unpack* +:Unpack ~ + +The `:Unpack` command helps you unpack an imported variable into its component +pieces. An example might looks something like this: + +> + import Ember from 'ember'; + + export default Ember.Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); +< +Running the `:Unpack` command with the cursor on "Ember.Controller" would lead +to the following result: +> + import Ember from 'ember'; + + const { Controller } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + }); +< +The command creates a `const { ... } = ` line that unpacks the `Ember` +variable's `Controller` component into its own variable. + +You can continue to run `:Unpack` on, for instance, "Ember.computed.equal", +and then once again on the remaining "computed.equal" (if you have repeat.vim +installed, you can just trigger the |.| mapping) to get: +> + import Ember from 'ember'; + + const { Controller, computed } = Ember; + const { equal } = computed; + + export default Controller.extend({ + foo: equal('bar', 'baz') + }); +< +The command adds new entries to the end of the list. If you'd like to sort +them in some way afterwards, you can try using a different plugin of mine, +sideways (https://github.com/AndrewRadev/sideways.vim). + + *ember_tools-:Inline* +:Inline ~ + +The `:Inline` command inlines an "unpacked" variable. If you have code like +this: +> + import Ember from 'ember'; + + const { computed, Controller } = Ember; + + export default Controller.extend({ + foo: computed.equal('bar', 'baz') + bar: computed.equal('baz', 'qux') + }); +< +Running `:Inline` on "computed" within the `const { ... }` definition will +remove it from that list and replace it across the file (ignoring strings and +comments) with "Ember.computed". +> + import Ember from 'ember'; + + const { Controller } = Ember; + + export default Controller.extend({ + foo: Ember.computed.equal('bar', 'baz') + bar: Ember.computed.equal('baz', 'qux') + }); +< +For now, this is simply a reversal of the `:Unpack` command. In the future, +the `:Inline` command might also inline other kinds of constructs, like local +variables or properties. ==============================================================================