Sublime Forum

Basic typing of text in ST is now broken

#1

A basic rule of an editor should be that if I put my cursor in a position and type a letter it should bloody well type it.

Try this

type

hello(

ST helpfully adds a closing ) not sure if this is really “helpful” but anyway, this kind of help has now got so intrusive you cant “just type” e.g you cant type

boo :( 

If you add another ( you get

hello(())

Probably not what you want, this is valid code, err never.

But what I find infuriating is that if you have a syntax error

hello(()

And you fix it by adding the missing )

Sublime text silently deletes your change leaving it wrong. I don’t know what it thinks it is doing, but if you type

where the cursor | is below, and you type )

hello((|)

it just ignores you. Because ST is now so “helpful” I find this type of bug often when I build code I’ve just written and I’ve taken to fixing with vi which always types something in edit mode.

Same thing with popup completion you can type a valid sequence of key strokes and because of popups ST enters to the file complete junk based on other words in the current file. Less annoying with code, very annoying with documentation.

Microsoft Word does this stupid Fixing of “” chars and method© to copyright etc. ad infinitum. ST is used for code, it needs to be precise. It can’t second guess what I want, and if its going to try, it has to at least follow the rules of the current language…

e.g. if I type

<ilurveyou \></   

in a text file it “helpfully” fills it in as if I were hand writing XML, and the writes invalid XML!

<ilurveyou \></ilurveyou>

I have to delete 10 characters for having written 14, that’s a really bad “what it got wrong / what I typed” ratio.
and this file is NOT XML.

I think its a key feature of an editor that if you open a blank page and start touch typing it should type what came off the keyboard. “Helpful” hints should be below Ctrl + space or any key combination, NOT any normal sequence of key strokes used for entering text. (Including the arrow keys!) Having helpful additions and deletions means that every time ST bring out a shiny new feature you have to re-learn some new stupid set of key strokes to type normal text. i.e. enter what you want on the keyboard then left, right, del, del space, del, now you have what you intended.

It were a single bug I’d report it, but its a pervasive, intrusive UI style now,

hence a rant. :()

0 Likes

#2

yes the ( and ) functionality can be super annoying, but why rant when you can just set "auto_match_enabled": false, in your preferences?
and "auto_complete": false, for that matter

typing </ is by default only autocompleted in XML or HTML syntax mode, maybe you have some dodgy third party packages installed?

7 Likes

#3

Why rant :slight_smile:

because in an editor you do want these features, just not below normal, single, key presses.

Another one for ya…

Tab is used for completion in C code, tab is fine on the CLI for bash completion because you never want to type a tab character. Writing code you very often what to write a tab.

ST does this…

char| *char_aligned_ptr;

Type tab there, ST gives you

char_aligned_ptr *char_aligned_ptr;

Again this is invalid code char_aligned_ptr is not a type. The only way to add a tab here is to type
space + tab + left + del

Because ST has this is a UI style rather than a specific key binding you can’t fix it for now and ever with config. There should be no special keybinding for the letter “a” or “tab” or “/” or “(”, because if there is, you cant type these characters. keybindings are for shortcuts, not textual key presses.

We need a config entry dont_mess_with_what_I_type_without_ctrl_key_pressed :slight_smile:

0 Likes

#4

Here are two situations where completions really annoy me.

( edit: the first example is actually DocBlockr, not stock sublime )

Edit: I know it is hard for a text editor to do the right thing here, that would require something more complex than a regex parser. Would it be too difficult to keep a count of open/closed braces and do some heuristics?

0 Likes

#5

like the gifs. :slight_smile:

another one that gets me all the time is

if (code) |
    [multiline existing code]

type a { and it fills in an incorrect closing } as if no other code existed

if (code) {}
    [multiline existing code]

I’ve noticed a trend where its hard to fix errors in ST because completion presumes the code is valid when it runs, not really a fair assumption in a code editor that does not keep a model of the syntax in memory like eclipse.

0 Likes

#6

I like it. It may take some getting used to, but it certainly is useful for me. It is also disableable, so…

For the xml issue, something about the syntax definition you use seems odd. If it’s not xml, it shouldn’t proclaim to be a subtype of it.

Regarding the tab issue, this is actually provided by key bindings and can be overridden. However, do you have the commit on tab setting enabled? Maybe that’s not your ideal way.

Agree with the second. That’s totally bonkers 95% of the time.

0 Likes

#7

ST also does not respect that the enter key has to be used to type new lines

wibbleFunction = "foo"
x = wibble[enter]

ST will auto-complete wibbleFunction in preference over a new line.

var a
var b
var c

ends up

varargumentsvarbodyContentvarcollector

Which happily ends the ASI debate :slight_smile:

0 Likes

#8

Well, if it helps, there are actually settings for all of these quirks (features):

// When enabled, pressing tab will insert the best matching completion.
// When disabled, tab will only trigger snippets or insert a tab.
// Shift+tab can be used to insert an explicit tab when tab_completion is
// enabled.
"tab_completion": true,

// Enable auto complete to be triggered automatically when typing.
"auto_complete": true,

// The maximum file size where auto complete will be automatically triggered.
"auto_complete_size_limit": 4194304,

// The delay, in ms, before the auto complete window is shown after typing
"auto_complete_delay": 50,

// Controls what scopes auto complete will be triggered in
"auto_complete_selector": "meta.tag - punctuation.definition.tag.begin, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc",

// Additional situations to trigger auto complete
"auto_complete_triggers": [ {"selector": "text.html", "characters": "<"} ],

// By default, auto complete will commit the current completion on enter.
// This setting can be used to make it complete on tab instead.
// Completing on tab is generally a superior option, as it removes
// ambiguity between committing the completion and inserting a newline.
"auto_complete_commit_on_tab": false,

// Controls if auto complete is shown when snippet fields are active.
// Only relevant if auto_complete_commit_on_tab is true.
"auto_complete_with_fields": false,

// Controls what happens when pressing the up key while the first item in
// the auto complete window is selected: if false, the window is hidden,
// otherwise the last item in the window is selected. Likewise for the
// down key when the last item is selected.
"auto_complete_cycle": false,
0 Likes

#9

Which syntax is your first example?

ST3 doesn’t auto-pair such comments out of the box for C/C++ syntax. So I guess it’s not ST to blame here. Even Doxygen package doesn’t pair /* and */.

0 Likes

#10

You are right, it’s docblockr, and it doesn’t check for a specific syntax

0 Likes

#11

Example 2 is caused by ST’s wrap_block command, which is implemented in Default/block.py. The implementation is indeed a bit too simple and does not catch most of the edge cases. I didn’t realize this function to exist as I (accidentally) override its key binding.

Even though it may not yet be very perfect, I managed to modify the block.py a bit to handle some more edge cases to decide when to insert the closing bracket or not.

In fact the whole function depends on indention levels only, but not on code analysis - as all those intelligent input helpers.

import sublime
import sublime_plugin


def next_line(view, pt):
    return view.line(pt).b + 1


def prev_line(view, pt):
    return view.line(pt).a - 1


def is_ws(str):
    for ch in str:
        if ch not in ' \t':
            return False
    return True


def indented_block(view, r):
    if r.empty():
        if r.a == view.size():
            return False

        nl = next_line(view, r.a)
        nr = view.indented_region(nl)
        if nr.a < nl:
            nr.a = nl

        this_indent = view.indentation_level(r.a)
        next_indent = view.indentation_level(nl)

        ok = this_indent + 1 <= next_indent

        if not ok:
            prev_indent = view.indentation_level(prev_line(view, r.a))

            # Mostly handle the case where the user has just pressed enter, and the
            # auto indent will update the indentation to something that will trigger
            # the block behavior when { is pressed
            line_str = view.substr(view.line(r.a))
            if is_ws(line_str) and len(view.get_regions("autows")) > 0:
                ok = prev_indent + 1 == this_indent and this_indent == next_indent

        if ok:
            # ensure that every line of nr is indented more than nl
            l = next_line(view, nl)
            while l < nr.b:
                if view.indentation_level(l) < next_indent:
                    return False
                l = next_line(view, l)
            return True

    return False


class BlockContext(sublime_plugin.EventListener):
    def on_query_context(self, view, key, operator, operand, match_all):
        if key != "indented_block":
            return None

        if operator != sublime.OP_EQUAL and operator != sublime.OP_NOT_EQUAL:
            return False

        is_all = True
        is_any = False

        for r in view.sel():
            if operator == sublime.OP_EQUAL:
                b = (operand == indented_block(view, r))
            else:
                b = (operand != indented_block(view, r))

            if b:
                is_any = True
            else:
                is_all = False

        return is_all if match_all else is_any


class WrapBlockCommand(sublime_plugin.TextCommand):
    def run(self, edit, begin, end):
        view = self.view
        view.run_command('insert', {'characters': begin})

        for r in reversed(view.sel() or []):
            if r.empty():

                nr = view.indented_region(next_line(view, r.a))

                slvl = view.indentation_level(r.a)
                elvl = view.indentation_level(nr.b)

                end_line = view.substr(view.line(nr.b)).strip()

                # Insert `end` if it does not exist at the end of the indented
                # block with the same indention level.
                if slvl != elvl or not end_line.startswith(end):

                    begin_line = view.substr(view.line(r.a))

                    ws = ""
                    for ch in begin_line:
                        if ch not in ' \t':
                            break
                        ws += ch

                    if nr.b == view.size() and view.substr(nr.b - 1) != "\n":
                        view.insert(edit, nr.b, "\n" + ws + end + "\n")
                    else:
                        view.insert(edit, nr.b, ws + end + "\n")

EDIT: Just for consistency a command to remove the closing brace would be needed, if the opening one is deleted.

1 Like

#12

I can’t get tab completion as described to work i.e. shift tab

For me tab inserts 4 spaces and shift tab removes them, in the file I’m looking at now there is no way to insert a tab character.

Point about this rant is I want tab to be tab and shift-tab can do whatever. Ditto all typing keystrokes that need to insert something in to the file.

Its pretty crazy that there are edits to a text file that cannot be achieved with ST at all. I have “translate_tabs_to_spaces”: false

I like that idea you can have either tab or enter for auto complete, see my point, both these key are for typing?

Can you un-assign a key binding in the keymap there are a few bindings for tab key?

0 Likes

#13

N.B you cant copy paste tabs either, copy paste not working :frowning:

0 Likes

#14

Be sure to not have it set in a syntax specific settings file, which would overwrite the value from Preferences.sublime-settings.

If it is not, you might check your package collection. There are a couple of packages out there, which promise improved indention handling, but don’t work correctly for most languages - making things confusing.

If I open a new file, set C syntax and set "translate_tabs_to_spaces": false ST correctly inserts tabs. Copy & paste works well, too.

With "translate_tabs_to_spaces": true all tabs are translated to spaces even on pasting tabs. Might be tricky in some situations but is just consequent and avoids mixed tabs/spaces.

0 Likes

#15

“Doen’t work” is not a valid problem description; Copy & Pasta totally works for me.

0 Likes

#16

Try it…

I have a space indented script that creates a Makefile with tabs

if foo
then 
    echo "
[tab]  rm -rf /foo
" >> Makefile

And you want to add another rm line, you cant do it SublimeText at all.

Copy paste

[tab]  rm -rf /foo

gets converted to

[space][space][space][space] rm -rf /foo

Arguably copy & paste should copy & paste.

If you try the Linux second copy buffer (select & middle click) thats also does not work. Not so surprising since Linux seconf copy paste buffer is not really supported properly.

Copy paste wierdness is kind of of topic, the root problem, there is no keybinding to enter a tab character in ST.

0 Likes

#17

Wow, you are correct. However, I am not sure, what the “right behaviour” should be here.

0 Likes

#18

This can indeed be frustrating. You are right. While translating everything to spaces can help to keep indention clean, it may sometimes be annoying.

Even the explicit command self.view.run_command("insert", {"characters": "\t"}) inserts spaces.

This command is bound to shift+tab, but doesn’t work at the beginning of a line as it will always call unindent then, even with "shift_tab_unindent": false. This function should apply to all situations.

The only way to add tabs to a space indented file is temporarily modifying the setting. I’ve created an insert_tab command for it.

import sublime_plugin


class InsertTabCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        settings = self.view.settings()
        value = settings.get("translate_tabs_to_spaces")

        try:
            settings.set("translate_tabs_to_spaces", False)
            self.view.run_command("insert", {"characters": "\t"})
        finally:
            settings.set("translate_tabs_to_spaces", value)

Depending of your needs you could map it to a key of choice or add the following snippet to your User/Default.sublime-keymap It ensures to insert a real \t by pressing shift+tab if shift_tab_unindent = false. To keep fields navigation working, you’ll need to add all three rules.

	// Insert tabs to a file of spaces
	{
		"keys": ["shift+tab"],
		"command": "insert_tab",
		"context": [
			{ "key": "setting.translate_tabs_to_spaces", "operator": "equal", "operand": true }
		]
	},
	{
		"keys": ["shift+tab"],
		"command": "unindent",
		"context": [
			{ "key": "setting.shift_tab_unindent", "operator": "equal", "operand": true }
		]
	},
	{ "keys": ["shift+tab"], "command": "prev_field", "context":
		[
			{ "key": "has_prev_field", "operator": "equal", "operand": true }
		]
	},

If you need to copy & paste tabs as is, you can try the TabNukerNuker package.

1 Like

#19

If you really wanted, you could edit default preferences (not User) with PackageResourceViewer, and remove tab/enter keybindings that trigger completion, like this one

{ "keys": ["enter"], "command": "commit_completion", "context":
	[
		{ "key": "auto_complete_visible" },
		{ "key": "setting.auto_complete_commit_on_tab", "operand": false }
	]
},

and change the binding to one that you like, this would disable tab/enter completion.

0 Likes