- Read
package.jsonto discover what packages VS Code depends on - Observe the root directory structure, and more specifically the
extensionsandsrcdirectories which contain the bulk of the source code- A lot of the code in the
extensionsdirectory appears to be dedicated to programming language support- The remainder of the extensions seem to provide functionality for things that aren't "core" to vscode, such as
configuration-editing,emmet,extension-editingand some color themes
- The remainder of the extensions seem to provide functionality for things that aren't "core" to vscode, such as
- A lot of the code in the
- If you look at the
.vscode/launch.json, you will find all the tasks that can be executed from within VS Code debugger. One task of interest isLaunch VS Codewhich will take care of launching VS Code for us so that we may debug it- In this file you will also discover that it runs
${workspaceFolder}/scripts/code.bat, which is the next script we'll take a look at
- In this file you will also discover that it runs
- In
./scripts/code.bat, we discover that this script will runyarnif thenode_modulesdirectory is missing, download the electron binaries if necessary and callgulp compileif theoutdirectory is missing, then finally start the electron/vs code binary in the.build/electrondirectory - We then start to look for common entry points file such as
index.ts/jsormain.ts/js, for which we find a match in thesrcdirectory - We take a quick look around, trying to find where electron is likely to be instantiated... There's a lot of code in
src/main.jsthat would be better elsewhere to make it easier to navigate this file - Close to the bottom of the file we discover the code we are interested in as a call to
app.once('ready', ...)- Once the app is ready, we want to call
src/bootstrap-amdand passvs/code/electron-main/mainas our entry point (per the signature of the exported function in./src/bootstrap-amd)- Here we can go to two places, either
src/bootstrap-amdorsrc/vs/code/electron-main/main- We take a quick peek at both files and we can quickly tell that
src/bootstrap-amdis used mainly to loadsrc/vs/code/electron-main/mainwhich is the file we're going to be interested in
- We take a quick peek at both files and we can quickly tell that
- Here we can go to two places, either
- Once the app is ready, we want to call
- Once again, we quickly look around
src/vs/code/electron-main/mainand find that the main logic is at the bottom of the file - First the command line arguments are parsed
- Then services are bootstrapped/instantiated
- Finally the
CodeApplicationis started up - This leads us to look into
src/vs/code/electron-main/app.ts - As the file is quite large, we start by skimming through it, looking at the available methods on the
CodeApplicationclass as well as its properties - Looking at the constructor, we can see that a lot of objects are given to it. We also observe the use of the @... syntax (those are decorators)
- In this case (and for most constructors), this is how VS Code does service (dependencies) injection
-
One will also notice that most, if not all parameters have a visibility assigned to it. What this does is that it will create an associated property in the class as well as assigning the parameter value to this property in the constructor. Thus, instead of writing
class AnotherClass { private someClass: SomeClass; constructor(someClass: SomeClass) { this.someClass = someClass; } }you simply write
class AnotherClass { constructor(private someClass: SomeClass) { } } - Upon its creation, the
CodeApplicationclass will register various event listeners on the electron app object - If we remember, in
src/vs/code/electron-main/main, after theCodeApplicationobject is instantiated, we callstartup()on it. So, we want to take a look at what that method does - Without knowing too much about the VS Code source, it appears that we are instantiating an IPC server (inter-process communication) and then the shared process
- After that is done, we initialize some more services in
CodeApplication::initServices, such as the update service (which I guess takes care of checking for VS Code updates) and the telemetry (data about VS Code feature usage) - We finally get to the point where we're about to open a window in
CodeApplication::openFirstWindow!- This leads us to go read the class
WindowsManagerinsrc/vs/code/electron-main/windows.ts. Once again, this file is pretty large, so we want to skim it to see what it contains (functions, classes, properties, methods)
- This leads us to go read the class
- There are a few large classes in
src/vs/code/electron-main/windows.tsthat I'd want to extract to make the file smaller and simpler (less cognitive load). However, the issue is that those classes are not declared as exported, and thus are only available in the local file. It would be possible to move these classes to other files and import them, but by doing so it would also "communicate" that others can use it, which is what having the classes as not exported prevents, at the cost of making single files larger and harder to comprehend - We know that the constructor is first called, then from
CodeApplication::openFirstWindow, we see thatWindowsManager::readyandWindowsManager::openare both called.- In the
constructorwe instantiate theDialogsclass (takes care of open/save dialog windows) and theWorkspacesManagerclass (takes care of workspace management, such as open/save) - In
readyevent listeners are registered - In
openthere is a lot of logic associated with the window finally opening
- In the
- If you start VS Code using the debug feature, you will not be able to open the Chrome DevTools (at this moment, 2018-05-26) because only 1 process is allowed to attach to the Chrome DevTools instance, and that process is the VS Code editor that started the debugged VS Code instance
Today I want to find out how VS Code restores a windows sessions when you start it. Apparently, if you run it as code ., it will not restore the same set of windows than if you called it simply with code.
- In
src/vs/code/electron-main/launch.ts, theLaunchService::startOpenWindowappears to implement logic based on how many arguments were given. In all cases, we end up doing a call to theIWindowsMainService::openmethod.- Note that in both cases, the path we're opening is within the
argsvariable, which is passed to thecliproperty of theIOpenConfigurationobject.
- Note that in both cases, the path we're opening is within the
- The implementation of
IWindowsMainServicewe are interested in lives insrc/vs/code/electron-main/windows.ts. - In the
WindowsManager::openmethod, we rapidly discover that the windows that will be opened will be retrieved inWindowsManager::getPathsToOpen. In there, we can observe that the windows that will be opened depend on whether something was passed from the API, we forced an empty window, we're extracting paths from the cli or we should restore from the previous session.- If we arrive at this last case, we can see that the logic is to call
WindowsManager::doGetWindowsFromLastSession, which is pretty self-explanatory, and will retrieve the previous set of windows from the last session. This is what happens when you startcodeusingcode - In the case where we pass a path, this path is in
openConfig.cli._. In this case, the windows that were previously opened, and part ofthis.windowsState.openedWindows(wherethisis aWindowsManagerobject)- Here we wonder how the
windowsState.openedWindowsstate gets restored on VS Code start. To figure that out, we start at theWindowsManager.constructormethod. There we findthis.windowsState = this.stateService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedWindows: [] };, which states to use get aIWindowStateobject from thestateServiceif one exists or to create an object with no opened windows. If we assume that this windows state is the same regardless of how we start VS Code, then it is not there that the difference in opened windows will occur.
- Here we wonder how the
- If we arrive at this last case, we can see that the logic is to call
- If you are building a product, it is your responsibility to define how much time you're willing to bet/spend on a feature, this is called the appetite
- Unlike a task duration estimate, an appetite is a fixed amount of time that is defined in order to define the boundaries of what the feature to develop should be vs an estimate which means that the task should be completed and we expect it to take X amount of time (variable)
- The appetite helps set the amount of time before a task is deemed without a proper scope
- Record initial task duration estimate
- Record task duration after its completion
- Make a high-level, gut feeling estimate of the duration of the whole project
- Lowest (fastest, optimistic), average (most likely), and highest (slowest, pessimistic) time duration estimates
- A final value is computed by taking (lowest + 4*average + highest)/6
- List the reasons why you think things may go right/wrong
- Heuristic estimation
- At a high level, you should be able to tell how much each portion of a project should have over the complete project
- Use historical data
- If you have underestimated in the past, you will keep underestimating if you do not adjust
- Break down any item that is larger than 10% of the project into smaller items
- Duration will depend on the skills of the person completing the task. An expert will generally take less time than a novice to accomplish the same task
- Being interrupted while working (difficulty tracking beginning and ending)
- Working on unrelated things at the same time (diluted productivity)
- Working on multiple tasks at the same time (task overlapping)
- Determining what is and what isn't part of a task (task isolation)
- Changes in task definition
- Estimating tasks for which we have no experience
- Easy to start/stop tracking a task (less than 15 seconds, otherwise it feels like an interruption)
The workstack is a very simple idea I had while working. It is based on the concept of a stack as the name clearly implies. As you work, you, like a computer, process things one at a time and as new things need to be done, you either throw them in a todo list (a queue), or you start doing them right away (you stack them).
The workstack is a way to record notes about what you work on. As you work on something, you can either work on them to completion, or be interrupted by the necessity of working on another task. In the first case, tasks are simply written one after the other with their begin and end time. In the second case, items are also indented, such that it is possible to observe when a task forced you to "switch context".
An example of this note taking format is as follow.
2018-05-18
Task 1 10:00-10:30
Task 2 10:35-10:50
Task 3 11:00-...
Task 4 11:05-11:15
Task 6 11:17-...
Task 7 11:20-...
Task 5 (not begun)
In this case, the person started working on tasks 1 and 2, then began working on task 3. As he began his work, he noticed that something else was necessary, which spawned task 4. While he was working on task 4, he observed something that could be done, but didn't have to be done right away, which spawned task 5. As he completed task 4, he returned to task 3, but noticed that something else also had to be done, which effectively spawned task 6. During task 6, something else also interrupted him, which forced him to work on task 7. In this case, it could have been a coworker asking you for help on something. Task 5 could be a coworker asking for help as soon as you're available, but not wanting to interrupt you.
Conceptually, you would want to always complete a stack of operations before moving to a new task. However, it is highly common while programming that a programmer will start going down such stack while working on code and then will not end up climbing back the stack, effectively not completing all he started working on.
This format thus allows a programmer (or anyone working on tasks that can spawn other tasks) to better track what they were doing and what they did and did not complete.
- https://en.wikipedia.org/wiki/Stack_(abstract_data_type)
- https://en.wikipedia.org/wiki/Context_switch
- Parnin, C., & Rugaber, S. (2011). "Resumption strategies for interrupted programming tasks." Software Quality Journal, 19(1), 5-34. https://doi.org/10.1007/s11219-010-9104-9
I'm a programmer. I like to write code and whenever I can, I like to reuse bits of code I've written in the past instead of writing it from scratch.
Lately I've been spending a lot of my time thinking. However, unlike programming, I feel that my writing is repetitive and that I sometimes don't make any progress with my ideas. Oh how I wished writing was more like programming.
There's many ways one can write. You have the top-down approach, where you first lay out a plan of the topics you want to write about. You also have the bottom-up approach, where you write things as they come to mind, sometimes rearranging the content. Generally, when I write, it is to dump what is on my mind about a certain topic, so it is very much like the bottom-up approach. However, the thing I dislike about it is that you can end up with subtle similar sections of content, but not know about it. You actually need to go through all of your writing to discover these bits of duplicate writing. That's a lot similar to writing code and sometimes finding out you have functions/methods that do almost the same thing...
In programming, when you realize that two bits of code are similar, you refactor your code to get one out in order to reduce duplication. When you write, it's not as simple. First off, in code you will generally have encapsulation, which provides you with the means of limiting the scope of the duplicate logic. With text, your context (encapsulation) often comes from the text prior to it.
One option to deal with this problem might be to extract ideas and then rewrite them in order to make them independent of their previous context. This will help making it possible to read any part of an article without having read what was said previously. However, this will also have the adverse effect of making things much more difficult to follow as there will appear to be no connection between the different sections.
Maybe a better approach to the problem is to think of how one makes code more reusable. One of the biggest issues with code is often that multiple levels of abstractions are used within the same method, which makes reasoning about the method more difficult.
- Should one limit the size of an idea, in the same fashion one tries to limit the number of lines or statements in a function?
- How can one track progress? Is there progress without explicitly specified goals?
- Dump everything in my brain in text documents
- Schedule things that need to be scheduled/reminded
- Decompose tasks and define time/effort estimate
- Write everything down in markdown documents
- Write thoughts in Google Keep while on the move, then merge them in existing documents
- Attempt to manually aggregate content
- No time tracking/evolution of articles
- Manual aggregation is difficult and time consuming
- Most of what is written is never reviewed and potentially forgotten
- Content is not consumed/reused
- Most of the time, the point is to make explicit what is in the brain, so that it can also be referred to later on in a reliable format
- Notes I leave myself aren't tagged
- Get feedback on what I wrote that is consumable by others