sublimehq / Packages

Syntax highlighting files shipped with Sublime Text and Sublime Merge
https://sublimetext.com
Other
2.95k stars 587 forks source link

Ability to apply different indent styles to completions and snippets #131

Open jrappen opened 8 years ago

jrappen commented 8 years ago

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 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 commented 8 years ago
FichteFoll commented 8 years ago

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.

jrappen commented 7 years ago

@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.

wbond commented 7 years ago

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.

jrappen commented 7 years ago

@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

wbond commented 7 years ago

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.

jrappen commented 7 years ago

Extended proposal

I'd like to extend:

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 commented 7 years ago

/cc @jsiwek FYI

rwols commented 7 years ago

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

jrappen commented 6 years ago

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
jeffreyscottgraham commented 5 years ago

Is this plugin done and available somewhere?

jrappen commented 5 years ago

@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 commented 5 years ago

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

jeffreyscottgraham commented 5 years ago

Thanks no.

FichteFoll commented 4 years ago

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.