[LC72] More Pipe Text enhancements and random plugin stuff

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Music] do [Music] [Music] do do [Music] [Music] [Music] [Music] do do [Music] [Music] do [Music] [Music] all right i think we should be good to go apologies for the slight delay there i was trying to arrange the fans in the room in a way where i could get a little bit of airflow without melting and without exploding the camera as well or sorry the headset rather i should say it has been a very warm warmer even than last week for the last few days uh barring a middle of the night rainstorm the other day and as a result all this week i've had not only that fan in the background that you can see behind me over my shoulder there that one running but also one right here and one right there that are both off of camera all three of them uh creating enough of a breeze to keep things going i better take this off too um and uh with that many tornado level fans you cannot hear a thing so i tried to adjust things such that i could still get a bit of a cooling breeze but at the same time we don't end up cooking me um as such and given the fact that it's not a super deep plan for this evening stream potentially this one might not be as long we might go a little bit short on this one depending on what it is that we're doing uh because uh we're spending all day in here i'm not entirely sure how much more i can stand but uh in any case hello fellow sublime text fanatics welcome to the stream and we are pretty much ready to kick her off i don't really have any set plans for this particular evening um except that one of the things i wanted to work on was uh some changes to the pipe text plugin that we worked on in the previous stream because there is a little bit of a i'll say issue with it uh per se um that i would like to get resolved for my own purposes and then we'll see where things take us because they don't really have a lot of uh real deep plans in that regard um so yeah we'll uh we'll see what is going down in beantown such it is so let's go ahead and press the magic button hello fellow sublime tech fanatics oh dad nerd here welcome back to uh this particular live stream for the day of august the 18th the day i was supposed to go to the dentist but they called at the last moment and said hey could you come another day so what could potentially have been a cancelled stream has ended up not being the case so uh huzzah for that not quite as huzzah for solving ongoing dental issues um but uh there we have it so we're gonna be working this evening on at the very least the pipe text plug-in that we were working on in the last stream this is working pretty good um there's some uh there's a few changes that we'd like to make to this one for sure the others it's hard to say what the good user experience is for these things um actually oh yeah there there is some more stuff that we can do so that's good um but um there's just some uh some stuff well we'll get to that uh when we get there so if you uh missed last stream or haven't caught up we are working here on this uh a package i'm calling pipe text because um a good friend uh keith hall wrote a plugin for our stealthy and hasty uh scraps repository um named pipe text which is the command that's actually in here uh thusly believe in uh no hang on there we go uh worked on a plug-in the other day and forgot the key binding for it i used a different one here than i did on my windows machine this uh pipe 2 pipe text command is what he implemented as a text command and oh someone is acting some stuff well we'll worry about that in a minute right before the stream started i was um uh helping somebody in the forum uh maybe we could even help them here as a matter of fact see what we can do about that um was i saying that yes uh keith created this plugin uh as uh something that you can do because the underlying code for executing code which is way up in here we did some stuff execute with standard i think that was at the top at the very beginning there um the let's scroll up a little bit more the subprocess dot run method is uh i gather a new thing in python 3.5 which uh makes it a lot easier to execute things capture their output send in some input so he created a plugin that would allow you to select some text and execute a command sending that text out to the command and then replacing what comes back with or replacing the selected text with what comes back and um i took that and made some modifications modifications that he himself wanted to make one of which was uh making this multi-threaded hello ashwin um and although this pipetext.pi plugin as we see it right here is actually part of the scraps repository which i probably should link at some point um i wanted to put it in its own repository so that i could work with it with impunity and wild abandon if you will and then when it's all said and done we could merge it back into the scraps repository or maybe it'll morph out into its own little mini package that we can release i don't know but we don't like to put anything into the scraps repository without um you know having other people check into the code and i just didn't want to mess up the history in that particular repository with a bunch of changes that may or may not actually exist so there's a couple of things that i'd like to do tonight for sure um related to changes that we made last week and for the purposes of that if i did this one of the things that we can do is execute say a command with uh an exclamation point in front of it which in this case is going to do a whole lot of nothing actually do i have an example of no i don't that's one of the other things we'd like to add here um sleep five and cat and if i select all this text and execute this command we see the item up in there saying what it's doing it's running in a thread the this is locked and then the insertion happens in there and the idea of this is you would run it with some text like so and that command uh would execute whatever result comes back is inserted into the replacement i added the sleep in there just so we can see it happening um but what the reason i wanted to add that is for the ability to do something like this and have it insert text into the buffer right at the current position and there's special logic that we had to include in here because normally if you select text and you execute then what comes back should replace it and i think that shouldn't have happened there so there may be some more uh action going on there that we need to look into um but in the case where you might want to actually do that thing that i did previously where i said this um you notice i'm catting and i'm not getting any output back and that's because the way that works normally what what should happen if i did this if i did ls without the pipe this is probably the best way to explain this and no text is selected what the code does is say nothing is selected so let's pretend everything is selected and the result of that is it erases the contents of the current file with that because it inherently pretends that the whole file was selected before it executes the command so that it has some text to send out but what i would like to do is that thing where i was just doing this and it inserts into the buffer and the way i implemented that was if the command starts with an exclamation point then don't grab the whole thing just use the selections as they currently exist which might be the reason why that thing is there yes youtube normally uh that works quite well i did notice earlier today that uh whatsapp if you use it from a computer will totally let you use emojis like that i'd never noticed that before but the the intention was uh still the same so the an issue with what i implemented here is that you still want to maybe say something like this and have it still grab the whole file by default but still only insert it and not replace it and right now that's not possible cat in this case does nothing when i do that because it's not actually getting any input the other issue with this and i should probably maybe fix that first is that if everything was selected and you did this is that not what i did previously it seems like it should be replacing the selection so there might be a couple of things that are actually wrong with that particular item um just very quickly before we get rolling here what somebody had a uh oh well okay um i was gonna say someone had a question in the forum um where they were doing something that said the console reports an error when triggered run missing two required positional arguments the reason for that would be that you know if we learned in plug-in 101 you executed a command and it didn't provide the arguments but he specifically said that it actually works if you open the plug-in and trigger a resave which means that that plug-in is likely doing something in the top level that it shouldn't be um hey look my lower third is working huzzah the first time i've ever actually seen it pop up in that display there um but when he posted the code in here to look at uh it is not properly indented so it's really hard to say what this might actually be doing also a command dimension is that actually one of these things yes that one and the other one is crunk so i was gonna say maybe we could uh help out with that but there's a lot of chinese characters in here and it's not indented properly so i'm not really interested in trying to spend time doing that um so let's get back to what it was that we were doing here's the pipe text command and here's the thing we're working with if not all of the selections are empty and we were asked to do a replace then just use uh the entire buffer but this is where i can't quite remember what the logic was that i was going for here use the user selections as that part if not all selections are not empty see this is one of those bits of code that isn't immediately obvious self-view cell returns the selection which is a list all iterates over the list right selections return true if they're empty and false if they're not is that how this works let's see whoops don't you self in that one fool let me move those out of the way a little bit that's a region if i did this it's false but true all right that's one of those things that's new in uh the new api i think the new 3.8 api or just added new in sublime text 4000 builds so new to the api wouldn't work in sublime text three that's a whole lot of indented unindented code block non-code blocked code yeah i know it's i think some of it automatically gets code blocked because it's indented four characters and that's the manual way to do that but it's not entirely clear why the other part wasn't that way unless it's also not indented properly in which case it doesn't seem like it would work except that he mentioned that it would work uh but at the same time it doesn't really look like he's executing a command that has those particular arguments either so that one is a real puzzler wow apologize in advance for that but uh the extreme heat in here is making my head a little sweaty and itchy under the headset let's fix that a little bit um so if not all of the selections are empty and we were asked to do a replace then use the buffer now if i remember correctly do replace is uh whether or not the command starts with an exclamation point and it would be false if it starts with an exclamation point and true if it does so there may actually be a little bit of an extra bug in here um the idea being um the exclamation point should what allow it to insert i actually wrote down what i did i haven't had a chance to look at this since then if the first character is that then the command operates as above but the result is inserted into the buffer instead of replacing the input text okay so that was actually doing what i thought it should do in that particular case if there's text selected the thing that's broken here is if i did this nothing happens or something does happen in that case because there's actually something selected sorry i'm i am this should be the same as this that is when there's an exclamation point mark and not all the selections are not empty it should still gather the whole buffer when it does it but it should still do the insert at the cursor location which currently it doesn't do or if you will this still this does nothing because there's nothing selected so it's not gathering the whole buffer so the cat command has nothing to return so there we go something else that might be good here is don't replace text if the result that came back was an empty string um because maybe you wanted to send output through a command that in a side effect kind of way that's something that this could do as well so if we came back to the magic of this thing if all not all of the selections are non-empty and again if we look there at the thing it returns true if the selection is not empty and false if it doesn't so this we could even test it right here on the console this is something we mentioned earlier in the discord if i paste this in here this is definitely code that seems like it should be bad or documented here if there's an error in the shell command does the selection get replaced with the error string i believe it does let's see it just says capture output i think the inherentness of that is standard error and standard output both get redirected to the same thing but we could test that out that would be interesting to see there are three selections and all of them are non-empty then it's true there are two selections please don't do that i don't know if you can see them i got my uh my faithful canine companion is back there laying over there because it's too hot to be under the desk letting me know that there's somebody in the hallway he's a real sweetheart self-use not all do you sell so again let's see that if there are three selections then that's true because they're all not empty but here they are empty but they are not empty so that's false so that is confusing in the extreme one of the things that keith mentioned uh is that the stuff that i threw into the readme could be better explained and i would agree and the code could be better explained as well it gathers the entire buffer if not all selections are non-empty this is one of those you know the heat is kind of getting to me kind of things because this made perfect sense last time so hmm what obvious thing am i missing here the idea is supposed to be that if you have two selections then it should use them so this should bail out with a false right because it's actually using the user's selections and if there's one full selection and one non-empty selection then this should return true in which case it would just use the whole buffer but this part messes that up it's probably easiest to fix both of those issues if we fix the underlying problem which is in the case where there's nothing selected i.e in the in the case where that returns true and do replace which means the opposite of that thing it should it should still do this in that case when it's do replace but it should keep the selections so that it can insert the text in the right place because i'm the same command would execute multiple times right maybe that's just a one selection kind of thing maybe i'm actually overthinking that um because again the idea is i should be able to do this and have the whole content of the file shuffled out to something external uh and then do it as a matter of fact what happens if we have this text selected and we send cat to uh let's see two greater than ampersand one is that a good way to test that i don't think that it is i don't think that it is a way to test that uh yeah we'll worry about that in a second trying to work through what exactly is we want to do here we want to be able to say this or any command but cat's a good one because it would actually capture all the output send it out and then still do an insert instead of a replace which right now doesn't work so we need to be the way that we send this code through into the thread is it just has a list of the regions that it's going to work with which is either the whole buffer or the selections and this is where this is going wrong because it's ending up in this one where it's passing the selection and the selection is nothing if there's nothing selected but you still wanted to do that part anyway and the reason we're doing it that way is because otherwise doing this and saying something else other than cat that's a bad example here would capture all of the output and send it the way you want but the implicit region constructed by zero and self view size puts the cursor position at the last point in the kit file so if this was a really large file it'll ship it all out do the command and insert it but it'll be way down at the bottom of the file and not necessarily where your cursor happens to be so if i wanted to actually inline the content of this file right here that wouldn't work with the other code because it would grab all of this and because it implicitly selected this the whole content of the file would get injected there and not there so at the very least our thread needs to have a distinction between the regions it should capture text from and the place where they should be inserted which can sometimes both be the same thing and could sometimes be use this whole region but still use the selections for insertion and we could potentially run a foul of the disturbing idea that [Music] there's multiple non-empty selections should the same thing be inserted in three places in that case should this handling only trigger when there's one selection might be um the way to go here right but if we modify this like so so the thread that we're executing gets that and we get let's say regions and selections like this and we pass the same oops same thing into both down here we execute the command and then we do pipe text action with this oops pardon me our region is the place where it's going to be inserted that particular text so i think we here would use oops oops selection is what we want for that and because it's going to bother me if i don't answer ashwin's question there let's say we very quickly touch gen error and world's smallest thing greater than ampersand one thus so if i said gen error that should be generating it to standard out right is that any different than what i did previously i don't think that it is i guess the easiest way to go would be this i am not an error would be the one and that would be the other you execute it you see the both and if you pipe it to dev null whoops then um yeah oh maybe the echo the redirection has to happen at the other part tells you how long it's been since i worked on anything bash related cat no you still want to echo that is there oops whoa is there actually a argument to echo to get it to generate the standard out maybe i'm even over complicating that [Music] that does not necessarily seem like something that should be an issue i mean i could write a c thing to do it man i'm pretty sure that something like that should send standard out but not standard error because i have a lot of crontab entries that do something different um let's do just a very quick oh okay i had the right idea i knew that was the case i was going to put it earlier and then i thought that's not going to work right to agree then it needs to be there not there theoretically oh oh whoa whoa whoa oh never mind i'm being a double dingus don't redirect standard error to standard output i do the same thing too often all right that's that's quadruple embarrassing pardon me redirect to standard arrows most of the time when i do something like this i'm specifically trying to redirect standard error into standard output so that i can actually capture all of the output there we go christ on a cracker okay ah we'll do that and that's gen error right so if i did this oh i broke my code how did i do that oh i got it oh all right okay we'll fix that in just a second because this part's more important um okay try that again oh oh no we'll make a new one and then life will be good i never thought you could enter vim and bash in terminus does that mean i can edit session files in vim through terminus and the changes would carry over i don't think so because i think it only loads the session file at startup i don't think it sees changes to the session file while it's running because it assumes that you know it already has its own state but that's why the session cleaner script on the stealthy and hasty repository recommends that you don't have sublime running while you do it but nevertheless you can give it a try and see it might do something that that project history thing if it actually had the ability to i think project that's the thing that was mentioned in discord using project history as a package to manage your project history because it lets you rename projects and stuff i think the way that that works is it doesn't use the information from the session file i think it remembers projects on its own and that's probably one of the reasons why and the answer to your question is no standard out doesn't get injected into the buffer uh only the standard input part so that might be uh something else to note maybe the errors need to go somewhere but we need to know a little bit more about how this part works so let's say should we capture standard er and redirect it to a panel or an annotation of some sort because that seems like something that it would be interesting to know if nothing gets inserted in there one of the questions that keith and i were also kicking around that we didn't come up with a particularly good answer to is what happens if the command fails in general or if you had three different selections and you were executing the command over three of them um and one of them failed what should happen to that one and should that affect the other two or not that's a bummer yeah it is at some level that's why i these launchers up here for starting merge and starting sublime text actually run a shell script i created uh which runs the session cleaner but only if the thing isn't already running so that as long as i remember occasionally to start the tools up there my list of projects is automatically pruned but that's probably a little trickier to pull off on windows without creating your own custom shortcut or mac os at all and there's got to be a way to run an application with arguments but i'm not sure what it would be or i guess you could just create a doc icon for a shell script seems like that's probably doable um making sure everything's cool cool so that's one thing we're going to note there and so let's see [Music] pipe text action is going to get a region begin and end oh yeah it has to remove that region from the list so there might be a little bit more action going on here the other do a replace or do an insert depending on what was actually provided so what we were doing is kicking off this thread getting a list of regions that represent the source of the text to grab which is that one and then another one which would be the place to actually insert it which would be that one and that requires remembering how to [Music] iterate over a list of things and get the index of the thing as well which is one of those things i do so unfrequently i can never remember [Music] uh we'll just go ahead and with index i can't remember where exactly where i've done that before i guess you'd probably have to uh maybe just use a reverse range another doubt is when a shell command is already running does the command again allow to run some other shell command at that time uh yeah that would probably be cool too um not have that happen as well i'll add that to the top of the file but i think that one would be easy to solve if the command disabled itself while it knew it was running or while that thread was running for example um which sort of goes to another item i'll add that up here should we block execution of the command if it's already running that's just a general one we don't want to have that separated out too much actually maybe the better place for that is down here one of the things that is pending implementation after we decide how to best pull it off is the idea that when you do that execution and you get the annotations on the right hand side there could be a little button in there you can click on to cancel and in that case does it just cancel that one or does it cancel all of them but the idea that you want to cancel commands at all means that whoops this um where we create the thread should be a member that's inside a member of this and not just a variable that's why we created it this way last time and then the command could disable itself if self.thread is not none and self.execute the last thing it does is it could be to set that to be none so that the command can implicitly disable itself while it's already running or alternatively when the file is read only which was the first thing i thought of but the file very briefly swaps from being read only to read write and back to read only if there's multiple selections and it's inserting this one it has to make the buffer temporarily not read only to insert it and then it puts it back and it would be possible for you to hit the key or otherwise execute the command in that split second and those are the sorts of threading race conditions that work 99 of the time or 99.99 of the time and then that .001 percent uh is the part where you want to stab yourself in the eye with a hot poker because something has gone wrong and you can't figure out why just looking to see if there's a fancy way to iterate and get an index without just saying range [Music] it seems like something i've done before but i can't remember whereabouts i would have done it you know enumerate right enumerate reverse enumerate uh i guess that would probably work out wouldn't it let's just go ahead and say is that the sort of thing that you can do seems like that's probably something cool uh let's go closer to the top of the file so it's easier to tell what it's doing 0 is 15 15 and 1 is 0 0. well that certainly looks like it did the right thing let's add one more zero thirty seven one fifteen two there um i guess the issue is that it's coming up the wrong thing it suddenly is raining heavily here and so it's becoming way more chillier oh i envy you sir i sort of uh fell asleep on the couch the other night while i was waiting for the latest video to upload and i got woken at midnight with the loudest thunder i heard in 15 years uh mostly because i live in the pacific northwest of canada so for most of you know in other parts of the world moisture will gather in the atmosphere and then hang out for a while before it falls out of the sky as rain in the pacific northwest any moisture that gets into the air immediately just starts falling as rain so it never really gets to thunderstorm type status most of the time but when it does it's pretty fantastic and i got woken up by a thunder peel that made me wonder if someone's house exploded followed by rain that was so loud it sounded like it was hailing against the barbecue outside but it only lasted for about 15 minutes and then it was super hot the next day so no luck there the index that we're getting here is not one that we necessarily want um because it's the index of the reversed list but we actually want the other uh that to go the other way because if we're gonna get the same list twice we need to be able to index the second one using the same index so we need those numbers to be two one zero and not zero one two based on how convoluted oh crap forgot that part based on how convoluted that last thing was let's see if index starts off at zero then we want the length of the region minus index minus one sorry length regions don't go crazy so with three regions length of regions is three minus the index is zero which is still three minus one is two and for the last one when the index is two that's three minus two is two minus one is zero so that seems like it would probably do what we want and just to verify two one zero we're getting the correct stuff and then i will have to quit or close and reopen that um but that'll give us what we like here um we'll say idx and region in enumerate sometimes i think i should have a little posty note stuck on the bottom of my monitor with all of the questions about python that i have on a routine basis and forget [Music] then we can say selection equals selections at the index of the length of well i'll say length of selections minus index minus one take that part out and now we have a selection that we can use here so it uses the region that it gets to pluck the substring and it uses the associated selection as the place where the insertion or the replacement happens and swap that over that should mean that this all whoops technically works out right i saved that and it seemed to work so that should leave things working exactly the same as they did before we'll just add a cat in here and oh shoot i guess i shouldn't have copied and pasted that code so much huh ah it's one of those days close that and reopen it thank you sort of a good way to reorganize tabs without doing that all right um again we'll close that create a new one select everything pipe it through that and it gets inserted at the end so it is doing what it did before which is good and we can also do that and that do the same thing and we should get the cool duplication of items and put that in out of the way quote unquote we could move on to the idea that pipe text command can gather the regions distinctly from the uh selections let's see if not all of the selections are non-empty um are there any selections that are empty that's probably a better way of saying if not all selections are not empty which i think is an uh official comment that keith added in there i'm not sure um as a matter of fact let's do this we'll we'll go up here and then we'll ah stop doing that compare as we dare so if not all self.view.cell like so else so it would look like this right so this part's the same think use the user selections is that so here whoops do that all selections are non-empty if you get into this part right and if all selections are non-empty then regions is equal to self.view.cell oh i wonder i think we need to do that otherwise we end up with two references to the same list probably that would not be good for us it's the same every time uh because it's the selection for this view so yeah i think we want that and selections is the same uh so that we end up with two and the basically that's because we want these two things to always be distinct and if you pass the same thing for twice then just because you have it in a different variable doesn't mean that it's a distinct object and they'll still end up being the same and that code we just added makes no effect whatsoever so we comment out no we got to do something there too um are there any selections that are not empty then we would say selections is still that as a matter of fact let's all selections are non-empty to replace every selection region with itself like so at least one selection is not empty so use the whole buffer but put it into every selection okay so no matter what the selections are always the same right so we can optimize that away once we know that this is actually working and then regions would be a list that is self dot view dots uh sorry sublime dot region zero land self.view which is different than i did down below um i can't remember if that was official code so let's just keep it that way just for simplicities sake and we multiply that by the length of selections so that there is one copy of the region for everything because the code is going to expect that this is the version that it used to use so we take that part out and this one becomes selections like so that still loads so that's nice if i did this i end up with a double lorem if that's selected and okay let's uh sorry that's selected and that's there we pipe it through and oh it blew up list remove x not inlet oh crap on a cracker uh yeah i forgot about how that command actually works this is probably still read only right oh no it's actually not but we don't want it anyway i'll close that so let's consider what we did here because we're invoking that command giving it the region that it should replace things in and the actual stuff to replace this is probably where p dot standard error would go and is probably the easiest way i could have answered your question ashman about what happens if there's an error the other part of that if that was actually what you were asking is uh it doesn't do this part at all and it just logs a failure and then tells you about it later in the status bar or in a error message i should say if the command literally returns a failure which maybe is different than what you would use the standard error for it seems like that might be better served in a panel so you could see things in line but the reason that this is here is because we add those annotation regions to the buffer and as we replace one of the regions uh we need to remove it so the fact that we decoupled this here is breaking this because it still needs to know the source region anyway so i think we might do this and this would be region let's go ahead and wonder where my mouse went there we go okay we want both of those and we find the actual command yeah thanks source region and replace regions so we're going to need to do a couple of these things aren't we okay so let's go ahead and paste a duplicate of that and do one of those and one of those don't actually need the source region here do i yeah i did i did that in the wrong order pardon me we need to remove the source region from the list of regions and then we do this in the replace region that was the ultimate intent so we have the place where the actual source of text came from so that we can remove the annotation for it which we do and we have the actual place that should either be replaced or inserted at depending on how we specified it so now we did this it's pulling the whole buffer and only inserting it in one place oh because that's still failing how is that not in the list should not the source region be the same because i did that magic business there hmm let's go here let's capture the regions as we see them there maybe we'll actually add a comma there though not that thing that breaks it and we execute that down here and then we say region is that and selection is that and then region selection the source region is the region the replace region is the selection that seems like it's probably correct so if this file looked like this and there was a selection there and selection there and we did this then setting no annotations at oh it's right there 448 and 448 that's 448 and 448 oh i see fail silently in that situation uh where's that actually failing it's failing in here right now what's the actual exception there value error except [Music] if we're running that's probably more along the lines of what we're actually expecting to have happen here sort of i mean it definitely worked what do we actually expect to have happen here not all of the selections are full so it's going to grab the whole buffer and then it should do an insert at two locations so the whole buffer is the lorum ipsum with two blank lines so we would expect to see one at the bottom and one after the word lorem there which is what happens uh we're running into some sequence issue here i think oh sorry that's the line so there to there is the one that was just inserted the cursor was there this is the remainder of the first one and there's the second one so that actually works um so if i was just down here and i did that i would still get the whole file which is what i wasn't getting previously so that actually seems to do what one would like maybe we'll move this comment up here we'll uh whoops wrap that a little bit nicer and let us don't want that that's the only one of those right uh yeah oops i don't want that one either righty yeah that's right should not have been quite as complicated as that now can this actually be cleaner yes selections is the list of selections no matter what so that only needs to happen once it doesn't need to be in both places at least one selection is not empty so use the whole buffer but put it into every selection the other way to do that would be to make sure that it's only the first selection that something happens in but i perhaps you get more functionality this way um so that's that i think that's probably pretty good something funky is happening because that command palette being teeny tiny at the top of the window only seems to happen when i'm working on this package though i haven't really done a heck of a lot lately a plug-in related nature in the last week so there's that i suppose oh let's stage that hunk and that hunk and uh we'll just say actually um maybe that's not actually what i want to do here as a matter of fact let's unstage that uh because i think let's throw them in here instead of having them in the code and uh up here and bang oops oh and uh if the what should happen if the command fails for one of multiple selections uh which is also something that keith and i were pondering but did not actually do anything about or come up with any any item shall we say we can leave that until later we verify here what changes we made this command now instead of taking a list of regions takes regions and selections and that takes regions and selections and it iterates reversed over regions but also gets a selection at the same index in a way that seems mildly crunky but [Music] still i mean the other way to do that actually now that i think about it maybe if we did it reversed we'd end up with zero one two we could just index with a minus version of the index but minus zero probably wouldn't do what we want so we did more stuff right yeah uh the source region and is the place where the data is getting pulled from and the replace region is the selection and our pipe text action command gets two regions now instead of one so that it can try to remove the source region and then do the replace in the replace region to make sure that that'll all work out so i think that's probably cool but uh far as things go but say with yeah that's probably a good way to say that if there are any selections that are empty then the regions to replace in is the whole buffer but one of them for every selection otherwise it's replacing over the same place that it source data from so that seems cool those arguments are being passed to do also report the selection index yeah i don't know what the good way to do that is do you actually want to see zero one two do you want to see the location in the file maybe we leave well enough alone on that one uh but uh i did cat hmm that does not seem quite right if i did this and nothing is selected it grabs the whole thing and puts it in there but oh actually i guess that works the same both ways doesn't it or is it supposed to oh do replace it doesn't figure in here like it's supposed to ah right i totally broke that crap i'm glad i thought of that let's find that code that i just finished dumping uh you only want to if not all selections are not empty use the entire buffer but only when if you're doing a replace right um if we're doing a replacement then crap i've already lost track uh if you're using the exclamation point point you're asking it to do an insert right sometimes it helps to keep track of your own stuff the intention was if this is the case the command operates as above but the result of the command is inserted into the buffer instead of replacing so do replace literally means are we replacing then use the yeah what's the best way to handle this i wonder let's see what we got here all selections are non-empty and we should insert we should insert is one two is all selections are non empty and we should replace and then uh the third case is at least one selection is empty and we should insert and at least one selection is empty and we should replace so we these are the all of the cases that we technically need to handle here um i'm willing to bet that no matter what the list of selections is the list of selections but maybe not if all selections are non-empty and we should be inserting then this list of source regions uh and the list of selections can be the same because the command knows to insert at the end let's push these two up to each other like this i'm not sure what the best logic for this actually is though so this is an interesting conundrum to have at this particular point um one way to handle this is to try all of the iterations of it and then see what feels best so if all the selections are not empty we have two selections let's go ahead and do this this way i'll pull this over here case one is this and we want to insert so we want to execute cat for each of those two things and insert at that position so both of these should be duplicated in one place so the source region and the selections in this case should be identical so and then the command would know that it should uh insert at the end if instead i said cat then we're replacing this we're executing the command twice sending this out and replacing the whole thing with itself which ends us up the same and then in that case again the place to pull the data from and the place that it should be inserting it are both the same if one selection is non-empty and we should replace that's the bit where we start to get a little funky if it looks like this and we execute cat the fact that one of these is non-empty means that it's going to grab the whole buffer but the selections need to be different right uh whole buffer dis selection um because we want to take the whole buffer and replace this with the whole buffer and replace that with the whole buffer is there actually a use case for that i'm not sure and if we should oh yes at least one selection is not empty sorry i was talking about the replacement i did that in the wrong order or did i also all selections are not empty but pardon me so let's just double check this all selections are non-empty like this and we should insert they should both be the same pull that insert there pull that insert there and this one at least one selection is empty and we should insert in that case that looks like this it's going to pull the whole buffer that would be one where it's the whole buffer in a different selection because the whole buffer is the one place to go and then it's going to insert it in two places so this one should actually look like this so maybe i actually have this correct right now and then we say all selections are non-empty and we should replace then they can both be the same because it's going to pull this it's going to replace back into its own spot so that would be both the same like so and at least one selection is empty and we should replace so if we did that and this it's going to pull the whole thing this one i think is the one that's causing me some mental static because if at least one selection is empty and you should replace the result of that would be this entire buffer gets inserted here it replaces that and then it replaces this empty selection so a new one gets inlined in so is that what we want is this the case where if at least one selection isn't empty then grab the whole buffer and only insert and only replace if at least one selection is empty it's going to grab the whole buffer and if that's the case no matter how many selections there are it should just replace the whole buffer right i think that might make the most logical sense when you're doing an insert it makes sense to grab the whole buffer shuffle it through the command when i say make sense i mean there's an idea that a person might want to do that shuffle the entire contents of this buffer out to an external command and then insert the results in two places regardless of what the selection is but if you were going to replace and you were going to grab the whole of the buffer and replace the selection with the whole of the buffer and there's at least one selection that's empty do we still want to do that or do we want to say in that case grab the whole thing and just replace the whole buffer as if it was selected because otherwise you're coming into a situation where we have an external program where we want to pass this whole content of this file and we want to replace a small part of this file with it and then we just want to insert the result of the command in one spot i can see why that might be a thing so yeah maybe that's what we want so when all the selections are non-empty they should both be the same which is what they are right now and otherwise they should be different it's the whole buffer selection times i think logically until we can come up with the reason why not to do that that's actually what we want um so yeah i'll say this gather the list of selections and replace insert regions if any selections um any of the selections is not empty is empty the whole buffer is used but it will apply to every selection for the purposes of inserting or replacing so then we can just say at least one selection is not empty use the whole buffer but operate over every selection and all the selections are not empty replace every selection region with the mixing the results over itself i think that is what we want to do for that man that's that's some rough action right there um yeah and then we'll go ahead and stage that hunk we have selections and selections passing that in and here it's using the select source selections and the region selections in both places so uh send the whole file content if there are some empty selections like so and we'll commit that and we'll worry about doing something with that later and we'll say i'll come back and fix some stuff about that later and swap those around uh so we'll commit that one now one other thing that we need to do here is capture the history and save it because right now if i did this and reload that plugin the history of this is now lost um so we want to save that history [Music] and the best way to pull that off do we think my theory is we edit the pipe text wrapper plug-in to have a plug-in loaded and a plug-in unloaded when plug-in unloaded is called we use the history object to write a text file one line per history item to a text file in the cache folder and use plug-in loaded to load that into the history i think that's probably kosher uh yeah we need to do that in that order don't we get return self storage self storage is the list so um our first order of business is maybe we make this uh no this is creating an instance of the thing right yeah yeah yeah it is uh so we know this is uh we don't need a plug-in loaded for this we only need a plug-in unloaded because we're not doing anything in here that's api related so i think uh that means we might want to say something like and we'll even add a separate [Music] uh pipe text history.txt let's say and we say uh that stores our history which technically doesn't even need to be a variable could just be inlined in here for all anybody cares but we would say history file like so and we need to import oops os and then init this would also have a file name and then with open something that file something like y'all open that particular file which would be this at important time plugins may not call any api functions with the exception of version sublime platform arch sublime channel i think that has been changed because they say we might need plug-in loaded but uh api executable path packages path installed path and cache path may now be called at import time which is in the change log but it's not in the official documentation which is why it's not in snappy yet so we can in fact call sublime cache path here and not worry about it so we actually want to have i'll put an r in there too west stop path join uh cache path and history file that's rough oops now i guess you probably want something like that oh actually yeah i guess i guess we did want that pardon me we can do that for that particular case uh but we can just say self.storage those file.readlines i think i've written this completely off the top of my head having a little conception of python read lines whether or not this is actually how you do this because as i pointed out in many a stream i don't do api or file io and python at this level often enough to know for sure but we're going to go ahead and click that [Music] read lines reads until end of file using readline and returns a list containing the lines if the optional size hint argument is present instead of returning up to it'll return that many so i think that'll do what one thought that it would do and here we need to say try accept pass and the same thing [Music] and this is where this cries out for a method that returns the file name for the thing my phone right lines is there a way to do that yes cool i think that's the uh deal that returns the storage uh so oh yeah that's that's the old one it's okay now uh so we'll say trying to load history history load failed and i'll try this trying to i did that wrong those are saves and just reload failed [Music] hmm let's double check in the console shall we does this come up with what we oh how about if we use the correct thing so we come over here and say ls and cat and also ls i think that might not be working the way keith wanted it to cat yeah i think we want to swap that back that's the thing that i noticed before so we'll we'll subvert history there um but in any case if i save that it's trying to save history it's trying to load history now dang it the same of history needs to do that i think we may need to delete that particular file uh that probably just put it back right package cd dot dot cd cache and yeah so let's just do this cat ls cat ls it's the actual content of this thing um oh yeah we're totally gonna get screwed again okay we'll just uh do that in an outside terminal then shall we that's the easiest way to fix that i guess package cash that is the history as it exists there so that's good fire this up and it should have said something i would imagine doesn't look like it but the new lines can't be in this part this is rough can you make reline strip new lines off that's an intriguing question i see to do i'm just looking to see that does not seem to be the magic yeah we'll worry about that later because this is pretty crude um but most importantly that is not doing what we want um so let's jump back to the other one for a minute this is pretty much i mean oh yeah it happens out of line there if you will okay we'll get we'll block that block that and the other way to do that incidentally would be with the settings thing and up here too we don't need that as a matter of fact it probably makes more sense to have this all inside of the uh class doesn't it so we'll worry about that in just a moment um at least one selection is not empty so use the whole buffer but operate over every selection [Music] once at least one selection isn't empty so the list of selections these actually do have to change and this in this one particular case everything else is still okay we'll again you know worry about this in just a moment so at least one selection isn't empty so use the okay at least one selection is not empty if we're replacing okay oh so use the whole buffer as the source region um so we can say i think we want this yeah actually let's do it this way um do that um if we're replacing and not inserting then use only a single selection that also oh yeah i guess we have to do that in a different order don't we yeah yeah yeah yeah okay this is how we're going to do this are we re are we replacing if do replace then regions is equal to we'll just copy this it's equal to that but only one of them because we want to use the whole thing and selections is equal to that as well and again we want to make sure this so source the whole buffer and replace it into the whole buffer not entirely sure now that i've started typing it whether that's actually correct otherwise we're inserting that's when you use the actual thing for and we're inserting so send out the whole buffer but replay but insert into the selections uh which is that and then this part goes away and that i think is actually what we want here so if we do if we do cat we end up with the exact same text which is what you might expect if we wanted to run a command and replace the whole buffer that's when we don't include that uh bang on the front of the command so this should replace the whole buffer with the list but if we said cat it should put the whole buffer into that position and if we did ls it should do the same and if we had just some selected text like that and we did cat um my history went away the history might still be broken thanks to that change i just made it should capture the whole line send it out and insert it in both places so the first line will double and then it'll also appear on line eight oh no sorry choosing the whole buffer because there's one non-empty selection so it inserts the whole thing and then there's another one starting on line eight so that is uh what one might expect if you wanted to do it that way i think what you really might want to do is something along those lines if you were going to do that if you wanted to actually insert them in addition to text more often than not for something like that you would just cat oh sorry let's take that back one more the most useless thing we can type here is just cat and then both of those actually do that and we could ls to replace both of those selections that i think is more like what we actually intended that to do so um let us stage that hunk and amend oh is it the previous commit now no crap uh do that and then we can stash and there that'll be a little bit better and we're back to our history type action which may or may not currently be broken um and we're almost out of time here so let's sort of tweak this up a little bit uh let's say def load history self and file name uh actually you know what uh let's do this we're gonna do it let's go to town pipe text history.txt so and then we can i think uh for class and return os.path dot join sublime dot cash path and class dot history file and here we can just say print thus cancel that out and here we don't need a file name anymore we'll just say self dot load history comment that bit of nonsense out and replace that with that comment that save quit restart to have us in an entirely clean state and now when i save this you kind of miss seeing the old monica yeah i i kind of do as well but i need to be able to work in this color scheme because it's the one that i'm going to be using in my videos once sublime 4 becomes active so it's actually growing on me a little bit now that i dropped the darkness of the background a little bit some of the colors are still a little weird but the thing that kind of surprised me about that swap is that mariana is a more mature color scheme such as it is because with the advancements that have been happening in the packages that ship with sublime and scopes have been changing mariana is the one that's been being updated because it's also used in merge i think is the reason why that's the case but i did a little diff with a plug-in of what scopes appear in mariana that don't appear in rules in monacai and they're very there's very few of them so i'm not sure why it wasn't possible to add extra rules for the things that aren't there to bring it up to spec uh the argument that will gave was people expect monaca to look the same everywhere so i guess they thought that if there were some things that are syntax highlighted differently here than elsewhere then someone would complain about it i'm not sure i necessarily follow the logic of that operation why am i doing that i don't need that it's definitely gathering the correct thing so that's nice is that still possible let's check that we're going to see it way up here that still works looks nice good good um so we have one thing getting the history file and then if we're going to load it that would be this right so this is the bit where we would say if self.history file whoops is a file then try this right that seems like the way to go comment that out seeing this function be called twice in quick succession like this makes me really want to memoize the history file class method so that it only has to call path join once instead of the eventual three times that it's inevitably gonna do something in um it's probably also worth noting that i don't think we want this to necessarily happen in plug-in unloaded so we probably want that to go away too because the only time the history would actually get saved is if this plug-in file was actually saved otherwise it wouldn't do anything at all which is probably not what we want [Music] so read lines brings in the new lines we need to add so we need to strip them off here um let's just double check here something seems like maybe instead of doing read lines if you want to read all the lines of a file in a list you can use lose lift f or read lines yeah i know that's what the thing i'm looking for bro i'm looking for read lines in this but it's not showing up often the documentation for python irritates the hell out of me when i know how to search a thing and i want to find it somewhere else but that's not how that works you see mentions to things that aren't links to other things file input module is that the thing [Music] doesn't look like it i think this is the thing that i was just looking at i should probably be doing that on this monitor over here but you know you know how i uh i like to keep things all secret like so we'll leave that for the time being um storage and load history so we copied that we don't need it here anymore we'll just say try to open it and strip all the new lines off store it in otherwise pass which isn't would do what you want to do but what's the good way to handle such a thing uh error reading pipe text pipe command history type text history yeah that'll do something right um right right right this isn't how i wanted to implement this son of a okay i i meant to have no that's no that's right this is the actual history command i was thinking that this was the uh the wrapper so when you create this class it will load its history if it can into its own storage list replacing the one that is there otherwise it'll do nothing and then we also need a def save history this is the one where i wish i hadn't looped that code out before we were all done uh with open self.history file right text as file uh file dot right lines l plus a new line for l in self dot storage thus and [Music] exception as e let's say this save percent the entries to the pipe text history file say we did a little thing and then say copy that file out overlay so error writing pipe text history um so did i just put something in the wrong place yeah i want to do something like this here too loaded zero entries from the pipe text history file all right let's quit that and do this uh cat ls cat and ls like so fire this up dude entries from the pipe text history file so what we really need is something that occasionally dumps the history out now how occasionally is occasionally every time a command is executed from the wrapper perhaps debounced after a delay this is one of those things that's open to interpretation [Music] wouldn't want to have a log in there uh but if we actually executed this and pushed it onto the history then we should say pipe command history dot save history like so um so we save that it's loaded four items let's back this all up and say sleep five and ls inserted into that location it's doing it over the whole buffer but it's inserting it at the end and it saved five entries to the pipe text history because it managed to pull that off um so we could do it here like we do here every time this command executes the history has been modified so write the history out um or we could just have something that saves the history [Music] once a minute if it's changed uh we could have a debounced set timeout operation here that once you run a command the history will be saved after a minute but if you enter another command within that minute then the timeout is reset to be a minute after that so we're not obsessively saving things to the history file every time but at the same time we still get a chance to save things because doing it in plug-in unloaded is only going to work if someone ignores the package right sublime doesn't call plug-in unloaded before it shuts down although now that we say that one of the things that's new of course is called once after the api has shut down immediately before the plug-in host process exits which isn't something that used to exist it only has you need sublime text 40 50 for this but that's an awesome place to throw this in um it's not necessary that it actually log anything mind you um we already have an event listener so that's nice so if we did that there then we wouldn't do it in the place where we were doing it just a moment ago so that part is gone now the fact that it's printing something doesn't really matter because you're never going to see it the api has shut down every time i save this we see it saying how many things it loaded so if i came to here and did this this is a new command and it'll after a bit of time blink onto the end now remember there were five entries in the history i quit still five that so that should have done something but it doesn't look like it did it's still five and not six called once immediately before the plug-in host process exits well i mean at some level we have to assume that it hasn't it might have would it unload plug-ins could we maybe do it do that let's give that a try i don't think plugin unloaded is called at shutdown that i'm aware of anyway but i guess it's easy enough to find out copy that and throw one in here that part doesn't have to be here that part does save quit start to make sure things are cleared out and boy one reason to do that in plug-in loaded i tell you what i'll make sure it ends up in the right part so i might just end up doing that anyway instead of but it would have to be called create it at yeah okay um so sleep five and echo hello and there it goes and let's say i quit whoops say i quit it's still five items so that also did nothing oh that's not going to do what i wanted i just noticed something was happening there well yeah that one doesn't count bro gotta redact that one out and fire that one up uh that was because i reloaded the plug-in um which is probably a good place to have that anyway just for reasons like this loaded five entries from the pipe history file like so right okay so we undo that and then say 20 and cripes do that can hover the mouse over that no no we can just sort of click on it to actually see what the whole command is that it's executing that's why i wanted to do that i don't think that was working in the last uh thing if the command was extra long it just seemed to be cut off now it has thrown that stuff out so we're gonna quit and there's still only five so i'm not entirely sure why that's not working because it really seems like uh if on exit is a thing then that is the thing right so just very quickly i guess what we could do before we call it a night for the stream sublime text lib on exit is called once the api is shut down which means the standard up for callback okay so that's in sublime plug-in so on exit it's an event yeah that's not surprising uh viewing event listener excluded yeah it's not a view event listener thing on exit is called once the api is it shutdown which means standard out will not be visible for debugging thus we write to a log file a log file you say oh i see uh if anything writes to standard out it writes it to a log file otherwise no huh okay what's log path that's the only other instance of that right oh i see we're not going to get to see that that's not invoked here so what the actual log is is its own thing um but on exit is definitely a thing and our plugin is definitely a python 3.8 plugin otherwise it wouldn't work at all so the fact that the on exit in this event listener doesn't seem to do anything is intriguing to say the least but nevertheless um maybe that's because the object went away beforehand i'm not sure but i guess we're gonna have to hang on that one and leave that for the next stream because we have gone a little bit over i am kind of stifling in here with only the one fan on and uh yeah so we're gonna go ahead and call it here and possibly pick this up in the next stream i i have some stuff in override audit that i need to be doing to push out or release but i keep putting it off because there's some other stuff that i want to do in there that i don't necessarily want to do on a stream because it would be boring maybe i need to go ahead and get into that because otherwise that next release is never gonna get out and we do have a couple of nice partial features already in there but uh in any case uh if you have been thank you for watching remember you can uh always subscribe if you'd like to know more if you hit the bell notification icon youtube will let you know when i'm going live uh in advance as far as i'm more you can follow me on twitter at nerd i also tweet out there when i'm going live and i have another channel where we do weekly videos linked down in the description below in the the next videos coming out what could they be we were recently uh working on plug-in 101 and talking about event listeners so one thing we could do if we get enough response is some samples of event listeners to hammer that home sorry input handlers otherwise we might talk about event listeners in the next plug-in 101 video and what will the next tutorial be you have to subscribe and ring the bell notification icon to find out uh until the next live stream and the next video this is odatnerd asking you to please have a sublime day and try to stay cool out there if your temperatures are super stifling the way they are here cause good gravy it is warm do you
Info
Channel: Terence Martin
Views: 31
Rating: 5 out of 5
Keywords:
Id: xbvoaumGpsE
Channel Id: undefined
Length: 127min 35sec (7655 seconds)
Published: Wed Aug 19 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.