Skip to content

Commit 9cb8ba5

Browse files
committedJul 14, 2019
run prettier on all files, setup on save
·
v0.19.40.2.0
1 parent 830c01a commit 9cb8ba5

File tree

27 files changed

+865
-862
lines changed

27 files changed

+865
-862
lines changed
 

‎.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@
3030
"search.exclude": {
3131
"out": true // set this to false to include "out" folder in search results
3232
},
33+
"prettier.eslintIntegration": true,
34+
"editor.formatOnSave": true,
3335
}

‎src/editor/ReactWebView.ts

Lines changed: 124 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -6,134 +6,135 @@ import * as path from 'path'
66
* Manages React webview panels
77
*/
88
class ReactWebView {
9-
// @ts-ignore
10-
private panel: vscode.WebviewPanel
11-
private extensionPath: string
12-
private disposables: vscode.Disposable[] = []
13-
private onReceive: any // TODO: properly type
14-
15-
public constructor(extensionPath: string) {
16-
this.extensionPath = extensionPath
17-
18-
// Create and show a new webview panel
19-
this.panel = this.createWebviewPanel(vscode.ViewColumn.Two)
20-
21-
// Set the webview's initial html content
22-
this.panel.webview.html = this.getHtmlForWebview()
23-
24-
// Listen for when the panel is disposed
25-
// This happens when the user closes the panel or when the panel is closed programatically
26-
// this.panel.onDidDispose(() => this.dispose(), null, this.disposables)
27-
28-
// Handle messages from the webview
29-
const onReceive = (action: string | CR.Action) => vscode.commands.executeCommand('coderoad.receive_action', action)
30-
this.panel.webview.onDidReceiveMessage(onReceive, null, this.disposables)
31-
32-
// update panel on changes
33-
const updateWindows = () => {
34-
35-
vscode.commands.executeCommand('vscode.setEditorLayout', { orientation: 0, groups: [{ groups: [{}], size: 0.6 }, { groups: [{}], size: 0.4 }] })
36-
this.panel.reveal(vscode.ViewColumn.Two)
37-
}
38-
39-
this.panel.onDidDispose(() => {
40-
updateWindows()
41-
})
42-
43-
// this.panel.onDidChangeViewState(() => {
44-
// console.log('onDidChangeViewState')
45-
// updateWindows()
46-
// })
47-
48-
// prevents new panels from going ontop of coderoad panel
49-
vscode.window.onDidChangeActiveTextEditor((param) => {
50-
if (!param || param.viewColumn !== vscode.ViewColumn.Two) {
51-
updateWindows()
52-
}
53-
})
54-
// // prevents moving coderoad panel on top of left panel
55-
vscode.window.onDidChangeVisibleTextEditors((param) => {
56-
updateWindows()
57-
})
58-
59-
// TODO: prevent window from moving to the left when no windows remain on rights
9+
// @ts-ignore
10+
private panel: vscode.WebviewPanel
11+
private extensionPath: string
12+
private disposables: vscode.Disposable[] = []
13+
private onReceive: any // TODO: properly type
14+
15+
public constructor(extensionPath: string) {
16+
this.extensionPath = extensionPath
17+
18+
// Create and show a new webview panel
19+
this.panel = this.createWebviewPanel(vscode.ViewColumn.Two)
20+
21+
// Set the webview's initial html content
22+
this.panel.webview.html = this.getHtmlForWebview()
23+
24+
// Listen for when the panel is disposed
25+
// This happens when the user closes the panel or when the panel is closed programatically
26+
// this.panel.onDidDispose(() => this.dispose(), null, this.disposables)
27+
28+
// Handle messages from the webview
29+
const onReceive = (action: string | CR.Action) => vscode.commands.executeCommand('coderoad.receive_action', action)
30+
this.panel.webview.onDidReceiveMessage(onReceive, null, this.disposables)
31+
32+
// update panel on changes
33+
const updateWindows = () => {
34+
vscode.commands.executeCommand('vscode.setEditorLayout', {
35+
orientation: 0,
36+
groups: [{ groups: [{}], size: 0.6 }, { groups: [{}], size: 0.4 }],
37+
})
38+
this.panel.reveal(vscode.ViewColumn.Two)
6039
}
6140

62-
public createOrShow(column: number): void {
63-
// If we already have a panel, show it.
64-
// Otherwise, create a new panel.
65-
if (this.panel && this.panel.webview) {
66-
this.panel.reveal(column)
67-
} else {
68-
this.panel = this.createWebviewPanel(column)
69-
}
41+
this.panel.onDidDispose(() => {
42+
updateWindows()
43+
})
44+
45+
// this.panel.onDidChangeViewState(() => {
46+
// console.log('onDidChangeViewState')
47+
// updateWindows()
48+
// })
49+
50+
// prevents new panels from going ontop of coderoad panel
51+
vscode.window.onDidChangeActiveTextEditor(param => {
52+
if (!param || param.viewColumn !== vscode.ViewColumn.Two) {
53+
updateWindows()
54+
}
55+
})
56+
// // prevents moving coderoad panel on top of left panel
57+
vscode.window.onDidChangeVisibleTextEditors(param => {
58+
updateWindows()
59+
})
60+
61+
// TODO: prevent window from moving to the left when no windows remain on rights
62+
}
63+
64+
public createOrShow(column: number): void {
65+
// If we already have a panel, show it.
66+
// Otherwise, create a new panel.
67+
if (this.panel && this.panel.webview) {
68+
this.panel.reveal(column)
69+
} else {
70+
this.panel = this.createWebviewPanel(column)
7071
}
71-
72-
private createWebviewPanel(column: number): vscode.WebviewPanel {
73-
const viewType = 'CodeRoad'
74-
const title = 'CodeRoad'
75-
const config = {
76-
// Enable javascript in the webview
77-
enableScripts: true,
78-
// And restric the webview to only loading content from our extension's `media` directory.
79-
localResourceRoots: [vscode.Uri.file(path.join(this.extensionPath, 'build'))],
80-
// prevents destroying the window when it is in the background
81-
retainContextWhenHidden: true,
82-
}
83-
return vscode.window.createWebviewPanel(viewType, title, column, config)
72+
}
73+
74+
private createWebviewPanel(column: number): vscode.WebviewPanel {
75+
const viewType = 'CodeRoad'
76+
const title = 'CodeRoad'
77+
const config = {
78+
// Enable javascript in the webview
79+
enableScripts: true,
80+
// And restric the webview to only loading content from our extension's `media` directory.
81+
localResourceRoots: [vscode.Uri.file(path.join(this.extensionPath, 'build'))],
82+
// prevents destroying the window when it is in the background
83+
retainContextWhenHidden: true,
8484
}
85-
86-
private getNonce(): string {
87-
let text = ''
88-
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
89-
for (let i = 0; i < 32; i++) {
90-
text += possible.charAt(Math.floor(Math.random() * possible.length))
91-
}
92-
return text
85+
return vscode.window.createWebviewPanel(viewType, title, column, config)
86+
}
87+
88+
private getNonce(): string {
89+
let text = ''
90+
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
91+
for (let i = 0; i < 32; i++) {
92+
text += possible.charAt(Math.floor(Math.random() * possible.length))
9393
}
94-
95-
public async postMessage(action: CR.Action): Promise<void> {
96-
// Send a message to the webview webview.
97-
// You can send any JSON serializable data.
98-
const success = await this.panel.webview.postMessage(action)
99-
if (!success) {
100-
throw new Error(`Message post failure: ${JSON.stringify(action)}`)
101-
}
94+
return text
95+
}
96+
97+
public async postMessage(action: CR.Action): Promise<void> {
98+
// Send a message to the webview webview.
99+
// You can send any JSON serializable data.
100+
const success = await this.panel.webview.postMessage(action)
101+
if (!success) {
102+
throw new Error(`Message post failure: ${JSON.stringify(action)}`)
102103
}
104+
}
103105

104-
public dispose(): void {
105-
// Clean up our resources
106-
this.panel.dispose()
106+
public dispose(): void {
107+
// Clean up our resources
108+
this.panel.dispose()
107109

108-
while (this.disposables.length) {
109-
const x = this.disposables.pop()
110-
if (x) {
111-
x.dispose()
112-
}
113-
}
110+
while (this.disposables.length) {
111+
const x = this.disposables.pop()
112+
if (x) {
113+
x.dispose()
114+
}
114115
}
115-
116-
private getHtmlForWebview(): string {
117-
118-
// eslint-disable-next-line
119-
const manifest = require(path.join(this.extensionPath, 'build', 'asset-manifest.json'))
120-
const mainScript = manifest.files['main.js']
121-
// grab first chunk
122-
const chunk = Object.keys(manifest.files).filter(f => f.match(/^static\/js\/.+\.js$/))[0]
123-
const chunkScript = manifest.files[chunk]
124-
const mainStyle = manifest.files['main.css']
125-
126-
const scriptPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', mainScript))
127-
const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' })
128-
const chunkPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', chunkScript))
129-
const chunkUri = chunkPathOnDisk.with({ scheme: 'vscode-resource' })
130-
const stylePathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', mainStyle))
131-
const styleUri = stylePathOnDisk.with({ scheme: 'vscode-resource' })
132-
133-
// Use a nonce to whitelist which scripts can be run
134-
const [n1, n2, n3] = [1, 2, 3].map(this.getNonce)
135-
136-
return `<!DOCTYPE html>
116+
}
117+
118+
private getHtmlForWebview(): string {
119+
// eslint-disable-next-line
120+
const manifest = require(path.join(this.extensionPath, 'build', 'asset-manifest.json'))
121+
const mainScript = manifest.files['main.js']
122+
// grab first chunk
123+
const chunk = Object.keys(manifest.files).filter(f => f.match(/^static\/js\/.+\.js$/))[0]
124+
const chunkScript = manifest.files[chunk]
125+
const mainStyle = manifest.files['main.css']
126+
127+
const scriptPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', mainScript))
128+
const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' })
129+
const chunkPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', chunkScript))
130+
const chunkUri = chunkPathOnDisk.with({ scheme: 'vscode-resource' })
131+
const stylePathOnDisk = vscode.Uri.file(path.join(this.extensionPath, 'build', mainStyle))
132+
const styleUri = stylePathOnDisk.with({ scheme: 'vscode-resource' })
133+
134+
// Use a nonce to whitelist which scripts can be run
135+
const [n1, n2, n3] = [1, 2, 3].map(this.getNonce)
136+
137+
return `<!DOCTYPE html>
137138
<html lang="en">
138139
<head>
139140
<meta charset="utf-8">
@@ -143,7 +144,9 @@ class ReactWebView {
143144
<link rel="manifest" href="./manifest.json" />
144145
<link rel="stylesheet" type="text/css" href="${styleUri}">
145146
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https:; script-src 'nonce-${n1}' 'nonce-${n2}' 'nonce-${n3}'; style-src vscode-resource: 'unsafe-inline' http: https: data:;">
146-
<base href="${vscode.Uri.file(path.join(this.extensionPath, 'build')).with({ scheme: 'vscode-resource' })}/">
147+
<base href="${vscode.Uri.file(path.join(this.extensionPath, 'build')).with({
148+
scheme: 'vscode-resource',
149+
})}/">
147150
<style></style>
148151
</head>
149152
@@ -155,7 +158,7 @@ class ReactWebView {
155158
<script nonce="${n3}" src="${scriptUri}"></script>
156159
</body>
157160
</html>`
158-
}
161+
}
159162
}
160163

161164
export default ReactWebView

‎src/editor/commands/index.ts

Lines changed: 116 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -7,127 +7,133 @@ import * as CR from 'typings'
77
import runTest from './runTest'
88

99
const COMMANDS = {
10-
START: 'coderoad.start',
11-
TUTORIAL_LAUNCH: 'coderoad.tutorial_launch',
12-
TUTORIAL_SETUP: 'coderoad.tutorial_setup',
13-
OPEN_WEBVIEW: 'coderoad.open_webview',
14-
SEND_STATE: 'coderoad.send_state',
15-
SEND_DATA: 'coderoad.send_data',
16-
RECEIVE_ACTION: 'coderoad.receive_action',
17-
OPEN_FILE: 'coderoad.open_file',
18-
RUN_TEST: 'coderoad.run_test',
19-
TEST_PASS: 'coderoad.test_pass',
20-
TEST_FAIL: 'coderoad.test_fail',
21-
SET_LAYOUT: 'coderoad.set_layout',
10+
START: 'coderoad.start',
11+
TUTORIAL_LAUNCH: 'coderoad.tutorial_launch',
12+
TUTORIAL_SETUP: 'coderoad.tutorial_setup',
13+
OPEN_WEBVIEW: 'coderoad.open_webview',
14+
SEND_STATE: 'coderoad.send_state',
15+
SEND_DATA: 'coderoad.send_data',
16+
RECEIVE_ACTION: 'coderoad.receive_action',
17+
OPEN_FILE: 'coderoad.open_file',
18+
RUN_TEST: 'coderoad.run_test',
19+
TEST_PASS: 'coderoad.test_pass',
20+
TEST_FAIL: 'coderoad.test_fail',
21+
SET_LAYOUT: 'coderoad.set_layout',
2222
}
2323

2424
interface CreateCommandProps {
25-
context: vscode.ExtensionContext,
26-
machine: CR.StateMachine,
27-
storage: any,
28-
git: any
29-
position: any
25+
context: vscode.ExtensionContext
26+
machine: CR.StateMachine
27+
storage: any
28+
git: any
29+
position: any
3030
}
3131

3232
// React panel webview
33-
let webview: any;
33+
let webview: any
3434

3535
export const createCommands = ({ context, machine, storage, git, position }: CreateCommandProps) => ({
36-
// initialize
37-
[COMMANDS.START]: () => {
38-
// set local storage workspace
39-
setStorage(context.workspaceState)
36+
// initialize
37+
[COMMANDS.START]: () => {
38+
// set local storage workspace
39+
setStorage(context.workspaceState)
4040

41-
// activate machine
42-
webview = new ReactWebView(context.extensionPath)
43-
console.log('webview', webview.panel.webview.postMessage)
44-
machine.activate()
45-
},
46-
// open React webview
47-
[COMMANDS.OPEN_WEBVIEW]: (column: number = vscode.ViewColumn.Two) => {
48-
// setup 1x1 horizontal layout
49-
vscode.commands.executeCommand('vscode.setEditorLayout', { orientation: 0, groups: [{ groups: [{}], size: 0.6 }, { groups: [{}], size: 0.4 }] })
50-
webview.createOrShow(column);
51-
// NOTE: createOrShow and layout command cannot be async
52-
// this creates an async issue where the webview cannot detect when it has been initialized
53-
setTimeout(() => {
54-
machine.send('WEBVIEW_INITIALIZED')
55-
}, 2000)
56-
},
57-
// launch a new tutorial
58-
// NOTE: may be better to move into action as logic is primarily non-vscode
59-
[COMMANDS.TUTORIAL_LAUNCH]: async (tutorial: CR.Tutorial) => {
60-
console.log('launch tutorial')
41+
// activate machine
42+
webview = new ReactWebView(context.extensionPath)
43+
console.log('webview', webview.panel.webview.postMessage)
44+
machine.activate()
45+
},
46+
// open React webview
47+
[COMMANDS.OPEN_WEBVIEW]: (column: number = vscode.ViewColumn.Two) => {
48+
// setup 1x1 horizontal layout
49+
vscode.commands.executeCommand('vscode.setEditorLayout', {
50+
orientation: 0,
51+
groups: [{ groups: [{}], size: 0.6 }, { groups: [{}], size: 0.4 }],
52+
})
53+
webview.createOrShow(column)
54+
// NOTE: createOrShow and layout command cannot be async
55+
// this creates an async issue where the webview cannot detect when it has been initialized
56+
setTimeout(() => {
57+
machine.send('WEBVIEW_INITIALIZED')
58+
}, 2000)
59+
},
60+
// launch a new tutorial
61+
// NOTE: may be better to move into action as logic is primarily non-vscode
62+
[COMMANDS.TUTORIAL_LAUNCH]: async (tutorial: CR.Tutorial) => {
63+
console.log('launch tutorial')
6164

62-
await isEmptyWorkspace()
65+
await isEmptyWorkspace()
6366

64-
await git.gitInitIfNotExists()
67+
await git.gitInitIfNotExists()
6568

66-
// TODO: use actual tutorial repo
67-
await Promise.all([git.gitSetupRemote(tutorial.meta.repo), storage.setTutorial(tutorial), storage.resetProgress()])
69+
// TODO: use actual tutorial repo
70+
await Promise.all([git.gitSetupRemote(tutorial.meta.repo), storage.setTutorial(tutorial), storage.resetProgress()])
6871

69-
// TODO: refactor to allow client to call initialization
70-
const pos: CR.Position = await position.getInitial(tutorial)
72+
// TODO: refactor to allow client to call initialization
73+
const pos: CR.Position = await position.getInitial(tutorial)
7174

72-
// eslint-disable-next-line
73-
const { steps } = tutorial.data
74-
const { setup } = steps[pos.stepId].actions
75-
await git.gitLoadCommits(setup)
76-
machine.send('TUTORIAL_LOADED')
77-
},
78-
[COMMANDS.TUTORIAL_SETUP]: async (tutorial: CR.Tutorial) => {
79-
console.log('tutorial setup', tutorial)
80-
// setup onSave hook
81-
const languageIds = tutorial.meta.languages
82-
console.log(`languageIds: ${languageIds.join(', ')}`)
83-
vscode.workspace.onDidSaveTextDocument((document: vscode.TextDocument) => {
84-
console.log('save document', document)
85-
if (languageIds.includes(document.languageId) && document.uri.scheme === 'file') {
86-
// do work
87-
machine.send('TEST_RUN')
88-
}
89-
})
90-
},
91-
// open a file
92-
[COMMANDS.OPEN_FILE]: async (relativeFilePath: string) => {
93-
console.log(`OPEN_FILE ${JSON.stringify(relativeFilePath)}`)
94-
try {
95-
const workspaceRoot = vscode.workspace.rootPath
96-
if (!workspaceRoot) {
97-
throw new Error('No workspace root path')
98-
}
99-
const absoluteFilePath = join(workspaceRoot, relativeFilePath)
100-
const doc = await vscode.workspace.openTextDocument(absoluteFilePath)
101-
await vscode.window.showTextDocument(doc, vscode.ViewColumn.One)
102-
} catch (error) {
103-
console.log(`Failed to open file ${relativeFilePath}`, error)
104-
}
105-
},
106-
// send messages to webview
107-
[COMMANDS.SEND_STATE]: (payload: { data: any, state: any }) => {
108-
webview.postMessage({ type: 'SET_STATE', payload })
109-
},
110-
[COMMANDS.SEND_DATA]: (payload: { data: any }) => {
111-
webview.postMessage({ type: 'SET_DATA', payload })
112-
},
113-
[COMMANDS.RECEIVE_ACTION]: (action: string | CR.Action) => {
114-
// send received actions from web-app into state machine
115-
machine.send(action)
116-
},
117-
[COMMANDS.RUN_TEST]: () => {
118-
runTest({
119-
onSuccess: () => machine.send('TEST_PASS'),
120-
onFail: () => machine.send('TEST_FAIL')
121-
})
122-
},
123-
[COMMANDS.TEST_PASS]: () => {
124-
vscode.window.showInformationMessage('PASS')
125-
},
126-
[COMMANDS.TEST_FAIL]: () => {
127-
vscode.window.showWarningMessage('FAIL')
128-
},
129-
[COMMANDS.SET_LAYOUT]: () => {
130-
console.log('setLayout')
131-
vscode.commands.executeCommand('vscode.setEditorLayout', { orientation: 0, groups: [{ groups: [{}], size: 0.6 }, { groups: [{}], size: 0.4 }] })
132-
},
133-
})
75+
// eslint-disable-next-line
76+
const { steps } = tutorial.data
77+
const { setup } = steps[pos.stepId].actions
78+
await git.gitLoadCommits(setup)
79+
machine.send('TUTORIAL_LOADED')
80+
},
81+
[COMMANDS.TUTORIAL_SETUP]: async (tutorial: CR.Tutorial) => {
82+
console.log('tutorial setup', tutorial)
83+
// setup onSave hook
84+
const languageIds = tutorial.meta.languages
85+
console.log(`languageIds: ${languageIds.join(', ')}`)
86+
vscode.workspace.onDidSaveTextDocument((document: vscode.TextDocument) => {
87+
console.log('save document', document)
88+
if (languageIds.includes(document.languageId) && document.uri.scheme === 'file') {
89+
// do work
90+
machine.send('TEST_RUN')
91+
}
92+
})
93+
},
94+
// open a file
95+
[COMMANDS.OPEN_FILE]: async (relativeFilePath: string) => {
96+
console.log(`OPEN_FILE ${JSON.stringify(relativeFilePath)}`)
97+
try {
98+
const workspaceRoot = vscode.workspace.rootPath
99+
if (!workspaceRoot) {
100+
throw new Error('No workspace root path')
101+
}
102+
const absoluteFilePath = join(workspaceRoot, relativeFilePath)
103+
const doc = await vscode.workspace.openTextDocument(absoluteFilePath)
104+
await vscode.window.showTextDocument(doc, vscode.ViewColumn.One)
105+
} catch (error) {
106+
console.log(`Failed to open file ${relativeFilePath}`, error)
107+
}
108+
},
109+
// send messages to webview
110+
[COMMANDS.SEND_STATE]: (payload: { data: any; state: any }) => {
111+
webview.postMessage({ type: 'SET_STATE', payload })
112+
},
113+
[COMMANDS.SEND_DATA]: (payload: { data: any }) => {
114+
webview.postMessage({ type: 'SET_DATA', payload })
115+
},
116+
[COMMANDS.RECEIVE_ACTION]: (action: string | CR.Action) => {
117+
// send received actions from web-app into state machine
118+
machine.send(action)
119+
},
120+
[COMMANDS.RUN_TEST]: () => {
121+
runTest({
122+
onSuccess: () => machine.send('TEST_PASS'),
123+
onFail: () => machine.send('TEST_FAIL'),
124+
})
125+
},
126+
[COMMANDS.TEST_PASS]: () => {
127+
vscode.window.showInformationMessage('PASS')
128+
},
129+
[COMMANDS.TEST_FAIL]: () => {
130+
vscode.window.showWarningMessage('FAIL')
131+
},
132+
[COMMANDS.SET_LAYOUT]: () => {
133+
console.log('setLayout')
134+
vscode.commands.executeCommand('vscode.setEditorLayout', {
135+
orientation: 0,
136+
groups: [{ groups: [{}], size: 0.6 }, { groups: [{}], size: 0.4 }],
137+
})
138+
},
139+
})

‎src/editor/commands/runTest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const getOutputChannel = (name: string): vscode.OutputChannel => {
2121
}
2222

2323
interface Props {
24-
onSuccess(): void,
24+
onSuccess(): void
2525
onFail(): void
2626
}
2727

‎src/editor/index.ts

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,64 +6,63 @@ import * as git from '../services/git'
66
import * as position from '../services/position'
77

88
interface Props {
9-
machine: CR.StateMachine,
10-
setWorkspaceRoot(rootPath: string): void
9+
machine: CR.StateMachine
10+
setWorkspaceRoot(rootPath: string): void
1111
}
1212

1313
class Editor {
14-
// extension context set on activation
15-
// @ts-ignore
16-
private context: vscode.ExtensionContext
17-
private machine: CR.StateMachine
14+
// extension context set on activation
15+
// @ts-ignore
16+
private context: vscode.ExtensionContext
17+
private machine: CR.StateMachine
1818

19-
constructor({ machine, setWorkspaceRoot }: Props) {
20-
this.machine = machine
19+
constructor({ machine, setWorkspaceRoot }: Props) {
20+
this.machine = machine
2121

22-
// set workspace root for node executions
23-
const { workspace } = vscode
24-
const { rootPath } = workspace
25-
if (!rootPath) {
26-
throw new Error('Requires a workspace. Please open a folder')
27-
}
28-
setWorkspaceRoot(rootPath)
22+
// set workspace root for node executions
23+
const { workspace } = vscode
24+
const { rootPath } = workspace
25+
if (!rootPath) {
26+
throw new Error('Requires a workspace. Please open a folder')
2927
}
28+
setWorkspaceRoot(rootPath)
29+
}
3030

31-
private activateCommands = (): void => {
32-
const commands = createCommands({
33-
context: this.context,
34-
machine: this.machine,
35-
storage,
36-
git,
37-
position,
38-
})
39-
for (const cmd in commands) {
40-
const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd])
41-
this.context.subscriptions.push(command)
42-
}
31+
private activateCommands = (): void => {
32+
const commands = createCommands({
33+
context: this.context,
34+
machine: this.machine,
35+
storage,
36+
git,
37+
position,
38+
})
39+
for (const cmd in commands) {
40+
const command: vscode.Disposable = vscode.commands.registerCommand(cmd, commands[cmd])
41+
this.context.subscriptions.push(command)
4342
}
44-
public activate = (context: vscode.ExtensionContext): void => {
45-
console.log('ACTIVATE!')
46-
this.context = context
47-
// commands
48-
this.activateCommands()
43+
}
44+
public activate = (context: vscode.ExtensionContext): void => {
45+
console.log('ACTIVATE!')
46+
this.context = context
47+
// commands
48+
this.activateCommands()
4949

50-
// setup tasks or views here
51-
52-
}
53-
public deactivate = (): void => {
54-
console.log('DEACTIVATE!')
55-
// cleanup subscriptions/tasks
56-
for (const disposable of this.context.subscriptions) {
57-
disposable.dispose()
58-
}
59-
// shut down state machine
60-
this.machine.deactivate()
50+
// setup tasks or views here
51+
}
52+
public deactivate = (): void => {
53+
console.log('DEACTIVATE!')
54+
// cleanup subscriptions/tasks
55+
for (const disposable of this.context.subscriptions) {
56+
disposable.dispose()
6157
}
58+
// shut down state machine
59+
this.machine.deactivate()
60+
}
6261

63-
// execute vscode command
64-
public dispatch = (type: string, payload?: any) => {
65-
vscode.commands.executeCommand(type, payload)
66-
}
62+
// execute vscode command
63+
public dispatch = (type: string, payload?: any) => {
64+
vscode.commands.executeCommand(type, payload)
65+
}
6766
}
6867

69-
export default Editor
68+
export default Editor

‎src/editor/storage.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,3 @@ export function get<T>(key: string): T | undefined {
1515
export function update<T>(key: string, value: string | Object): Thenable<void> {
1616
return storage.update(key, value)
1717
}
18-

‎src/extension.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import { setWorkspaceRoot } from './services/node'
33
import StateMachine from './state'
44
import Editor from './editor'
55

6-
76
// state machine that governs application logic
87
export const machine = new StateMachine({ dispatch: vscode.commands.executeCommand })
98

109
// vscode editor
1110
export const editor = new Editor({
12-
machine,
13-
setWorkspaceRoot,
11+
machine,
12+
setWorkspaceRoot,
1413
})
1514

1615
// activate run on vscode extension initialization

‎src/services/node/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ export const exists = (...paths: string[]): boolean => fs.existsSync(join(worksp
3131
// console.error(stderr)
3232
// throw new Error('Error removing all files & folders')
3333
// }
34-
// }
34+
// }

‎src/services/position.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ export async function loadProgressPosition() {
6262
storage.setPosition(position)
6363
}
6464

65-
export async function getPrev(): Promise<void> { }
65+
export async function getPrev(): Promise<void> {}

‎src/services/storage.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import * as storage from '../editor/storage'
55
const STORE_TUTORIAL = 'coderoad:tutorial'
66

77
export async function getTutorial(): Promise<CR.Tutorial | undefined> {
8-
return storage.get<CR.Tutorial>(STORE_TUTORIAL)
8+
return storage.get<CR.Tutorial>(STORE_TUTORIAL)
99
}
1010

1111
export async function setTutorial(tutorial: CR.Tutorial): Promise<void> {
12-
await storage.update<CR.Tutorial>(STORE_TUTORIAL, tutorial)
12+
await storage.update<CR.Tutorial>(STORE_TUTORIAL, tutorial)
1313
}
1414

1515
// POSITION
@@ -18,12 +18,12 @@ const STORE_POSITION = 'coderoad:position'
1818
const defaultPosition = { levelId: '', stageId: '', stepId: '' }
1919

2020
export async function getPosition(): Promise<CR.Position> {
21-
const position: CR.Position | undefined = storage.get<CR.Position>(STORE_POSITION)
22-
return position || defaultPosition
21+
const position: CR.Position | undefined = storage.get<CR.Position>(STORE_POSITION)
22+
return position || defaultPosition
2323
}
2424

2525
export async function setPosition(position: CR.Position): Promise<void> {
26-
await storage.update<CR.Position>(STORE_POSITION, position)
26+
await storage.update<CR.Position>(STORE_POSITION, position)
2727
}
2828

2929
// PROGRESS
@@ -32,46 +32,46 @@ const STORE_PROGRESS = 'coderoad:progress'
3232
const defaultProgress = { levels: {}, stages: {}, steps: {}, hints: {}, complete: false }
3333

3434
export async function getProgress(): Promise<CR.Progress> {
35-
const progress: CR.Progress | undefined = await storage.get<CR.Progress>(STORE_PROGRESS)
36-
return progress || defaultProgress
35+
const progress: CR.Progress | undefined = await storage.get<CR.Progress>(STORE_PROGRESS)
36+
return progress || defaultProgress
3737
}
3838

3939
export async function resetProgress(): Promise<void> {
40-
await storage.update<CR.Progress>(STORE_PROGRESS, defaultProgress)
40+
await storage.update<CR.Progress>(STORE_PROGRESS, defaultProgress)
4141
}
4242

4343
interface ProgressUpdate {
44-
levels?: {
45-
[levelId: string]: boolean
46-
}
47-
stages?: {
48-
[stageid: string]: boolean
49-
}
50-
steps?: {
51-
[stepId: string]: boolean
52-
}
44+
levels?: {
45+
[levelId: string]: boolean
46+
}
47+
stages?: {
48+
[stageid: string]: boolean
49+
}
50+
steps?: {
51+
[stepId: string]: boolean
52+
}
5353
}
5454

5555
export async function setProgress(record: ProgressUpdate): Promise<void> {
56-
const progress = await getProgress()
57-
if (record.levels) {
58-
progress.levels = {
59-
...progress.levels,
60-
...record.levels,
61-
}
56+
const progress = await getProgress()
57+
if (record.levels) {
58+
progress.levels = {
59+
...progress.levels,
60+
...record.levels,
6261
}
63-
if (record.stages) {
64-
progress.stages = {
65-
...progress.stages,
66-
...record.stages,
67-
}
62+
}
63+
if (record.stages) {
64+
progress.stages = {
65+
...progress.stages,
66+
...record.stages,
6867
}
69-
if (record.steps) {
70-
progress.steps = {
71-
...progress.steps,
72-
...record.steps,
73-
}
68+
}
69+
if (record.steps) {
70+
progress.steps = {
71+
...progress.steps,
72+
...record.steps,
7473
}
74+
}
7575

76-
await storage.update<CR.Progress>(STORE_PROGRESS, progress)
76+
await storage.update<CR.Progress>(STORE_PROGRESS, progress)
7777
}

‎src/state/actions/index.ts

Lines changed: 138 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -8,162 +8,159 @@ import * as git from '../../services/git'
88

99
let currentTutorial: CR.Tutorial | undefined
1010
let currentProgress: CR.Progress = {
11-
levels: {},
12-
stages: {},
13-
steps: {},
14-
complete: false,
11+
levels: {},
12+
stages: {},
13+
steps: {},
14+
complete: false,
1515
}
1616

1717
export default (dispatch: CR.EditorDispatch) => ({
18-
createWebview() {
19-
dispatch('coderoad.open_webview')
20-
},
21-
async newOrContinue() {
22-
// verify that the user has a tutorial & progress
23-
// verify git is setup with a coderoad remote
24-
const [tutorial, progress, hasGit, hasGitRemote] = await Promise.all([
25-
storage.getTutorial(),
26-
storage.getProgress(),
27-
git.gitVersion(),
28-
git.gitCheckRemoteExists(),
29-
])
30-
const canContinue = !!(tutorial && progress && hasGit && hasGitRemote)
18+
createWebview() {
19+
dispatch('coderoad.open_webview')
20+
},
21+
async newOrContinue() {
22+
// verify that the user has a tutorial & progress
23+
// verify git is setup with a coderoad remote
24+
const [tutorial, progress, hasGit, hasGitRemote] = await Promise.all([
25+
storage.getTutorial(),
26+
storage.getProgress(),
27+
git.gitVersion(),
28+
git.gitCheckRemoteExists(),
29+
])
30+
const canContinue = !!(tutorial && progress && hasGit && hasGitRemote)
3131

32-
if (canContinue) {
33-
// continue
34-
currentTutorial = tutorial
35-
currentProgress = progress
36-
}
32+
if (canContinue) {
33+
// continue
34+
currentTutorial = tutorial
35+
currentProgress = progress
36+
}
3737

38-
machine.send(canContinue ? 'CONTINUE' : 'NEW')
38+
machine.send(canContinue ? 'CONTINUE' : 'NEW')
39+
},
40+
async tutorialLaunch() {
41+
// TODO: add selection of tutorial id
42+
const tutorial: CR.Tutorial = await api({ resource: 'getTutorial', params: { id: '1' } })
43+
currentTutorial = tutorial
44+
console.log('api')
45+
console.log(tutorial)
46+
dispatch('coderoad.tutorial_launch', tutorial)
47+
},
48+
tutorialSetup() {
49+
dispatch('coderoad.tutorial_setup', currentTutorial)
50+
dispatch('coderoad.open_webview', 2)
51+
},
52+
initializeNewTutorial: assign({
53+
position: (context: any): CR.Position => {
54+
const { data } = context
55+
const levelId = data.summary.levelList[0]
56+
const stageId = data.levels[levelId].stageList[0]
57+
const stepId = data.stages[stageId].stepList[0]
58+
return {
59+
levelId,
60+
stageId,
61+
stepId,
62+
}
3963
},
40-
async tutorialLaunch() {
41-
// TODO: add selection of tutorial id
42-
const tutorial: CR.Tutorial = await api({ resource: 'getTutorial', params: { id: '1' } })
43-
currentTutorial = tutorial
44-
console.log('api')
45-
console.log(tutorial)
46-
dispatch('coderoad.tutorial_launch', tutorial)
64+
}),
65+
tutorialContinue: assign({
66+
// load initial data, progress & position
67+
data(): CR.TutorialData {
68+
console.log('ACTION: tutorialLoad.data')
69+
if (!currentTutorial) {
70+
throw new Error('No Tutorial loaded')
71+
}
72+
return currentTutorial.data
4773
},
48-
tutorialSetup() {
49-
dispatch('coderoad.tutorial_setup', currentTutorial)
50-
dispatch('coderoad.open_webview', 2)
74+
progress(): CR.Progress {
75+
console.log('ACTION: tutorialLoad.progress')
76+
return currentProgress
5177
},
52-
initializeNewTutorial: assign({
53-
position: (context: any): CR.Position => {
54-
const { data } = context
55-
const levelId = data.summary.levelList[0]
56-
const stageId = data.levels[levelId].stageList[0]
57-
const stepId = data.stages[stageId].stepList[0]
58-
return {
59-
levelId,
60-
stageId,
61-
stepId
62-
}
63-
}
64-
}),
65-
tutorialContinue: assign({
66-
// load initial data, progress & position
67-
data(): CR.TutorialData {
68-
console.log('ACTION: tutorialLoad.data')
69-
if (!currentTutorial) {
70-
throw new Error('No Tutorial loaded')
71-
}
72-
return currentTutorial.data
73-
74-
},
75-
progress(): CR.Progress {
76-
console.log('ACTION: tutorialLoad.progress')
77-
return currentProgress
78-
},
79-
position(context: any): CR.Position {
80-
console.log('ACTION: tutorialLoad.position')
81-
if (!currentTutorial) {
82-
throw new Error('No Tutorial loaded')
83-
}
78+
position(context: any): CR.Position {
79+
console.log('ACTION: tutorialLoad.position')
80+
if (!currentTutorial) {
81+
throw new Error('No Tutorial loaded')
82+
}
8483

85-
const { data } = currentTutorial
84+
const { data } = currentTutorial
8685

87-
const { levelList } = data.summary
88-
// take next incomplete level or the final step
89-
const levelId = levelList.find((id: string) => !currentProgress.levels[id]) || levelList[levelList.length - 1]
90-
const { stageList } = data.levels[levelId]
91-
const stageId = stageList.find((id: string) => !currentProgress.stages[id]) || stageList[stageList.length - 1]
92-
const { stepList } = data.stages[stageId]
93-
const stepId = stepList.find((id: string) => !currentProgress.steps[id]) || stepList[stepList.length - 1]
86+
const { levelList } = data.summary
87+
// take next incomplete level or the final step
88+
const levelId = levelList.find((id: string) => !currentProgress.levels[id]) || levelList[levelList.length - 1]
89+
const { stageList } = data.levels[levelId]
90+
const stageId = stageList.find((id: string) => !currentProgress.stages[id]) || stageList[stageList.length - 1]
91+
const { stepList } = data.stages[stageId]
92+
const stepId = stepList.find((id: string) => !currentProgress.steps[id]) || stepList[stepList.length - 1]
9493

95-
const position = {
96-
levelId,
97-
stageId,
98-
stepId
99-
}
100-
console.log('position', position)
101-
return position
102-
}
103-
}),
104-
testStart() {
105-
dispatch('coderoad.run_test')
94+
const position = {
95+
levelId,
96+
stageId,
97+
stepId,
98+
}
99+
console.log('position', position)
100+
return position
106101
},
107-
testPass() {
108-
dispatch('coderoad.test_pass')
109-
},
110-
testFail() {
111-
dispatch('coderoad.test_fail')
112-
},
113-
// @ts-ignore
114-
progressUpdate: assign({
115-
progress: (context: CR.MachineContext): CR.Progress => {
116-
const { progress, position, data } = context
117-
const nextProgress = progress
102+
}),
103+
testStart() {
104+
dispatch('coderoad.run_test')
105+
},
106+
testPass() {
107+
dispatch('coderoad.test_pass')
108+
},
109+
testFail() {
110+
dispatch('coderoad.test_fail')
111+
},
112+
// @ts-ignore
113+
progressUpdate: assign({
114+
progress: (context: CR.MachineContext): CR.Progress => {
115+
const { progress, position, data } = context
116+
const nextProgress = progress
118117

119-
nextProgress.steps[position.stepId] = true
120-
const { stepList } = data.stages[position.stageId]
121-
const stageComplete = stepList[stepList.length - 1] === position.stepId
122-
if (stageComplete) {
123-
nextProgress.stages[position.stageId] = true
124-
const { stageList } = data.levels[position.levelId]
125-
const levelComplete = stageList[stageList.length - 1] === position.stageId
126-
if (levelComplete) {
127-
nextProgress.levels[position.levelId] = true
128-
const { levelList } = data.summary
129-
const tutorialComplete = levelList[levelList.length - 1] === position.levelId
130-
if (tutorialComplete) {
131-
nextProgress.complete = true
132-
}
133-
}
134-
}
135-
console.log('progress update', nextProgress)
136-
storage.setProgress(nextProgress)
137-
return nextProgress
118+
nextProgress.steps[position.stepId] = true
119+
const { stepList } = data.stages[position.stageId]
120+
const stageComplete = stepList[stepList.length - 1] === position.stepId
121+
if (stageComplete) {
122+
nextProgress.stages[position.stageId] = true
123+
const { stageList } = data.levels[position.levelId]
124+
const levelComplete = stageList[stageList.length - 1] === position.stageId
125+
if (levelComplete) {
126+
nextProgress.levels[position.levelId] = true
127+
const { levelList } = data.summary
128+
const tutorialComplete = levelList[levelList.length - 1] === position.levelId
129+
if (tutorialComplete) {
130+
nextProgress.complete = true
131+
}
138132
}
139-
}),
140-
stepLoadNext: assign({
141-
position: (context: any): CR.Position => {
142-
const { data, position } = context
143-
const { stepList } = data.stages[position.stageId]
144-
const currentStepIndex = stepList.indexOf(position.stepId)
133+
}
134+
console.log('progress update', nextProgress)
135+
storage.setProgress(nextProgress)
136+
return nextProgress
137+
},
138+
}),
139+
stepLoadNext: assign({
140+
position: (context: any): CR.Position => {
141+
const { data, position } = context
142+
const { stepList } = data.stages[position.stageId]
143+
const currentStepIndex = stepList.indexOf(position.stepId)
145144

146-
const nextStepId = (currentStepIndex < stepList.length)
147-
? stepList[currentStepIndex + 1]
148-
: position.stepId
145+
const nextStepId = currentStepIndex < stepList.length ? stepList[currentStepIndex + 1] : position.stepId
149146

150-
const nextPosition = {
151-
...context.position,
152-
stepId: nextStepId,
153-
}
147+
const nextPosition = {
148+
...context.position,
149+
stepId: nextStepId,
150+
}
154151

155-
return nextPosition
156-
}
157-
}),
158-
loadLevel() {
159-
console.log('loadLevel')
160-
},
161-
loadStage() {
162-
console.log('loadStage')
152+
return nextPosition
163153
},
164-
stepLoadCommits(context: CR.MachineContext): void {
165-
const { data, position } = context
166-
const { setup } = data.steps[position.stepId].actions
167-
git.gitLoadCommits(setup, dispatch)
168-
}
169-
})
154+
}),
155+
loadLevel() {
156+
console.log('loadLevel')
157+
},
158+
loadStage() {
159+
console.log('loadStage')
160+
},
161+
stepLoadCommits(context: CR.MachineContext): void {
162+
const { data, position } = context
163+
const { setup } = data.steps[position.stepId].actions
164+
git.gitLoadCommits(setup, dispatch)
165+
},
166+
})

‎src/state/context/index.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import basicTutorialData from '../../tutorials/basic'
22
import * as CR from 'typings'
33

44
const tutorialContext: CR.MachineContext = {
5-
position: {
6-
levelId: '',
7-
stageId: '',
8-
stepId: '',
9-
},
10-
progress: {
11-
levels: {},
12-
stages: {},
13-
steps: {},
14-
complete: false,
15-
},
16-
// TODO: load tutorial instead of preloading demo
17-
data: basicTutorialData.data,
5+
position: {
6+
levelId: '',
7+
stageId: '',
8+
stepId: '',
9+
},
10+
progress: {
11+
levels: {},
12+
stages: {},
13+
steps: {},
14+
complete: false,
15+
},
16+
// TODO: load tutorial instead of preloading demo
17+
data: basicTutorialData.data,
1818
}
1919

20-
export default tutorialContext
20+
export default tutorialContext

‎src/state/guards/index.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
import * as CR from 'typings'
22

33
export default {
4-
hasNextStep: (context: CR.MachineContext): boolean => {
5-
const { data, position, progress } = context
6-
const steps = data.stages[position.stageId].stepList
7-
// isn't final step yet
8-
const hasNext = (steps[steps.length - 1] !== position.stepId) || !progress.stages[position.stageId]
9-
console.log('GUARD: hasNextStep', hasNext)
10-
return hasNext
11-
},
12-
hasNextStage: (context: CR.MachineContext): boolean => {
13-
const { data, position } = context
14-
const stages = data.levels[position.levelId].stageList
15-
const hasNext = stages[stages.length - 1] !== position.stageId
16-
console.log('GUARD: hasNextStage', hasNext)
17-
return hasNext
18-
},
19-
hasNextLevel: (context: CR.MachineContext): boolean => {
20-
const { data, position } = context
21-
const levels = data.summary.levelList
22-
const hasNext = levels[levels.length - 1] !== position.levelId
23-
console.log('GUARD: hasNextLevel', hasNext)
24-
return hasNext
25-
},
4+
hasNextStep: (context: CR.MachineContext): boolean => {
5+
const { data, position, progress } = context
6+
const steps = data.stages[position.stageId].stepList
7+
// isn't final step yet
8+
const hasNext = steps[steps.length - 1] !== position.stepId || !progress.stages[position.stageId]
9+
console.log('GUARD: hasNextStep', hasNext)
10+
return hasNext
11+
},
12+
hasNextStage: (context: CR.MachineContext): boolean => {
13+
const { data, position } = context
14+
const stages = data.levels[position.levelId].stageList
15+
const hasNext = stages[stages.length - 1] !== position.stageId
16+
console.log('GUARD: hasNextStage', hasNext)
17+
return hasNext
18+
},
19+
hasNextLevel: (context: CR.MachineContext): boolean => {
20+
const { data, position } = context
21+
const levels = data.summary.levelList
22+
const hasNext = levels[levels.length - 1] !== position.levelId
23+
console.log('GUARD: hasNextLevel', hasNext)
24+
return hasNext
25+
},
2626
}

‎src/state/index.ts

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,42 @@ import createMachine from './machine'
66
// https://xstate.js.org/docs/guides/interpretation.html
77

88
interface Props {
9-
dispatch: CR.EditorDispatch
9+
dispatch: CR.EditorDispatch
1010
}
1111

1212
class StateMachine {
13-
private machineOptions = {
14-
logger: console.log,
15-
devTools: true,
16-
deferEvents: true,
17-
execute: true
18-
}
19-
private service: Interpreter<CR.MachineContext, CR.MachineStateSchema, CR.MachineEvent>
20-
constructor({ dispatch }: Props) {
21-
const machine = createMachine(dispatch)
22-
this.service = interpret(machine, this.machineOptions)
23-
// logging
24-
.onTransition(state => {
25-
console.log('onTransition', state)
26-
if (state.changed) {
27-
console.log('next state')
28-
console.log(state.value)
29-
dispatch('coderoad.send_state', { state: state.value, data: state.context })
30-
} else {
31-
dispatch('coderoad.send_data', { data: state.context })
32-
}
33-
})
34-
}
35-
activate() {
36-
// initialize
37-
this.service.start()
38-
}
39-
deactivate() {
40-
this.service.stop()
41-
}
42-
send(action: string | CR.Action) {
43-
this.service.send(action)
44-
}
13+
private machineOptions = {
14+
logger: console.log,
15+
devTools: true,
16+
deferEvents: true,
17+
execute: true,
18+
}
19+
private service: Interpreter<CR.MachineContext, CR.MachineStateSchema, CR.MachineEvent>
20+
constructor({ dispatch }: Props) {
21+
const machine = createMachine(dispatch)
22+
this.service = interpret(machine, this.machineOptions)
23+
// logging
24+
.onTransition(state => {
25+
console.log('onTransition', state)
26+
if (state.changed) {
27+
console.log('next state')
28+
console.log(state.value)
29+
dispatch('coderoad.send_state', { state: state.value, data: state.context })
30+
} else {
31+
dispatch('coderoad.send_data', { data: state.context })
32+
}
33+
})
34+
}
35+
activate() {
36+
// initialize
37+
this.service.start()
38+
}
39+
deactivate() {
40+
this.service.stop()
41+
}
42+
send(action: string | CR.Action) {
43+
this.service.send(action)
44+
}
4545
}
4646

4747
export default StateMachine

‎src/state/machine.ts

Lines changed: 157 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -5,169 +5,168 @@ import createActions from './actions'
55
import guards from './guards'
66
import initialContext from './context'
77

8-
export const machine = (dispatch: CR.EditorDispatch) => Machine<
9-
CR.MachineContext,
10-
CR.MachineStateSchema,
11-
CR.MachineEvent
12-
>(
8+
export const machine = (dispatch: CR.EditorDispatch) =>
9+
Machine<CR.MachineContext, CR.MachineStateSchema, CR.MachineEvent>(
1310
{
14-
id: 'root',
15-
context: initialContext,
16-
initial: 'SelectTutorial',
17-
states: {
18-
SelectTutorial: {
19-
onEntry: ['createWebview'],
20-
initial: 'Initial',
21-
states: {
22-
Initial: {
23-
on: {
24-
WEBVIEW_INITIALIZED: 'Startup'
25-
}
26-
},
27-
Startup: {
28-
onEntry: ['newOrContinue'],
29-
on: {
30-
CONTINUE: 'ContinueTutorial',
31-
NEW: 'NewTutorial',
32-
},
33-
},
34-
NewTutorial: {
35-
initial: 'SelectTutorial',
36-
states: {
37-
SelectTutorial: {
38-
on: {
39-
TUTORIAL_START: 'InitializeTutorial',
40-
},
41-
},
42-
InitializeTutorial: {
43-
onEntry: ['tutorialLaunch'],
44-
on: {
45-
TUTORIAL_LOADED: '#tutorial'
46-
}
47-
},
48-
}
49-
},
50-
ContinueTutorial: {
51-
onEntry: ['tutorialContinue'],
52-
on: {
53-
TUTORIAL_START: '#tutorial-load-current'
54-
}
55-
},
56-
}
11+
id: 'root',
12+
context: initialContext,
13+
initial: 'SelectTutorial',
14+
states: {
15+
SelectTutorial: {
16+
onEntry: ['createWebview'],
17+
initial: 'Initial',
18+
states: {
19+
Initial: {
20+
on: {
21+
WEBVIEW_INITIALIZED: 'Startup',
22+
},
23+
},
24+
Startup: {
25+
onEntry: ['newOrContinue'],
26+
on: {
27+
CONTINUE: 'ContinueTutorial',
28+
NEW: 'NewTutorial',
29+
},
30+
},
31+
NewTutorial: {
32+
initial: 'SelectTutorial',
33+
states: {
34+
SelectTutorial: {
35+
on: {
36+
TUTORIAL_START: 'InitializeTutorial',
37+
},
38+
},
39+
InitializeTutorial: {
40+
onEntry: ['tutorialLaunch'],
41+
on: {
42+
TUTORIAL_LOADED: '#tutorial',
43+
},
44+
},
45+
},
46+
},
47+
ContinueTutorial: {
48+
onEntry: ['tutorialContinue'],
49+
on: {
50+
TUTORIAL_START: '#tutorial-load-current',
51+
},
52+
},
53+
},
54+
},
55+
Tutorial: {
56+
id: 'tutorial',
57+
initial: 'Initialize',
58+
onEntry: ['tutorialSetup'],
59+
states: {
60+
Initialize: {
61+
onEntry: ['initializeNewTutorial'],
62+
after: {
63+
0: 'Summary',
64+
},
65+
},
66+
LoadCurrent: {
67+
id: 'tutorial-load-current',
68+
// TODO: verify current is not complete
69+
after: {
70+
0: 'Stage',
71+
},
72+
},
73+
LoadNext: {
74+
id: 'tutorial-load-next',
75+
after: {
76+
0: [
77+
{
78+
target: 'Stage',
79+
cond: 'hasNextStage',
80+
},
81+
{
82+
target: 'Level',
83+
cond: 'hasNextLevel',
84+
},
85+
{
86+
target: '#end-tutorial',
87+
},
88+
],
89+
},
5790
},
58-
Tutorial: {
59-
id: 'tutorial',
60-
initial: 'Initialize',
61-
onEntry: ['tutorialSetup'],
62-
states: {
63-
Initialize: {
64-
onEntry: ['initializeNewTutorial'],
65-
after: {
66-
0: 'Summary'
67-
}
68-
},
69-
LoadCurrent: {
70-
id: 'tutorial-load-current',
71-
// TODO: verify current is not complete
72-
after: {
73-
0: 'Stage'
74-
},
75-
},
76-
LoadNext: {
77-
id: 'tutorial-load-next',
78-
after: {
79-
0: [
80-
{
81-
target: 'Stage',
82-
cond: 'hasNextStage',
83-
},
84-
{
85-
target: 'Level',
86-
cond: 'hasNextLevel'
87-
},
88-
{
89-
target: '#end-tutorial'
90-
}
91-
],
92-
},
93-
},
94-
95-
Summary: {
96-
on: {
97-
NEXT: 'Level',
98-
},
99-
},
100-
Level: {
101-
onEntry: ['loadLevel'],
102-
on: {
103-
NEXT: 'Stage',
104-
BACK: 'Summary',
105-
},
106-
},
107-
Stage: {
108-
onEntry: ['loadStage'],
109-
initial: 'Normal',
110-
states: {
111-
Normal: {
112-
on: {
113-
TEST_RUN: 'TestRunning',
114-
STEP_SOLUTION_LOAD: {
115-
actions: ['callSolution'],
116-
},
117-
},
118-
},
119-
TestRunning: {
120-
onEntry: ['testStart'],
121-
on: {
122-
TEST_PASS: 'TestPass',
123-
TEST_FAIL: 'TestFail',
124-
},
125-
},
126-
TestPass: {
127-
onEntry: ['testPass', 'progressUpdate'],
128-
onExit: ['stepLoadNext'],
129-
after: {
130-
1000: 'StepNext',
131-
},
13291

133-
},
134-
TestFail: {
135-
onEntry: ['testFail'],
136-
after: {
137-
0: 'Normal'
138-
},
139-
},
140-
StepNext: {
141-
after: {
142-
0: [{
143-
target: 'Normal',
144-
cond: 'hasNextStep',
145-
actions: ['stepLoadCommits']
146-
}, {
147-
target: 'StageComplete'
148-
}]
149-
},
150-
},
151-
StageComplete: {
152-
on: {
153-
STAGE_NEXT: '#tutorial-load-next',
154-
},
155-
},
156-
},
157-
},
158-
EndTutorial: {
159-
id: 'end-tutorial',
160-
type: 'final'
92+
Summary: {
93+
on: {
94+
NEXT: 'Level',
95+
},
96+
},
97+
Level: {
98+
onEntry: ['loadLevel'],
99+
on: {
100+
NEXT: 'Stage',
101+
BACK: 'Summary',
102+
},
103+
},
104+
Stage: {
105+
onEntry: ['loadStage'],
106+
initial: 'Normal',
107+
states: {
108+
Normal: {
109+
on: {
110+
TEST_RUN: 'TestRunning',
111+
STEP_SOLUTION_LOAD: {
112+
actions: ['callSolution'],
161113
},
162-
}
163-
}
164-
}
114+
},
115+
},
116+
TestRunning: {
117+
onEntry: ['testStart'],
118+
on: {
119+
TEST_PASS: 'TestPass',
120+
TEST_FAIL: 'TestFail',
121+
},
122+
},
123+
TestPass: {
124+
onEntry: ['testPass', 'progressUpdate'],
125+
onExit: ['stepLoadNext'],
126+
after: {
127+
1000: 'StepNext',
128+
},
129+
},
130+
TestFail: {
131+
onEntry: ['testFail'],
132+
after: {
133+
0: 'Normal',
134+
},
135+
},
136+
StepNext: {
137+
after: {
138+
0: [
139+
{
140+
target: 'Normal',
141+
cond: 'hasNextStep',
142+
actions: ['stepLoadCommits'],
143+
},
144+
{
145+
target: 'StageComplete',
146+
},
147+
],
148+
},
149+
},
150+
StageComplete: {
151+
on: {
152+
STAGE_NEXT: '#tutorial-load-next',
153+
},
154+
},
155+
},
156+
},
157+
EndTutorial: {
158+
id: 'end-tutorial',
159+
type: 'final',
160+
},
161+
},
162+
},
163+
},
165164
},
166165
{
167-
actions: createActions(dispatch),
168-
guards,
169-
activities: {},
166+
actions: createActions(dispatch),
167+
guards,
168+
activities: {},
170169
},
171-
)
170+
)
172171

173-
export default machine
172+
export default machine

‎src/test/extension.test.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ import * as assert from 'assert'
1212
// import * as myExtension from '../extension'
1313

1414
// Defines a Mocha test suite to group tests of similar kind together
15-
suite("Extension Tests", function () {
16-
17-
// Defines a Mocha unit test
18-
test("Something 1", function () {
19-
assert.equal(-1, [1, 2, 3].indexOf(5))
20-
assert.equal(-1, [1, 2, 3].indexOf(0))
21-
})
22-
})
15+
suite('Extension Tests', function() {
16+
// Defines a Mocha unit test
17+
test('Something 1', function() {
18+
assert.equal(-1, [1, 2, 3].indexOf(5))
19+
assert.equal(-1, [1, 2, 3].indexOf(0))
20+
})
21+
})

‎src/test/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import * as testRunner from 'vscode/lib/testrunner'
1616
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options
1717
// for more info
1818
testRunner.configure({
19-
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
20-
useColors: true // colored output from test results
19+
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
20+
useColors: true, // colored output from test results
2121
})
2222

23-
module.exports = testRunner
23+
module.exports = testRunner

‎typings/context.d.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import * as CR from './index'
22

33
export interface Step extends Exclude<CR.TutorialStep, 'actions'> {
4-
status: {
5-
complete: boolean
6-
active: boolean
7-
}
4+
status: {
5+
complete: boolean
6+
active: boolean
7+
}
88
}
99

1010
export interface ReceivedEvent {
11-
data: CR.Action
11+
data: CR.Action
1212
}
1313

1414
export interface StageStepStatus {
15-
active: boolean
16-
complete: boolean
15+
active: boolean
16+
complete: boolean
1717
}
1818

1919
export interface StageWithStatus extends CR.TutorialStage {
20-
status: StageStepStatus
20+
status: StageStepStatus
2121
}

‎typings/index.d.ts

Lines changed: 117 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,176 @@
1-
import { send } from "xstate";
1+
import { send } from 'xstate'
22

33
export interface TutorialLevel {
4-
stageList: string[]
5-
content: {
6-
title: string
7-
text: string
8-
}
4+
stageList: string[]
5+
content: {
6+
title: string
7+
text: string
8+
}
99
}
1010

1111
export interface TutorialStage {
12-
stepList: string[]
13-
content: {
14-
title: string
15-
text: string
16-
}
12+
stepList: string[]
13+
content: {
14+
title: string
15+
text: string
16+
}
1717
}
1818

1919
export interface TutorialHint {
20-
text: string
21-
displayed?: boolean
20+
text: string
21+
displayed?: boolean
2222
}
2323

2424
export interface TutorialAction {
25-
commits: string[]
26-
commands?: string[]
27-
files?: string[]
25+
commits: string[]
26+
commands?: string[]
27+
files?: string[]
2828
}
2929

3030
export interface TutorialStepContent {
31-
text: string
32-
title?: string
31+
text: string
32+
title?: string
3333
}
3434

3535
export interface TutorialStep {
36-
content: TutorialStepContent
37-
actions: {
38-
setup: TutorialAction
39-
solution: TutorialAction
40-
}
41-
hints?: TutorialHint[]
36+
content: TutorialStepContent
37+
actions: {
38+
setup: TutorialAction
39+
solution: TutorialAction
40+
}
41+
hints?: TutorialHint[]
4242
}
4343

4444
export interface TutorialData {
45-
summary: TutorialSummary
46-
levels: {
47-
[levelId: string]: TutorialLevel
48-
}
49-
stages: {
50-
[stageId: string]: TutorialStage
51-
}
52-
steps: {
53-
[stepId: string]: TutorialStep
54-
}
45+
summary: TutorialSummary
46+
levels: {
47+
[levelId: string]: TutorialLevel
48+
}
49+
stages: {
50+
[stageId: string]: TutorialStage
51+
}
52+
steps: {
53+
[stepId: string]: TutorialStep
54+
}
5555
}
5656

5757
export interface TutorialMeta {
58-
version: string
59-
repo: string
60-
createdBy: string
61-
createdAt: string
62-
updatedBy: string
63-
updatedAt: string
64-
contributors: string[]
65-
languages: string[]
66-
testRunner: string
58+
version: string
59+
repo: string
60+
createdBy: string
61+
createdAt: string
62+
updatedBy: string
63+
updatedAt: string
64+
contributors: string[]
65+
languages: string[]
66+
testRunner: string
6767
}
6868

6969
export interface TutorialSummary {
70-
title: string
71-
description: string
72-
levelList: string[]
70+
title: string
71+
description: string
72+
levelList: string[]
7373
}
7474

7575
export interface Tutorial {
76-
id: string
77-
meta: TutorialMeta
78-
data: TutorialData
76+
id: string
77+
meta: TutorialMeta
78+
data: TutorialData
7979
}
8080

8181
export interface Progress {
82-
levels: {
83-
[levelId: string]: boolean
84-
}
85-
stages: {
86-
[stageId: string]: boolean
87-
}
88-
steps: {
89-
[stepId: string]: boolean
90-
}
91-
complete: boolean
82+
levels: {
83+
[levelId: string]: boolean
84+
}
85+
stages: {
86+
[stageId: string]: boolean
87+
}
88+
steps: {
89+
[stepId: string]: boolean
90+
}
91+
complete: boolean
9292
}
9393

9494
// current tutorial position
9595
export interface Position {
96-
levelId: string
97-
stageId: string
98-
stepId: string
99-
complete?: boolean
96+
levelId: string
97+
stageId: string
98+
stepId: string
99+
complete?: boolean
100100
}
101101

102102
// current tutorial state
103103

104104
export interface Action {
105-
type: string
106-
payload?: any
107-
meta?: any
105+
type: string
106+
payload?: any
107+
meta?: any
108108
}
109109

110110
export interface MachineContext {
111-
position: Position
112-
data: {
113-
summary: TutorialSummary
114-
levels: {
115-
[levelId: string]: TutorialLevel
116-
}
117-
stages: {
118-
[stageId: string]: TutorialStage
119-
}
120-
steps: {
121-
[stepId: string]: TutorialStep
122-
}
111+
position: Position
112+
data: {
113+
summary: TutorialSummary
114+
levels: {
115+
[levelId: string]: TutorialLevel
116+
}
117+
stages: {
118+
[stageId: string]: TutorialStage
119+
}
120+
steps: {
121+
[stepId: string]: TutorialStep
123122
}
124-
progress: Progress
123+
}
124+
progress: Progress
125125
}
126126

127127
export interface MachineEvent {
128-
type: string
129-
payload?: any
128+
type: string
129+
payload?: any
130130
}
131131

132132
export interface MachineStateSchema {
133-
states: {
134-
SelectTutorial: {
135-
states: {
136-
Initial: {}
137-
Startup: {}
138-
NewTutorial: {
139-
states: {
140-
SelectTutorial: {}
141-
InitializeTutorial: {}
142-
}
143-
}
144-
ContinueTutorial: {}
145-
}
133+
states: {
134+
SelectTutorial: {
135+
states: {
136+
Initial: {}
137+
Startup: {}
138+
NewTutorial: {
139+
states: {
140+
SelectTutorial: {}
141+
InitializeTutorial: {}
142+
}
146143
}
147-
Tutorial: {
148-
states: {
149-
Initialize: {}
150-
Summary: {}
151-
LoadCurrent: {}
152-
LoadNext: {}
153-
Level: {}
154-
Stage: {
155-
states: {
156-
Normal: {}
157-
TestRunning: {}
158-
TestPass: {}
159-
TestFail: {}
160-
StepNext: {}
161-
StageComplete: {}
162-
}
163-
}
164-
EndTutorial: {}
165-
}
144+
ContinueTutorial: {}
145+
}
146+
}
147+
Tutorial: {
148+
states: {
149+
Initialize: {}
150+
Summary: {}
151+
LoadCurrent: {}
152+
LoadNext: {}
153+
Level: {}
154+
Stage: {
155+
states: {
156+
Normal: {}
157+
TestRunning: {}
158+
TestPass: {}
159+
TestFail: {}
160+
StepNext: {}
161+
StageComplete: {}
162+
}
166163
}
164+
EndTutorial: {}
165+
}
167166
}
167+
}
168168
}
169169

170170
export interface StateMachine {
171-
activate(): void
172-
deactivate(): void
173-
send(action: string | Action): void
171+
activate(): void
172+
deactivate(): void
173+
send(action: string | Action): void
174174
}
175175

176-
export type EditorDispatch = (type: string, payload?: any) => void
176+
export type EditorDispatch = (type: string, payload?: any) => void

‎web-app/.storybook/addons.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import '@storybook/addon-actions/register'
22
import '@storybook/addon-links/register'
3-
import '@storybook/addon-knobs/register'
3+
import '@storybook/addon-knobs/register'

‎web-app/.storybook/config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { configure } from '@storybook/react';
2-
import '@alifd/next/dist/next.css';
1+
import { configure } from '@storybook/react'
2+
import '@alifd/next/dist/next.css'
33

44
// automatically import all files ending in *.stories.tsx
5-
const req = require.context('../stories', true, /\.stories\.tsx$/);
5+
const req = require.context('../stories', true, /\.stories\.tsx$/)
66

77
function loadStories() {
8-
req.keys().forEach(req);
8+
req.keys().forEach(req)
99
}
1010

11-
configure(loadStories, module);
11+
configure(loadStories, module)

‎web-app/src/components/Cond/index.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import * as React from 'react'
22
import { stateMatch } from './utils/state'
33

44
interface Props {
5-
state: any
6-
path: string
7-
children: React.ReactElement
5+
state: any
6+
path: string
7+
children: React.ReactElement
88
}
99

1010
const Cond = (props: Props) => {
11-
if (!stateMatch(props.state, props.path)) {
12-
return null
13-
}
14-
return props.children
11+
if (!stateMatch(props.state, props.path)) {
12+
return null
13+
}
14+
return props.children
1515
}
1616

1717
export default Cond
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
export function stateMatch(state: any, statePath: string) {
2-
let current = state
3-
let paths = statePath.split('.')
4-
let complete = false
5-
try {
6-
for (const p of paths) {
7-
if (p === current && !complete) {
8-
// handle strings
9-
complete = true
10-
} else {
11-
// handle objects
12-
current = current[p]
13-
}
14-
}
15-
} catch (error) {
16-
return false
2+
let current = state
3+
let paths = statePath.split('.')
4+
let complete = false
5+
try {
6+
for (const p of paths) {
7+
if (p === current && !complete) {
8+
// handle strings
9+
complete = true
10+
} else {
11+
// handle objects
12+
current = current[p]
13+
}
1714
}
18-
return current !== undefined
19-
}
15+
} catch (error) {
16+
return false
17+
}
18+
return current !== undefined
19+
}

‎web-app/src/components/Debugger/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react'
22

33
interface Props {
4-
value: any
4+
value: any
55
}
66

77
const Debugger = ({ value }: Props) => (
@@ -13,4 +13,4 @@ const Debugger = ({ value }: Props) => (
1313
</div>
1414
)
1515

16-
export default Debugger
16+
export default Debugger

‎web-app/src/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React from 'react';
2-
import ReactDOM from 'react-dom';
3-
import App from './App';
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
import App from './App'
44

5-
import './styles/index.css';
5+
import './styles/index.css'
66

77
ReactDOM.render(<App />, document.getElementById('root') as HTMLElement)

‎web-app/src/utils/vscode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ declare var acquireVsCodeApi: any
55
const vscode = acquireVsCodeApi()
66

77
export function send(event: string | Action) {
8-
return vscode.postMessage(event)
8+
return vscode.postMessage(event)
99
}

‎web-app/stories/Continue.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react'
33
import { storiesOf } from '@storybook/react'
44
import { action } from '@storybook/addon-actions'
55

6-
import { ContinuePage } from '../src/containers/Continue'
6+
import { ContinuePage } from '../src/containers/Continue'
77
import demo from './data/basic'
88

99
storiesOf('Continue', module).add('Page', () => <ContinuePage tutorials={[demo]} onContinue={action('onContinue')} />)

0 commit comments

Comments
 (0)
Please sign in to comment.