>> DAN ARMENDARIZ: Hello, and welcome to a CS50 seminar on Customizing Cloud 9,
and the CS50 IDE.
So today we're going to just talk a little bit
about some of the technical information that
goes behind the architecture of Cloud 9, and how
we have implemented some of the plug-ins as part of Cloud 9 to provide CS50 IDE.
So let's just jump right in and start talking about plug-ins.
>> So plug-ins are really at the core of the Cloud 9 experience.
Cloud 9 is the underlying technology that provides to us the IDE,
and also the terminal window at the bottom, along with the workspace--
the Ubuntu workspace-- that we use to compile all of our projects
and perform our problem sets, complete our problem sets.
But at the heart of all of it, all the technology
is really just a bunch of plug-ins.
Everything is a plug-in that can be modified,
extended, you can create your own plug-ins,
you can remove other plug-ins, to really alter
quite a lot of the functionality of the existing Cloud 9 IDE.
>> So this is an existing Cloud 9 environment.
This is really just the default Cloud 9 environment.
And take a look at how it actually is different from the CS50 IDE.
So this is the CS50 IDE.
Notice that there are some visual changes between the regular Cloud 9
environment and the CS50 IDE.
Specifically, you'll notice a couple of changes.
First of all, there's fewer menu items to begin with.
There's actually a way that you can change that.
It's called Simple Mode.
>> By default, Simple Mode is enabled, and that simplifies some of the menu items.
It removes some of the more advanced ones,
because frankly, a lot of students that are coming in
and don't need to know about some of the more advanced features
are not overwhelmed with a bunch of options.
But we do provide the ability to take off those so-called training wheels
and remove that scaffolding so that people
can become more accustomed to the advanced features of the IDE.
Immediately to the right of that, there's
also a debug button, which in the standard Cloud
9 environment is simply called run.
But by default, we have configured the debug button in the CS50 IDE
to bring up the debugger, automatically compile the source code,
connect the debugger to the underlying GDB--
and since that's running to perform the debugging-- and some other steps
as well.
Also the terminal window at the very-- or rather the terminal
tab at the console section at the window at the very bottom of the screen,
was renamed to simply be terminal.
Whereas in the standard Cloud 9 environment,
it actually shows the running application.
This way, it simplifies, again, the GUI, just a little bit.
>> We also added some buttons to the upper right of the IDE
to show some basic information about the underlying instance.
And also we have removed one of the options on the items
from the far right.
So all of this is implemented not by modifying necessarily the Cloud 9
source code, but instead through a couple
of plug-ins that we have implemented throughout the past year or so.
And Cloud 9 has hosted and has running in the CS50 workspace.
Or rather in the workspaces that are designated as CS50 IDEs.
>> So really that's the main difference between the Cloud 9 and the CS50
workspaces.
There's also, in the underlying instance, the primary change is
that we install some pre-configured items like GDB,
and some other basic things that we've created, like Update 50,
and a couple of other steps that make the whole experience a little bit more
unified.
But overall, this is just a modification of an existing system
that Cloud 9 has created for us.
What we're going to show a little bit later
is how to actually create a somewhat basic plug-in that
allows us to insert something into the menu system, and open up a dialogue.
And if we have enough time, we'll actually
see how we can populate that dialogue with some information
from the underlying instance.
In order for us to get to that point, let's first talk a little bit
about the architecture of Cloud 9.
>> So the way that we're used to interacting with Cloud 9
is entirely through the client.
We open up a web browser, we type in the CS50.io.
We eventually, after authentication, are presented with the IDE.
But realize that there's a couple of steps
that actually get us to this point.
The very first thing is that my client, the web browser,
upon request and after authentication, loads the GUI and the plug-ins
from some CDN, from some Content Delivery Network.
This can be frankly anywhere.
>> And this is actually separate from the underlying instance.
It's important to realize that this is actually just a bunch of static files.
It's a bunch of JavaScript that is downloaded from the CDN
on to my web browser, and all of the GUI that you
see here-- this is really key-- all of the GUI that you see here
is run on the client side.
Everything that you see in the Cloud 9 workspace
is actually run in the browser.
And anything that you send along to the underlying instance
is communicated with the instance along this second separate channel,
and is then saved onto that docker instance.
>> So the reason that I'm saying this is a docker instance
is that the underlying technology is not using virtual machines,
but instead is using a technology called docker,
which basically allows-- the closest analogy is a virtual machine.
But it is subtly different in that there are
plenty of opportunities to run a combination of different docker
instances on a single machine.
And they can be spun up in very rapid succession.
It is not quite the more strict differentiation
between different docker instances as there are in virtual machines,
but still there's a lot of differentiation and separation
between these different instances.
OK.
So the two steps here that are important to realize
is that when we go to CS50.io, we are downloading the GUI and the plug-ins,
which are written in JavaScript, on to the browser or in to the browser.
And this is maybe a couple megabytes worth of information.
At that point, once the GUI has loaded and the plug-ins have started,
then it begins to communicate with the docker instance, which doesn't
necessarily have to be the same server.
>> Now there is one exception to this.
We can actually have both the delivery mechanism for the GUI itself
and the docker instance in all of the same server, which
is actually something that we do for the offline version of Cloud 9.
This is not something that we've publicized very much this Fall,
but we do also have an offline version that
allows you to download a packaged version of all of these things
and allow you to run Cloud 9 in an offline environment.
>> Moving this off the cloud and onto your local machine has some impact.
In particular, you no longer have the ability
to share your workspace with others.
You no longer can open Cloud 9 from, and the CS50 IDE, from any computer
and see the same files that you were working with before.
But instead it works wholly on your own local machine
without needing access to the internet.
But still even in that model, even though we have one virtual machine
that is essentially running these things.
>> We have a separate server that is delivering essentially the Cloud 9
content, and then we have a docker instance
that is responsible for communicating with that IDE for the back end.
OK.
So the entire GUI is written entirely in JavaScript
using Node.js and the Cloud 9 SDK, which we'll get to in just a minute.
And all of the plug-ins are run on the client side.
So let's focus then a little bit by assuming
that this first step has actually completed successfully,
and look only at this docker instance.
>> So in this case, this is the standard way
of thinking about doing Cloud 9 plug-in development is that you are going
to be writing code that will be run in the user's browser,
and you have the opportunity of using some APIs to communicate
with the underlying docker instance, run some code there, and do anything
that you might want to do.
So this is going to be the context that we'll
be using for the rest of this talk.
And just keep this model in mind.
This will be very important in just a few minutes.
There's a couple of links I want to show you.
First of all, Cloud 9 has provided quite a lot of documentation
that's pretty good, that shows how to do basic plug-in development.
So if you go to this URL, cloud9-sdk.readme.io,
you'll see the documentation there.
And a lot of this information that you're about to see
can also be found there, plus more.
You can also find the open source version of Cloud 9 at this URL,
github.com/c9/core, which we use as part of the offline version of the IDE,
so that you will be able to have the same Cloud 9 experience,
but in an offline way.
All right.
So let's take a look at an actual Cloud 9 workspace here.
And again, I want to point out a couple of things.
This is, right here, a normal CS50 IDE.
And I have disabled a less comfortable mode under the View menu
so that I can see all of the menu options.
What I want to show you right now is a couple of things.
First off, if I go into the Preferences and then
click on Plug-in manager, and type in CS50 here,
we can see that there are, in fact, a couple of CS50 plug-ins that
were installed and our running to enable the modifications that we've
seen for the CS50 IDE.
>> Right now there's essentially two plug-ins that are running.
There's one called Simple, which is the simplified mode that
allows you to reduce the number of menu options along the top,
and have this so-called less comfortable mode.
And then there's also CS50 Stats, which is maybe only slightly misnamed.
Because this is what shows you the information
about the underlying workspace.
It injects these buttons along the top right.
It also injects a menu item into the window item, into the Window menu
right here, CS50 IDE Info.
And it's responsible for instantiating this dialogue that we've seen here,
with all the information that we're accustomed to seeing in a CS50 IDE
workspace.
OK.
So there's actually one more plug-in that we have developed as well.
And there's a couple of others that we also haven't publicly released.
But one of them is the overall plug-in that allows GDB to function.
So one of the aspects of Cloud 9 is that they have provided already
this GUI to implement a debugger.
And one of the plug-ins that we have created basically
hooks GDB with this GUI based version of a debugger.
And is responsible for mediating all of the requests that a user might have,
between stepping over or creating breakpoints or anything
along those lines, to translating that to commands that GDB can understand,
issuing those commands to GDB.
And as soon as GDB issues a response, then
we interpret that and update the GUI as necessary.
>> That's probably one of the more complicated
of the plug-ins, so instead what we're going to focus on today is in fact
the Stats plug-in, and we'll talk a little bit about Simple, as well.
So I mentioned before, and I really want to make very, very clear, that again,
all of this that we're seeing here is essentially
operating on the client side.
We do see a terminal window below, and that, of course,
if we type in commands into that, that will be then issued
to the underlying instance.
Similarly, if we open a new file and type some stuff into it and save it,
that file will be saved on the underlying instance.
>> But the editor itself, this terminal window itself,
is all implemented in JavaScript and is all a plug-in
that we can interact with, and therefore modify.
There's a lot of extensibility here.
I recommend taking a look at Cloud 9's GitHub account
to see the vast quantity of plug-ins that are available,
and just how well written a lot of the code is for extensibility.
So there's something that I want to mention
about this as well, which is that there are a lot of-- there's
a lot of functionality that's provided in the preferences
window, which we've seen just alluded to just a few minutes ago.
>> One of the things that we can do as IDE developers, or as Cloud 9 developers,
is to actually inject some preference panels of our own.
So the simple mode, or the less comfortable mode,
and also the IDE information, or the stats plug-in,
has each some preference panels that allow us to modify
the behavior of each of those.
You'll notice that if I go to the Preferences and go to User settings,
there's a CS50 tab, one that says IDE information.
And I can change the information refresh rate.
>> Effectively, what's happening in the stats plug-in
is that every n-seconds, where n-seconds is defined by this preference
panel, information is being fetched from the underlying instance, being sent
back to the client side, the plug-in is then
interpreting the information from the underlying instance
and updating the GUI as necessary.
We can see that right now this is set to 30 seconds, and that's the default.
But I can definitely change how quickly this happens just
by modifying this value.
>> Now one of the interesting things is that this preference panel, GUI,
is really just a GUI version of a lot of underlying settings
that are presented in JSON.
So if I go, for example, under the CS50 IDE menu, or the Cloud 9 menu,
depending on which version you're looking at,
and go to one of these settings-- in this case,
the project settings-- the project settings in this case
applies to all of the settings for this one workspace.
Whereas user settings apply to all of the workspaces
that you might have in your account.
>> So just as an aside here, to be extra clear,
there's a separation between the two because although by default we
have one workspace, which is the CS50 IDE, if you
were to click on your little avatar up here and go to dashboard,
you'll find that you can actually create additional workspaces as well.
You can see here that I do have an IDE 50 workspace, and also
for the purposes of this seminar, a workspace
called workspace, which is right here.
So anyway, I have different preferences.
Each one of these might have separate project preferences,
but the user preferences are shared amongst all my workspaces.
By the way, this is also very handy if you
want to create a workspace with different customization options.
And it's very handy to create a new workspace.
And choose one that might be pre-configured in some other way,
perhaps for PHP specifically, or Django specifically.
Or even just custom.
The CS50 workspace template is the one that we
use that automatically installs Update 50 and all of the settings
that we have there, including all the plug-ins that we have for the CS50 IDE.
OK.
But let's come back to this.
So again, there are projects settings, and those are the ones
that we are looking at here.
And notice that there are a bunch of settings,
many of them correspond to preference panels, but not all of them.
But we can see that here, in this-- oh no, did I put it in the user settings?
Perhaps I put it in the user settings.
And this, here we go.
In the user settings, we can see that we have a CS50 section,
and this is being written to by these Cloud 9 plug-ins
that we've implemented.
There's the simple one to correspond with that simple plug-in,
and the stats one which corresponds to that GUI based
version of the preference panel.
The refresh rate, in this case, is set to 20 seconds.
>> This is all, hopefully, the beginnings of some information
that we'll actually see in a little bit more detail, momentarily.
OK.
So let's say we want to actually get down to it.
We understand that all of the stuff that's happening on the client side
is done actually on the browser, which means that any plug-ins that I write
are going to be written with the browser in mind.
And if I actually want to do anything on the workspace,
I will perhaps have to initiate some sort of communication
between the browser and the workspace to be sure
that that is actually accomplished.
But let's say that now I want to get down to it
and actually create my first plug-in.
Well, the way that you would be able to do that is actually pretty easy.
It's given in the Cloud 9 SDK.
But it's to take your existing workspace name, which
is the URL that you have at the top of your bar, and add the following to it.
?
Sdk=1&debub=2.
Now what this is going to enable is that the sdk=1 will actually set the SDK
mode to true, which will enable a couple of additional things.
And debug=2 will allow error messages to be a little bit more verbose.
>> And so if you bring up the JavaScript console in your Chrome developer tab,
you will actually be able to see a lot more information
than you would otherwise.
So I recommend turning both of these on at the same time, because it really
is useful to have all this additional information.
It's important to note, however, that turning on debug to the value of 2
means that it's very verbose, and it actually
will somewhat noticeably slow down your IDE, especially when loading
or when do doing heavy tasks.
So just keep that in mind.
It's useful for development, but you may not want to have it on all the time.
>> So let's actually do that.
But in this case, I actually have already set up a workspace with this.
So let's see, sdk=1&debug=2.
With a couple of plug-ins hopefully already installed.
All right.
So now that I've turned on the SDK debug mode, notice that we are in debug mode,
so we can look at the dev tools to see any errors, which I will do offscreen.
We can see that there's a bunch of errors here.
Now it's actually pretty common for Cloud 9 to have a couple of errors,
and I wouldn't worry about them until you
see something that might be specific to the plug-in
that you happen to be creating.
>> So here, for example, we get a couple of 404s-- not found.
We see we're unable to load some information off the underlying instance
itself.
And there's a bunch of additional information, but most of this we're
actually going to ignore for now.
Because this is pretty common for a workspace
to have just a couple of errors.
OK.
I'm going to move this out of the way and come back here.
And now the easy way, the nice thing about having this developer
mode enabled is that it allows me to easily create a new plug-in.
>> So whereas before I actually did not have this new plug-in option available,
which we can see if I go back to my non developer mode over here,
there's no new plug-in.
By enabling the SDK mode, I have a new plug-in available
and I can easily create one.
In this case, there's a couple of different options, simple,
an empty plug-in, a full plug-in, installer, Cloud 9 bundle.
Let's just choose an empty plug-in for now
so that we can see a very simple version of one.
>> Now notice that along the left side that's there is now
something under Favorites, which is listing a couple of plug-ins
that are now available to me.
If I expand that, we will be able to see those.
Now I want you to notice something here, which
is that this is not actually contained within the workspace directory
on my underlying Ubuntu instance, but it is contained on the hard drive.
And where this is located, because this is very useful to know,
especially if you're going to be doing anything with Git,
where this is located is in a .c9 folder, /plugins.
So if I go there, we can see that now the list of plug-ins that are here
matches the list the plug-ins that I saw on the left side of my workspace.
>> Now by default, and this is kind of strange,
by default when I create a new plug-in in a workspace,
it creates this plug-in that by default has a name of an underscore.
Generally that might cause some problems.
So the very next step that I usually perform
is just to remove that particular plug-in
and leave only the simple plug-in that was there initially, plugin.simple.
And that's the only one that's there.
OK.
>> So what does this actually look like?
Well, this is, again, a JavaScript package
that includes some files, including a plugin.js file, where
the main content of my plug-in is located,
a package.json file, which actually specifies
some metadata about this plug-in, such as who the author is,
any additional information about it, a description of the plug-in, and so on.
And also by default, it creates a blank README file,
and an empty test file for you to create some additional documentation,
and a testing harness if you'd like to do that.
>> So let's just take a look first at, very quickly, at the package.json file.
It's easy enough to just go through it and fill in the areas of this
that you would want to fill in, like provide it a name, give a description,
iterate the version number every so often, provide
an author for every person that contributes,
add them to the contributors section, and the rest of this you
can pretty much just leave as it is for now.
>> There's one thing that's important to notice,
that under the plug-ins section, there is a key called plug-in.
And this is matched to the name of the JavaScript file called plugin.js.
So this is how Cloud 9 knows, when it reads this package.json file, which
of the js files to actually load.
If I create additional js files or want to rename that js file from plug-in,
I also have to change it in the package.json file.
>> Any questions from the audience?
No.
That one person who is following along with me so far.
>> OK.
So I actually have created a couple of plug-ins already,
I think, in this workspace.
So let's get rid of some of these, add sdk=1&debug=2, reload this workspace,
and let's see if we have those plug-ins now.
c9/plugins.
Here we go.
We can see now in my c9/plugins of this one, we have plugin.1 and plugin.2.
So we're going to just step up the-- just increase the amount of difficulty
in each of these plug-ins.
But here if I create a new plug-in, I can add it to my favorites.
And I'll just delete those from the underlying file system.
>> Let's remove that.
Now if I open my first plug-in, and open plugin.js,
we can see here the underlying simple version of a plug-in.
Let me actually go back to this other workspace
because you can at least, here see, what a blank plug-in actually looks like.
So underneath the hood, this looks very similar to technology called,
I think, it's RequireJS.
Notice that this doesn't really look, perhaps,
the same as some other JavaScript files that we might have seen.
But instead, there's a couple of just basic lines that it actually has.
>> All of this stuff is first interpreted by the IDE,
but it's not actually run until specified.
And I'll tell you about when that actually means in just a moment.
But notice that here there's a main.consumes line in line 2,
and this lists all of the plug-ins that this plug-in is dependent on.
So by default, we perhaps don't to have any dependencies on other plug-ins,
but when we actually need to depend on features supplied
by other plug-ins in the IDE, we need to list those plug-ins
in the main.consumes line.
And then we need to link them to the code
below, which I'll show you in just a moment about how we can do that.
>> Main.provides gives a name to this plug-in
that other plug-ins could use in their consumes line.
So in this case, my plug-in is just set there by default,
and we should change that to be applicable to our own plug-in,
as we'll see in just moment.
Now here in the function main, this function main
is actually run and interpreted, but it doesn't actually do very much.
It just gets everything set up, but it doesn't actually start the plug-in,
despite what the name sounds like.
That actually happens through a sequence of methods that
are contained throughout this plug-in.
>> So if I scroll down, we can see that we have a life cycle where
on some event called load, or some event called unload,
some events actually happen.
It's really in here where as the plug-in starts that those methods are called.
So let's be a little bit more concrete about this and look at an example.
So here for plugin.1, what we are basically going to do
is to create a menu item called CS50 Seminar Dialog 1--
because we have two of them-- and we're going
to inject it into the Window menu.
And when we click on it, we're going to open up a dialogue that
shows us some very basic information.
>> In this case, just a hello world.
So this is a very simple hello world dialogue
that we can implement as a plug-in in Cloud 9.
So let's see how this actually looks.
We'll step through it, just relatively quickly so we
can look at the next plug-in as well.
Notice that here we are consuming a variety of plug-ins.
We're consuming a dialogue plug-in, commands, menus, and UI.
Looks like I actually am consuming dialogue twice, so I can remove that.
>> And notice that the way that I'm connecting them,
this is sort of metadata that's telling the plug-in system what requirements
are actually necessary for this plug-in to load.
It's also important to note that the order that plug-ins are loaded
is not guaranteed.
But what is guaranteed is that if I specify some plug-in as a requirement,
that plug-in will be loaded before this one is loaded.
>> So that means that if your plug-in requires on some functionality provided
by something else in the IDE, you should be sure that your plug-in consumes
that plug-in so that the dependencies are certain to create your plug-in,
or instantiate your plug-in, only after that existing framework exists.
So here I have mentioned the metadata to the plug-in manager,
which ones I'm going to consume.
And in the main function here, I'm going to then connect
my code to those imported plug-ins.
>> So I'm just going to create some variables that
match the names of each of those so that I can very quickly reference them
throughout my code.
The reason I'm importing dialogue is because I want my plug-in
to act as a dialogue plug-in.
And the way that I'm able to do that is to, of course,
connect my code to the dialogue plug-in by importing it
and then by specifying by creating a new dialogue variable
and connecting it to that imported plug-in.
And then by defining my plug-in as a new dialogue.
>> So in the initialization, I want to define my plug-in.
And this variable called plug-in you'll see
is just used commonly throughout Cloud 9 plug-ins.
I'm going to instantiate a new dialogue with some attributes,
give it a specific name, say that it is possible for users to close it.
That it'll show a little x in the upper right hand corner,
or a little button in the lower right hand corner.
Whether or not I can select text from it, what the title of that dialogue
would be, and so on.
Now this just defines the dialogue, but it doesn't yet show it.
I actually have to define an action for it to be shown.
>> And again, I encourage you to take a look at the Cloud 9 SDK,
because there are a variety dialogues and they are really well documented.
You can see the different types that there are, and use them
in whatever plug-in you have in mind.
Now there's going to be a load section, and this load
function as you recall is used by the plug-in life cycle
to actually instantiate everything and get everything ready to go.
Now when this plug-in loads, I don't want it to immediately show a dialogue,
because this plug-in will load along with the rest of the IDE.
And when I load that IDE, I don't want the dialogue to show up automatically.
I only want it to show when I click on the menu option in my Window menu item
that I will add to it momentarily.
>> So there's two distinct steps here that have to happen.
I have to create a command, and the command
will be responsible for actually showing the dialogue on the Cloud 9 IDE.
And then I have to connect, I have to create a new window menu
item, that runs that command.
So that when I click on that Window menu item, that command is then run
and therefore my dialogue is then shown.
And so this is actually a pretty nice way of thinking about it.
>> Because first I can create a command whose name is CS50 Seminar Dialog 1,
that provides some general context for it.
And the important bit here is to notice the EXEC attribute, which
lists a function that will be called in my plug-in when this command is run.
So this command is sort of like a-- it's just an arbitrary Cloud 9
command that's available to any Cloud 9 plug-in.
But the actual function that it's going to call when this command is run
is the show dialogue function in my plugin.js file.
>> And we can actually see these plug-ins and the list
of commands that are available to us in Preferences, scroll all the way down--
let's see, actually do we see this now?
Plug-in manager, no, I think I-- it's definitely around here somewhere.
Well, there's a list of commands somewhere, but I must have,
I've forgotten where it is.
So OK, we'll move on.
All right.
So we have a list of commands that are available to us,
and those commands are these just arbitrary Cloud 9
commands that run specific code.
So just keep that in mind, that we're going to run the show dialog
function momentarily.
Now when I actually want to add a menu item, I can add that item by path,
and just specify exactly where I want it to be, Window/CS50 Seminar Dialog 1.
And at that point, I want to create a new item that
is going to run a command, CS50 Seminar Dialog 1.
Notice that again, this is the Cloud 9 command I've created just above.
>> Now I also want to create a little divider,
and so I can do the same in the subsequent line.
Now you might notice that as part of defining this menu item,
there's a number associated with it which
actually says where exactly I want that menu item to be
located in the list of menus.
But you might notice that I don't actually
see any numbers with these menus, by default.
So there's a little hidden thing that we can do, a little change to our URL.
So in addition to sdk=1&debug=2, I'll set menu to 1--
and hopefully it's menu, not menus.
Oh, it's menus=1.
Hold on.
>> And what we will see upon reloading that IDE is that I am still in debug mode,
but now there are numbers associated with all the menus.
And this tells you exactly what the number
is when you're trying to inject something anywhere in this menu system.
So in the Window menu, I can see that item 45 is collaborate,
and item 38 before it is installer.
So when I want to inject an item between those two,
I would just select a number between those two items.
So I selected 41 and inserted my Seminar Dialog 1 menu item at that location.
>> And that's why this number that appears here is number 41.
That is the location of that menu item in the Cloud 9 menu.
Now similarly, I wanted to create a divider
and add it so that there's a nice division between each of these menu
items.
So I added that at location 43.
So far, so good, I hope?
So now let's actually look at the specific code in show dialog
that's actually responsible for opening this dialog window.
>> Scrolling down, I see that I want to have this function show dialog,
and it's extremely simple.
I'm going to run the show method on the plug-in variable.
And remember that we defined this plug-in variable above as a dialogue.
So bar plug-in, in this case, is this object that we've defined internally.
And it's going to be a new dialogue contained within this plug-in.
And so we actually referenced this plug-in variable in many places
in a typical Cloud 9 development environment.
>> We'll see it here.
Notice that as we scroll down, there's additional life cycles.
So notice that these respond to events that are fired
by this object, this dialogue object.
By default, there's load and unload, which
are available to all Cloud 9 plug-ins.
But in the event of this dialogue, there's
another event that can fire as well called
Draw, which is fired when that is about to be drawn on screen,
at the first instance of the dialogue showing.
>> So when it is actually going to be shown,
there is going to provide to us some HTML.
And using just standard HTML practices can we
inject some very simple information, our hello world, in that dialogue.
So if we then scroll up, we can see then the procession of things
that it's occurring.
First we created a Cloud 9 command that when executed by Cloud 9,
is going to fire this function.
It's going to run this function called show dialogue, that I've
written in my code.
>> I created a menu item and attached to it that same command
that when that item is clicked in the menu, that command is then run
and that function is then run.
And inside of the function I'm just going to call the-- I'm
just going to execute the show method of this plug-in, which
is going to first call the draw method, and going
to fire this method down below, the draw event handler, in our life cycle.
And then it will actually show the dialogue.
>> There's also a hide method, so that if I need the opportunity to hide
my dialogue, I can do the same thing.
So that's pretty much it to get all of this to work.
Notice that's if we scroll down further there's a freeze public API.
This basically just says that I want these methods to effectively
be public, but not overwriteable outside of the context of this plug-in.
And below, at the very bottom, is perhaps the last thing
that we really need to pay much attention to here, which
is that we are going to register our plug-in with the name C9 Seminar 1,
and the object plug-in.
>> Which as you'll recall, is that plug-in that plug-in
object that we've been defining throughout the entirety of this source
code.
And C9 Seminar 1 is the string that we promised
to provide at the top of this definition.
OK.
So let's ramp it up a little bit and see if we can do something
a little bit more interesting.
Now this is only so great.
I mean, this is, it's actually pretty cool that in so few lines of code,
can we modify the existing IDE and add an existing menu item
or add a new menu item, add a command, and show a dialogue,
and just a bunch of functionality that exists.
It's really pretty great.
>> But it doesn't do a lot because it can't communicate with the underlying Ubuntu
instance.
So let's say that I actually want to find out
some information about the underlying Ubuntu instance,
like the stats plug-in does.
So let's take a look at a little bit more information
about how the stats plug-in works.
And in fact, it is very similar to this.
You might recall that there's a dialogue.
You might recall that there's some information that's
shown just along the menu bar along the top, which hopefully you are now
getting a flavor of how we implemented this,
just by inserting menu items at specific points and by instantiating a dialogue
and displaying that dialogue.
>> But we haven't yet shown you how we can connect it
to a command in the underlying Ubuntu instance.
So let's take a look then at plugin.2, which does precisely this thing.
We're going to open plugin.js, which is the code for this.
But under the Window menu, if I click on dialog 2,
we can see that it says this instead, hello CS50, which
doesn't look all that enticing, right?
Except, watch this.
Let me change something down here.
>> I'm going to go to my workspace and change seminar
to say something else, like Hello, Dan.
I'm going to close it and save it.
And now I'm going to re-run my dialogue menu item.
And notice that it has now changed what it says.
Hello, Dan.
I seem to have done something down here in the underlying Ubuntu instance
without having manipulated any code that again is running solely
on the client side.
So there's obviously some sort of communication that's happening here.
>> Now this is where a lot of the power of the Cloud 9 SDK comes in,
is that in this plugin.2, we can actually cause
a call to happen to the underlying instance,
and run some arbitrary command.
So in this case, I have actually created a very simple bash script
in my workspace called Seminar.
So let me open that up.
And we can see that it looks like this.
It's just a very simple bash script whose sole purpose in life
is going to be to echo out this text, Hello, Dan.
Or before it, it said Hello, CS50.
And that's all it's going to do.
>> Because this is now a command that I can run.
I can actually run it down here.
We can see that it actually is just a command that I run naturally.
I can ask my plug-in to execute this command on the underlying workspace
and parse the information that was returned from it,
and do something with it, and modify my plug-in and the behavior that I
have in my plug-in as a result. OK.
So let's see how this happens.
We saw this very simple Seminar batch script that I
[INAUDIBLE] that it can actually run.
>> And now let's see the modifications that are necessary to tie this together
with the dialog window, and run it.
So here we're going to do pretty much the same thing as we saw before.
But notice that among the things that I've consumed now--
in addition to the dialogue, which again I happen to have done twice,
which isn't necessary-- in addition to the dialogue and the commands
and the menus plug-ins, which were necessary in the first one for me
to define a new command and inject an item into the menus,
I also have this proc plug-in.
>> And this proc plug-in allows us to manipulate processes
on the underlying instance.
I've connected it after I've said that my plug-in requires it.
I've then connected it to code, as we saw in the main function.
Again, I'm going to initialize my dialogue.
And if we move down now, we can see how this is different.
The load function is the same, so it's just going to create a command
and create a menu item and connect that menu item to that command.
But if we now scroll down to show dialogue,
we can start to see where the differences arise.
>> We have function show dialogue, which is just going to show the dialogue.
But when I show this dialogue, and when the show method is called,
it fires the draw function.
And I'm going to then specify some content inside of that.
So I'm just going to create a div, provide to it
some basic text, Hello, world.
But notice that here I'm going to attach to it an ID.
And the very next line I am going to find that ID using JavaScript,
and store that object into another variable
that I'm just going to call content.
>> So now whenever I have content, all I need to do
is just modify this object's inner HTML.
And the dialogue HTML will then be modified as well.
So when this plug-in is then shown, which can happen
and this is a new event that is new to this plug-in,
but happens in every plug-in with the dialogue, when this is now shown,
I'm going to call a function called fetch info.
And this function is the meat of it here.
I'm going to use that proc plug-in that we described before, which by the way,
is really just a Node.js library that Cloud 9 is using here.
>> So you can actually look up about how this
works if you go into the Node.js documentation
and look up the exec file method for processes there.
I'm going to run this specific command, home Ubuntu workspace
seminar, which is that same one, that same command that I created before.
Provide to it a current working directory, just
to be ultra safe in terms of the context of where this is running.
And once that has been completed and returned,
I'm going to run this function called parse output.
OK.
So this then is going to execute a command on the local, or rather
the remote Ubuntu instance.
When I get back some information, I'm then
going to call a separate function called parse output, that
has this signature here, err, stdout, stderr,
and perform some computation on this.
So if I received an error of some kind at all,
I will actually check to see if error actually has some data.
And if so, then I will modify the inner HTML of that content object, which
is you recall, we connected to the dialogue
at the instantiattion of that dialogue, or at the first drawing
of that dialogue.
I'm going to just say that some error has happened.
>> Now this is overly simplistic.
The error value will typically contain something that was useful,
perhaps an error number and an error from the script itself.
Or if the script wrote something to standard error,
that data would be populated in that parameter as well.
And I could be a little bit more careful about what error
I'm actually showing people.
But this simplistic example is good enough for now
to at least see how all this functions.
Otherwise if there is no error, then I'm just
going to provide the raw output of that function
into the inner HTML of this content element, and then update my plug-in
and show it here.
And that's pretty much all that's necessary to get this to run.
And so let's think about then how this operates, overall.
When I first loaded this plug-in, just like the first plug-in,
I'm going to add a command that's available to anything, to any plug-in
in Cloud 9, called CS50 Seminar Dialog 2, whose responsibility
will be to run this show dialogue, which is you recall,
will actually show the dialogue that I requested below.
>> Then I will add that command into a window menu
so that I have access to that.
And when that dialogue is requested to be shown,
I will fetch some info from the underlying Ubuntu instance
by using the exec file command.
Excuse me.
Once that is returned and I receive some information, then on the client side
again, I will be able to parse the output of that command
and update the inner HTML of the elements
that we had connected to earlier.
>> And with that, we then have this fully functional plug-in
that allows me to fetch information from this arbitrary script
that we have created on our underlying instance.
But again, it's really important and really key
to separate out where each of these things is happening,
that we have all of this client side code that is being run in the browser,
and doesn't yet have access to the underlying file system
until we actually run some of these commands that are provided by the Cloud
9 SDK like proc, and some other ones that allow us to read some data,
or read some files if we need to do anything of the kind.
>> Now this is a somewhat simple example.
There are other things that we would perhaps want to do as well.
For example, perhaps we actually want to save some information
to the preferences.
Or perhaps we want to add a new preference pane
or something along those lines.
Then that is something that you can take a look at the Cloud 9 SDK
to get more information on.
But this really is enough to get started.
So just to be a little bit more concrete about the way that some of these things
function, I did want to show just one more example, which is just some
of the code from the Stats 50 plug-in.
>> And this code is basically based on these examples
that you've actually seen, but has some more safeguards
and has some more complexity in order to achieve some of the additional things
that we want to achieve.
For example, if I want to be able to save some settings,
then I need to have access to the Settings plug-in,
and every time I want to read some settings,
I can, for example, attach to various events in the Settings plug-in.
So every time the settings is red, for example, in my own plug-in,
if I am going to read some settings from the saved storage
preferences of the user, then I can set defaults for a specific location,
for specific preference, in that JSON style options file that we've
saved earlier, that we've seen earlier.
>> And if I'm going to write some settings to it,
then I can-- or as soon as we detect that the settings have been written
to-- because for example, those settings have been modified by the user
either directly in their settings file, or the preference pane
has changed the settings file directly, then
I can actually update some of the code or update the behavior of my plug-in
based on those changes that have occurred on the settings file as well.
But really, the rest of it is pretty much this that you've already seen.
>> We add some, perhaps something that's new.
So we add a preference file, or rather a preference pane,
to the preferences window, which again you can find out more information
in the Cloud 9 SDK read me.
But everything else is pretty much, pretty
similar to what we saw before, just with some additional error checking
and some additional protections and some additional functionality
to feature all of the various things that we've seen.
If you haven't actually seen it yet, you might
be wondering what the underlying command is in the Stats 50 window.
It's actually function.
Well, it is-- oh, dammit.
One second.
Hold on.
OK.
Let me move this up so we can actually see what happens when I type Stats 50.
Notice that in this case, the command that I'm out putting
is really again, just the script that's going to output a bunch of information
in JSON format.
And the reason that we do it this way is that because we
are operating, because we've written the client side code in JavaScript,
we can very easily just parse JavaScript objects,
and really be able to manipulate those objects just like we would
any other JavaScript object.
>> And so this is a very convenient way of providing some information
from the underlying Ubuntu instance, such as the version number, the host
name, whether a server is listening, what the server is, so on and so forth,
and parse that very easily and very quickly
in the plug-in that is then going to show us all of that information.
So again, the key insight here to keep in mind
is this separation between the underlying instance,
and also where the code is being loaded.
That when I have a client, it's first loading.
Just this static JavaScript file from somewhere, perhaps CDN of some kind.
We don't really have to care too much about this first step.
>> We just know that it's running on the client side
and that's when I'm going to communicate with this docker instance.
That we're going to do so through the Cloud 9 SDK.
We don't actually have the ability, using JavaScript or Node.js,
to write directly to it.
We just use those existing SDK APIs to do that action for us.
And again, one of the really important things
is that when I have created this, when I've created this plug-in,
and as I am working on it, one of the important things to do
is to bring up the JavaScript console.
>> Because since this is all operating on the JavaScript,
on the client side, if I use a console.log method-- so just
to show an example here.
Let's try modifying this simple plug-in to also log some data.
And we'll add standard out.
So let's change that to be right here.
Now when I execute console.log, keep in mind
that since this is running on the client side,
I will see this in my own JavaScript console, which
is how I will then be able to try to troubleshoot and debug
some of the problems that I have.
>> Notice that because I am in SDK mode, I'm
then able to manipulate plug-ins on this instance.
So even though it is saved on this instance,
this is the one example of that previous rule being violated, where the Cloud 9
SDK will actually pull plug-ins from the underlying instance and then run them.
So that in this way, I'm then able to manipulate and construct
some plug-ins here.
But in order for me to see updates to this plug-in,
because it's on the client side, I need to save that plug-in
and then reload the entire workspace, so that the new plug-in is then
downloaded onto my client, onto my web browser, and then is being used.
>> So now when I click on this Seminar Dialog 2, we still see this.
But now hopefully in my JavaScript dialogue, should we see my-- wait,
hold on.
I might have to open the dialogue fresh.
We should see my received data here.
Received data, Hello, Dan, which is you recall,
was a console.log operation that we requested in the plug-in.
So this description then that I mentioned before,
this dialogue, or this diagram rather, that I
mentioned before is what happens.
But there's an important exception only for development,
which is that the plug-in itself might actually
be stored on the docker instance temporarily, since presumably Cloud 9
doesn't want us to be able to manipulate objects on their CDN.
>> And the Cloud 9 clients, upon loading, will find those plug-ins in that .
C9 plug-in folder, and load those.
But those are still happening only on the client side.
So then this is just the an exception that occurs only
for development of Cloud 9 plug-ins.
But the rest of the time-- and the reason
that we want to really hammer this home--
is because most of the time this is going to be running from the CDN,
and just going to be run purely from the client side.
>> Normally there'll be no interaction with the plug-in,
or there doesn't even have to be any reason that the plug-ins themselves
are actually stored on the underlying docker instance,
except in the case of this SDK development.
So again, this changes just slightly because of the fact
that we are running SDK mode.
It's going to then load those plug-ins from development,
and the sole purpose here is so that I can use Cloud 9 to do development
for Cloud 9 plugins.
But even so, this doesn't change again how any of this works.
It's still being run entirely on the client side.
>> It just means that whenever I want to create Cloud 9 plug-ins in Cloud 9,
I create those plug-ins, put them in my .
C9/plugins folder in my home directory, make sure I'm in SDK mode,
reload the page every time I actually make changes to that plug-in,
and see what happens in my JavaScript console.
Just manage my JavaScript console to make sure
that any troubleshooting information that I place to console,
or anything else that might occur because of errors that I've written
in my code or the like, appears there.
>> And with that, it's really enough to get started, I think,
with the Cloud 9 plug-ins.
Especially taking a look at the SDK documentation that Cloud 9 provides.
It's a great way to get started, and very easily
create some plug-ins for Cloud 9.
And with that, I want to thank you all very much for joining me.
I hope you'll have fun writing plug-ins for Cloud 9.
Perhaps improving plug-ins that we have now, or maybe even
adding some functionality of your own.
We look forward to seeing what it is you create.
Until then, bye.