Adding content/directory steps
This commit is contained in:
Родитель
e5b30ff5fa
Коммит
557c74407a
|
@ -18,17 +18,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"file": "src/status.ts",
|
||||
"file": "src/player/status.ts",
|
||||
"line": 35,
|
||||
"description": "### Registration\nIf the currently opened workspace has any code tours, then we register the `Start Code Tour` status bar item."
|
||||
},
|
||||
{
|
||||
"file": "src/status.ts",
|
||||
"file": "src/player/status.ts",
|
||||
"line": 35,
|
||||
"description": "### Waiting for user\nUsing MobX, we wait for the user to start and/or navigate a code tour."
|
||||
},
|
||||
{
|
||||
"file": "src/status.ts",
|
||||
"file": "src/player/status.ts",
|
||||
"line": 47,
|
||||
"description": "### Updating\nWhen a tour is activate and/or navigated, we update the status bar item to indicate the current step and title."
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"eg2.tslint"
|
||||
]
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
"files.exclude": {
|
||||
"out": false
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true
|
||||
},
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": { "source.organizeImports": true }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
## v0.0.24 (05/03/2020)
|
||||
## v0.0.25 (05/03/2020)
|
||||
|
||||
- Introduced the `Add CodeTour Step` context menu to directories in the `Explorer` tree, which allows you to add steps that point at directories, in addition to files.
|
||||
- Added the `CodeTour: Add Tour Step` command, which allows you to create a content-only step, that isn't associated with a file or directory.
|
||||
- Fixed a bug where new steps weren't properly focused in the `CodeTour` tree when recording a new tour.
|
||||
|
||||
## v0.0.24 (05/02/2020)
|
||||
|
||||
- Explicitly marking the `CodeTour` extension as a "workspace extension", since it needs access to the workspace files and Git extension.
|
||||
- Temporarily removed the `View Notebook` command, since this isn't officially supported in VS Code.
|
||||
|
|
95
README.md
95
README.md
|
@ -1,10 +1,20 @@
|
|||
# CodeTour 🗺️
|
||||
|
||||
CodeTour is a Visual Studio Code extension, which allows you to record and playback guided walkthroughs of your codebases. It's like a virtual brownbag, or table of contents, that can make it easier to onboard (or reboard!) to a new project/feature area, visualize bug reports, or understand the context of a code review/PR change. A "code tour" is simply a series of interactive steps, each of which are associated with a specific file/line, and include a description of the respective code. This allows developers to clone a repo, and then immediately start **learning it**, without needing to refer to a `CONTRIBUTING.md` file and/or rely on help from others. Tours can either be checked into a repo, to enable sharing with other contributors, or [exported](#exporting-tours) to a "tour file", which allows anyone to replay the same tour, without having to clone any code to do it!
|
||||
CodeTour is a Visual Studio Code extension, which allows you to record and playback guided walkthroughs of your codebases. It's like a virtual brownbag, or table of contents, that can make it easier to onboard (or re-board!) to a new project/feature area, visualize bug reports, or understand the context of a code review/PR change. A "code tour" is simply a series of interactive steps, each of which are associated with a specific directory, or file/line, and include a description of the respective code. This allows developers to clone a repo, and then immediately start **learning it**, without needing to refer to a `CONTRIBUTING.md` file and/or rely on help from others. Tours can either be checked into a repo, to enable sharing with other contributors, or [exported](#exporting-tours) to a "tour file", which allows anyone to replay the same tour, without having to clone any code to do it!
|
||||
|
||||
<img width="800px" src="https://user-images.githubusercontent.com/116461/76165260-c6c00500-6112-11ea-9cda-0a6cb9b72e8f.gif" />
|
||||
|
||||
## Starting a tour
|
||||
## Getting Started
|
||||
|
||||
In order to get started, install the [CodeTour extension](https://aka.ms/codetour), and then following one of the following guides, depending on whether you want to record or playback a tour:
|
||||
|
||||
- [Starting Tours](#starting-tours)
|
||||
- [Navigating Tours](#navigating-tours)
|
||||
- [Recording Tours](#recording-tours)
|
||||
- [Exporting Tours](#exporting-tours)
|
||||
- [Reference](#reference)
|
||||
|
||||
## Starting Tours
|
||||
|
||||
In order to start a tour, simply open up a codebase that has one or more tours. If this is the first time you've ever opened this codebase, you'll be presented with a toast notification asking if you'd like to take a tour of it.
|
||||
|
||||
|
@ -23,7 +33,7 @@ Otherwise, you can manually start a tour via any of the following methods:
|
|||
|
||||
If the current workspace only has a single code tour, then any of the above actions will automatically start that tour. Otherwise, you'll be presented with a list of tours to select from.
|
||||
|
||||
### Opening a tour
|
||||
### Opening Tours
|
||||
|
||||
In addition to taking tours that are part of the currently open workspace, you can also open a tour file that someone else sent you and/or you created yourself. Simply run the `CodeTour: Open Tour File...` command and/or click the folder icon in the title bar of the `CodeTour` tree view.
|
||||
|
||||
|
@ -31,7 +41,7 @@ In addition to taking tours that are part of the currently open workspace, you c
|
|||
|
||||
Additionally, if someone has [exported](#exporting-tours) a tour, and uploaded it to a publically accessible location, they can send you the URL, and you can open it by running the `CodeTour: Open Tour URL...` command.
|
||||
|
||||
### Tour markers
|
||||
### Tour Markers
|
||||
|
||||
As you explore a codebase, you might encounter a "tour marker", which displays the CodeTour icon in the file gutter. This indicates that a line of code participates in a tour for the open workspace, which makes it easier to discover tours that might be relevant to what you're currently working on. When you see a marker, simply hover over the line and click the `Start Tour` link in the hover tooltip. This will start the tour that's associated with this line of code, at the specific step.
|
||||
|
||||
|
@ -52,7 +62,7 @@ In addition to taking a tour through a series of files, you can also view a tour
|
|||
|
||||
-->
|
||||
|
||||
## Navigating a tour
|
||||
## Navigating Tours
|
||||
|
||||
Once you've started a tour, the comment UI will guide you, and includes navigation actions that allow you to perform the following:
|
||||
|
||||
|
@ -79,15 +89,15 @@ At any time, you can end the current code tour by means of one of the following
|
|||
- Click the stop button next to the active tour in the `CodeTour` tree
|
||||
- Running the `CodeTour: End Tour` command in the command palette
|
||||
|
||||
## Authoring tours
|
||||
## Recording Tours
|
||||
|
||||
If you'd like to create a code tour for your codebase, you can simply click the `+` button in the `CodeTour` tree view (if it's visible) and/or run the `CodeTour: Record Tour` command. This will start the "tour recorder", which allows you to begin opening files, clicking the "comment bar" for the line you want to annotate, and then adding the respective description (including markdown!). Add as many steps as you want, and then when done, simply click the stop tour action (the red square button).
|
||||
If you'd like to record a code tour for your codebase, you can simply click the `+` button in the `CodeTour` tree view (if it's visible) and/or run the `CodeTour: Record Tour` command. This will start the tour recorder, which allows you to begin opening files, clicking the "comment bar" for the line you want to annotate, and then adding the respective description (including markdown!). Add as many steps as you want, and then when done, simply click the stop tour action (the red square button). You can also create [directory steps](#directory-steps), or [content steps](#content-steps) to add an introductory or intermediate explainations to a tour.
|
||||
|
||||
While you're recording, the `CodeTour` [tree view](#tree-view) will display the currently recorded tour, and it's current set of steps. You can tell which tour is being recorded because it will have a microphone icon to the left of its name.
|
||||
|
||||
<img width="800px" src="https://user-images.githubusercontent.com/116461/76165260-c6c00500-6112-11ea-9cda-0a6cb9b72e8f.gif" />
|
||||
|
||||
If you need to edit or delete a step while recording, click the `...` menu next to the step's description, and select the appropriate action.
|
||||
If you need to edit or delete a step while recording, click the `...` menu next to the step's description, and select the appropriate action. Alternatively, you can edit/delete steps from the CodeTour [tree view](#tree-view).
|
||||
|
||||
<img width="500px" src="https://user-images.githubusercontent.com/116461/76168548-1f50cb80-612e-11ea-9aca-8598b9e1c730.png" />
|
||||
|
||||
|
@ -95,7 +105,7 @@ If you need to edit or delete a step while recording, click the `...` menu next
|
|||
|
||||
If you record a tour within a "multi-root workspace", you'll be asked to select the folder that you'd like to save the tour to. This is neccessary because tours are written as [files](#tour-files) to your workspace, and so we need to disamgiuate which folder the tour should be persisted to. That said, when you're recording a tour, you can add steps that span any of the folders within the workspace, which allows you to create tours for a specific folder and/or that demonstrate concepts across multiple folders within the workspace.
|
||||
|
||||
### Step titles
|
||||
### Step Titles
|
||||
|
||||
By default, the `CodeTour` tree displays each tour step using the following display name format: `#<stepNumber> - <filePath>`. However, if you'd like to give the step a more friendly name, you can edit the step's description and add a markdown heading to the top of it, using whichever heading level you prefer (e.g. `#`, `##`, etc.). For example, if you add a step whose description starts with `### Activation`, the step and tree view would look like the following:
|
||||
|
||||
|
@ -105,7 +115,7 @@ While you can any heading level for the step title, we'd recommend using `###` o
|
|||
|
||||
> If you'd like to add step titles to a tour, but don't want to add markdown headings to their descriptions, you can manually set the `title` property for each step in the tour's underlying `JSON` file (which can be found in the `.vscode/tours` directory). See [tour schema](#tour-schema) for more detials.
|
||||
|
||||
### Recording text selection
|
||||
### Text Selection
|
||||
|
||||
By default, each step is associated with the line of code you created the comment on (i.e. the line you clicked the `+` on the comment bar for). However, if you want to call out a specific span of code as part of the step, simply highlight the code before you add the step (clicking the `Add Tour to Step` button), and the selection will be captured as part of the step.
|
||||
|
||||
|
@ -142,7 +152,7 @@ In order to add more interactivity to a tour, you can embed shell commands into
|
|||
|
||||
<img width="600px" src="https://user-images.githubusercontent.com/116461/78858896-91912600-79e2-11ea-8002-196c12273ebc.gif" />
|
||||
|
||||
### Versioning tours
|
||||
### Versioning Tours
|
||||
|
||||
When you record a tour, you'll be asked which git "ref" to associate it with. This allows you to define how resilient you want the tour to be, as changes are made to the respective codebase.
|
||||
|
||||
|
@ -157,27 +167,26 @@ You can choose to associate with the tour with the following ref types:
|
|||
|
||||
At any time, you can edit the tour's ref by right-clicking it in the `CodeTour` tree and selecting `Change Git Ref`. This let's you "rebase" a tour to a tag/commit as you change/update your code and/or codebase.
|
||||
|
||||
### Content Steps
|
||||
|
||||
Code tours are primarily meant to describe code, however, when you're recording a tour, it may help to provide some intro explaination about the tour itself. To do this, you can create a "content step", which is a tour step that includes a title and markdown content, but isn't associated with a directory or file. To create a content step, perform one of the following actions:
|
||||
|
||||
1. Click the `Add tour step...` node in the `CodeTour` tree, underneath the node that represents your currently recording tour. _Note: This option is only available when the tour doesn't have any steps._
|
||||
2. Run the `CodeTour: Add Tour Step` command.
|
||||
|
||||
When you create a content step, you'll be asked for a title of the step (e.g. `Introduction`), and then a "virtual" file will be created with an associated comment that you can edit. This allows the viewer to navigate between steps in a consistent fashion, regardless if the step is associated with a file or not.
|
||||
|
||||
### Directory Steps
|
||||
|
||||
If you want to call out a directory as part of a tour, then while recording, you can right-click a directory in the `Explorer` tree and select `Add CodeTour Step`. This will create a new step that allows you to add a description for the selected directory. When the tour is played back, the directory will be focused in the `Explorer` tree, and the viewer will be presented with the description in a "virtual" CodeTour document.
|
||||
|
||||
### Tour Files
|
||||
|
||||
Behind the scenes, the tour will be written as a JSON file to the `.tours` directory of the current workspace. This file is pretty simple and can be hand-edited if you'd like. Additionally, you can manually create tour files, by following the [tour schema](#tour-schema). You can then store these files to the `.tours` (or `.vscode/tours`) directory, or you can also create a tour at any of the following well-known locations: `.tour`, `main.tour`
|
||||
|
||||
Within the `.tours` (or `.vscode/tours`) directory, you can organize your tour files into arbitrarily deep sub-directories, and the CodeTour player will properly discover them.
|
||||
|
||||
### Exporting Tours
|
||||
|
||||
By default, when you record a tour, it is written to the currently open workspace. This makes it easy to check-in the tour and share it with the rest of the team. However, there may be times where you want to record a tour for yourself, or a tour to help explain a one-off to someone, and in those situations, you might not want to check the tour into the repo.
|
||||
|
||||
So support this, after you finish recording a tour, you can right-click it in the `CodeTour` tree and select `Export Tour...`. This will allow you to save the tour to a new location, and then you can delete the tour file from your repo. Furthermore, when you export a tour, the tour file itself will embed the contents of all files needed by the tour, which ensures that someone can play it back, regardless if the have the respective code available locally. This enables a powerful form of collaboration.
|
||||
|
||||
<img width="700px" src="https://user-images.githubusercontent.com/116461/77705325-9682be00-6f7c-11ea-9532-6975b19b8fcb.gif" />
|
||||
|
||||
#### GitHub Gists
|
||||
|
||||
If you install the [GistPad](https://aka.ms/gistpad) extension, then you'll see an additional `Export Tour to Gist...` option added to the `CodeTour` tree. This lets you export the tour file to a new/existing gist, which allows you to easily create your own private tours and/or create tours that can be shared with others on your team.
|
||||
|
||||
Once a tour is exported as a gist, you can right-click the `main.tour` file in the `GistPad` tree, and select `Copy GitHub URL`. If you send that to someone, and they run the `CodeTour: Open Tour URL...` command, then they'll be able to take the exact same tour, regardless if they have the code locally available or not.
|
||||
|
||||
### Tour Schema
|
||||
#### Tour Schema
|
||||
|
||||
Within the tour file, you need to specify the following required properties:
|
||||
|
||||
|
@ -193,7 +202,25 @@ Within the tour file, you need to specify the following required properties:
|
|||
|
||||
For an example, refer to the `.tours/tree.tour` file of this repository.
|
||||
|
||||
## Tree View
|
||||
## Exporting Tours
|
||||
|
||||
By default, when you record a tour, it is written to the currently open workspace. This makes it easy to check-in the tour and share it with the rest of the team. However, there may be times where you want to record a tour for yourself, or a tour to help explain a one-off to someone, and in those situations, you might not want to check the tour into the repo.
|
||||
|
||||
So support this, after you finish recording a tour, you can right-click it in the `CodeTour` tree and select `Export Tour...`. This will allow you to save the tour to a new location, and then you can delete the tour file from your repo. Furthermore, when you export a tour, the tour file itself will embed the contents of all files needed by the tour, which ensures that someone can play it back, regardless if the have the respective code available locally. This enables a powerful form of collaboration.
|
||||
|
||||
<img width="700px" src="https://user-images.githubusercontent.com/116461/77705325-9682be00-6f7c-11ea-9532-6975b19b8fcb.gif" />
|
||||
|
||||
### GitHub Gists
|
||||
|
||||
If you install the [GistPad](https://aka.ms/gistpad) extension, then you'll see an additional `Export Tour to Gist...` option added to the `CodeTour` tree. This lets you export the tour file to a new/existing gist, which allows you to easily create your own private tours and/or create tours that can be shared with others on your team.
|
||||
|
||||
Once a tour is exported as a gist, you can right-click the `main.tour` file in the `GistPad` tree, and select `Copy GitHub URL`. If you send that to someone, and they run the `CodeTour: Open Tour URL...` command, then they'll be able to take the exact same tour, regardless if they have the code locally available or not.
|
||||
|
||||
## Reference
|
||||
|
||||
The following sections describe the VS Code integrations that the CodeTour extension contributes (e.g. tree, status bar, settings):
|
||||
|
||||
### Tree View
|
||||
|
||||
If the currently opened workspace has any code tours, or you're actively taking/recording a tour, you'll see a new tree view called `CodeTour`, that's added to the `Explorer` tab. This view simply lists the set of available code tours, along with their title and number of steps. If you select a tour it will start it, and therefore, this is simply a more convenient alternative to running the `CodeTour: Start Tour` command. However, you can also expand a tour and start it at a specific step, edit/delete steps, re-order steps, and change the tour's description/title/git ref.
|
||||
|
||||
|
@ -203,7 +230,7 @@ Additionally, the tree view will display the tour currently being [recorded](#au
|
|||
|
||||
> The tree view is automatically kept up-to-date, as you add/edit/delete tours within the current workspace. So feel free to [record](#authoring-tours) and/or edit tours, and then navigate them when done.
|
||||
|
||||
## Status Bar
|
||||
### Status Bar
|
||||
|
||||
In addition to the `CodeTour` tree view, the CodeTour extension also contributes a new status bar item called `Start CodeTour` to your status bar. It's only visible when the current workspace has one or more tours, and when clicked, it allows you to select a tour and then begin navigating it.
|
||||
|
||||
|
@ -211,7 +238,7 @@ While you're within a tour, the status bar will update to show the title and ste
|
|||
|
||||
> If you don't want to display the status bar item, simply right-click it and select `Hide CodeTour (Extension)`.
|
||||
|
||||
## Contributed Commands
|
||||
### Contributed Commands
|
||||
|
||||
In addition to the `CodeTour` tree view and the status bar item, the CodeTour extension also contributes the following commands to the command palette:
|
||||
|
||||
|
@ -231,22 +258,24 @@ In addition to the `CodeTour` tree view and the status bar item, the CodeTour ex
|
|||
|
||||
- `CodeTour: End Tour` - Ends the currently active tour. This command is only visible while you're actively recording/playing a tour.
|
||||
|
||||
- `CodeTour: Resume Current Tour` - Resumse the current tour by navigating to the file/line number that's associated with the current step. This command is only visible while you're actively recording/playing a tour.
|
||||
- `CodeTour: Resume Current Tour` - Resume the current tour by navigating to the file/line number that's associated with the current step. This command is only visible while you're actively recording/playing a tour.
|
||||
|
||||
## Configuration Settings
|
||||
- `CodeTour: Add Tour Step` - Add a new [content-only step](#content-steps) after the current step in the active tour. This command is only visible while you're actively recording a tour.
|
||||
|
||||
### Configuration Settings
|
||||
|
||||
The `CodeTour` extension contributes the following settings:
|
||||
|
||||
- `codetour.showMarkers` - Specifies whether or not to show [tour markers](#tour-markers). Defaults to `true`.
|
||||
|
||||
## Keybindings
|
||||
### Keybindings
|
||||
|
||||
In addition to the available commands, the Code Tour extension also contributes the following commands, which are active while you're currently taking a tour:
|
||||
|
||||
- `ctrl+right` (Windows/Linux), `cmd+right` (macOS) - Move to the next step in the tour
|
||||
- `ctrl+left` (Windows/Linux) `cmd+left` (macOS) - Move to the previous step in the tour
|
||||
|
||||
## Extension API (Experimental)
|
||||
## Extension API
|
||||
|
||||
In order to enable other extensions to contribute/manage their own code tours, the CodeTour extension exposes an API with the following methods:
|
||||
|
||||
|
|
25
package.json
25
package.json
|
@ -3,7 +3,7 @@
|
|||
"displayName": "CodeTour",
|
||||
"description": "VS Code extension that allows you to record and playback guided tours of codebases, directly within the editor",
|
||||
"publisher": "vsls-contrib",
|
||||
"version": "0.0.24",
|
||||
"version": "0.0.25",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vsls-contrib/codetour"
|
||||
|
@ -44,6 +44,15 @@
|
|||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "codetour.addContentStep",
|
||||
"title": "Add Tour Step",
|
||||
"category": "CodeTour"
|
||||
},
|
||||
{
|
||||
"command": "codetour.addDirectoryStep",
|
||||
"title": "Add CodeTour Step"
|
||||
},
|
||||
{
|
||||
"command": "codetour.addTourStep",
|
||||
"title": "Add Step to Tour",
|
||||
|
@ -170,6 +179,10 @@
|
|||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "codetour.addContentStep",
|
||||
"when": "codetour:inTour && codetour:recording && codetour:canEditTour"
|
||||
},
|
||||
{
|
||||
"command": "codetour.editTour",
|
||||
"when": "codetour:inTour && !codetour:recording && codetour:canEditTour"
|
||||
|
@ -198,6 +211,10 @@
|
|||
"command": "codetour.startTour",
|
||||
"when": "codetour:hasTours"
|
||||
},
|
||||
{
|
||||
"command": "codetour.addDirectoryStep",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codetour.addTourStep",
|
||||
"when": "false"
|
||||
|
@ -417,6 +434,12 @@
|
|||
"when": "viewItem =~ /^codetour.tourStep/",
|
||||
"group": "manage@2"
|
||||
}
|
||||
],
|
||||
"explorer/context": [
|
||||
{
|
||||
"command": "codetour.addDirectoryStep",
|
||||
"when": "codetour:recording && explorerResourceIsFolder"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
|
|
49
schema.json
49
schema.json
|
@ -22,27 +22,64 @@
|
|||
"default": [],
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["line", "description"],
|
||||
"required": ["description"],
|
||||
"properties": {
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "Specifies the file path (relative to the workspace root) that is associated with the current step."
|
||||
"description": "File path (relative to the workspace root) that the step is associated with."
|
||||
},
|
||||
"uri": {
|
||||
"type": "string",
|
||||
"description": "Specifies an absolute URI that is associated with the current step."
|
||||
"description": "Absolute URI that is associated with the step."
|
||||
},
|
||||
"line": {
|
||||
"type": "number",
|
||||
"description": "Specifies the line number that is associated with the current step."
|
||||
"description": "Line number that the step is associated with."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Specifies an optional title for the step"
|
||||
"description": "An optional title for the step."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Specifies the description of the current step."
|
||||
"description": "Description of the step."
|
||||
},
|
||||
"selection": {
|
||||
"type": "object",
|
||||
"required": ["start", "end"],
|
||||
"description": "Text selection that's associated with the step.",
|
||||
"properties": {
|
||||
"start": {
|
||||
"type": "object",
|
||||
"required": ["line", "character"],
|
||||
"description": "Starting position (line, column) of the text selection range.",
|
||||
"properties": {
|
||||
"line": {
|
||||
"type": "number",
|
||||
"description": "Line number (1-based) that the text selection begins on."
|
||||
},
|
||||
"character": {
|
||||
"type": "number",
|
||||
"description": "Column number (1-based) that the text selection begins on."
|
||||
}
|
||||
}
|
||||
},
|
||||
"end": {
|
||||
"type": "object",
|
||||
"required": ["line", "character"],
|
||||
"description": "Ending position (line, column) of the text selection range.",
|
||||
"properties": {
|
||||
"line": {
|
||||
"type": "number",
|
||||
"description": "Line number (1-based) that the text selection ends on."
|
||||
},
|
||||
"character": {
|
||||
"type": "number",
|
||||
"description": "Column number (1-based) that the text selection end on."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
124
src/commands.ts
124
src/commands.ts
|
@ -1,17 +1,17 @@
|
|||
import { comparer, runInAction, when } from "mobx";
|
||||
import { action, comparer, runInAction, when } from "mobx";
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { workspace } from "vscode";
|
||||
import { EXTENSION_NAME } from "./constants";
|
||||
import { EXTENSION_NAME, FS_SCHEME_CONTENT } from "./constants";
|
||||
import { api, RefType } from "./git";
|
||||
import { CodeTourComment, focusPlayer } from "./player";
|
||||
import { CodeTour, store } from "./store";
|
||||
import {
|
||||
endCurrentCodeTour,
|
||||
exportTour,
|
||||
moveCurrentCodeTourBackward,
|
||||
moveCurrentCodeTourForward,
|
||||
startCodeTour,
|
||||
exportTour
|
||||
startCodeTour
|
||||
} from "./store/actions";
|
||||
import { discoverTours } from "./store/provider";
|
||||
import { CodeTourNode, CodeTourStepNode } from "./tree/nodes";
|
||||
|
@ -294,54 +294,95 @@ export function registerCommands() {
|
|||
}
|
||||
}
|
||||
|
||||
vscode.commands.registerCommand(
|
||||
`${EXTENSION_NAME}.addContentStep`,
|
||||
action(async () => {
|
||||
const value = store.activeTour?.step === -1 ? "Introduction" : "";
|
||||
const title = await vscode.window.showInputBox({
|
||||
prompt: "Specify the title of the step",
|
||||
value
|
||||
});
|
||||
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stepNumber = ++store.activeTour!.step;
|
||||
const tour = store.activeTour!.tour;
|
||||
|
||||
tour.steps.splice(stepNumber, 0, {
|
||||
title,
|
||||
description: ""
|
||||
});
|
||||
|
||||
saveTour(tour);
|
||||
})
|
||||
);
|
||||
|
||||
vscode.commands.registerCommand(
|
||||
`${EXTENSION_NAME}.addDirectoryStep`,
|
||||
action(async (uri: vscode.Uri) => {
|
||||
const stepNumber = ++store.activeTour!.step;
|
||||
const tour = store.activeTour!.tour;
|
||||
|
||||
const workspaceRoot = getActiveWorkspacePath();
|
||||
const directory = path.relative(workspaceRoot, uri.toString());
|
||||
|
||||
tour.steps.splice(stepNumber, 0, {
|
||||
directory,
|
||||
description: ""
|
||||
});
|
||||
|
||||
saveTour(tour);
|
||||
})
|
||||
);
|
||||
|
||||
vscode.commands.registerCommand(
|
||||
`${EXTENSION_NAME}.addTourStep`,
|
||||
(reply: vscode.CommentReply) => {
|
||||
action((reply: vscode.CommentReply) => {
|
||||
if (store.activeTour!.thread) {
|
||||
store.activeTour!.thread.dispose();
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
store.activeTour!.thread = reply.thread;
|
||||
store.activeTour!.step++;
|
||||
store.activeTour!.thread = reply.thread;
|
||||
store.activeTour!.step++;
|
||||
|
||||
const tour = store.activeTour!.tour;
|
||||
const thread = store.activeTour!.thread;
|
||||
const stepNumber = store.activeTour!.step;
|
||||
const tour = store.activeTour!.tour;
|
||||
const thread = store.activeTour!.thread;
|
||||
const stepNumber = store.activeTour!.step;
|
||||
|
||||
const workspaceRoot = getActiveWorkspacePath();
|
||||
const file = path.relative(workspaceRoot, thread!.uri.toString());
|
||||
const workspaceRoot = getActiveWorkspacePath();
|
||||
const file = path.relative(workspaceRoot, thread!.uri.toString());
|
||||
|
||||
const step = {
|
||||
file,
|
||||
line: thread!.range.start.line + 1,
|
||||
description: reply.text
|
||||
};
|
||||
const step = {
|
||||
file,
|
||||
line: thread!.range.start.line + 1,
|
||||
description: reply.text
|
||||
};
|
||||
|
||||
const selection = getStepSelection();
|
||||
if (selection) {
|
||||
(step as any).selection = selection;
|
||||
}
|
||||
const selection = getStepSelection();
|
||||
if (selection) {
|
||||
(step as any).selection = selection;
|
||||
}
|
||||
|
||||
tour.steps.splice(stepNumber, 0, step);
|
||||
tour.steps.splice(stepNumber, 0, step);
|
||||
|
||||
saveTour(tour);
|
||||
saveTour(tour);
|
||||
|
||||
let label = `Step #${stepNumber + 1} of ${tour.steps.length}`;
|
||||
let label = `Step #${stepNumber + 1} of ${tour.steps.length}`;
|
||||
|
||||
const contextValues = [];
|
||||
if (tour.steps.length > 1) {
|
||||
contextValues.push("hasPrevious");
|
||||
}
|
||||
const contextValues = [];
|
||||
if (tour.steps.length > 1) {
|
||||
contextValues.push("hasPrevious");
|
||||
}
|
||||
|
||||
if (stepNumber < tour.steps.length - 1) {
|
||||
contextValues.push("hasNext");
|
||||
}
|
||||
if (stepNumber < tour.steps.length - 1) {
|
||||
contextValues.push("hasNext");
|
||||
}
|
||||
|
||||
thread!.contextValue = contextValues.join(".");
|
||||
thread!.comments = [new CodeTourComment(reply.text, label, thread!)];
|
||||
});
|
||||
}
|
||||
thread!.contextValue = contextValues.join(".");
|
||||
thread!.comments = [new CodeTourComment(reply.text, label, thread!)];
|
||||
})
|
||||
);
|
||||
|
||||
vscode.commands.registerCommand(
|
||||
|
@ -581,6 +622,17 @@ export function registerCommands() {
|
|||
) {
|
||||
store.activeTour!.step--;
|
||||
}
|
||||
|
||||
if (store.activeTour.step === step) {
|
||||
// The only reason that a CodeTour content editor would be
|
||||
// open is because it was associated with the current step.
|
||||
// So detect if there are any, and if so, hide them.
|
||||
vscode.window.visibleTextEditors.forEach(editor => {
|
||||
if (editor.document.uri.scheme === FS_SCHEME_CONTENT) {
|
||||
editor.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
saveTour(tour);
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { Uri } from "vscode";
|
||||
|
||||
export const EXTENSION_NAME = "codetour";
|
||||
|
||||
export const FS_SCHEME = EXTENSION_NAME;
|
||||
export const FS_SCHEME_CONTENT = `${FS_SCHEME}-content`;
|
||||
export const CONTENT_URI = Uri.parse(`${FS_SCHEME_CONTENT}://current/CodeTour`);
|
||||
|
||||
export const ICON_URL =
|
||||
"https://cdn.jsdelivr.net/gh/vsls-contrib/code-tour/images/icon.png";
|
||||
export const SMALL_ICON_URL =
|
||||
"https://cdn.jsdelivr.net/gh/vsls-contrib/code-tour/images/icon-small.png";
|
||||
|
||||
export const VSCODE_DIRECTORY = ".vscode";
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import * as vscode from "vscode";
|
||||
import { registerCommands } from "./commands";
|
||||
import { registerFileSystemProvider } from "./fileSystem";
|
||||
import { registerTextDocumentContentProvider } from "./fileSystem/documentProvider";
|
||||
import { initializeGitApi } from "./git";
|
||||
import { registerStatusBar } from "./status";
|
||||
import { registerDecorators } from "./player/decorator";
|
||||
import { registerStatusBar } from "./player/status";
|
||||
import { store } from "./store";
|
||||
import {
|
||||
endCurrentCodeTour,
|
||||
exportTour,
|
||||
promptForTour,
|
||||
startCodeTour,
|
||||
exportTour
|
||||
startCodeTour
|
||||
} from "./store/actions";
|
||||
import { discoverTours } from "./store/provider";
|
||||
import { registerTreeProvider } from "./tree";
|
||||
import { registerDecorators } from "./decorator";
|
||||
import { store } from "./store";
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
registerCommands();
|
||||
|
@ -45,6 +46,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
// enabling other extensions to start a tour.
|
||||
registerTreeProvider(context.extensionPath);
|
||||
registerFileSystemProvider();
|
||||
registerTextDocumentContentProvider();
|
||||
registerStatusBar();
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import * as vscode from "vscode";
|
||||
import { FS_SCHEME_CONTENT } from "../constants";
|
||||
|
||||
class CodeTourTextDocumentContentProvider
|
||||
implements vscode.TextDocumentContentProvider {
|
||||
onDidChange?: vscode.Event<vscode.Uri> | undefined;
|
||||
|
||||
provideTextDocumentContent(
|
||||
uri: vscode.Uri,
|
||||
token: vscode.CancellationToken
|
||||
): vscode.ProviderResult<string> {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function registerTextDocumentContentProvider() {
|
||||
vscode.workspace.registerTextDocumentContentProvider(
|
||||
FS_SCHEME_CONTENT,
|
||||
new CodeTourTextDocumentContentProvider()
|
||||
);
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { reaction } from "mobx";
|
||||
import * as vscode from "vscode";
|
||||
import { ICON_URL } from "./constants";
|
||||
import { CodeTour, CodeTourStep, store } from "./store";
|
||||
import { getStepFileUri, getWorkspacePath } from "./utils";
|
||||
import { ICON_URL, FS_SCHEME_CONTENT } from "../constants";
|
||||
import { CodeTour, CodeTourStep, store } from "../store";
|
||||
import { getStepFileUri, getWorkspacePath } from "../utils";
|
||||
|
||||
const TOUR_DECORATOR = vscode.window.createTextEditorDecorationType({
|
||||
gutterIconPath: vscode.Uri.parse(ICON_URL),
|
||||
|
@ -42,6 +42,10 @@ async function getTourSteps(
|
|||
}
|
||||
|
||||
async function setDecorations(editor: vscode.TextEditor) {
|
||||
if (editor.document.uri.scheme === FS_SCHEME_CONTENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tourSteps = await getTourSteps(editor.document);
|
||||
if (tourSteps.length === 0) {
|
||||
return;
|
|
@ -1,5 +1,6 @@
|
|||
import { reaction } from "mobx";
|
||||
import {
|
||||
commands,
|
||||
Comment,
|
||||
CommentAuthorInformation,
|
||||
CommentController,
|
||||
|
@ -17,7 +18,7 @@ import {
|
|||
} from "vscode";
|
||||
import { ICON_URL } from "../constants";
|
||||
import { store } from "../store";
|
||||
import { getActiveWorkspacePath, getStepFileUri } from "../utils";
|
||||
import { getActiveWorkspacePath, getFileUri, getStepFileUri } from "../utils";
|
||||
|
||||
const CONTROLLER_ID = "codetour";
|
||||
const CONTROLLER_LABEL = "CodeTour";
|
||||
|
@ -129,9 +130,22 @@ async function renderCurrentStep() {
|
|||
const workspaceRoot = getActiveWorkspacePath();
|
||||
const uri = await getStepFileUri(step, workspaceRoot, currentTour.ref);
|
||||
store.activeTour!.thread = controller!.createCommentThread(uri, range, []);
|
||||
store.activeTour!.thread.comments = [
|
||||
new CodeTourComment(step.description, label, store.activeTour!.thread!)
|
||||
];
|
||||
|
||||
const comment = new CodeTourComment(
|
||||
step.description,
|
||||
label,
|
||||
store.activeTour!.thread!
|
||||
);
|
||||
|
||||
// If we're currently recording, and the current
|
||||
// step doesn't have a description than render
|
||||
// it in editing mode under the assumption that
|
||||
// this is a new content/directory step.
|
||||
if (store.isRecording && !step.description) {
|
||||
comment.mode = CommentMode.Editing;
|
||||
}
|
||||
|
||||
store.activeTour!.thread.comments = [comment];
|
||||
|
||||
const contextValues = [];
|
||||
if (currentStep > 0) {
|
||||
|
@ -161,7 +175,12 @@ async function renderCurrentStep() {
|
|||
selection = new Selection(range.start, range.end);
|
||||
}
|
||||
|
||||
showDocument(uri, range, selection);
|
||||
await showDocument(uri, range, selection);
|
||||
|
||||
if (step.directory) {
|
||||
const directoryUri = getFileUri(workspaceRoot, step.directory);
|
||||
commands.executeCommand("revealInExplorer", directoryUri);
|
||||
}
|
||||
}
|
||||
|
||||
async function showDocument(uri: Uri, range: Range, selection?: Selection) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as vscode from "vscode";
|
||||
import { store } from "./store";
|
||||
import { EXTENSION_NAME } from "./constants";
|
||||
import { store } from "../store";
|
||||
import { EXTENSION_NAME } from "../constants";
|
||||
import { reaction } from "mobx";
|
||||
|
||||
function createCurrentTourItem() {
|
||||
|
@ -57,8 +57,9 @@ export function registerStatusBar() {
|
|||
}
|
||||
|
||||
const prefix = store.isRecording ? "Recording " : "";
|
||||
currentTourItem.text = `${prefix}CodeTour: #${store.activeTour.step +
|
||||
1} of ${store.activeTour.tour.steps.length} (${
|
||||
currentTourItem.text = `${prefix}CodeTour: #${
|
||||
store.activeTour.step + 1
|
||||
} of ${store.activeTour.tour.steps.length} (${
|
||||
store.activeTour.tour.title
|
||||
})`;
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import { commands, Memento, Uri, window, workspace } from "vscode";
|
||||
import { CodeTour, store } from ".";
|
||||
import { EXTENSION_NAME, FS_SCHEME } from "../constants";
|
||||
import { EXTENSION_NAME, FS_SCHEME, FS_SCHEME_CONTENT } from "../constants";
|
||||
import { startPlayer, stopPlayer } from "../player";
|
||||
import { getWorkspaceKey, getWorkspaceUri, getStepFileUri } from "../utils";
|
||||
import { getStepFileUri, getWorkspaceKey, getWorkspaceUri } from "../utils";
|
||||
|
||||
const CAN_EDIT_TOUR_KEY = `${EXTENSION_NAME}:canEditTour`;
|
||||
const IN_TOUR_KEY = `${EXTENSION_NAME}:inTour`;
|
||||
const RECORDING_KEY = `${EXTENSION_NAME}:recording`;
|
||||
|
||||
export function startCodeTour(
|
||||
tour: CodeTour,
|
||||
|
@ -32,14 +33,14 @@ export function startCodeTour(
|
|||
|
||||
if (startInEditMode) {
|
||||
store.isRecording = true;
|
||||
commands.executeCommand("setContext", "codetour:recording", true);
|
||||
commands.executeCommand("setContext", RECORDING_KEY, true);
|
||||
}
|
||||
}
|
||||
|
||||
export async function endCurrentCodeTour() {
|
||||
if (store.isRecording) {
|
||||
store.isRecording = false;
|
||||
commands.executeCommand("setContext", "codetour:recording", false);
|
||||
commands.executeCommand("setContext", RECORDING_KEY, false);
|
||||
}
|
||||
|
||||
stopPlayer();
|
||||
|
@ -48,7 +49,10 @@ export async function endCurrentCodeTour() {
|
|||
commands.executeCommand("setContext", IN_TOUR_KEY, false);
|
||||
|
||||
window.visibleTextEditors.forEach(editor => {
|
||||
if (editor.document.uri.scheme === FS_SCHEME) {
|
||||
if (
|
||||
editor.document.uri.scheme === FS_SCHEME ||
|
||||
editor.document.uri.scheme === FS_SCHEME_CONTENT
|
||||
) {
|
||||
editor.hide();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { observable } from "mobx";
|
||||
import { CommentThread, Uri } from "vscode";
|
||||
|
||||
export const PENDING_TOUR_ID = "@@RECORDING";
|
||||
|
||||
export interface CodeTourStepPosition {
|
||||
line: number;
|
||||
character: number;
|
||||
|
@ -12,6 +10,7 @@ export interface CodeTourStep {
|
|||
title?: string;
|
||||
description: string;
|
||||
file?: string;
|
||||
directory?: string;
|
||||
uri?: string;
|
||||
line?: number;
|
||||
selection?: { start: CodeTourStepPosition; end: CodeTourStepPosition };
|
||||
|
|
|
@ -65,7 +65,19 @@ class CodeTourTreeProvider implements TreeDataProvider<TreeItem>, Disposable {
|
|||
}
|
||||
} else if (element instanceof CodeTourNode) {
|
||||
if (element.tour.steps.length === 0) {
|
||||
return [new TreeItem("No steps recorded yet")];
|
||||
let item;
|
||||
|
||||
if (store.isRecording && store.activeTour?.tour.id == element.tour.id) {
|
||||
item = new TreeItem("Add tour step...");
|
||||
item.command = {
|
||||
command: "codetour.addContentStep",
|
||||
title: "Add tour step..."
|
||||
};
|
||||
} else {
|
||||
item = new TreeItem("No steps recorded");
|
||||
}
|
||||
|
||||
return [item];
|
||||
} else {
|
||||
return element.tour.steps.map(
|
||||
(_, index) => new CodeTourStepNode(element.tour, index)
|
||||
|
@ -105,10 +117,14 @@ export function registerTreeProvider(extensionPath: string) {
|
|||
: null
|
||||
],
|
||||
() => {
|
||||
if (store.activeTour) {
|
||||
treeView.reveal(
|
||||
new CodeTourStepNode(store.activeTour.tour, store.activeTour!.step)
|
||||
);
|
||||
if (store.activeTour && store.activeTour.step >= 0) {
|
||||
// Give the tree a little bit of time to render the new
|
||||
// node before trying to reveal it.
|
||||
setTimeout(() => {
|
||||
treeView.reveal(
|
||||
new CodeTourStepNode(store.activeTour!.tour, store.activeTour!.step)
|
||||
);
|
||||
}, 300);
|
||||
} else {
|
||||
// TODO: Once VS Code supports it, we want
|
||||
// to de-select the step node once the tour ends.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as path from "path";
|
||||
import { ThemeIcon, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
|
||||
import { EXTENSION_NAME, FS_SCHEME } from "../constants";
|
||||
import { CONTENT_URI, EXTENSION_NAME, FS_SCHEME } from "../constants";
|
||||
import { CodeTour, store } from "../store";
|
||||
import { getFileUri, getWorkspacePath } from "../utils";
|
||||
|
||||
|
@ -61,7 +61,9 @@ function getStepLabel(tour: CodeTour, stepNumber: number) {
|
|||
} else if (HEADING_PATTERN.test(step.description.trim())) {
|
||||
label = step.description.trim().match(HEADING_PATTERN)![1];
|
||||
} else {
|
||||
label = step.uri ? step.uri! : decodeURIComponent(step.file!);
|
||||
label = step.uri
|
||||
? step.uri!
|
||||
: decodeURIComponent(step.directory || step.file!);
|
||||
}
|
||||
|
||||
return `${prefix}${label}`;
|
||||
|
@ -90,17 +92,24 @@ export class CodeTourStepNode extends TreeItem {
|
|||
if (step.uri) {
|
||||
resourceUri = Uri.parse(step.uri);
|
||||
} else if (step.contents) {
|
||||
resourceUri = Uri.parse(`${FS_SCHEME}://${step.file}`);
|
||||
} else {
|
||||
resourceUri = Uri.parse(`${FS_SCHEME}://current/${step.file}`);
|
||||
} else if (step.file || step.directory) {
|
||||
const resourceRoot = workspaceRoot
|
||||
? workspaceRoot.toString()
|
||||
: getWorkspacePath(tour);
|
||||
|
||||
resourceUri = getFileUri(resourceRoot, step.file!);
|
||||
resourceUri = getFileUri(resourceRoot, step.directory || step.file!);
|
||||
} else {
|
||||
resourceUri = CONTENT_URI;
|
||||
}
|
||||
|
||||
this.resourceUri = resourceUri;
|
||||
this.iconPath = ThemeIcon.File;
|
||||
|
||||
if (step.directory) {
|
||||
this.iconPath = ThemeIcon.Folder;
|
||||
} else {
|
||||
this.iconPath = ThemeIcon.File;
|
||||
}
|
||||
|
||||
const contextValues = ["codetour.tourStep"];
|
||||
if (stepNumber > 0) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as path from "path";
|
||||
import { Uri, workspace } from "vscode";
|
||||
import { FS_SCHEME } from "./constants";
|
||||
import { FS_SCHEME, CONTENT_URI } from "./constants";
|
||||
import { api } from "./git";
|
||||
import { CodeTour, CodeTourStep, store } from "./store";
|
||||
|
||||
|
@ -27,7 +27,7 @@ export async function getStepFileUri(
|
|||
let uri;
|
||||
if (step.contents) {
|
||||
uri = Uri.parse(`${FS_SCHEME}://current/${step.file}`);
|
||||
} else {
|
||||
} else if (step.uri || step.file) {
|
||||
uri = step.uri
|
||||
? Uri.parse(step.uri)
|
||||
: getFileUri(workspaceRoot, step.file!);
|
||||
|
@ -46,7 +46,10 @@ export async function getStepFileUri(
|
|||
uri = await api.toGitUri(uri, ref);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uri = CONTENT_URI;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
|
12
tslint.json
12
tslint.json
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"rules": {
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-duplicate-variable": true,
|
||||
"curly": true,
|
||||
"class-name": true,
|
||||
"semicolon": [true, "always"],
|
||||
"triple-equals": true
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
Загрузка…
Ссылка в новой задаче