Skip to content

Instantly share code, notes, and snippets.

@johnnymo87
Last active April 24, 2023 13:52
Show Gist options
  • Save johnnymo87/a13f4e086b0b0194e0c67843fe4e1b2a to your computer and use it in GitHub Desktop.
Save johnnymo87/a13f4e086b0b0194e0c67843fe4e1b2a to your computer and use it in GitHub Desktop.
How I pascal-cased a bunch screaming snake case variables
  • I am a neovim user and software engineer working on a rails 6.1 ruby 2.7 application.

  • All constants in the application use the SCREAMING_SNAKE_CASE naming convention.

  • The rubocop style guide suggests that I don't have to follow this convention when the constant refers to a class. For those that refer to a class, they can be PascalCased like classes normally are in ruby.

    image
  • I'm using ripgrep, and in vim I have a plugin where I can send search queries to ripgrep via the :Rg command.

  • I've written a ripgrep query to find all instances of where a class is assigned to a SCREAMING_SNAKE_CASE constant.

    :Rg "[A-Z][A-Z0-9_]+\\s\+=\\s\+[A-Z][a-z]" -g "**/*.rb"
    
    • The query finds all SCREAMING_SNAKE_CASE constants that are followed by an equals sign and a capital letter, since a capital letter usually denotes a class.
    • There are false positives! So I sanity check each result before running with it.
  • I've got a plan of attack to automate this task.

    • I will run the above ripgrep query, which will return a list of results in a quickview window.
    • For each result, I will trigger vim to switch this variable to PascalCase across the entire codebase.
" Change the given variable from to Pascal case everywhere in the current file.
function! ToPascalCase(variable_name)
" Search for the screaming snake case variable.
let search_pattern = a:variable_name
" Downcase the entire variable.
let replace_pattern = tolower(a:variable_name)
" Upcase the first letter.
let replace_pattern = substitute(replace_pattern, '\(\<\w\+\>\)', '\u\1', 'g')
" Remove underscores and upcase letters that follow underscores.
let replace_pattern = substitute(replace_pattern, '_\(\a\)', '\u\1', 'g')
" Apply the change to the entire file.
execute '%s/' . search_pattern . '/' . replace_pattern . '/g'
endfunction
" Change the given variable from to Pascal case everywhere in all Ruby files.
function! ToPascalCaseAllFiles(variable_name)
" Get the list of Ruby files with incorrect @param syntax.
let ruby_files = systemlist('rg --files-with-matches ' . a:variable_name)
" Join the list elements into a single string with space as the separator.
let ruby_files_string = join(ruby_files, ' ')
" Populate the argument list with the Ruby files.
execute 'args' ruby_files_string
" Execute ToPascalCase() on all files in the argument list.
argdo call ToPascalCase(a:variable_name)
" Write all changes to disk.
argdo update
endfunction
" Invoke the ToPascalCaseAllFiles on the word under the cursor.
map <leader>tpc :call ToPascalCaseAllFiles(expand("<cWORD>"))<CR>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment