Skip to content
This repository

Support for custom paths in Sass, Less and Coffee #58

Closed
wants to merge 4 commits into from

5 participants

Leonid Shevtsov Michał Lipski Tim Pope Joshua Davey bpo217
Leonid Shevtsov

Hi Tim,

I replaced hardcoded paths to stylesheets and scripts, like public/stylesheets/sass, with some fancy path detection.

The detection depends on the corresponding gem being declared in Gemfile/environment.rb, then proceeds to look for files in a list of possible directories (like app/stylesheets, which seems pretty popular), then falls back to retrieving the path from the app itself (slow but sure, and only in rare cases).

Works with Sass, Less, and Coffeescript; I only tested it on Sass since that's what was available.

It's my first experience with vimscript - I tried to be nice. :)

Leonid.

Michał Lipski

Great idea! For example I have CoffeeScript files inside app/coffeescripts.

Tim Pope
Owner
tpope commented

How does Rails 3.1 asset handling affect this?

The sheer volume of code in this pull request made me squirm, so I've kind of let it sit.

Leonid Shevtsov

If you mean that introducing a standard convention for .sass and .coffee locations will deprecate this code - well, I do hope it will.

But in my opinion, existing 3.0 and 2.3 applications won't migrate to sprockets, even if they upgrade to 3.1, so for them the problem of locating the correct path remains.

Other than that, the only thing that 3.1 changes is that app/javascripts should be added to the list of .coffee default locations.

Leonid Shevtsov

I've gave the matter more thought, and it looks like the "asset pipeline" makes opening the correct file from Vim a much more complex process.

So, I'll look more into the logic and probably submit another patch. Rails 3.1 will probably make the current asset-related commands useless.

Tim Pope
Owner

What's the current thinking on this? Now that the asset pipeline has started to catch on, I'd like to simplify a bit, perhaps dropping app/scripts and app/stylesheets (which, if memory serves, weren't supported back when this pull request was introduced).

In theory, the new config/editor.json projections should allow for people stuck on nonstandard setups to override :Rjavascript and :Rstylesheet.

Joshua Davey

@tpope Could projections also be used to switch the order of preference :Rjavascript and :Rstylesheet for new files? That is, could a projection be used to put new files into app/assets/javascripts?

Also, at this point in time, should we invert the above? Default to app/assets and let a projection handle forcing public?

Tim Pope
Owner
tpope commented

The problem with sticking new files under app/assets is that we don't know what extension to give them. .js? .js.coffee? .js.coffee.erb? Rails further complicates this with its infuriatingly mushy file name handling, allowing such bastardizations as .coffee and .coffee.erb (and maybe even a naked .erb for all I know). And if that wasn't bad enough, CSS adds both SCSS and Sass to the mix, so we can't just look at what gems are installed.

What does rails generate create? Is it customizable? Can we detect that and use it to guide our default?

Joshua Davey
jgdavey commented

Crazy idea... what about looking at the surrounding files as a guideline? For example, if there are 2 .js.coffee files in app/assets/javascripts, but only .js file, we go with the .js.coffee extension. The same would work for stylesheets because presumably people either prefer .sass or .scss, but not both.

Basically, rather than deciding a default based on Rails "conventions" or generators, we let the existing project decide.

Crazy? Or craziest?

bpo217
bpo217 commented

@tpope You can of course make your own rails generator; however, I am unsure whether you can customize the built in ones.

I really believe this feature should be: if they input 1 or more file extensions explicitly, then that should be the file name (don't add anything); if they don't then it should default to the core language extension for that particular context. The core extension for a stylesheet being simply .css. The core extension for a view being simply .html. Since these extensions are the "base" extensions for a website, this fallback won't break for a long time. So you have to enter .html.erb if you want the erb... Big deal. It's better than it breaking for the other guy who uses .html.haml.

This method seems like it would take a lot less code than any other route, and yet solve the problem. Would this be difficult to implement?

Tim Pope
Owner
tpope commented

File creation is now correctly handled based on configured generators. I'm thinking that should address all remaining concerns here.

Tim Pope tpope closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 90 additions and 24 deletions. Show diff stats Hide diff stats

  1. +90 24 autoload/rails.vim
114 autoload/rails.vim
@@ -787,22 +787,88 @@ function! s:app_default_locale() dict abort
787 787 endif
788 788 return self.cache.get('default_locale')
789 789 endfunction
  790 +
  791 +function s:app_gemfile_path() dict
  792 + if self.cache.needs('gemfile_path')
  793 + if findfile('Gemfile', self.path())!=''
  794 + call self.cache.set('gemfile_path', self.path()+'/Gemfile')
  795 + else
  796 + call self.cache.set('gemfile_path', self.path()+'/config/environment.rb')
  797 + endif
  798 + end
  799 + return self.cache.get('gemfile_path')
  800 +endfunction
  801 +
  802 +" yep, this is quite heuristic
  803 +function! s:app_has_gem(gem) dict
  804 + return system('grep -P "^[^#]*gem\s+[''\"]'.a:gem.'[''\"]" '.self.gemfile_path())!=''
  805 +endfunction
  806 +
  807 +function! s:app_module_path(name, gem_name, guess_directories, guess_file_mask, application_query) dict
  808 + if self.cache.needs(a:name.'_path')
  809 + call self.cache.set(a:name.'_path', '')
  810 + if self.has_gem(a:gem_name)
  811 + for guess_dir in a:guess_directories
  812 + if glob(self.path(guess_dir.a:guess_file_mask))!=''
  813 + call self.cache.set(a:name.'_path', guess_dir)
  814 + return guess_dir
  815 + endif
  816 + endfor
  817 + if self.cache.get(a:name.'_path')=='' && a:application_query!=''
  818 + " Query the app - it knows the path for sure. This is slow, hence the guesswork above
  819 + call self.cache.set(a:name.'_path', system(self.path('script/runner').' '''.a:application_query.''''))
  820 + end
  821 + endif
  822 + end
  823 + return self.cache.get(a:name.'_path')
  824 +endfunction
  825 +
  826 +function! s:app_sass_path() dict
  827 + return self.module_path(
  828 + \ 'sass',
  829 + \ 'haml',
  830 + \ ['app/stylesheets/', 'public/stylesheets/sass/', 'public/stylesheets/'],
  831 + \ '*.s[ac]ss',
  832 + \ 'print defined?(Sass) ? Sass::Plugin.template_location_array[0][0].gsub(/^#{Rails.root}\//,"")+"/" : ""'
  833 + \ )
  834 +endfunction
  835 +
  836 +function! s:app_lesscss_path() dict
  837 + return self.module_path(
  838 + \ 'lesscss',
  839 + \ 'more',
  840 + \ ['app/stylesheets/', 'public/stylesheets/'],
  841 + \ '*.less',
  842 + \ 'print defined?(Less) ? Less::More.source_path.gsub(/^#{Rails.root}\//,"")+"/" : ""'
  843 + \ )
  844 +endfunction
  845 +
  846 +function! s:app_coffee_path() dict
  847 + return self.module_path(
  848 + \ 'coffee',
  849 + \ '(barista|bistro_car)',
  850 + \ ['app/scripts/', 'app/coffeescripts/', 'public/javascripts/'],
  851 + \ '*.coffee',
  852 + \ ''
  853 + \ )
  854 +endfunction
790 855
791 856 function! s:app_has(feature) dict
792 857 let map = {
793   - \'test': 'test/',
794   - \'spec': 'spec/',
  858 + \'test': 'test/',
  859 + \'spec': 'spec/',
795 860 \'cucumber': 'features/',
796   - \'sass': 'public/stylesheets/sass/',
797   - \'lesscss': 'app/stylesheets/',
798   - \'coffee': 'app/scripts/'}
  861 + \'sass': rails#app().sass_path(),
  862 + \'lesscss': rails#app().lesscss_path(),
  863 + \'coffee': rails#app().coffee_path()
  864 + \ }
799 865 if self.cache.needs('features')
800 866 call self.cache.set('features',{})
801 867 endif
802 868 let features = self.cache.get('features')
803 869 if !has_key(features,a:feature)
804 870 let path = get(map,a:feature,a:feature.'/')
805   - let features[a:feature] = isdirectory(rails#app().path(path))
  871 + let features[a:feature] = path!='/' && isdirectory(rails#app().path(path))
806 872 endif
807 873 return features[a:feature]
808 874 endfunction
@@ -812,7 +878,7 @@ function! s:app_test_suites() dict
812 878 return filter(['test','spec','cucumber'],'self.has(v:val)')
813 879 endfunction
814 880
815   -call s:add_methods('app',['default_locale','environments','file','has','test_suites'])
  881 +call s:add_methods('app',['default_locale','environments','file','gemfile_path', 'has_gem', 'module_path', 'sass_path', 'lesscss_path', 'coffee_path', 'has','test_suites'])
816 882 call s:add_methods('file',['path','name','lines','getline'])
817 883 call s:add_methods('buffer',['app','number','path','name','lines','getline','type_name'])
818 884 call s:add_methods('readable',['app','calculate_file_type','type_name','line_count'])
@@ -1382,14 +1448,14 @@ function! s:readable_preview_urls(lnum) dict abort
1382 1448 if has_key(self,'getvar') && self.getvar('rails_preview') != ''
1383 1449 let url += [self.getvar('rails_preview')]
1384 1450 end
1385   - if self.name() =~ '^public/stylesheets/sass/'
1386   - let urls = urls + [s:sub(s:sub(self.name(),'^public/stylesheets/sass/','/stylesheets/'),'\.s[ac]ss$','.css')]
  1451 + if rails#app().has('sass') && self.name() =~ '^'.rails#app().sass_path()
  1452 + let urls = urls + [s:sub(s:sub(self.name(),'^'.rails#app().sass_path(),'/stylesheets/'),'\.s[ac]ss$','.css')]
1387 1453 elseif self.name() =~ '^public/'
1388 1454 let urls = urls + [s:sub(self.name(),'^public','')]
1389   - elseif self.name() =~ '^app/stylesheets/'
1390   - let urls = urls + [s:sub(s:sub(self.name(),'^app/stylesheets/','/stylesheets/'),'\.less$','.css')]
1391   - elseif self.name() =~ '^app/scripts/'
1392   - let urls = urls + [s:sub(s:sub(self.name(),'^app/scripts/','/javascripts/'),'\.coffee$','.js')]
  1455 + elseif rails#app().has('lesscss') && self.name() =~ '^'.rails#app().lesscss_path()
  1456 + let urls = urls + [s:sub(s:sub(self.name(),'^'.rails#app().lesscss_path(),'/stylesheets/'),'\.less$','.css')]
  1457 + elseif rails#app().has('coffee') && self.name() =~ '^'.rails#app().coffee_path()
  1458 + let urls = urls + [s:sub(s:sub(self.name(),'^'.rails#app().coffee_path(),'/javascripts/'),'\.coffee$','.js')]
1393 1459 elseif self.controller_name() != '' && self.controller_name() != 'application'
1394 1460 if self.type_name('controller') && self.last_method(a:lnum) != ''
1395 1461 let urls += ['/'.self.controller_name().'/'.self.last_method(a:lnum).'/']
@@ -2243,7 +2309,7 @@ endfunction
2243 2309 function! s:stylesheetList(A,L,P)
2244 2310 let list = rails#app().relglob('public/stylesheets/','**/*','.css')
2245 2311 if rails#app().has('sass')
2246   - call extend(list,rails#app().relglob('public/stylesheets/sass/','**/*','.s?ss'))
  2312 + call extend(list,rails#app().relglob(rails#app().sass_path(),'**/*','.s?ss'))
2247 2313 call s:uniq(list)
2248 2314 endif
2249 2315 return s:completion_filter(list,a:A)
@@ -2662,12 +2728,12 @@ endfunction
2662 2728
2663 2729 function! s:stylesheetEdit(cmd,...)
2664 2730 let name = a:0 ? a:1 : s:controller(1)
2665   - if rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.sass')
2666   - return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".sass",1)
2667   - elseif rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.scss')
2668   - return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".scss",1)
2669   - elseif rails#app().has('lesscss') && rails#app().has_file('app/stylesheets/'.name.'.less')
2670   - return s:EditSimpleRb(a:cmd,"stylesheet",name,"app/stylesheets/",".less",1)
  2731 + if rails#app().has('sass') && rails#app().has_file(rails#app().sass_path().name.'.sass')
  2732 + return s:EditSimpleRb(a:cmd,"stylesheet",name,rails#app().sass_path(),".sass",1)
  2733 + elseif rails#app().has('sass') && rails#app().has_file(rails#app().sass_path().name.'.scss')
  2734 + return s:EditSimpleRb(a:cmd,"stylesheet",name,rails#app().sass_path(),".scss",1)
  2735 + elseif rails#app().has('lesscss') && rails#app().has_file(rails#app().lesscss_path().name.'.less')
  2736 + return s:EditSimpleRb(a:cmd,"stylesheet",name,rails#app().lesscss_path(),".less",1)
2671 2737 else
2672 2738 return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/",".css",1)
2673 2739 endif
@@ -2675,10 +2741,10 @@ endfunction
2675 2741
2676 2742 function! s:javascriptEdit(cmd,...)
2677 2743 let name = a:0 ? a:1 : s:controller(1)
2678   - if rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.coffee')
2679   - return s:EditSimpleRb(a:cmd,'javascript',name,'app/scripts/','.coffee',1)
2680   - elseif rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.js')
2681   - return s:EditSimpleRb(a:cmd,'javascript',name,'app/scripts/','.js',1)
  2744 + if rails#app().has('coffee') && rails#app().has_file(rails#app().coffee_path().name.'.coffee')
  2745 + return s:EditSimpleRb(a:cmd,'javascript',name,rails#app().coffee_path(),'.coffee',1)
  2746 + elseif rails#app().has('coffee') && rails#app().has_file(rails#app().coffee_path().name.'.js')
  2747 + return s:EditSimpleRb(a:cmd,'javascript',name,rails#app().coffee_path(),'.js',1)
2682 2748 else
2683 2749 return s:EditSimpleRb(a:cmd,'javascript',name,'public/javascripts/','.js',1)
2684 2750 endif

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.