Sublime Forum

Retrieving inserted and deleted text

#26

I made a slight change to the on_modified this morning that should handle multiple cursors:

	def on_modified (self, view):		

		#get all of the cursors (this only works with one)
		updatedRegions = view.sel()
		
		#get the current size of the text inside the active buffer
		newBufferSize = view.size()

		#get the id of the buffer for a multiple file project
		bufferId = view.buffer_id();

		#if we are tracking this view
		if bufferId in self.allBufferSizes and bufferId in self.allInsertEventsByFile:
			
			#holds the number of characters that were either inserted or deleted
			numCharsChanged = abs(newBufferSize - self.allBufferSizes[bufferId])

			#MM- change here!!
			#if all of the changes are exactly the same length we can handle
			#them all
			numCharsChanged = numCharsChanged / len(updatedRegions)

			#go through each of the regions (usually only one- multiple regions if 
			#there are multiple cursors)
			for region in updatedRegions:
				
				#if the current buffer size is exactly the same as the last edit 
				if(newBufferSize == self.allBufferSizes[bufferId]):

					#I'm not sure when this happens!!!
					print("Size the same-- When does this happen????")

				#new buffer size is smaller than the old buffer size, this must be a delete
				elif(newBufferSize < self.allBufferSizes[bufferId]):

					#remove some data from the shadow container
					self.removeEvent(numCharsChanged, region.a, self.allInsertEventsByFile[bufferId], view)

				#buffer is larger, must be an insert
				else:
					self.insertEvent(numCharsChanged, region.a, self.allInsertEventsByFile[bufferId], view)

			#store the new buffer size for next time
			self.allBufferSizes[bufferId] = newBufferSize

This works only if all of the changes being added are exactly the same length (like in a multi-cursor situation). I take the difference in buffer size and divide by the number of cursors.Three instances of inserting the word ‘cat’ would have a difference of 9 characters. Divide by the three cursors and each cursor contributes 3 characters. Send the position of each cursor to the insert/delete function and it seems to work.

This will not work if unequal sized chunks of text are ever entered at multiple points by the tool. I’m not sure if that ever happens in sublime. Some other editors are more transparent about which changes are occurring.

I also checked and found that find and replace text of exactly the same length does not work… still thinking about how I might solve that problem.

Yes, if I end up going with sublime I will put it on github soon. I am still checking out a few other editors.

1 Like

#27

Very nice!

1 Like

#28

Nice!   Glad I asked what you were using that code for, this is a really cool project :smiley:

1 Like

#29

@fico @kingkeith I decided to go with visual studio code for my more complete version. Sublime is awesome but I wanted to reuse what I had in js. Here is the project page: https://github.com/markm208/storyteller#storyteller

I am curious for your input!

3 Likes

#30

wow, that is really cool! I’m exclusively an Sublime Text user, so I haven’t tried the plugin itself, but the playback mode is awesome! great work! I’m jealous that you didn’t write the storyteller extension for Sublime Text :wink:

1 Like

#31

Works great, nice job man! :grin:

 

Suggestion:

  • Add an icon-based UI group for controls

 

The first time I tried it out, I couldn’t tell that there was a speed setting. Then when I skimmed through the documentation, I noticed the keyboard commands.

A control panel can add visual indication of the current speed, and IMO looks better & is more intuitive than text buttons (paired with hover-tooltips for context).

 

Here are a few mockups showing potential states of said UI:
#@Imgur

 


 

Edit:

I didn’t notice the StepBackward & StepForward buttons when I made the mockups, so here’s an adjusted one:

1 Like

#32

Thanks for posting this. How did you discover those sublime_plugin.EventListener.on_insert, on_delete methods? This is not documented, is it?

1 Like

#33

the on_insert and on_delete methods are not a built in part of sublime_plugin.EventListener - I had to do some coding wizardry to determine what text was inserted and deleted and where - if you look closely, the code I posted uses on_selected_modified_async and on_modified_async together to determine this information - the idea being someone could extend my RecordSessionListener class which has already done all the hard work :wink:

1 Like

#34

Hey thanks for the answer. I should have seen that. Didn’t pay enough attention.

1 Like

#35

no worries - I’ve done that (not pay enough attention) many times recently :wink:

1 Like

#36

Any idea about how to catch undo/redo (preferably multi-cursor too) modifications?

When a multi-cursor undo/redo modification occurs, the selection does not change to the selection inside the undo/redo action.

1 Like

#37

I don’t have time right now to have a look, but a horrible hack might be to listen for when an undo or redo command is about to be executed, cache the entire buffer, then after it is executed, compare the cache with the current buffer… terrible though, I know…

1 Like

#38

one relatively unknown little gem in ST is that you can create a keybinding that will operate on any character press:

import sublime
import sublime_plugin


class CharsInsertedCommand(sublime_plugin.TextCommand):
    def run(self, edit, character):
        print('character "{}" inserted at positions {}'.format(character, [sel for sel in self.view.sel()]))
        self.view.run_command('insert', { 'characters': character })

{ "keys": ["<character>"], "command": "chars_inserted" },

character “t” inserted at positions [(0, 0)]
character “e” inserted at positions [(1, 1)]
character “s” inserted at positions [(2, 2)]
character “t” inserted at positions [(3, 3)]

though doing such a thing could easily conflict with other plugins if they also use it, and may affect they way undo states are calculated…

1 Like

Block default keypess event programatically
#39

Here is what I want to achieve in my plugin.

  • Enter a special command (some text) in a ST tab
  • Press a key combination
  • Launch a subprocess and pass the entered text to the subprocess’ stdin.
  • Capture the output of the subprocess and paste it into the view next to the entered command (POSITION).

The main goal here is to NOT block the editor tab (view) while the subprocess is working. The user must be able to freely modify the view’s buffer. The user also must be able to run many subprocesses.

To make this work I need to find a way to adjust the POSITION for each subprocess when the user modifies the view.

1 Like

#40

have you considered just using ST’s built in bookmarks functionality? these seem to stay in place even when text before it is edited.

1 Like

#41

Wow! Great idea! Need to check bookmarks API. Thanks.

1 Like

#42

You can just use view.add_regions to add invisible regions and retrieve them afterwards with view.get_regions. Those regions move with the text to stay at the position they were added.

4 Likes

#43

Apparently there is no documented bookmarks API :frowning:

1 Like

#45

(Bookmarks can be accessed via the regions API using “bookmarks” as the key.)

so basically the bookmarks functionality just uses what @r-stein suggested:

1 Like

#46

Thank you for the suggestion. This might work.

1 Like