Sublime Text Sorting with Respect to Indentation
Asked Answered
P

2

5

I'm looking for a Sublime Text plugin or any kind of program that can sort alphabetically but respect the indentation.

For example,

beatsByUserPath: (userId) ->
  "/api/beats/by_user/#{userId}"
sendMassMessagePath: ->
  "/api/send_mass_message"
sendMessagePath: (userId) ->
  "/api/send_message/#{userId}"
feedbackCreatePath: ->
  "/api/feedbacks"

Would be sorted by the function names. Using the default sort in Sublime Text leads to:

  "/api/beats/by_user/#{userId}"
  "/api/feedbacks"
  "/api/send_mass_message"
  "/api/send_message/#{userId}"
beatsByUserPath: (userId) ->
feedbackCreatePath: ->
sendMassMessagePath: ->
sendMessagePath: (userId) ->

Here is the full file that I would like to sort.

RouteHelper =
  EXTERNAL:
    soundcloudConvertPath: (url) ->
      url = encodeURIComponent(url)
      "http://streampocket.com/?stream=#{url}"
    youtubeConvertPath: (url) ->
      url = encodeURIComponent(url)
      "http://www.video2mp3.net/loading.php?url=#{url}"
  UTIL:
    imageProxy: (url) ->
      url = encodeURIComponent(url)
      "/image-proxy/#{url}"
  API:
    beatsReportPath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/report"
    beatsTrackDownloadPath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/track_download"
    beatSetDownloadPath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/set_download_url"
    beatsToggleVisibilityPath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/toggle_visibility"
    beatsToggleRecordingPath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/toggle_recording"
    beatsDisownPath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/disown"
    beatsEditNotePath: (param) ->
      beatId = param
      beatId = param.id if typeof param is 'object' && param.id
      "/api/beats/#{beatId}/edit_note"
    beatsByUserPath: (userId) ->
      "/api/beats/by_user/#{userId}"
    discussPath: ->
      "/api/discuss"
    sendMassMessagePath: ->
      "/api/send_mass_message"
    sendMessagePath: (userId) ->
      "/api/send_message/#{userId}"
    feedbackCreatePath: ->
      "/api/feedbacks"
    feedbacksForRapPath: (arg) ->
      rapId = if typeof rap is 'object' then arg.id else arg
      "/api/feedbacks/feedback_for/#{rapId}"
    followersPath: (userId) ->
      "/api/followers/#{userId}"
    followingPath: (userId) ->
      "/api/following/#{userId}"
    followPath: (userId) ->
      "/api/follow/#{userId}"
    unfollowPath: (userId) ->
      "/api/unfollow/#{userId}"
    propsPath: ->
      "/api/props"
    userBattlesPath_deprecated: (userId) ->
      "/api/battles/for_user/#{userId}"
    battlesLeaderboardPath: ->
      "/api/battles/leaderboard"
    battlesUsersWhoVotedForPath: (opts) ->
      throw Error('RouteHelper: Expected ID and WHICH') if !opts.id || !opts.which
      "/api/battles/#{opts.id}/users_who_voted_for/#{opts.which}"
    rapProppersPath: (rapId) ->
      "/api/raps/#{rapId}/proppers"
    rapUntagPath: (rapId) ->
      "/api/raps/#{rapId}/untag"
    rapShowPath: (param) ->
      if typeof param is 'object'
        rapId = param.id
      else rapId = param
      "/api/raps/#{rapId}/show_v2"
    userPinPath: ->
      "/api/users/pin"
    userBattlesPath: (userId) ->
      "/api/users/#{userId}/battles"
    userBeatsPath: (userId) ->
      "/api/users/#{userId}/beats"
    userRapsPath: (userId) ->
      "/api/users/#{userId}/raps_v2"
    userSetColorsPath: (userId) ->
      "/api/users/#{userId}/set_colors"
    userShowPath: (userId) ->
      "/api/users/#{userId}"
    usersWhoGaveProps: (userId) ->
      "/api/users/#{userId}/users_who_gave_props"
    userUnreadNotifCount: (userId) ->
      "/api/users/#{userId}/unread_notif_count"
    userRecordNetegoPath: ->
      "/api/users/record_net_ego"
  albumShowPath: (param) ->
    param = param.slug if _.isObject(param)
    "/albums/#{param}"
  blueprintShowPathFromRap: (rap) ->
    "/blueprints/#{rap.blueprint_id}"
  battleDestroyPath: (battle) ->
    "/battles/#{battle.id}"
  battlesPath: ->
    "/battles"
  battleNewPath: ->
    "/battles/new"
  battleShowPath: (battle) ->
    "/battles/#{battle.id}"
  beatNewPath: ->
    "/beats/new"
  beatShowPath: (beat) ->
    if typeof beat is 'number'
      "/beats/#{beat}"
    else if typeof beat is 'object'
      if beat.slug
        "/beats/#{beat.slug}"
      else
        "/beats/#{beat.id}"
  beatTagShowPath: (beatTag) ->
    "#{beatTag.slug}/instrumentals"
  beatsSearchQueryPath: ->
    "/beats/search_query"
  beatsRecentSearchesPath: ->
    "/beats/recent_searches"
  cypherJudgeVotePath: ->
    "/cyphers/judge-vote"
  cypherJudgeShowPath: ->
    "/cyphers/judge-show"
  cypherSubmitPath: ->
    "/cyphers/submit"
  dashboardPath: ->
    "/dashboard"
  defaultRapThumbnailPath: ->
    "/images/default_rap.png"
  rhymePath: ->
    "/rhyme"
  contextPath: ->
    "/context"
  searchLyricsPath: ->
    "/rap/search_lyrics"
  editorSavePath: ->
    "/editor/save"
  editorPath: (param) ->
    param = param.id if typeof param is 'object'
    if param
      "/editor/#{param}"
    else
      "/editor"
  onRapSaveDialogPath: (rapId) ->
    "/rap/#{rapId}/on_save_dialog"
  lyricSyncSavePath: ->
    "/lyric-sync/save"
  lyricSyncDestroyPath: ->
    "/lyric-sync/destroy"
  notificationsPath: ->
    "/notifications"
  rapEditPath: (rap) ->
    "/editor/#{rap.id}"
  rapShowPath: (rap) ->
    "/rap/#{rap.id}"
  rapsForCypher: (cypherId) ->
    "/cyphers/#{cypherId}/submissions"
  isSubscribedPath: (listId) ->
    "/is_subscribed/#{listId}"
  subscribeToPath: (listId) ->
    "/subscribe_to/#{listId}"
  userShowPath: (username) ->
    "/users/#{username}"
  userNotificationSettingPath: ->
    "/users/notification_setting"

@RouteHelper = RouteHelper
Polychromatic answered 12/9, 2015 at 15:11 Comment(2)
Do you always have one line of per indented block or possibly multiple lines per block? I'm asking to see if you only want to sort the function names without breaking the structure or sort the indented lines respective to their block too.Candleberry
Yeah, you wouldn't want to sort the indented blocks - just the top-level blocks (ie function names), in which case they would always be 1 line. I guess you raise a valid point though and why functionality like this doesn't already exist.Polychromatic
C
6

After seeing the updated question with an example of a full file, I went ahead and created a Sublime Text plugin with some basic options to control over sorting.

  1. Install IndentRespectfulSort from Package Control (See https://packagecontrol.io/packages/Indent%20Respectful%20Sort for instructions).
  2. Open the console by Ctrl + ` or Cmd(⌘) + `(OSX).
  3. Assuming your file is indented by 2 spaces as in the question and you would like to sort function names only, type

    view.run_command("indent_respectful_sort", {"indent": "  ", "onlyDepth" : 3})
    

    into the console and hit Enter. This will sort only the function names while respecting the rest of the structure.

If you would like to sort with different options, you may refer to the plugin page https://github.com/mvnural/sublime-indent-respectful-sort to see more options.

Candleberry answered 24/9, 2015 at 23:22 Comment(2)
File "/Applications/Sublime Text 3 Beta.app/Contents/MacOS/sublime_plugin.py", line 550, in run_ return self.run(edit, **args) File "IndentRespectfulSort in /Users/amirsharif/Library/Application Support/Sublime Text 3/Installed Packages/Indent Respectful Sort.sublime-package", line 14, in run AttributeError: 'module' object has no attribute 'maxint' Traceback (most recent call last): File "/Applications/Sublime Text 3 Beta.app/Contents/MacOS/sublime_plugin.py",Polychromatic
I have fixed it now. It may take a while for Package Control to detect the update. Meanwhile you may pull the updated package directly from the github repository.Candleberry
C
4

I'm not aware of a generic solution since the definition of a block (for sorting purposes) is highly dependent on the context.

For the specific case you have posted however, it can be easily accomplished by a simple macro with the following steps:

  1. Merge each block into a single line (replace each occurrence of ->\n with ->)
  2. Sort single lines
  3. Unmerge each block from a single line back to original indented form (replace each occurrence of -> with ->\n)

The only problem is, Sublime doesn't record find & replace commands in a macro. Instead, we need to install RegReplace package which provides find & replace commands that can be recorded in a macro. (See http://facelessuser.github.io/RegReplace/installation/ for installation instructions)

Once RegReplace is installed, just put the following two files under Packages/User/ in your data directory (What is the full path to the Packages folder for Sublime text 2 on Mac OS Lion)

You can now run smart_sort by selecting Tools -> Macros -> smart_sort from the top menu.

Disclaimer: If you have more than 1 indented line per function, you need to adjust the regex definitions accordingly.

smart_sort.sublime-macro (the macro)

[
    {
        "args":
        {
            "replacements":
            [
                "merge_block_into_a_line"
            ]
        },
        "command": "reg_replace"
    },
    {
        "args":
        {
            "case_sensitive": false
        },
        "command": "sort_lines"
    },
    {
        "args":
        {
            "replacements":
            [
                "restore_block"
            ]
        },
        "command": "reg_replace"
    }
]

reg_replace.sublime-settings (reg_replace definitions)

{
    "replacements": {
        "merge_block_into_a_line": {
            "find": "->\\n",
            "replace": "->",
            "greedy": true,
            "case": true
        },
        "restore_block": {
            "find": "->",
            "replace": "->\\n",
            "greedy": true,
            "case": true
        }
    }
}
Candleberry answered 23/9, 2015 at 0:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.