🎁 feat: run user code against tests

This commit is contained in:
ysandler 2020-09-07 21:56:33 -05:00 committed by Joshua Shoemaker
commit 0486a6d141
29 changed files with 2769 additions and 0 deletions

19
.eslintrc.json Normal file
View File

@ -0,0 +1,19 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
}
}

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
out
node_modules
.vscode-test/
*.vsix

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint"
]
}

37
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,37 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"${workspaceFolder}/demo/",
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

11
.vscode/settings.json vendored Normal file
View File

@ -0,0 +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"
}

20
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

10
.vscodeignore Normal file
View File

@ -0,0 +1,10 @@
.vscode/**
.vscode-test/**
out/test/**
src/**
.gitignore
vsc-extension-quickstart.md
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts

9
CHANGELOG.md Normal file
View File

@ -0,0 +1,9 @@
# Change Log
All notable changes to the "brightscreen" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

80
README.md Normal file
View File

@ -0,0 +1,80 @@
# <img alt="brightScreen" src="./src/media/logoBlue.svg" width="26px" /> brightscreen
## VS Code Extention for Students
brightScreen is an extention for your favorite text editor (as long as your text editor is VS Code) that brings interactive coding tutorials to help bring a new dynamic to learning. Instead of just following along with an article or video, you can download coding challanges that help solidify those theoretical lessons into something tangible.
After installing a brightScreen course and activating brightScreen an new icon will appear in the Activity Pannel to the side. This will open a tree view showing all the available tests. By clicking on the `Run Tests` icon, you can test the code in your currently active editor against the instructors test senerios.
## New Platform for Instructors
brightScreen is a great place for software instructors to create interactive homework for those that follow their courses, videos, or articles. The amount of effort in seting up a brightScreen course is virtually non existant.
All that is required is a Git repo with a brightScreen.json configuration file that looks like something like this:
{
"courseName": "Test Lesson",
"documentationUrl": "https://brightScreen.io/courses/Test-Lesson/",
"lessons": [
{
"name": "Lesson 1",
"location": "lessonOne/indexTest.js",
"description": "Test Description for Lesson 1",
"executionPrefix": "node",
"fileExtention": "js",
"replacementSubstring": "USER_CODE",
"documentationUrl": "https://brightScreen.io/courses/Test-Lesson/lesson-One/"
}
]
}
A course testing file will look something like this:
const codeFromUser = USER_CODE
const test = () => {
const input = 'yo'
const expectedOutput = 'yo'
const testedValue = codeFromUser(input)
if (testedValue === expectedOutput) {
console.log({
didPass: true,
message: 'Passed Test 1'
})
} else {
console.log({
didPass: false,
message: 'Did not return expected value'
})
}
}
test()
In the brightScreen.json file, for each, lesson you specify a substring you would like to replace with the User's own code and brightScreen throws it in there for you.
In theory, any interpreted language can be used for a course as long as it can be executed with a CLI and file location.
Your tests are created as a child process. To interface vack with brightScreen, your tests should return a JSON object through the Standard Out of what every platform you are testing with.
Example of Standard Iut from Tests
{
didPass: false,
message: 'Value was not FizzBuzz'
}
## Requirements
`git` should be installed on your device to install courses.
Whatever languages or platforms the instructor has listed should also be installed on your machine. For instance, if you are working on a `JavaScript` course, you may need `Node.js` installed
## Release Notes
### 0.0.1
This extention is still very much in development. If you would like to see it expanded then please reach out with suggestions or sponser me on Github.

View File

@ -0,0 +1,24 @@
{
"courseName": "Test Lesson",
"documentationUrl": "https://brightScreen.io/courses/Test-Lesson/",
"lessons": [
{
"name": "Lesson 1",
"location": "lessonOne/indexTest.js",
"description": "Test Description for Lesson 1",
"executionPrefix": "node",
"fileExtention": "js",
"replacementSubstring": "USER_CODE",
"documentationUrl": "https://brightScreen.io/courses/Test-Lesson/lesson-One/"
},
{
"name": "Lesson 2",
"location": "lessonTwo/indexTest.js",
"description": "Test Description for Lesson 2",
"executionPrefix": "node",
"fileExtention": "js",
"replacementSubstring": "USER_CODE",
"documentationUrl": "https://brightScreen.io/courses/Test-Lesson/lesson-Two/"
}
]
}

View File

@ -0,0 +1,21 @@
const codeFromUser = USER_CODE
const test = () => {
const input = 'yo'
const expectedOutput = 'yo'
const testedValue = codeFromUser(input)
if (testedValue === expectedOutput) {
console.log({
didPass: true,
message: 'Passed Test 1'
})
} else {
console.log({
didPass: false,
message: 'Did not return expected value'
})
}
}
test()

View File

@ -0,0 +1,18 @@
const codeFromUser = USER_CODE || function() { console.log('No User code Provided') }
const test = () => {
const input = 'yo'
const expectedOutput = 'yo'
const testedValue = codeFromUser(input)
if (testedValue === expectedOutput) {
console.log('Passed Test 2')
return true
} else {
console.log('Failed Test Two')
return false
}
}
test()

View File

@ -0,0 +1,20 @@
const codeFromUser = function (props) {
return props + 'js'
} || function() { console.log('No User code Provided') }
const test = () => {
const input = 'yo'
const expectedOutput = 'yo'
const testedValue = codeFromUser(input)
if (testedValue === expectedOutput) {
console.log('Passed Test 2')
return true
} else {
console.log('Failed Test Two')
return false
}
}
test()

3
demo/userTestOne.js Normal file
View File

@ -0,0 +1,3 @@
function (props) {
return props + 'js'
}

1949
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

96
package.json Normal file
View File

@ -0,0 +1,96 @@
{
"name": "brightscreen",
"displayName": "brightScreen",
"description": "Interactive Code Tutorials",
"version": "0.0.1",
"engines": {
"vscode": "^1.48.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onView:brightScreen"
],
"main": "./out/extension.js",
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "brightScreen",
"title": "brightScreen",
"icon": "src/media/logoBlue.svg"
}
]
},
"views": {
"brightScreen": [
{
"id": "brightScreen",
"name": "brightScreen",
"icon": "src/media/logoBlue.svg",
"contextualTitle": "brightScreen"
}
]
},
"commands": [
{
"command": "brightscreen.startBrightScreen",
"title": "Start Bright Screen",
"icon": {
"light": "src/media/logoBlue.svg",
"dark": "src/media/logoBlue.svg"
}
},
{
"command": "brightscreen.runTests",
"title": "Run Tests",
"icon": {
"light": "src/media/runCommand.svg",
"dark": "src/media/runCommand.svg"
}
}
],
"menus": {
"view/title": [
{
"command": "brightscreen.startBrightScreen",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "brightscreen.runTests",
"group": "inline"
}
]
},
"viewsWelcome": [
{
"view": "brightScreen",
"contents": "No Course Selected\n[Find Them Here](https://brightScreen.io/)"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"lint": "eslint src --ext ts",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^1.48.0",
"@types/glob": "^7.1.3",
"@types/mocha": "^8.0.0",
"@types/node": "^14.0.27",
"eslint": "^7.6.0",
"@typescript-eslint/eslint-plugin": "^3.8.0",
"@typescript-eslint/parser": "^3.8.0",
"glob": "^7.1.6",
"mocha": "^8.0.1",
"typescript": "^3.8.3",
"vscode-test": "^1.4.0"
}
}

View File

@ -0,0 +1,41 @@
import BrightScreenInterface from '../Interfaces/BrightScreenInterface'
import LessonInterface from '../Interfaces/LessonInterface'
class BrightScreen {
private static instance: BrightScreen
public workspaceFolder: string
public courseName: string
public documentationUrl: string
public lessons: LessonInterface[]
constructor (props: BrightScreenInterface) {
if (!BrightScreen.instance) BrightScreen.instance = this
this.workspaceFolder = props.workspaceFolder
this.courseName = props.courseName || ''
this.documentationUrl = props.documentationUrl || ''
this.lessons = props.lessons || []
return BrightScreen.instance
}
public static getInstance(): BrightScreen {
return BrightScreen.instance
}
public get props (): BrightScreenInterface {
return {
workspaceFolder: this.workspaceFolder,
courseName: this.courseName,
lessons: this.lessons
}
}
executeLessonTest (activeUserCode: string, lessonCode: string): void {
eval(activeUserCode)
eval(lessonCode)
}
}
export default BrightScreen

View File

@ -0,0 +1,10 @@
import LessonInterface from '../Interfaces/LessonInterface'
interface BrightScreenInterface {
workspaceFolder: string,
courseName?: string,
documentationUrl?: string,
lessons?: LessonInterface[]
}
export default BrightScreenInterface

View File

@ -0,0 +1,11 @@
interface LessonInterface {
name: string,
location: string,
description: string,
executionPrefix: string,
fileExtention: string,
replacementSubstring: string,
documentationUrl: string
}
export default LessonInterface

View File

@ -0,0 +1,56 @@
import * as vscode from 'vscode'
import BrightScreen from '../Entities/BrightScreen'
class LessonsProvider implements vscode.TreeDataProvider<LessonTreeItem> {
constructor() { }
getTreeItem (element: LessonTreeItem): vscode.TreeItem {
return element
}
getChildren (): LessonTreeItem[] {
const brightScreen = BrightScreen.getInstance()
if (brightScreen.lessons) {
const treeItems: LessonTreeItem[] = brightScreen.lessons.map(l => {
return new LessonTreeItem(
l.name,
vscode.TreeItemCollapsibleState.None,
l.executionPrefix,
l.fileExtention,
l.replacementSubstring,
l.location
)
})
return treeItems
} else return []
}
}
class LessonTreeItem extends vscode.TreeItem {
constructor (
public readonly label: string,
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
public readonly executionPrefix: string,
public readonly fileExtention: string,
public readonly replacementSubstring: string,
public readonly location: string
) {
super(label, collapsibleState)
}
get tooltip (): string {
return this.label
}
get description(): string {
return this.fileExtention
}
iconPath = {
light: `../media/runCommand.svg`,
dark: '../media/runCommand.svg'
}
}
export default LessonsProvider

View File

@ -0,0 +1,46 @@
import * as vscode from 'vscode'
import * as fs from 'fs'
import * as path from 'path'
import BrightScreen from '../Entities/BrightScreen'
import LessonInterface from '../Interfaces/LessonInterface'
function setupBrightScreen (): void {
const workspaceFolder: string = vscode.workspace.rootPath || ''
let courseName: string
let lessons: LessonInterface[]
try {
const brightScreenPath = path.join(workspaceFolder, '.brightScreen')
const doesBrightScreenDirectoryExist: boolean = fs.existsSync(brightScreenPath)
if (!doesBrightScreenDirectoryExist) fs.mkdirSync(brightScreenPath)
} catch (err) {
console.log(err)
vscode.window.showErrorMessage('Could not create .brightScreen directory')
}
let brightScreenCourseConfig: any
try {
brightScreenCourseConfig = JSON.parse(fs.readFileSync(`${workspaceFolder}/.brightScreen/brightScreen.json`, 'utf-8'))
} catch (err) {
console.log(err)
vscode.window.showErrorMessage('Could not find brightScreen config for lesson')
return
}
courseName = brightScreenCourseConfig.courseName
lessons = brightScreenCourseConfig.lessons
if (!courseName || !lessons || !workspaceFolder) {
vscode.window.showErrorMessage('Required configurations not provided')
return
}
new BrightScreen({
workspaceFolder: workspaceFolder,
courseName: courseName,
lessons: lessons
})
vscode.window.showInformationMessage(`brightScreen has been configured for ${courseName}`)
}
export default setupBrightScreen

92
src/extension.ts Normal file
View File

@ -0,0 +1,92 @@
import * as vscode from 'vscode'
import * as fs from 'fs'
import BrightScreen from './Entities/BrightScreen'
import setupBrightScreen from './UseCases/setupBrightScreen'
import LessonsProvider from './UseCases/LessonsProvider'
import LessonInterface from './Interfaces/LessonInterface'
import { spawn, exec } from 'child_process'
import { stderr } from 'process'
export function activate(context: vscode.ExtensionContext) {
console.log('brightscreen is now active')
let brightScreen: BrightScreen
let workspaceFolder: string
let lessons: LessonInterface[]
setupBrightScreen()
brightScreen = BrightScreen.getInstance()
workspaceFolder = brightScreen.workspaceFolder
lessons = brightScreen.lessons
try {
vscode.window.registerTreeDataProvider('brightScreen', new LessonsProvider())
} catch (err) {
console.log(err)
}
const startupBrightScreenCommand = vscode.commands.registerCommand('brightscreen.startBrightScreen', (value) => {
setupBrightScreen()
brightScreen = BrightScreen.getInstance()
workspaceFolder = brightScreen.workspaceFolder
lessons = brightScreen.lessons
})
const runTestCommand = vscode.commands.registerCommand('brightscreen.runTests', (treeItemContext) => {
let lessonCodeAsString: string
try {
lessonCodeAsString = fs.readFileSync(`${workspaceFolder}/.brightScreen/${treeItemContext.location}`, 'utf-8')
} catch (err) {
console.log(err)
vscode.window.showErrorMessage('Could not find file')
return
}
let userCodeAsString: string | undefined
try {
userCodeAsString = vscode.window.activeTextEditor?.document.getText()
if (typeof userCodeAsString !== 'string') {
vscode.window.showErrorMessage('Could not get active text editor')
return
}
} catch (err) {
console.log(err)
vscode.window.showErrorMessage('Could not get active text editor')
return
}
let mergedCodeAsString: string
try {
mergedCodeAsString = lessonCodeAsString.replace(treeItemContext.replacementSubstring, userCodeAsString)
} catch (err) {
console.log(err)
vscode.window.showErrorMessage('Could not create test')
return
}
const runningTestPath = `${workspaceFolder}/.brightScreen/runningTest.${treeItemContext.fileExtention}`
try {
fs.writeFileSync(runningTestPath, mergedCodeAsString)
} catch (err) {
console.log(err)
vscode.window.showErrorMessage('Could not create ./runningTest file')
}
exec(`${treeItemContext.executionPrefix} ${runningTestPath}`, (error, stdout, stderr) => {
if (stderr) {
console.log('stderr: ', stderr)
return
}
if (error !== null) {
console.log('exec error: ', error)
}
console.log(stdout)
})
})
context.subscriptions.push(...[runTestCommand, startupBrightScreenCommand])
}
export function deactivate() {}

45
src/media/logoBlue.svg Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 331 276" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-miterlimit:10;">
<g id="Artboard1" transform="matrix(0.838537,0,0,0.897683,-89.4004,-44.0313)">
<rect x="106.615" y="49.05" width="393.853" height="306.879" style="fill:none;"/>
<clipPath id="_clip1">
<rect x="106.615" y="49.05" width="393.853" height="306.879"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g id="Layer_1" transform="matrix(1.19255,0,0,1.11398,-58.9948,-25.8648)">
<path d="M223.83,336.73L388.17,336.73" style="fill:none;stroke:rgb(102,252,241);stroke-width:12px;"/>
<path d="M286.43,293.25L328.17,293.25C329,298.81 331.3,308.51 338.6,317.6C354.89,337.85 381.41,336.9 384.69,336.73" style="fill:none;stroke:rgb(102,252,241);stroke-width:12px;"/>
<path d="M282.44,220.94L283.89,221.15C283.97,221.16 284.04,221.18 284.12,221.19L285.87,221.6L286.57,221.76C286.99,221.86 287.42,221.85 287.84,221.74L291.43,220.78C292.09,220.6 292.66,220.17 293,219.57L293.34,218.98C293.68,218.39 293.78,217.68 293.6,217.02L293.06,215C292.88,214.34 292.46,213.78 291.87,213.44L284.85,209.31C284.77,209.26 284.68,209.21 284.61,209.15L270.06,198.19C268.14,196.74 269.05,193.68 271.45,193.54C271.85,193.52 272.26,193.5 272.68,193.49C275.7,193.41 282.17,193.54 282.12,193.54C282.12,193.54 289.11,191.95 291.21,191.4C294.34,190.59 295.42,190.31 295.5,190.25C295.81,190.01 296.07,189.74 296.29,189.47C297.04,188.58 297.06,187.29 296.39,186.34L295.72,185.39C295.27,184.76 294.57,184.36 293.8,184.3L289.16,183.95C289.04,183.94 288.92,183.92 288.8,183.9L265.18,178.77C264.92,178.71 264.66,178.7 264.39,178.72C262.31,178.91 259.92,179.13 257.22,179.38C257.02,179.4 256.83,179.44 256.65,179.5C252.83,180.76 242.03,184.33 231.61,187.77C231.3,187.87 230.98,187.91 230.66,187.9L218.72,187.25C218.45,187.24 218.17,187.26 217.91,187.33L212.98,188.65C212.53,188.77 212.13,189.01 211.8,189.33L206.36,194.86C206.22,195 206.06,195.13 205.89,195.24L183.69,209.15L149.78,228.77C149.68,228.83 149.58,228.89 149.49,228.97L146.47,231.31C146.17,231.55 145.92,231.85 145.74,232.19L140.43,242.77C140.15,243.32 140.08,243.95 140.23,244.55L178.68,277.2C178.68,277.2 180.77,266.98 180.48,265.9C180.22,264.91 182.63,260.89 183.02,260.23C183.06,260.17 183.1,260.11 183.14,260.06L183.88,259.04C184,258.88 184.13,258.73 184.28,258.6L223.41,224.9C223.61,224.73 223.84,224.59 224.08,224.48L237.39,218.76C237.76,218.6 238.15,218.53 242.45,211.16L264.97,212.37L282.19,220.9C282.28,220.92 282.36,220.93 282.44,220.94Z" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path d="M288.55,221L245.72,203.7L183.37,246.63L179.88,256.77C179.88,256.77 187.23,258.83 187.41,258.89C188.17,259.15 199.17,256.89 199.17,256.89C199.17,256.89 212.17,254.33 213.1,254.31C214.02,254.29 224.28,245.79 224.28,245.79L233.07,241.36L240.18,241.06L248.94,241.7C248.94,241.7 256.43,241.07 257.73,240.73C259.03,240.38 263.58,239.16 263.58,239.16L266.79,235.54L261.53,228.9L252.75,224.7L245.66,217.28L248.04,216.64L252.44,216.61L256.48,214.38L261.95,216.6L275.18,221.8L281.93,226.2L285.39,225.27L286.99,223.46L288.55,221" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path d="M223.76,227.76L232.91,223.7L234.71,221.83L238.46,223.82L242.08,226.99L223.76,227.76Z" style="fill:white;fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path id="Layer_3" d="M423.29,165.92L434.85,161.25C434.85,161.25 439.74,153.25 439.74,152.58C439.74,151.91 442.63,141.25 442.63,141.25L444.85,134.14L447.29,130.36L462.62,122.8L462.62,77.48L459.06,73.26L447.28,74.82L421.72,93.26L410.16,100.37L397.72,109.04L389.72,117.04L377.72,131.48L368.83,141.92L354.61,153.25L348.39,157.69L338.61,166.8L324.61,177.8L313.72,184.64L313.72,187.46L315.5,189.46L321.94,189.46L331.5,187.02L340.39,179.46L345.5,175.46L347.72,173.9L357.94,171.62L368.16,169.23L377.05,160.56L372.16,175.23L364.16,187.01L363.05,195.23L366.16,198.34C369.05,200.12 372.8,198.34 374.38,198.34L377.94,197.23" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path id="Layer_7" d="M385.09,187.36L385.09,198.41L386.51,207.21C386.51,207.21 391.12,216.3 390.94,216.81C390.76,217.32 392.71,218.92 392.71,218.92L396.96,218.92C396.96,218.92 399.45,217.88 399.45,214.67C399.45,213.94 398.63,209.96 397.81,206.11C396.97,202.18 396.12,198.4 396.12,198.4C396.12,198.4 396.34,190.36 396.12,188.59C395.9,186.82 397.67,179.37 397.67,179.37L385.09,187.36Z" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path id="Layer_8" d="M397.68,179.38L396.13,201.19L399.46,213.37L400.34,215.36C400.34,215.36 404.77,216.06 405.48,215.36L408.32,212.18L408.32,207.93C408.32,207.93 406.72,203.74 406.72,202.91C406.72,202.08 404.95,198.59 405.48,197.05C406.01,195.52 406.19,191.44 406.37,190.2C406.55,188.96 406.9,182.22 407.08,181.16C407.26,180.1 408.32,174.78 408.32,174.78L409.56,171.41L397.68,179.38Z" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path id="Layer_9" d="M407.84,171.38L405.71,178.74L409.96,175.19L411.96,175.9L411.96,188.13L416.17,195.37C416.17,195.37 419.41,196.7 420.63,196.76C422.98,196.01 423.17,193.87 422.56,190.08C422.61,189.29 421.01,187.72 420.63,185.97C420.25,184.23 420.63,175.18 420.63,175.18L419.72,170.11L422.56,168.43L426.81,162.59L433.19,159.58" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<g id="Layer_10">
<path d="M422.56,168.45L433.2,161.25L424.88,161.25L422.56,168.45Z" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
<path d="M409.97,175.19C409.97,175.19 410.8,188.8 411.97,188.13C413.14,187.46 415.54,174.12 414.65,174.12C413.75,174.13 409.97,175.19 409.97,175.19Z" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
</g>
<path d="M463.13,262.99C463.13,269.59 457.73,274.99 451.13,274.99L156.87,274.99C150.27,274.99 144.87,269.59 144.87,262.99L144.87,85.25C144.87,78.65 150.27,73.25 156.87,73.25L451.13,73.25C457.73,73.25 463.13,78.65 463.13,85.25L463.13,262.99Z" style="fill:none;stroke:rgb(102,252,241);stroke-width:12px;"/>
<path d="M144.94,244.57L144.94,270.24L183.39,277.22" style="fill:rgb(102,252,241);fill-rule:nonzero;stroke:rgb(102,252,241);stroke-width:0.75px;"/>
</g>
<g id="Layer_5" transform="matrix(1.19255,0,0,1.11398,-58.9948,-25.8648)">
<path d="M392.376,181.649C396.196,174.059 398.54,164.19 400.87,161.58C401,161.44 401.92,160.42 403.14,158.95C403.14,158.95 403.82,158.13 404.43,157.35C409.16,151.33 411.54,145.57 411.54,145.57C412.75,142.65 414.26,138.03 414.65,132.01" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M445.34,136.21C440.39,143.7 435.67,147.32 432.23,149.24C429.48,150.78 426.8,151.64 419.12,155.46C418.3,155.87 413.34,158.42 413.34,158.42C412.6,158.81 411.86,159.21 411.12,159.6" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M189.87,233.08C192.21,233.53 195.89,233.88 199.83,232.48C202.38,231.58 204.57,229.76 208.94,226.13C212.74,222.97 215.01,220.58 219.52,217.54C220.52,216.87 221.31,216.38 222.08,215.93C226.07,213.63 227.8,213.54 229.89,212.46C233.02,210.83 232.27,209.4 239.08,201.25C242.37,197.32 243.25,196.81 243.8,196.53C246.68,195.09 248.06,196.23 253.47,194.86C256.45,194.1 256.67,193.59 258.84,193.42C261.17,193.24 262.24,193.72 265.21,194.01C266.77,194.17 269.06,194.29 271.89,194.06" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M239.34,211.62C242.53,209.15 245.24,208.39 247.15,208.15C249.07,207.91 249.65,208.27 251.4,207.7C253.93,206.87 254.65,205.48 257.12,205.02C258.48,204.76 259.56,204.94 260.36,205.07C265.72,205.96 271.89,211.89 274.53,214.16C277.41,216.64 282.01,219.84 289.11,222.22" style="fill:none;stroke:white;stroke-width:0.75px;"/>
</g>
<g id="Layer_6" transform="matrix(1.19255,0,0,1.11398,-58.9948,-25.8648)">
<path d="M195.89,217.96L203.45,220.18L211,220.18L222.33,217.96L231.49,213.93L239.44,210.4C239.44,210.4 242.77,210.62 244.77,209.51C246.77,208.4 256.1,203.29 256.1,203.29L260.77,200.18L266.99,198.85C266.99,198.85 272.77,199.96 273.43,199.96" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M254.11,209.51L257.44,209.51L260.33,208.18C260.33,208.18 264.77,209.07 266.11,209.51C267.45,209.95 272.11,209.51 272.11,209.51L273.89,208.94L278.33,213.06L286.631,221.356" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M384.972,192.779C384.972,192.779 391.44,186.4 391.44,185.51C391.44,184.62 394.55,174.12 394.55,174.12L398.55,167.28L400.11,161.72C400.11,161.72 402.33,160.16 403.67,159.05C405,157.94 413,151.26 413.23,148.82C413.45,146.38 415.01,139.49 415.01,139.49" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M443.89,141.51C442.56,142.62 425.67,155.73 425.67,155.73C425.67,155.73 421,159.29 418.56,159.95C416.12,160.62 408.56,162.17 408.56,162.17L405.45,163.28L402.12,163.28" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M399.44,172.84L404.11,172.84L415,171.73L421.44,169.84" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M397,214.62L393.44,208.18C393.44,208.18 392.77,205.38 392.55,203C392.33,200.62 390.99,199.51 390.33,197.29C389.66,195.07 387.3,192.69 387.3,192.69" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M408.33,196.4C408.33,196.4 405.66,186.18 405.66,185.51C405.66,184.84 405.66,181.73 405.66,180.29C405.66,178.85 404.55,174.13 404.55,174.13L404.11,172.85" style="fill:none;stroke:white;stroke-width:0.75px;"/>
<path d="M375.952,162.292L380.33,151.07L386.33,139.51L389.89,135.07" style="fill:none;stroke:white;stroke-width:0.75px;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

1
src/media/runCommand.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="16px" height="16px"><path fill="#c2e8ff" d="M24.533 26L0 26 0 34 22.933 34zM21.133 44L22.2 38 0 38 0 44zM20 52L20.533 48 0 48 0 52z"/><path fill="#fff" d="M14.605 65.5L24.413 14.5 79.39 14.5 69.19 65.5z"/><path fill="#788b9c" d="M78.78,15l-10,50h-53.57l9.615-50H78.78 M80,14H24L14,66h55.6L80,14L80,14z"/><path fill="#8bb7f0" d="M26.511 49.5L30.408 30.5 67.39 30.5 63.59 49.5z"/><path fill="#4e7ab5" d="M66.78,31l-3.6,18H27.124l3.691-18H66.78 M68,30H30l-4.102,20H64L68,30L68,30z"/><path fill="#788b9c" d="M79 14L25 14 23 24 77 24z"/><path fill="#fff" d="M31.673 42L61.4 42 62.6 36 32.873 36z"/><path fill="#37474f" d="M38.433 42L39.633 36 38.594 36 37.394 42z"/></svg>

After

Width:  |  Height:  |  Size: 734 B

23
src/test/runTest.ts Normal file
View File

@ -0,0 +1,23 @@
import * as path from 'path';
import { runTests } from 'vscode-test';
async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
// The path to test runner
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');
// Download VS Code, unzip it and run the integration test
await runTests({ extensionDevelopmentPath, extensionTestsPath });
} catch (err) {
console.error('Failed to run tests');
process.exit(1);
}
}
main();

View File

@ -0,0 +1,15 @@
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../../extension';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
test('Sample test', () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});

38
src/test/suite/index.ts Normal file
View File

@ -0,0 +1,38 @@
import * as path from 'path';
import * as Mocha from 'mocha';
import * as glob from 'glob';
export function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: 'tdd',
color: true
});
const testsRoot = path.resolve(__dirname, '..');
return new Promise((c, e) => {
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
if (err) {
return e(err);
}
// Add files to the test suite
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
});
}

21
tsconfig.json Normal file
View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"exclude": [
"node_modules",
".vscode-test"
]
}

View File

@ -0,0 +1,42 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension.
* `package.json` - this is the manifest file in which you declare your extension and command.
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesnt yet need to load the plugin.
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
## Get up and running straight away
* Press `F5` to open a new window with your extension loaded.
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
* Find output from your extension in the debug console.
## Make changes
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Explore the API
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
## Run tests
* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`.
* Press `F5` to run the tests in a new window with your extension loaded.
* See the output of the test result in the debug console.
* Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder.
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
* You can create folders inside the `test` folder to structure your tests any way you want.
## Go further
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace.
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).