Sublime Forum

How to show % progress when flashing firmware

#1

Part of my build process is an output that flashes the firmware of a device.
On a command line it shows up as:
Programming Firmware: xx%

Where xx% updates with progress.
But in the build window, it shows as
Programming Firmware: 0% BS BS BS BS BS 1% BS BS …etc

Obviously the BS is a backspace character.
Any way to get the output window to adjust the cursor location according the backspace character instead of printing it?

0 Likes

#2

You could copy the file exec.py from the Default package using https://packagecontrol.io/packages/PackageResourceViewer and customize it so that every time to sees a backspace char it deletes output. Then use your customized version by the target key of your build configuration.

4 Likes

#3

I tried this in a couple ways, but every time I add the target setting to my build, it fails. I tried putting the exec_elatec.py file in the following directories:

  1. sublime\Data\Packages\User\
  2. the directory that shell_cmd runs out of

In both cases my build command (CTRL-B) fails silently.

This is my custom build. It works fine without the target line.

{
"shell_cmd" : "c:\\bin\\develop\\Elatec\\build_elatec.bat $file",
"target": "exec_elatec.py",
"shell": true,
"file_regex": "^([^:]*):([0-9]+):[0-9]+: (?:error|note):",
"working_dir" : "$file_path"
}

(I was able to open and copy the exec.py using PackageResourceViewer)

My error in the output always seems to be:
Unable to find target command: c:\bin\develop\Elatec\exec_elatec.py
(location changes per the specified directory)

0 Likes

#4

The target in the build system should be the name of the command, not the name of the python file that contains the command. That’s an easy mistake to make though since the default exec command is stored in exec.py.

Your modified version of exec.py needs to have also renamed ExecCommand to e.g. ExecElatecCommand or something similar so that your version of the command doesn’t clobber the built in version.

Note also that your path in the shell command is also not technically correct because the \ characters are special in JSON. In order to use them as path separators on windows you should double them up.

A corrected build system might look something like:

{
    "shell_cmd" : "c:\\bin\\develop\\Elatec\\build_elatec.bat $file",
    "target": "exec_elatec",
    "shell": true,
    "file_regex": "^([^:]*):([0-9]+):[0-9]+: (?:error|note):",
    "working_dir" : "$file_path"
}

If you name the command something other than ExecElatecCommand, modify the target as appropriate; Sublime converts the command class to a command name by throwing away the Command suffix and then converting the CamelCase to snake_case by replacing all upper case letters with lower case letters prefixed with an underscore (except for the very first one at the start of the command class name).

1 Like

#5

Thanks - this got my custom exec to work. This works by updating the default exec.py file - not by creating my own custom exec_elatec.py file which is how I thought it should work. And nowhere could I find documentation to match the below quote from the last post, which was critical knowledge :slight_smile: :

If you name the command something other than ExecElatecCommand, modify the target as appropriate; Sublime converts the command class to a command name by throwing away the Command suffix and then converting the CamelCase to snake_case by replacing all upper case letters with lower case letters prefixed with an underscore (except for the very first one at the start of the command class name)

Funny note: the double backslash characters were escaped out by the editor on the forum.
I updated my original post to have the build system shown as code.

I am now able to edit the exec.py in place using the PackageResourceViewer and am using my own custom exec. By adding this line in on_data

characters = characters.replace('\b', '')

Under
# Normalize newlines, Sublime Text always uses a single \n separator
# in memory.
characters = characters.replace('\r\n', '\n').replace('\r', '\n')

It gets me closer.

This is much better. But I’m not able to affect prior entries in the text_queue deque object in the append_string function, which would be where I would imagine I would edit the text in the output window… Anyone else have any further ideas? At this point it is very minor aethetics, but I would like to know if it is possible.

0 Likes

#6

Note that you make modifications to the original exec.py, your modifications override the file that ships with Sublime and you won’t get notified if the file that ships with Sublime changes, which means any new features/bug fixes in that file would be masked if you update Sublime to a newer build and it’s been modified. That’s why @wbond mentioned copying the file and customizing it.

To your actual problem, your modification is detecting when there are backspace characters and removing them from the string as it gets queued for processing. That stops them from outputting into the panel but doesn’t take any actual backspace action for them.

I may be wrong but I don’t think this can be done purely with string replacement on the fly, because the output is captured and added to the panel as the build is running, so if your build system was to output "10%", then 1/2 a second later "\b\b\b\b20%" to overwrite it, it’s already too late for simple string replacement to throw the “10%” away because it has already been added to the output view.

Off the top of my head (not having tried it at all), you may be able to do something at the point where the text is actually put into the output panel by first collecting the trailing end of the existing buffer text and modifying it based on the backspaces in the passed in text.

The part of the system that is actually putting the text into the panel is inside of service_text_queue:

        self.output_view.run_command(
            'append',
            {'characters': characters, 'force': True, 'scroll_to_end': True})
1 Like

#7

Yes, I would recommend creating your own file named my_exec.py in Packages/User/ and renaming the classes to your own class names rather than overriding the built in exec.py.

In terms of handling the backspaces, I would do that when adding the text to the output view, not when adding the text to the queue. In other words, in service_text_queue(). I would use View.erase() or View.replace() to delete or overwrite the previous characters when you encounter backspace chars. Seeing as the current implementation uses the append command, you’ll likely need to write a new sublime_plugin.TextCommand that loops through each char in the string to see if backspace operations should happen. See http://www.sublimetext.com/docs/3/api_reference.html for more info about the View class methods.

0 Likes

#8

How does sublime know to use the classes in my_exec.py over the default exec.py classes?

Does it always load all *.py files it finds?

0 Likes

#9

Sublime automatically loads all Python files that are loose in the Packages directory and outside of a package (e.g. Packages\sample.py) as well as all Python files that are directly inside of a package at the top level of the package.

That is to say, it would automatically load Packages\User\sample.py but not Packages\User\something\sample.py. Such files can still be loaded, but you need to do so explicitly with an import statement in one of the files that Sublime automatically loads.

This is to allow you full control over what files get loaded, so that you could for example ship a set of files that are specific to certain platforms and then only load the ones that are currently relevant by detecting the platform and importing the one you require.

Generally speaking, this is due to the scoping rules of Python itself. If you have two files, a.py and b.py and both of them contain for example a class with the same name, the contents of that file will refer to the version of the class from the file that it’s in unless you tell python differently.

import sublime_plugin

class SampleClass():
    value = "I am inside file A"

class TestOneCommand(sublime_plugin.TextCommand):
    def run(self,edit):
        self.view.insert(edit, 0, SampleClass.value)
import sublime_plugin

class SampleClass():
    value = "I am inside file B"

class TestTwoCommand(sublime_plugin.TextCommand):
    def run(self,edit):
        self.view.insert(edit, 0, SampleClass.value)

Here even though both files contain a class named SampleClass, the two commands defined (test_one and test_two) will refer to the version that’s in their own file.

In the context of Sublime, this does not carry through to classes that subclass from special Sublime classes such as ApplicationCommand, WindowCommand or TextCommand. For those classes, Sublime is doing its own processing after all of the files are loaded: finding the classes, creating instances of them, and naming the resulting commands based on the class name.

This means that if both files above contained e.g. TestOneCommand, then as Sublime is processing things the first class of this name becomes the command test_one. As Sublime proceeds through the list, it finds the second class which also becomes the command test_one, and now the first one is effectively hidden.

This is decidedly Bad Mojo :tm: and is why you should rename those sort of special classes when you’re duplicating code.

1 Like