Packages icon indicating copy to clipboard operation
Packages copied to clipboard

Ability to apply different indent styles to completions and snippets

Open jrappen opened this issue 9 years ago • 15 comments

Applying different indent styles

Should be possible by using a *.tmPreferences metadata file that holds whitespace settings that are for example set to either:

:warning: You'll need unix line endings, otherwise this messes up.

<key>shellVariables</key>
<array>
    <dict>
        <key>name</key><string>SPACE_OR_NEWLINE</string>
        <key>value</key><string><![CDATA[
]]></string>
    </dict>
</array>

or

<key>shellVariables</key>
<array>
    <dict>
        <key>name</key><string>SPACE_OR_NEWLINE</string>
        <key>value</key><string><![CDATA[ ]]></string>
    </dict>
</array>

and then used by snippets or completions like (⚠️ beware it's ${SPACE_OR_NEWLINE} not $SPACE_OR_NEWLINE in this particular case):

    { "trigger": "my_class\tclass", "contents": "my_class()${SPACE_OR_NEWLINE}{\n\t${1:first line}\n\t${2:second line}\n}" }

I already merged (most) snippet files into completions file for easier handling in #128. I could add some commits or open a new PR if this is desired. As far as my understanding goes, this would need three settings for:

  • in between class and first line
  • in the indented block between lines like first line and second line
  • in between second line and the end

in the example above.


I'd be willing to do this for all languages, however I'd like to have a basic structure set first as well as a list of styles that should be included.

jrappen avatar Feb 05 '16 18:02 jrappen

  • https://github.com/jsiwek/WhitesmithsIndentStyle
  • https://github.com/SublimeTextIssues/DefaultPackages/issues/61

jrappen avatar Apr 26 '16 17:04 jrappen

If there was better tmPreferences integration for variables such as these, things like https://github.com/SublimeText/AsciiDoc/pull/5/commits/c8ea5940987509c7e53d8e3f66f63c496c0083d8 could also become feasable. Currently those have a bit too high barrier of entry and would basically require a plugin to walk users through the changes, imo.

FichteFoll avatar May 20 '16 22:05 FichteFoll

@wbond Maybe you could ask Jon to implement some improvements regarding the variables in those TextMate files (see FichteFoll's link) in the next build. I believe this would be a quick fix and require only minor changes? Especially the dropdown lists support would greatly enhance snippets and completions for all languages.

jrappen avatar Feb 14 '17 08:02 jrappen

Jon and I have not yet discussed ideas surrounding the improvement of dealing with different indentation rules. I would not expect such enhancements to be part of the next dev build. In general, I think we may want a larger change to how indentation rules are implemented and how preferences may affect those.

@keith-hall mentioned recently the idea of working on documenting the current behavior and features surrounding indentation. This would be helpful in us coming up with a more elegant solution for dealing with these sorts of issues moving forward.

wbond avatar Feb 15 '17 11:02 wbond

@wbond have you decided, yet?


linking @keith-hall's docs on indentation here, too: https://forum.sublimetext.com/t/everything-you-n-ever-wanted-to-know-about-indentation-in-st3/26207

jrappen avatar Oct 09 '17 21:10 jrappen

No, my focus recently has been on performance issues related to default packages. Unfortunately indentation stuff is somewhat low priority, in the grand scheme of things.

wbond avatar Oct 09 '17 21:10 wbond

Extended proposal

I'd like to extend:

  • use five variables
    • WHITESPACE_BEFORE
    • WHITESPACE_INSIDE_FIRST
    • WHITESPACE_INSIDE
    • WHITESPACE_INSIDE_LAST
    • WHITESPACE_AFTER

One could argue that WHITESPACE_AFTER doesn't make sense cause it's the same for all styles...but just for clarity's sake to get my point across I included it here.

compare the usage:

return_value func_name(params)${WHITESPACE_BEFORE}{${WHITESPACE_INSIDE_FIRST}func_a();${WHITESPACE_INSIDE}func_b();${WHITESPACE_INSIDE_LAST}}${WHITESPACE_AFTER}$0

for some fake code like (pipe indicates cursor pos for $0):

output: Allman style

return_value func_name(params)
{
    func_a();
    func_b();
}
|

output: Whitesmiths style

return_value func_name(params)
    {
    func_a();
    func_b();
    }
|

output: K-and-R style

return_value func_name(params) {
    func_a();
    func_b();
}
|

output: pico style

return_value func_name(params)
{  func_a();
    func_b(); }
|

compare https://en.wikipedia.org/wiki/Indentation_style

jrappen avatar Oct 13 '17 08:10 jrappen

/cc @jsiwek FYI

jrappen avatar Oct 13 '17 09:10 jrappen

I like this proposal a lot; it mirrors the options for brace placement styles in clang-format.

rwols avatar Oct 13 '17 16:10 rwols

Implementation idea

Using mdpopups works great for feeding it a small code block to use sublime_plugin.ListInputHandler for displaying a preview for each indent style during the selection via the command palette.

Make a package with:

package_root/
    main.py
    indent_styles/
        allman.xml # metadata for copy & paste
        allman.cpp # example code
        gnu.xml
        gnu.cpp
        horstmann.xml
        horstmann.cpp
        ...

and the following code:

#!/usr/bin/env python
# coding: utf-8


import sublime
import sublime_plugin

import os
import mdpopups


PKG_NAME = __package__.split('.')[0]
STYLES_DIR = None
INDENT_STYLE = None


def set_indent_style(name='allman'):

    try:
        styles = [
            'allman',
            'gnu',
            'horstmann',
            'k_and_r',
            'lisp',
            'pico',
            'ratliff',
            'whitesmiths'
        ]

        if name not in styles:
            name = 'allman'

        new_style = sublime.load_resource('{}/{}.xml'\
                                          .format(STYLES_DIR, name))

        with open(INDENT_STYLE, mode='w', newline='\n') as file:
            file.write(new_style)

    except Exception as e:
        print(e)


def check_existance_of_indent_style():

    global INDENT_STYLE
    INDENT_STYLE = os.path.join(sublime.packages_path(),
                                    'User',
                                    'INDENT_STYLE.tmPreferences')

    if not os.path.exists(INDENT_STYLE):
        set_indent_style('allman')


def plugin_loaded():

    check_existance_of_indent_style()

    global STYLES_DIR
    STYLES_DIR = 'Packages/{}/indent_styles'\
                 .format(PKG_NAME)


class IndentStyleSelectionInputHandler(sublime_plugin.ListInputHandler):

    def name(self):
        return 'name'

    def placeholder(self):
        return 'Allman'

    def list_items(self):
        items = [
            ('Allman', 'allman'),
            ('GNU', 'gnu'),
            ('Horstmann', 'horstmann'),
            ('K & R', 'k_and_r'),
            ('Lisp', 'lisp'),
            ('Pico', 'pico'),
            ('Ratliff', 'ratliff'),
            ('Whitesmiths', 'whitesmiths')
        ]
        return items

    def preview(self, value):
        items = [
            'allman',
            'gnu',
            'horstmann',
            'k_and_r',
            'lisp',
            'pico',
            'ratliff',
            'whitesmiths'
        ]
        if value not in items:
            return None

        v = sublime.active_window().active_view()
        code_block = sublime.load_resource('{}/{}.cpp'\
                                          .format(STYLES_DIR, value))
        code_block = '```cpp\n{}\n```'.format(code_block)

        return sublime.Html(mdpopups.md2html(v, code_block))


class ChangeIndentCommand(sublime_plugin.WindowCommand):

    def run(self, name):
        set_indent_style(name)

    def input(self, args):
        if 'name' not in args:
            return IndentStyleSelectionInputHandler()
        return None

jrappen avatar Jan 04 '18 21:01 jrappen

Is this plugin done and available somewhere?

jeffreyscottgraham avatar May 10 '19 16:05 jeffreyscottgraham

@jeffreyscottgraham mdpopups is available as a Sublime Text 3 dependency for your plug-in.

https://packagecontrol.io/docs/dependencies

https://github.com/facelessuser/sublime-markdown-popups

jrappen avatar May 14 '19 14:05 jrappen

@jeffreyscottgraham did you need help with the implementation on your side?

jrappen avatar May 15 '19 21:05 jrappen

Thanks no.

jeffreyscottgraham avatar May 16 '19 16:05 jeffreyscottgraham

Random thought I had the other day:

This would be easier if snippets could also read variables from other locations, such as a view's settings, or if plugins could inject shellVariables (which are per selector), so they wouldn't have to generate tmPreferences files in some temporary location.

FichteFoll avatar May 07 '20 00:05 FichteFoll